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