klee
CachingSolver.cpp
Go to the documentation of this file.
1//===-- CachingSolver.cpp - Caching expression solver ---------------------===//
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
11#include "klee/Solver/Solver.h"
12
14#include "klee/Expr/Expr.h"
18
19#include <unordered_map>
20
21using namespace klee;
22
23class CachingSolver : public SolverImpl {
24private:
26 bool &negationUsed);
27
28 void cacheInsert(const Query& query,
30
31 bool cacheLookup(const Query& query,
33
34 struct CacheEntry {
36 : constraints(c), query(q) {}
37
40
43
44 bool operator==(const CacheEntry &b) const {
45 return constraints==b.constraints && *query.get()==*b.query.get();
46 }
47 };
48
50 unsigned operator()(const CacheEntry &ce) const {
51 unsigned result = ce.query->hash();
52
53 for (auto const &constraint : ce.constraints) {
54 result ^= constraint->hash();
55 }
56
57 return result;
58 }
59 };
60
61 typedef std::unordered_map<CacheEntry, IncompleteSolver::PartialValidity,
62 CacheEntryHash>
64
67
68public:
70 ~CachingSolver() { cache.clear(); delete solver; }
71
72 bool computeValidity(const Query&, Solver::Validity &result);
73 bool computeTruth(const Query&, bool &isValid);
74 bool computeValue(const Query& query, ref<Expr> &result) {
76 return solver->impl->computeValue(query, result);
77 }
78 bool computeInitialValues(const Query& query,
79 const std::vector<const Array*> &objects,
80 std::vector< std::vector<unsigned char> > &values,
81 bool &hasSolution) {
83 return solver->impl->computeInitialValues(query, objects, values,
84 hasSolution);
85 }
87 char *getConstraintLog(const Query&);
88 void setCoreSolverTimeout(time::Span timeout);
89};
90
95 bool &negationUsed) {
96 ref<Expr> negatedQuery = Expr::createIsZero(originalQuery);
97
98 // select the "smaller" query to the be canonical representation
99 if (originalQuery.compare(negatedQuery) < 0) {
100 negationUsed = false;
101 return originalQuery;
102 } else {
103 negationUsed = true;
104 return negatedQuery;
105 }
106}
107
112 bool negationUsed;
113 ref<Expr> canonicalQuery = canonicalizeQuery(query.expr, negationUsed);
114
115 CacheEntry ce(query.constraints, canonicalQuery);
116 cache_map::iterator it = cache.find(ce);
117
118 if (it != cache.end()) {
119 result = (negationUsed ?
120 IncompleteSolver::negatePartialValidity(it->second) :
121 it->second);
122 return true;
123 }
124
125 return false;
126}
127
131 bool negationUsed;
132 ref<Expr> canonicalQuery = canonicalizeQuery(query.expr, negationUsed);
133
134 CacheEntry ce(query.constraints, canonicalQuery);
136 (negationUsed ? IncompleteSolver::negatePartialValidity(result) : result);
137
138 cache.insert(std::make_pair(ce, cachedResult));
139}
140
142 Solver::Validity &result) {
144 bool tmp, cacheHit = cacheLookup(query, cachedResult);
145
146 if (cacheHit) {
147 switch(cachedResult) {
148 case IncompleteSolver::MustBeTrue:
149 result = Solver::True;
151 return true;
152 case IncompleteSolver::MustBeFalse:
153 result = Solver::False;
155 return true;
156 case IncompleteSolver::TrueOrFalse:
157 result = Solver::Unknown;
159 return true;
160 case IncompleteSolver::MayBeTrue: {
162 if (!solver->impl->computeTruth(query, tmp))
163 return false;
164 if (tmp) {
165 cacheInsert(query, IncompleteSolver::MustBeTrue);
166 result = Solver::True;
167 return true;
168 } else {
169 cacheInsert(query, IncompleteSolver::TrueOrFalse);
170 result = Solver::Unknown;
171 return true;
172 }
173 }
174 case IncompleteSolver::MayBeFalse: {
176 if (!solver->impl->computeTruth(query.negateExpr(), tmp))
177 return false;
178 if (tmp) {
179 cacheInsert(query, IncompleteSolver::MustBeFalse);
180 result = Solver::False;
181 return true;
182 } else {
183 cacheInsert(query, IncompleteSolver::TrueOrFalse);
184 result = Solver::Unknown;
185 return true;
186 }
187 }
188 default: assert(0 && "unreachable");
189 }
190 }
191
193
194 if (!solver->impl->computeValidity(query, result))
195 return false;
196
197 switch (result) {
198 case Solver::True:
199 cachedResult = IncompleteSolver::MustBeTrue; break;
200 case Solver::False:
201 cachedResult = IncompleteSolver::MustBeFalse; break;
202 default:
203 cachedResult = IncompleteSolver::TrueOrFalse; break;
204 }
205
206 cacheInsert(query, cachedResult);
207 return true;
208}
209
211 bool &isValid) {
213 bool cacheHit = cacheLookup(query, cachedResult);
214
215 // a cached result of MayBeTrue forces us to check whether
216 // a False assignment exists.
217 if (cacheHit && cachedResult != IncompleteSolver::MayBeTrue) {
219 isValid = (cachedResult == IncompleteSolver::MustBeTrue);
220 return true;
221 }
222
224
225 // cache miss: query solver
226 if (!solver->impl->computeTruth(query, isValid))
227 return false;
228
229 if (isValid) {
230 cachedResult = IncompleteSolver::MustBeTrue;
231 } else if (cacheHit) {
232 // We know a true assignment exists, and query isn't valid, so
233 // must be TrueOrFalse.
234 assert(cachedResult == IncompleteSolver::MayBeTrue);
235 cachedResult = IncompleteSolver::TrueOrFalse;
236 } else {
237 cachedResult = IncompleteSolver::MayBeFalse;
238 }
239
240 cacheInsert(query, cachedResult);
241 return true;
242}
243
246}
247
249 return solver->impl->getConstraintLog(query);
250}
251
254}
255
257
259 return new Solver(new CachingSolver(_solver));
260}
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)
CachingSolver(Solver *s)
ref< Expr > canonicalizeQuery(ref< Expr > originalQuery, bool &negationUsed)
cache_map cache
std::unordered_map< CacheEntry, IncompleteSolver::PartialValidity, CacheEntryHash > cache_map
bool computeValidity(const Query &, Solver::Validity &result)
void setCoreSolverTimeout(time::Span timeout)
bool cacheLookup(const Query &query, IncompleteSolver::PartialValidity &result)
bool computeValue(const Query &query, ref< Expr > &result)
void cacheInsert(const Query &query, IncompleteSolver::PartialValidity result)
Inserts the given query, result pair into the cache.
char * getConstraintLog(const Query &)
virtual unsigned hash() const
Returns the pre-computed hash of the current expression.
Definition: Expr.h:222
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
int compare(const ref &rhs) const
Definition: Ref.h:217
T * get() const
Definition: Ref.h:129
Statistic queryCacheHits
Statistic queryCacheMisses
Definition: main.cpp:291
Solver * createCachingSolver(Solver *s)
unsigned operator()(const CacheEntry &ce) const
bool operator==(const CacheEntry &b) const
CacheEntry(const ConstraintSet &c, ref< Expr > q)
CacheEntry(const CacheEntry &ce)
ref< Expr > expr
Definition: Solver.h:35
const ConstraintSet & constraints
Definition: Solver.h:34
Query negateExpr() const
negateExpr - Return a copy of the query with the expression negated.
Definition: Solver.h:52