1 //
2 // Copyright (c) 2015-2017 Benjamin Kaufmann
3 //
4 // This file is part of Potassco.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to
8 // deal in the Software without restriction, including without limitation the
9 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 // sell copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 // IN THE SOFTWARE.
23 //
24 #include <potassco/theory_data.h>
25 #include <memory>
26 #include <stdexcept>
27 #include <algorithm>
28 #include <cstring>
29 namespace Potassco {
30 template <class T>
nBytes(const IdSpan & ids)31 static std::size_t nBytes(const IdSpan& ids) {
32 	return sizeof(T) + (ids.size * sizeof(Id_t));
33 }
34 struct FuncData {
35 	static FuncData* newFunc(int32_t base, const IdSpan& args);
36 	static void destroy(FuncData*);
37 	int32_t  base;
38 	uint32_t size;
39 POTASSCO_WARNING_BEGIN_RELAXED
40 	Id_t     args[0];
41 POTASSCO_WARNING_END_RELAXED
42 };
newFunc(int32_t base,const IdSpan & args)43 FuncData* FuncData::newFunc(int32_t base, const IdSpan& args) {
44 	std::size_t nb = nBytes<FuncData>(args);
45 	FuncData* f = new (::operator new(nb)) FuncData;
46 	f->base = base;
47 	f->size = static_cast<uint32_t>(Potassco::size(args));
48 	std::memcpy(f->args, begin(args), f->size * sizeof(Id_t));
49 	return f;
50 }
destroy(FuncData * f)51 void FuncData::destroy(FuncData* f) {
52 	if (f) { f->~FuncData(); ::operator delete(f); }
53 }
54 const uint64_t nulTerm  = static_cast<uint64_t>(-1);
55 const uint64_t typeMask = static_cast<uint64_t>(3);
56 
TheoryTerm()57 TheoryTerm::TheoryTerm() : data_(nulTerm) {}
TheoryTerm(int num)58 TheoryTerm::TheoryTerm(int num) {
59 	data_ = (static_cast<uint64_t>(num) << 2) | Theory_t::Number;
60 }
TheoryTerm(const char * sym)61 TheoryTerm::TheoryTerm(const char* sym) {
62 	data_ = (assertPtr(sym) | Theory_t::Symbol);
63 	assert(sym == symbol());
64 }
TheoryTerm(const FuncData * c)65 TheoryTerm::TheoryTerm(const FuncData* c) {
66 	data_ = (assertPtr(c) | Theory_t::Compound);
67 }
assertPtr(const void * p) const68 uint64_t TheoryTerm::assertPtr(const void* p) const {
69 	uint64_t data = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(p));
70 	POTASSCO_REQUIRE((data & 3u) == 0u, "Invalid pointer alignment");
71 	return data;
72 }
assertType(Theory_t t) const73 void TheoryTerm::assertType(Theory_t t) const {
74 	POTASSCO_REQUIRE(type() == t, "Invalid term cast");
75 }
valid() const76 bool TheoryTerm::valid() const { return data_ != nulTerm; }
type() const77 Theory_t TheoryTerm::type() const { POTASSCO_REQUIRE(valid(), "Invalid term"); return static_cast<Theory_t>(data_&typeMask); }
number() const78 int TheoryTerm::number() const {
79 	assertType(Theory_t::Number);
80 	return static_cast<int>(data_ >> 2);
81 }
getPtr() const82 uintptr_t TheoryTerm::getPtr() const {
83 	return static_cast<uintptr_t>(data_ & ~typeMask);
84 }
symbol() const85 const char* TheoryTerm::symbol() const {
86 	assertType(Theory_t::Symbol);
87 	return reinterpret_cast<const char*>(getPtr());
88 }
func() const89 FuncData* TheoryTerm::func() const {
90 	return reinterpret_cast<FuncData*>(getPtr());
91 }
compound() const92 int TheoryTerm::compound() const {
93 	assertType(Theory_t::Compound);
94 	return func()->base;
95 }
isFunction() const96 bool TheoryTerm::isFunction() const { return type() == Theory_t::Compound && func()->base >= 0; }
isTuple() const97 bool TheoryTerm::isTuple()    const { return type() == Theory_t::Compound && func()->base < 0; }
function() const98 Id_t TheoryTerm::function()   const { POTASSCO_REQUIRE(isFunction(), "Term is not a function"); return static_cast<Id_t>(func()->base); }
tuple() const99 Tuple_t TheoryTerm::tuple()   const { POTASSCO_REQUIRE(isTuple(), "Term is not a tuple"); return static_cast<Tuple_t>(func()->base); }
size() const100 uint32_t TheoryTerm::size()   const { return type() == Theory_t::Compound ? func()->size : 0; }
begin() const101 TheoryTerm::iterator TheoryTerm::begin() const { return type() == Theory_t::Compound ? func()->args : 0; }
end() const102 TheoryTerm::iterator TheoryTerm::end()   const { return type() == Theory_t::Compound ? func()->args + func()->size : 0; }
103 
TheoryElement(const IdSpan & terms,Id_t c)104 TheoryElement::TheoryElement(const IdSpan& terms, Id_t c) : nTerms_(static_cast<uint32_t>(Potassco::size(terms))), nCond_(c != 0) {
105 	std::memcpy(term_, Potassco::begin(terms), nTerms_ * sizeof(Id_t));
106 	if (nCond_ != 0) { term_[nTerms_] = c; }
107 }
newElement(const IdSpan & terms,Id_t c)108 TheoryElement* TheoryElement::newElement(const IdSpan& terms, Id_t c) {
109 	std::size_t nb = nBytes<TheoryElement>(terms);
110 	if (c != 0) { nb += sizeof(Id_t); }
111 	return new (::operator new(nb)) TheoryElement(terms, c);
112 }
destroy(TheoryElement * e)113 void TheoryElement::destroy(TheoryElement* e) {
114 	if (e) {
115 		e->~TheoryElement();
116 		::operator delete(e);
117 	}
118 }
condition() const119 Id_t TheoryElement::condition() const {
120 	return nCond_ == 0 ? 0 : term_[nTerms_];
121 }
setCondition(Id_t c)122 void TheoryElement::setCondition(Id_t c) {
123 	term_[nTerms_] = c;
124 }
125 
TheoryAtom(Id_t a,Id_t term,const IdSpan & args,Id_t * op,Id_t * rhs)126 TheoryAtom::TheoryAtom(Id_t a, Id_t term, const IdSpan& args, Id_t* op, Id_t* rhs)
127 	: atom_(a)
128 	, guard_(op != 0)
129 	, termId_(term)
130 	, nTerms_(static_cast<uint32_t>(Potassco::size(args))) {
131 	std::memcpy(term_, Potassco::begin(args), nTerms_ * sizeof(Id_t));
132 	if (op) {
133 		term_[nTerms_] = *op;
134 		term_[nTerms_ + 1] = *rhs;
135 	}
136 }
137 
newAtom(Id_t a,Id_t term,const IdSpan & args)138 TheoryAtom* TheoryAtom::newAtom(Id_t a, Id_t term, const IdSpan& args) {
139 	return new (::operator new(nBytes<TheoryAtom>(args))) TheoryAtom(a, term, args, 0, 0);
140 }
newAtom(Id_t a,Id_t term,const IdSpan & args,Id_t op,Id_t rhs)141 TheoryAtom* TheoryAtom::newAtom(Id_t a, Id_t term, const IdSpan& args, Id_t op, Id_t rhs) {
142 	std::size_t nb = nBytes<TheoryAtom>(args) + (2*sizeof(Id_t));
143 	return new (::operator new(nb)) TheoryAtom(a, term, args, &op, &rhs);
144 }
destroy(TheoryAtom * a)145 void TheoryAtom::destroy(TheoryAtom* a) {
146 	if (a) {
147 		a->~TheoryAtom();
148 		::operator delete(a);
149 	}
150 }
guard() const151 const Id_t* TheoryAtom::guard() const {
152 	return guard_ != 0 ? &term_[nTerms_] : 0;
153 }
rhs() const154 const Id_t* TheoryAtom::rhs() const {
155 	return guard_ != 0 ? &term_[nTerms_ + 1] : 0;
156 }
157 //////////////////////////////////////////////////////////////////////////////////////////////////////
158 // TheoryData
159 //////////////////////////////////////////////////////////////////////////////////////////////////////
160 struct TheoryData::Data {
161 	template <class T>
162 	struct RawStack {
RawStackPotassco::TheoryData::Data::RawStack163 		RawStack() : top(0) {}
pushPotassco::TheoryData::Data::RawStack164 		void push(const T& x = T()) {
165 			mem.grow(top += sizeof(T));
166 			new (mem[top-sizeof(T)])T(x);
167 		}
beginPotassco::TheoryData::Data::RawStack168 		T*       begin() const { return static_cast<T*>(mem.begin()); }
sizePotassco::TheoryData::Data::RawStack169 		uint32_t size()  const { return static_cast<uint32_t>(top / sizeof(T)); }
popPotassco::TheoryData::Data::RawStack170 		void     pop()         { top -= sizeof(T); }
resetPotassco::TheoryData::Data::RawStack171 		void     reset()       { mem.release(); top = 0; }
172 		MemoryRegion mem;
173 		std::size_t  top;
174 	};
175 	RawStack<TheoryAtom*>    atoms;
176 	RawStack<TheoryElement*> elems;
177 	RawStack<TheoryTerm>     terms;
178 	struct Up {
UpPotassco::TheoryData::Data::Up179 		Up() : atom(0), term(0), elem(0) {}
180 		uint32_t atom;
181 		uint32_t term;
182 		uint32_t elem;
183 	} frame;
184 };
185 struct TheoryData::DestroyT {
operator ()Potassco::TheoryData::DestroyT186 	template <class T> void operator()(T* x) const { return T::destroy(x); }
operator ()Potassco::TheoryData::DestroyT187 	void operator()(TheoryTerm& t) const {
188 		if (t.valid()) {
189 			if (t.type() == Theory_t::Compound) {
190 				this->operator()(t.func());
191 			}
192 			else if (t.type() == Theory_t::Symbol) {
193 				delete[] const_cast<char*>(t.symbol());
194 			}
195 		}
196 	}
197 };
TheoryData()198 TheoryData::TheoryData() : data_(new Data()) {}
~TheoryData()199 TheoryData::~TheoryData() {
200 	reset();
201 	delete data_;
202 }
addTerm(Id_t termId,int number)203 const TheoryTerm& TheoryData::addTerm(Id_t termId, int number) {
204 	return setTerm(termId) = TheoryTerm(number);
205 }
addTerm(Id_t termId,const StringSpan & name)206 const TheoryTerm& TheoryData::addTerm(Id_t termId, const StringSpan& name) {
207 	TheoryTerm& t = setTerm(termId);
208 	// Align to 4-bytes to disable false-positives from valgrind
209 	// in subsequent calls to strlen etc.
210 	char* buf = new char[((name.size + 1 + 3)/4) * 4];
211 	*std::copy(Potassco::begin(name), Potassco::end(name), buf) = 0;
212 	return (t = TheoryTerm(buf));
213 }
addTerm(Id_t termId,const char * name)214 const TheoryTerm& TheoryData::addTerm(Id_t termId, const char* name) {
215 	return addTerm(termId, Potassco::toSpan(name, name ? std::strlen(name) : 0));
216 }
addTerm(Id_t termId,Id_t funcId,const IdSpan & args)217 const TheoryTerm& TheoryData::addTerm(Id_t termId, Id_t funcId, const IdSpan& args) {
218 	return setTerm(termId) = TheoryTerm(FuncData::newFunc(static_cast<int32_t>(funcId), args));
219 }
addTerm(Id_t termId,Tuple_t type,const IdSpan & args)220 const TheoryTerm& TheoryData::addTerm(Id_t termId, Tuple_t type, const IdSpan& args) {
221 	return setTerm(termId) = TheoryTerm(FuncData::newFunc(static_cast<int32_t>(type), args));
222 }
removeTerm(Id_t termId)223 void TheoryData::removeTerm(Id_t termId) {
224 	if (hasTerm(termId)) {
225 		DestroyT()(terms()[termId]);
226 		terms()[termId] = Term();
227 	}
228 }
addElement(Id_t id,const IdSpan & terms,Id_t cId)229 const TheoryElement& TheoryData::addElement(Id_t id, const IdSpan& terms, Id_t cId) {
230 	if (!hasElement(id)) {
231 		for (uint32_t i = numElems(); i <= id; ++i) { data_->elems.push(); }
232 	}
233 	else {
234 		POTASSCO_REQUIRE(!isNewElement(id), "Redefinition of theory element '%u'", id);
235 		DestroyT()(elems()[id]);
236 	}
237 	return *(elems()[id] = TheoryElement::newElement(terms, cId));
238 }
239 
addAtom(Id_t atomOrZero,Id_t termId,const IdSpan & elems)240 const TheoryAtom& TheoryData::addAtom(Id_t atomOrZero, Id_t termId, const IdSpan& elems) {
241 	data_->atoms.push();
242 	return *(atoms()[numAtoms()-1] = TheoryAtom::newAtom(atomOrZero, termId, elems));
243 }
addAtom(Id_t atomOrZero,Id_t termId,const IdSpan & elems,Id_t op,Id_t rhs)244 const TheoryAtom& TheoryData::addAtom(Id_t atomOrZero, Id_t termId, const IdSpan& elems, Id_t op, Id_t rhs) {
245 	data_->atoms.push();
246 	return *(atoms()[numAtoms()-1] = TheoryAtom::newAtom(atomOrZero, termId, elems, op, rhs));
247 }
248 
setTerm(Id_t id)249 TheoryTerm& TheoryData::setTerm(Id_t id) {
250 	if (!hasTerm(id)) {
251 		for (uint32_t i = numTerms(); i <= id; ++i) { data_->terms.push(); }
252 	}
253 	else {
254 		POTASSCO_REQUIRE(!isNewTerm(id), "Redefinition of theory term '%u'", id);
255 		removeTerm(id);
256 	}
257 	return terms()[id];
258 }
setCondition(Id_t elementId,Id_t newCond)259 void TheoryData::setCondition(Id_t elementId, Id_t newCond) {
260 	POTASSCO_ASSERT(getElement(elementId).condition() == COND_DEFERRED);
261 	elems()[elementId]->setCondition(newCond);
262 }
263 
reset()264 void TheoryData::reset() {
265 	DestroyT destroy;
266 	std::for_each(terms(), terms() + numTerms(), destroy);
267 	std::for_each(elems(), elems() + numElems(), destroy);
268 	std::for_each(atoms(), atoms() + numAtoms(), destroy);
269 	data_->atoms.reset();
270 	data_->elems.reset();
271 	data_->terms.reset();
272 	data_->frame = Data::Up();
273 }
update()274 void TheoryData::update() {
275 	data_->frame.atom = numAtoms();
276 	data_->frame.term = numTerms();
277 	data_->frame.elem = numElems();
278 }
terms() const279 TheoryTerm* TheoryData::terms() const {
280 	return data_->terms.begin();
281 }
elems() const282 TheoryElement** TheoryData::elems() const {
283 	return data_->elems.begin();
284 }
atoms() const285 TheoryAtom** TheoryData::atoms() const {
286 	return data_->atoms.begin();
287 }
numAtoms() const288 uint32_t TheoryData::numAtoms() const {
289 	return data_->atoms.size();
290 }
numTerms() const291 uint32_t TheoryData::numTerms() const {
292 	return data_->terms.size();
293 }
numElems() const294 uint32_t TheoryData::numElems() const {
295 	return data_->elems.size();
296 }
resizeAtoms(uint32_t newSize)297 void TheoryData::resizeAtoms(uint32_t newSize) {
298 	if (newSize != numAtoms()) {
299 		if (newSize > numAtoms()) { do { data_->atoms.push(); } while (numAtoms() != newSize); }
300 		else                      { do { data_->atoms.pop();  } while (numAtoms() != newSize); }
301 	}
302 }
begin() const303 TheoryData::atom_iterator TheoryData::begin() const {
304 	return atoms();
305 }
currBegin() const306 TheoryData::atom_iterator TheoryData::currBegin() const {
307 	return begin() + data_->frame.atom;
308 }
end() const309 TheoryData::atom_iterator TheoryData::end() const {
310 	return begin() + numAtoms();
311 }
hasTerm(Id_t id) const312 bool TheoryData::hasTerm(Id_t id) const {
313 	return id < numTerms() && terms()[id].valid();
314 }
isNewTerm(Id_t id) const315 bool TheoryData::isNewTerm(Id_t id) const {
316 	return hasTerm(id) && id >= data_->frame.term;
317 }
hasElement(Id_t id) const318 bool TheoryData::hasElement(Id_t id) const {
319 	return id < numElems() && elems()[id] != 0;
320 }
isNewElement(Id_t id) const321 bool TheoryData::isNewElement(Id_t id) const {
322 	return hasElement(id) && id >= data_->frame.elem;
323 }
getTerm(Id_t id) const324 const TheoryTerm& TheoryData::getTerm(Id_t id) const {
325 	POTASSCO_REQUIRE(hasTerm(id), "Unknown term '%u'", unsigned(id));
326 	return terms()[id];
327 }
getElement(Id_t id) const328 const TheoryElement& TheoryData::getElement(Id_t id) const {
329 	POTASSCO_REQUIRE(hasElement(id), "Unknown element '%u'", unsigned(id));
330 	return *elems()[id];
331 }
accept(Visitor & out,VisitMode m) const332 void TheoryData::accept(Visitor& out, VisitMode m) const {
333 	for (atom_iterator aIt = m == visit_current ? currBegin() : begin(), aEnd = end(); aIt != aEnd; ++aIt) {
334 		out.visit(*this, **aIt);
335 	}
336 }
accept(const TheoryTerm & t,Visitor & out,VisitMode m) const337 void TheoryData::accept(const TheoryTerm& t, Visitor& out, VisitMode m) const {
338 	if (t.type() == Theory_t::Compound) {
339 		for (TheoryTerm::iterator it = t.begin(), end = t.end(); it != end; ++it) {
340 			if (doVisitTerm(m, *it)) out.visit(*this, *it, getTerm(*it));
341 		}
342 		if (t.isFunction() && doVisitTerm(m, t.function())) { out.visit(*this, t.function(), getTerm(t.function())); }
343 	}
344 }
accept(const TheoryElement & e,Visitor & out,VisitMode m) const345 void TheoryData::accept(const TheoryElement& e, Visitor& out, VisitMode m) const {
346 	for (TheoryElement::iterator it = e.begin(), end = e.end(); it != end; ++it) {
347 		if (doVisitTerm(m, *it)) { out.visit(*this, *it, getTerm(*it)); }
348 	}
349 }
accept(const TheoryAtom & a,Visitor & out,VisitMode m) const350 void TheoryData::accept(const TheoryAtom& a, Visitor& out, VisitMode m) const {
351 	if (doVisitTerm(m, a.term())) { out.visit(*this, a.term(), getTerm(a.term())); }
352 	for (TheoryElement::iterator eIt = a.begin(), eEnd = a.end(); eIt != eEnd; ++eIt) {
353 		if (doVisitElem(m, *eIt)) { out.visit(*this, *eIt, getElement(*eIt)); }
354 	}
355 	if (a.guard() && doVisitTerm(m, *a.guard())) { out.visit(*this, *a.guard(), getTerm(*a.guard())); }
356 	if (a.rhs() && doVisitTerm(m, *a.rhs()))     { out.visit(*this, *a.rhs(),   getTerm(*a.rhs())); }
357 }
~Visitor()358 TheoryData::Visitor::~Visitor() {}
toSpan(const char * x)359 StringSpan toSpan(const char* x) {
360 	return Potassco::toSpan(x, std::strlen(x));
361 }
362 
363 } // namespace Potassco
364