klee
CexCachingSolver.cpp
Go to the documentation of this file.
1//===-- CexCachingSolver.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#include "klee/Solver/Solver.h"
11
12#include "klee/ADT/MapOfSets.h"
15#include "klee/Expr/Expr.h"
16#include "klee/Expr/ExprUtil.h"
23
24#include "llvm/Support/CommandLine.h"
25
26using namespace klee;
27using namespace llvm;
28
29namespace {
30cl::opt<bool> DebugCexCacheCheckBinding(
31 "debug-cex-cache-check-binding", cl::init(false),
32 cl::desc("Debug the correctness of the counterexample "
33 "cache assignments (default=false)"),
34 cl::cat(SolvingCat));
35
36cl::opt<bool>
37 CexCacheTryAll("cex-cache-try-all", cl::init(false),
38 cl::desc("Try substituting all counterexamples before "
39 "asking the SMT solver (default=false)"),
40 cl::cat(SolvingCat));
41
42cl::opt<bool>
43 CexCacheSuperSet("cex-cache-superset", cl::init(false),
44 cl::desc("Try substituting SAT superset counterexample "
45 "before asking the SMT solver (default=false)"),
46 cl::cat(SolvingCat));
47
48cl::opt<bool> CexCacheExperimental(
49 "cex-cache-exp", cl::init(false),
50 cl::desc("Optimization for validity queries (default=false)"),
51 cl::cat(SolvingCat));
52
53} // namespace
54
56
57typedef std::set< ref<Expr> > KeyType;
58
60 bool operator()(const Assignment *a, const Assignment *b) const {
61 return a->bindings < b->bindings;
62 }
63};
64
65
67 typedef std::set<Assignment*, AssignmentLessThan> assignmentsTable_ty;
68
70
72 // memo table
74
76 Assignment *&result);
77
78 bool lookupAssignment(const Query& query, KeyType &key, Assignment *&result);
79
80 bool lookupAssignment(const Query& query, Assignment *&result) {
81 KeyType key;
82 return lookupAssignment(query, key, result);
83 }
84
85 bool getAssignment(const Query& query, Assignment *&result);
86
87public:
88 CexCachingSolver(Solver *_solver) : solver(_solver) {}
90
91 bool computeTruth(const Query&, bool &isValid);
92 bool computeValidity(const Query&, Solver::Validity &result);
93 bool computeValue(const Query&, ref<Expr> &result);
94 bool computeInitialValues(const Query&,
95 const std::vector<const Array*> &objects,
96 std::vector< std::vector<unsigned char> > &values,
97 bool &hasSolution);
99 char *getConstraintLog(const Query& query);
100 void setCoreSolverTimeout(time::Span timeout);
101};
102
104
106 bool operator()(Assignment *a) const { return !a; }
107};
108
110 bool operator()(Assignment *a) const { return a!=0; }
111};
112
115
117
118 bool operator()(Assignment *a) const {
119 return !a || a->satisfies(key.begin(), key.end());
120 }
121};
122
131 Assignment * const *lookup = cache.lookup(key);
132 if (lookup) {
133 result = *lookup;
134 return true;
135 }
136
137 if (CexCacheTryAll) {
138 // Look for a satisfying assignment for a superset, which is trivially an
139 // assignment for any subset.
140 Assignment **lookup = 0;
141 if (CexCacheSuperSet)
142 lookup = cache.findSuperset(key, NonNullAssignment());
143
144 // Otherwise, look for a subset which is unsatisfiable, see below.
145 if (!lookup)
146 lookup = cache.findSubset(key, NullAssignment());
147
148 // If either lookup succeeded, then we have a cached solution.
149 if (lookup) {
150 result = *lookup;
151 return true;
152 }
153
154 // Otherwise, iterate through the set of current assignments to see if one
155 // of them satisfies the query.
156 for (assignmentsTable_ty::iterator it = assignmentsTable.begin(),
157 ie = assignmentsTable.end(); it != ie; ++it) {
158 Assignment *a = *it;
159 if (a->satisfies(key.begin(), key.end())) {
160 result = a;
161 return true;
162 }
163 }
164 } else {
165 // FIXME: Which order? one is sure to be better.
166
167 // Look for a satisfying assignment for a superset, which is trivially an
168 // assignment for any subset.
169 Assignment **lookup = 0;
170 if (CexCacheSuperSet)
171 lookup = cache.findSuperset(key, NonNullAssignment());
172
173 // Otherwise, look for a subset which is unsatisfiable -- if the subset is
174 // unsatisfiable then no additional constraints can produce a valid
175 // assignment. While searching subsets, we also explicitly the solutions for
176 // satisfiable subsets to see if they solve the current query and return
177 // them if so. This is cheap and frequently succeeds.
178 if (!lookup)
179 lookup = cache.findSubset(key, NullOrSatisfyingAssignment(key));
180
181 // If either lookup succeeded, then we have a cached solution.
182 if (lookup) {
183 result = *lookup;
184 return true;
185 }
186 }
187
188 return false;
189}
190
200 KeyType &key,
201 Assignment *&result) {
202 key = KeyType(query.constraints.begin(), query.constraints.end());
203 ref<Expr> neg = Expr::createIsZero(query.expr);
204 if (ConstantExpr *CE = dyn_cast<ConstantExpr>(neg)) {
205 if (CE->isFalse()) {
206 result = (Assignment*) 0;
208 return true;
209 }
210 } else {
211 key.insert(neg);
212 }
213
214 bool found = searchForAssignment(key, result);
215 if (found)
218
219 return found;
220}
221
223 KeyType key;
224 if (lookupAssignment(query, key, result))
225 return true;
226
227 std::vector<const Array*> objects;
228 findSymbolicObjects(key.begin(), key.end(), objects);
229
230 std::vector< std::vector<unsigned char> > values;
231 bool hasSolution;
232 if (!solver->impl->computeInitialValues(query, objects, values,
233 hasSolution))
234 return false;
235
236 Assignment *binding;
237 if (hasSolution) {
238 binding = new Assignment(objects, values);
239
240 // Memoize the result.
241 std::pair<assignmentsTable_ty::iterator, bool>
242 res = assignmentsTable.insert(binding);
243 if (!res.second) {
244 delete binding;
245 binding = *res.first;
246 }
247
248 if (DebugCexCacheCheckBinding)
249 if (!binding->satisfies(key.begin(), key.end())) {
250 query.dump();
251 binding->dump();
252 klee_error("Generated assignment doesn't match query");
253 }
254 } else {
255 binding = (Assignment*) 0;
256 }
257
258 result = binding;
259 cache.insert(key, binding);
260
261 return true;
262}
263
265
267 cache.clear();
268 delete solver;
269 for (assignmentsTable_ty::iterator it = assignmentsTable.begin(),
270 ie = assignmentsTable.end(); it != ie; ++it)
271 delete *it;
272}
273
275 Solver::Validity &result) {
277 Assignment *a;
278 if (!getAssignment(query.withFalse(), a))
279 return false;
280 assert(a && "computeValidity() must have assignment");
281 ref<Expr> q = a->evaluate(query.expr);
282 assert(isa<ConstantExpr>(q) &&
283 "assignment evaluation did not result in constant");
284
285 if (cast<ConstantExpr>(q)->isTrue()) {
286 if (!getAssignment(query, a))
287 return false;
288 result = !a ? Solver::True : Solver::Unknown;
289 } else {
290 if (!getAssignment(query.negateExpr(), a))
291 return false;
292 result = !a ? Solver::False : Solver::Unknown;
293 }
294
295 return true;
296}
297
299 bool &isValid) {
301
302 // There is a small amount of redundancy here. We only need to know
303 // truth and do not really need to compute an assignment. This means
304 // that we could check the cache to see if we already know that
305 // state ^ query has no assignment. In that case, by the validity of
306 // state, we know that state ^ !query must have an assignment, and
307 // so query cannot be true (valid). This does get hits, but doesn't
308 // really seem to be worth the overhead.
309
310 if (CexCacheExperimental) {
311 Assignment *a;
312 if (lookupAssignment(query.negateExpr(), a) && !a)
313 return false;
314 }
315
316 Assignment *a;
317 if (!getAssignment(query, a))
318 return false;
319
320 isValid = !a;
321
322 return true;
323}
324
326 ref<Expr> &result) {
328
329 Assignment *a;
330 if (!getAssignment(query.withFalse(), a))
331 return false;
332 assert(a && "computeValue() must have assignment");
333 result = a->evaluate(query.expr);
334 assert(isa<ConstantExpr>(result) &&
335 "assignment evaluation did not result in constant");
336 return true;
337}
338
339bool
341 const std::vector<const Array*>
342 &objects,
343 std::vector< std::vector<unsigned char> >
344 &values,
345 bool &hasSolution) {
347 Assignment *a;
348 if (!getAssignment(query, a))
349 return false;
350 hasSolution = !!a;
351
352 if (!a)
353 return true;
354
355 // FIXME: We should use smarter assignment for result so we don't
356 // need redundant copy.
357 values = std::vector< std::vector<unsigned char> >(objects.size());
358 for (unsigned i=0; i < objects.size(); ++i) {
359 const Array *os = objects[i];
360 Assignment::bindings_ty::iterator it = a->bindings.find(os);
361
362 if (it == a->bindings.end()) {
363 values[i] = std::vector<unsigned char>(os->size, 0);
364 } else {
365 values[i] = it->second;
366 }
367 }
368
369 return true;
370}
371
374}
375
377 return solver->impl->getConstraintLog(query);
378}
379
382}
383
385
387 return new Solver(new CexCachingSolver(_solver));
388}
std::set< ref< Expr > > KeyType
CexCachingSolver(Solver *_solver)
SolverRunStatus getOperationStatusCode()
getOperationStatusCode - get the status of the last solver operation
bool computeInitialValues(const Query &, const std::vector< const Array * > &objects, std::vector< std::vector< unsigned char > > &values, bool &hasSolution)
bool computeValue(const Query &, ref< Expr > &result)
bool computeValidity(const Query &, Solver::Validity &result)
assignmentsTable_ty assignmentsTable
bool searchForAssignment(KeyType &key, Assignment *&result)
bool lookupAssignment(const Query &query, Assignment *&result)
MapOfSets< ref< Expr >, Assignment * > cache
bool lookupAssignment(const Query &query, KeyType &key, Assignment *&result)
bool getAssignment(const Query &query, Assignment *&result)
char * getConstraintLog(const Query &query)
std::set< Assignment *, AssignmentLessThan > assignmentsTable_ty
bool computeTruth(const Query &, bool &isValid)
void setCoreSolverTimeout(time::Span timeout)
const unsigned size
Definition: Expr.h:489
bool satisfies(InputIterator begin, InputIterator end)
Definition: Assignment.h:91
ref< Expr > evaluate(const Array *mo, unsigned index) const
Definition: Assignment.h:69
bindings_ty bindings
Definition: Assignment.h:26
constraint_iterator end() const
constraint_iterator begin() const
V * lookup(const std::set< K > &set)
Definition: MapOfSets.h:198
V * findSuperset(const std::set< K > &set, const Predicate &p)
Definition: MapOfSets.h:365
void insert(const std::set< K > &set, const V &value)
Definition: MapOfSets.h:188
V * findSubset(const std::set< K > &set, const Predicate &p)
Definition: MapOfSets.h:371
SolverImpl - Abstract base clase for solver implementations.
Definition: SolverImpl.h:25
virtual void setCoreSolverTimeout(time::Span timeout)
Definition: SolverImpl.h:109
virtual bool computeInitialValues(const Query &query, const std::vector< const Array * > &objects, std::vector< std::vector< unsigned char > > &values, bool &hasSolution)=0
virtual char * getConstraintLog(const Query &query)
Definition: SolverImpl.h:104
virtual SolverRunStatus getOperationStatusCode()=0
getOperationStatusCode - get the status of the last solver operation
SolverImpl * impl
Definition: Solver.h:77
Statistic cexCacheTime
Statistic queryCexCacheMisses
Statistic queryCexCacheHits
Definition: main.cpp:291
void findSymbolicObjects(ref< Expr > e, std::vector< const Array * > &results)
Definition: ExprUtil.cpp:132
Solver * createCexCachingSolver(Solver *s)
void klee_error(const char *msg,...) __attribute__((format(printf
llvm::cl::OptionCategory SolvingCat
bool operator()(const Assignment *a, const Assignment *b) const
bool operator()(Assignment *a) const
bool operator()(Assignment *a) const
bool operator()(Assignment *a) const
NullOrSatisfyingAssignment(KeyType &_key)
ref< Expr > expr
Definition: Solver.h:35
const ConstraintSet & constraints
Definition: Solver.h:34
void dump() const
Dump query.
Definition: Solver.cpp:224
Query withFalse() const
withFalse - Return a copy of the query with a false expression.
Definition: Solver.h:47
Query negateExpr() const
negateExpr - Return a copy of the query with the expression negated.
Definition: Solver.h:52