klee
IndependentSolver.cpp
Go to the documentation of this file.
1//===-- IndependentSolver.cpp ---------------------------------------------===//
2//
3// The KLEE Symbolic Virtual Machine
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#define DEBUG_TYPE "independent-solver"
11#include "klee/Solver/Solver.h"
12
15#include "klee/Expr/Expr.h"
16#include "klee/Expr/ExprUtil.h"
17#include "klee/Support/Debug.h"
19
20#include "llvm/Support/raw_ostream.h"
21
22#include <list>
23#include <map>
24#include <ostream>
25#include <vector>
26
27using namespace klee;
28using namespace llvm;
29
30template<class T>
31class DenseSet {
32 typedef std::set<T> set_ty;
34
35public:
37
38 void add(T x) {
39 s.insert(x);
40 }
41 void add(T start, T end) {
42 for (; start<end; start++)
43 s.insert(start);
44 }
45
46 // returns true iff set is changed by addition
47 bool add(const DenseSet &b) {
48 bool modified = false;
49 for (typename set_ty::const_iterator it = b.s.begin(), ie = b.s.end();
50 it != ie; ++it) {
51 if (modified || !s.count(*it)) {
52 modified = true;
53 s.insert(*it);
54 }
55 }
56 return modified;
57 }
58
59 bool intersects(const DenseSet &b) {
60 for (typename set_ty::iterator it = s.begin(), ie = s.end();
61 it != ie; ++it)
62 if (b.s.count(*it))
63 return true;
64 return false;
65 }
66
67 std::set<unsigned>::iterator begin(){
68 return s.begin();
69 }
70
71 std::set<unsigned>::iterator end(){
72 return s.end();
73 }
74
75 void print(llvm::raw_ostream &os) const {
76 bool first = true;
77 os << "{";
78 for (typename set_ty::iterator it = s.begin(), ie = s.end();
79 it != ie; ++it) {
80 if (first) {
81 first = false;
82 } else {
83 os << ",";
84 }
85 os << *it;
86 }
87 os << "}";
88 }
89};
90
91template <class T>
92inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
93 const ::DenseSet<T> &dis) {
94 dis.print(os);
95 return os;
96}
97
99public:
100 typedef std::map<const Array*, ::DenseSet<unsigned> > elements_ty;
101 elements_ty elements; // Represents individual elements of array accesses (arr[1])
102 std::set<const Array*> wholeObjects; // Represents symbolically accessed arrays (arr[x])
103 std::vector<ref<Expr> > exprs; // All expressions that are associated with this factor
104 // Although order doesn't matter, we use a vector to match
105 // the ConstraintManager constructor that will eventually
106 // be invoked.
107
110 exprs.push_back(e);
111 // Track all reads in the program. Determines whether reads are
112 // concrete or symbolic. If they are symbolic, "collapses" array
113 // by adding it to wholeObjects. Otherwise, creates a mapping of
114 // the form Map<array, set<index>> which tracks which parts of the
115 // array are being accessed.
116 std::vector< ref<ReadExpr> > reads;
117 findReads(e, /* visitUpdates= */ true, reads);
118 for (unsigned i = 0; i != reads.size(); ++i) {
119 ReadExpr *re = reads[i].get();
120 const Array *array = re->updates.root;
121
122 // Reads of a constant array don't alias.
123 if (re->updates.root->isConstantArray() && !re->updates.head)
124 continue;
125
126 if (!wholeObjects.count(array)) {
127 if (ConstantExpr *CE = dyn_cast<ConstantExpr>(re->index)) {
128 // if index constant, then add to set of constraints operating
129 // on that array (actually, don't add constraint, just set index)
130 ::DenseSet<unsigned> &dis = elements[array];
131 dis.add((unsigned) CE->getZExtValue(32));
132 } else {
133 elements_ty::iterator it2 = elements.find(array);
134 if (it2!=elements.end())
135 elements.erase(it2);
136 wholeObjects.insert(array);
137 }
138 }
139 }
140 }
142 elements(ies.elements),
144 exprs(ies.exprs) {}
145
147 elements = ies.elements;
149 exprs = ies.exprs;
150 return *this;
151 }
152
153 void print(llvm::raw_ostream &os) const {
154 os << "{";
155 bool first = true;
156 for (std::set<const Array*>::iterator it = wholeObjects.begin(),
157 ie = wholeObjects.end(); it != ie; ++it) {
158 const Array *array = *it;
159
160 if (first) {
161 first = false;
162 } else {
163 os << ", ";
164 }
165
166 os << "MO" << array->name;
167 }
168 for (elements_ty::const_iterator it = elements.begin(), ie = elements.end();
169 it != ie; ++it) {
170 const Array *array = it->first;
171 const ::DenseSet<unsigned> &dis = it->second;
172
173 if (first) {
174 first = false;
175 } else {
176 os << ", ";
177 }
178
179 os << "MO" << array->name << " : " << dis;
180 }
181 os << "}";
182 }
183
184 // more efficient when this is the smaller set
186 // If there are any symbolic arrays in our query that b accesses
187 for (std::set<const Array*>::iterator it = wholeObjects.begin(),
188 ie = wholeObjects.end(); it != ie; ++it) {
189 const Array *array = *it;
190 if (b.wholeObjects.count(array) ||
191 b.elements.find(array) != b.elements.end())
192 return true;
193 }
194 for (elements_ty::iterator it = elements.begin(), ie = elements.end();
195 it != ie; ++it) {
196 const Array *array = it->first;
197 // if the array we access is symbolic in b
198 if (b.wholeObjects.count(array))
199 return true;
200 elements_ty::const_iterator it2 = b.elements.find(array);
201 // if any of the elements we access are also accessed by b
202 if (it2 != b.elements.end()) {
203 if (it->second.intersects(it2->second))
204 return true;
205 }
206 }
207 return false;
208 }
209
210 // returns true iff set is changed by addition
211 bool add(const IndependentElementSet &b) {
212 for(unsigned i = 0; i < b.exprs.size(); i ++){
213 ref<Expr> expr = b.exprs[i];
214 exprs.push_back(expr);
215 }
216
217 bool modified = false;
218 for (std::set<const Array*>::const_iterator it = b.wholeObjects.begin(),
219 ie = b.wholeObjects.end(); it != ie; ++it) {
220 const Array *array = *it;
221 elements_ty::iterator it2 = elements.find(array);
222 if (it2!=elements.end()) {
223 modified = true;
224 elements.erase(it2);
225 wholeObjects.insert(array);
226 } else {
227 if (!wholeObjects.count(array)) {
228 modified = true;
229 wholeObjects.insert(array);
230 }
231 }
232 }
233 for (elements_ty::const_iterator it = b.elements.begin(),
234 ie = b.elements.end(); it != ie; ++it) {
235 const Array *array = it->first;
236 if (!wholeObjects.count(array)) {
237 elements_ty::iterator it2 = elements.find(array);
238 if (it2==elements.end()) {
239 modified = true;
240 elements.insert(*it);
241 } else {
242 // Now need to see if there are any (z=?)'s
243 if (it2->second.add(it->second))
244 modified = true;
245 }
246 }
247 }
248 return modified;
249 }
250};
251
252inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
253 const IndependentElementSet &ies) {
254 ies.print(os);
255 return os;
256}
257
258// Breaks down a constraint into all of it's individual pieces, returning a
259// list of IndependentElementSets or the independent factors.
260//
261// Caller takes ownership of returned std::list.
262static std::list<IndependentElementSet>*
264 std::list<IndependentElementSet> *factors = new std::list<IndependentElementSet>();
265 ConstantExpr *CE = dyn_cast<ConstantExpr>(query.expr);
266 if (CE) {
267 assert(CE && CE->isFalse() && "the expr should always be false and "
268 "therefore not included in factors");
269 } else {
270 ref<Expr> neg = Expr::createIsZero(query.expr);
271 factors->push_back(IndependentElementSet(neg));
272 }
273
274 for (const auto &constraint : query.constraints) {
275 // iterate through all the previously separated constraints. Until we
276 // actually return, factors is treated as a queue of expressions to be
277 // evaluated. If the queue property isn't maintained, then the exprs
278 // could be returned in an order different from how they came it, negatively
279 // affecting later stages.
280 factors->push_back(IndependentElementSet(constraint));
281 }
282
283 bool doneLoop = false;
284 do {
285 doneLoop = true;
286 std::list<IndependentElementSet> *done =
287 new std::list<IndependentElementSet>;
288 while (factors->size() > 0) {
289 IndependentElementSet current = factors->front();
290 factors->pop_front();
291 // This list represents the set of factors that are separate from current.
292 // Those that are not inserted into this list (queue) intersect with
293 // current.
294 std::list<IndependentElementSet> *keep =
295 new std::list<IndependentElementSet>;
296 while (factors->size() > 0) {
297 IndependentElementSet compare = factors->front();
298 factors->pop_front();
299 if (current.intersects(compare)) {
300 if (current.add(compare)) {
301 // Means that we have added (z=y)added to (x=y)
302 // Now need to see if there are any (z=?)'s
303 doneLoop = false;
304 }
305 } else {
306 keep->push_back(compare);
307 }
308 }
309 done->push_back(current);
310 delete factors;
311 factors = keep;
312 }
313 delete factors;
314 factors = done;
315 } while (!doneLoop);
316
317 return factors;
318}
319
320static
322 std::vector< ref<Expr> > &result) {
323 IndependentElementSet eltsClosure(query.expr);
324 std::vector< std::pair<ref<Expr>, IndependentElementSet> > worklist;
325
326 for (const auto &constraint : query.constraints)
327 worklist.push_back(
328 std::make_pair(constraint, IndependentElementSet(constraint)));
329
330 // XXX This should be more efficient (in terms of low level copy stuff).
331 bool done = false;
332 do {
333 done = true;
334 std::vector< std::pair<ref<Expr>, IndependentElementSet> > newWorklist;
335 for (std::vector< std::pair<ref<Expr>, IndependentElementSet> >::iterator
336 it = worklist.begin(), ie = worklist.end(); it != ie; ++it) {
337 if (it->second.intersects(eltsClosure)) {
338 if (eltsClosure.add(it->second))
339 done = false;
340 result.push_back(it->first);
341 // Means that we have added (z=y)added to (x=y)
342 // Now need to see if there are any (z=?)'s
343 } else {
344 newWorklist.push_back(*it);
345 }
346 }
347 worklist.swap(newWorklist);
348 } while (!done);
349
350 KLEE_DEBUG(
351 std::set< ref<Expr> > reqset(result.begin(), result.end());
352 errs() << "--\n";
353 errs() << "Q: " << query.expr << "\n";
354 errs() << "\telts: " << IndependentElementSet(query.expr) << "\n";
355 int i = 0;
356 for (const auto &constraint: query.constraints) {
357 errs() << "C" << i++ << ": " << constraint;
358 errs() << " " << (reqset.count(constraint) ? "(required)" : "(independent)") << "\n";
359 errs() << "\telts: " << IndependentElementSet(constraint) << "\n";
360 }
361 errs() << "elts closure: " << eltsClosure << "\n";
362 );
363
364
365 return eltsClosure;
366}
367
368
369// Extracts which arrays are referenced from a particular independent set. Examines both
370// the actual known array accesses arr[1] plus the undetermined accesses arr[x].
371static
373 std::vector<const Array *> &returnVector){
374 std::set<const Array*> thisSeen;
375 for(std::map<const Array*, ::DenseSet<unsigned> >::const_iterator it = ie.elements.begin();
376 it != ie.elements.end(); it ++){
377 thisSeen.insert(it->first);
378 }
379 for(std::set<const Array *>::iterator it = ie.wholeObjects.begin();
380 it != ie.wholeObjects.end(); it ++){
381 thisSeen.insert(*it);
382 }
383 for(std::set<const Array *>::iterator it = thisSeen.begin(); it != thisSeen.end();
384 it ++){
385 returnVector.push_back(*it);
386 }
387}
388
390private:
392
393public:
395 : solver(_solver) {}
397
398 bool computeTruth(const Query&, bool &isValid);
399 bool computeValidity(const Query&, Solver::Validity &result);
400 bool computeValue(const Query&, ref<Expr> &result);
401 bool computeInitialValues(const Query& query,
402 const std::vector<const Array*> &objects,
403 std::vector< std::vector<unsigned char> > &values,
404 bool &hasSolution);
406 char *getConstraintLog(const Query&);
407 void setCoreSolverTimeout(time::Span timeout);
408};
409
411 Solver::Validity &result) {
412 std::vector< ref<Expr> > required;
413 IndependentElementSet eltsClosure =
414 getIndependentConstraints(query, required);
415 ConstraintSet tmp(required);
416 return solver->impl->computeValidity(Query(tmp, query.expr),
417 result);
418}
419
420bool IndependentSolver::computeTruth(const Query& query, bool &isValid) {
421 std::vector< ref<Expr> > required;
422 IndependentElementSet eltsClosure =
423 getIndependentConstraints(query, required);
424 ConstraintSet tmp(required);
425 return solver->impl->computeTruth(Query(tmp, query.expr),
426 isValid);
427}
428
430 std::vector< ref<Expr> > required;
431 IndependentElementSet eltsClosure =
432 getIndependentConstraints(query, required);
433 ConstraintSet tmp(required);
434 return solver->impl->computeValue(Query(tmp, query.expr), result);
435}
436
437// Helper function used only for assertions to make sure point created
438// during computeInitialValues is in fact correct. The ``retMap`` is used
439// in the case ``objects`` doesn't contain all the assignments needed.
441 const Query &query, const std::vector<const Array *> &objects,
442 std::vector<std::vector<unsigned char>> &values,
443 std::map<const Array *, std::vector<unsigned char>> &retMap) {
444 // _allowFreeValues is set to true so that if there are missing bytes in the
445 // assigment we will end up with a non ConstantExpr after evaluating the
446 // assignment and fail
447 Assignment assign = Assignment(objects, values, /*_allowFreeValues=*/true);
448
449 // Add any additional bindings.
450 // The semantics of std::map should be to not insert a (key, value)
451 // pair if it already exists so we should continue to use the assignment
452 // from ``objects`` and ``values``.
453 if (retMap.size() > 0)
454 assign.bindings.insert(retMap.begin(), retMap.end());
455
456 for (auto const &constraint : query.constraints) {
457 ref<Expr> ret = assign.evaluate(constraint);
458
459 assert(isa<ConstantExpr>(ret) &&
460 "assignment evaluation did not result in constant");
461 ref<ConstantExpr> evaluatedConstraint = dyn_cast<ConstantExpr>(ret);
462 if (evaluatedConstraint->isFalse()) {
463 return false;
464 }
465 }
466 ref<Expr> neg = Expr::createIsZero(query.expr);
467 ref<Expr> q = assign.evaluate(neg);
468 assert(isa<ConstantExpr>(q) &&
469 "assignment evaluation did not result in constant");
470 return cast<ConstantExpr>(q)->isTrue();
471}
472
474 const std::vector<const Array*> &objects,
475 std::vector< std::vector<unsigned char> > &values,
476 bool &hasSolution){
477 // We assume the query has a solution except proven differently
478 // This is important in case we don't have any constraints but
479 // we need initial values for requested array objects.
480 hasSolution = true;
481 // FIXME: When we switch to C++11 this should be a std::unique_ptr so we don't need
482 // to remember to manually call delete
483 std::list<IndependentElementSet> *factors = getAllIndependentConstraintsSets(query);
484
485 //Used to rearrange all of the answers into the correct order
486 std::map<const Array*, std::vector<unsigned char> > retMap;
487 for (std::list<IndependentElementSet>::iterator it = factors->begin();
488 it != factors->end(); ++it) {
489 std::vector<const Array*> arraysInFactor;
490 calculateArrayReferences(*it, arraysInFactor);
491 // Going to use this as the "fresh" expression for the Query() invocation below
492 assert(it->exprs.size() >= 1 && "No null/empty factors");
493 if (arraysInFactor.size() == 0){
494 continue;
495 }
496 ConstraintSet tmp(it->exprs);
497 std::vector<std::vector<unsigned char> > tempValues;
498 if (!solver->impl->computeInitialValues(Query(tmp, ConstantExpr::alloc(0, Expr::Bool)),
499 arraysInFactor, tempValues, hasSolution)){
500 values.clear();
501 delete factors;
502 return false;
503 } else if (!hasSolution){
504 values.clear();
505 delete factors;
506 return true;
507 } else {
508 assert(tempValues.size() == arraysInFactor.size() &&
509 "Should be equal number arrays and answers");
510 for (unsigned i = 0; i < tempValues.size(); i++){
511 if (retMap.count(arraysInFactor[i])){
512 // We already have an array with some partially correct answers,
513 // so we need to place the answers to the new query into the right
514 // spot while avoiding the undetermined values also in the array
515 std::vector<unsigned char> * tempPtr = &retMap[arraysInFactor[i]];
516 assert(tempPtr->size() == tempValues[i].size() &&
517 "we're talking about the same array here");
518 ::DenseSet<unsigned> * ds = &(it->elements[arraysInFactor[i]]);
519 for (std::set<unsigned>::iterator it2 = ds->begin(); it2 != ds->end(); it2++){
520 unsigned index = * it2;
521 (* tempPtr)[index] = tempValues[i][index];
522 }
523 } else {
524 // Dump all the new values into the array
525 retMap[arraysInFactor[i]] = tempValues[i];
526 }
527 }
528 }
529 }
530 for (std::vector<const Array *>::const_iterator it = objects.begin();
531 it != objects.end(); it++){
532 const Array * arr = * it;
533 if (!retMap.count(arr)){
534 // this means we have an array that is somehow related to the
535 // constraint, but whose values aren't actually required to
536 // satisfy the query.
537 std::vector<unsigned char> ret(arr->size);
538 values.push_back(ret);
539 } else {
540 values.push_back(retMap[arr]);
541 }
542 }
543 assert(assertCreatedPointEvaluatesToTrue(query, objects, values, retMap) && "should satisfy the equation");
544 delete factors;
545 return true;
546}
547
550}
551
553 return solver->impl->getConstraintLog(query);
554}
555
558}
559
561 return new Solver(new IndependentSolver(s));
562}
static IndependentElementSet getIndependentConstraints(const Query &query, std::vector< ref< Expr > > &result)
bool assertCreatedPointEvaluatesToTrue(const Query &query, const std::vector< const Array * > &objects, std::vector< std::vector< unsigned char > > &values, std::map< const Array *, std::vector< unsigned char > > &retMap)
llvm::raw_ostream & operator<<(llvm::raw_ostream &os, const ::DenseSet< T > &dis)
static void calculateArrayReferences(const IndependentElementSet &ie, std::vector< const Array * > &returnVector)
static std::list< IndependentElementSet > * getAllIndependentConstraintsSets(const Query &query)
void print(llvm::raw_ostream &os) const
std::set< unsigned >::iterator begin()
std::set< T > set_ty
bool intersects(const DenseSet &b)
void add(T start, T end)
std::set< unsigned >::iterator end()
void add(T x)
bool add(const DenseSet &b)
IndependentElementSet(ref< Expr > e)
std::set< const Array * > wholeObjects
bool intersects(const IndependentElementSet &b)
IndependentElementSet & operator=(const IndependentElementSet &ies)
IndependentElementSet(const IndependentElementSet &ies)
std::map< const Array *, ::DenseSet< unsigned > > elements_ty
std::vector< ref< Expr > > exprs
bool add(const IndependentElementSet &b)
void print(llvm::raw_ostream &os) const
bool computeValidity(const Query &, Solver::Validity &result)
void setCoreSolverTimeout(time::Span timeout)
bool computeValue(const Query &, ref< Expr > &result)
SolverRunStatus getOperationStatusCode()
getOperationStatusCode - get the status of the last solver operation
bool computeInitialValues(const Query &query, const std::vector< const Array * > &objects, std::vector< std::vector< unsigned char > > &values, bool &hasSolution)
bool computeTruth(const Query &, bool &isValid)
char * getConstraintLog(const Query &)
IndependentSolver(Solver *_solver)
bool isConstantArray() const
Definition: Expr.h:525
const unsigned size
Definition: Expr.h:489
const std::string name
Definition: Expr.h:486
ref< Expr > evaluate(const Array *mo, unsigned index) const
Definition: Assignment.h:69
bindings_ty bindings
Definition: Assignment.h:26
bool isFalse() const
isFalse - Is this the false expression.
Definition: Expr.h:1104
Class representing a one byte read from an array.
Definition: Expr.h:565
ref< Expr > index
Definition: Expr.h:572
UpdateList updates
Definition: Expr.h:571
SolverImpl - Abstract base clase for solver implementations.
Definition: SolverImpl.h:25
virtual void setCoreSolverTimeout(time::Span timeout)
Definition: SolverImpl.h:109
virtual bool computeTruth(const Query &query, bool &isValid)=0
virtual bool computeInitialValues(const Query &query, const std::vector< const Array * > &objects, std::vector< std::vector< unsigned char > > &values, bool &hasSolution)=0
virtual bool computeValue(const Query &query, ref< Expr > &result)=0
virtual char * getConstraintLog(const Query &query)
Definition: SolverImpl.h:104
virtual bool computeValidity(const Query &query, Solver::Validity &result)
Definition: SolverImpl.cpp:17
virtual SolverRunStatus getOperationStatusCode()=0
getOperationStatusCode - get the status of the last solver operation
SolverImpl * impl
Definition: Solver.h:77
ref< UpdateNode > head
pointer to the most recent update node
Definition: Expr.h:546
const Array * root
Definition: Expr.h:543
Definition: main.cpp:291
void findReads(ref< Expr > e, bool visitUpdates, std::vector< ref< ReadExpr > > &result)
Definition: ExprUtil.cpp:19
Solver * createIndependentSolver(Solver *s)
ref< Expr > expr
Definition: Solver.h:35
const ConstraintSet & constraints
Definition: Solver.h:34