CexCachingSolver.cpp

Go to the documentation of this file.
00001 //===-- CexCachingSolver.cpp ----------------------------------------------===//
00002 //
00003 //                     The KLEE Symbolic Virtual Machine
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 
00010 #include "klee/Solver.h"
00011 
00012 #include "klee/Constraints.h"
00013 #include "klee/Expr.h"
00014 #include "klee/SolverImpl.h"
00015 #include "klee/TimerStatIncrementer.h"
00016 #include "klee/util/Assignment.h"
00017 #include "klee/util/ExprUtil.h"
00018 #include "klee/util/ExprVisitor.h"
00019 #include "klee/Internal/ADT/MapOfSets.h"
00020 
00021 #include "SolverStats.h"
00022 
00023 #include "llvm/Support/CommandLine.h"
00024 
00025 using namespace klee;
00026 using namespace llvm;
00027 
00028 namespace {
00029   cl::opt<bool>
00030   DebugCexCacheCheckBinding("debug-cex-cache-check-binding");
00031 
00032   cl::opt<bool>
00033   CexCacheTryAll("cex-cache-try-all",
00034                  cl::desc("try substituting all counterexamples before asking STP"),
00035                  cl::init(false));
00036 
00037   cl::opt<bool>
00038   CexCacheExperimental("cex-cache-exp", cl::init(false));
00039 
00040 }
00041 
00043 
00044 typedef std::set< ref<Expr> > KeyType;
00045 
00046 struct AssignmentLessThan {
00047   bool operator()(const Assignment *a, const Assignment *b) {
00048     return a->bindings < b->bindings;
00049   }
00050 };
00051 
00052 
00053 class CexCachingSolver : public SolverImpl {
00054   typedef std::set<Assignment*, AssignmentLessThan> assignmentsTable_ty;
00055 
00056   Solver *solver;
00057   
00058   MapOfSets<ref<Expr>, Assignment*> cache;
00059   // memo table
00060   assignmentsTable_ty assignmentsTable;
00061 
00062   bool searchForAssignment(KeyType &key, 
00063                            Assignment *&result);
00064   
00065   bool lookupAssignment(const Query& query, Assignment *&result);
00066 
00067   bool getAssignment(const Query& query, Assignment *&result);
00068   
00069 public:
00070   CexCachingSolver(Solver *_solver) : solver(_solver) {}
00071   ~CexCachingSolver();
00072   
00073   bool computeTruth(const Query&, bool &isValid);
00074   bool computeValidity(const Query&, Solver::Validity &result);
00075   bool computeValue(const Query&, ref<Expr> &result);
00076   bool computeInitialValues(const Query&,
00077                             const std::vector<const Array*> &objects,
00078                             std::vector< std::vector<unsigned char> > &values,
00079                             bool &hasSolution);
00080 };
00081 
00083 
00084 struct NullAssignment {
00085   bool operator()(Assignment *a) const { return !a; }
00086 };
00087 
00088 struct NonNullAssignment {
00089   bool operator()(Assignment *a) const { return a!=0; }
00090 };
00091 
00092 struct NullOrSatisfyingAssignment {
00093   KeyType &key;
00094   
00095   NullOrSatisfyingAssignment(KeyType &_key) : key(_key) {}
00096 
00097   bool operator()(Assignment *a) const { 
00098     return !a || a->satisfies(key.begin(), key.end()); 
00099   }
00100 };
00101 
00102 bool CexCachingSolver::searchForAssignment(KeyType &key, Assignment *&result) {
00103   Assignment * const *lookup = cache.lookup(key);
00104   if (lookup) {
00105     result = *lookup;
00106     return true;
00107   }
00108 
00109   if (CexCacheTryAll) {
00110     Assignment **lookup = cache.findSuperset(key, NonNullAssignment());
00111     if (!lookup) lookup = cache.findSubset(key, NullAssignment());
00112     if (lookup) {
00113       result = *lookup;
00114       return true;
00115     }
00116     for (assignmentsTable_ty::iterator it = assignmentsTable.begin(), 
00117            ie = assignmentsTable.end(); it != ie; ++it) {
00118       Assignment *a = *it;
00119       if (a->satisfies(key.begin(), key.end())) {
00120         result = a;
00121         return true;
00122       }
00123     }
00124   } else {
00125     // XXX which order? one is sure to be better
00126     Assignment **lookup = cache.findSuperset(key, NonNullAssignment());
00127     if (!lookup) lookup = cache.findSubset(key, NullOrSatisfyingAssignment(key));
00128     if (lookup) {
00129       result = *lookup;
00130       return true;
00131     }
00132   }
00133   
00134   return false;
00135 }
00136 
00137 bool CexCachingSolver::lookupAssignment(const Query &query, 
00138                                         Assignment *&result) {
00139   KeyType key(query.constraints.begin(), query.constraints.end());
00140   ref<Expr> neg = Expr::createNot(query.expr);
00141   if (ConstantExpr *CE = dyn_cast<ConstantExpr>(neg)) {
00142     if (!CE->getConstantValue()) {
00143       result = (Assignment*) 0;
00144       return true;
00145     }
00146   } else {
00147     key.insert(neg);
00148   }
00149 
00150   return searchForAssignment(key, result);
00151 }
00152 
00153 bool CexCachingSolver::getAssignment(const Query& query, Assignment *&result) {
00154   KeyType key(query.constraints.begin(), query.constraints.end());
00155   ref<Expr> neg = Expr::createNot(query.expr);
00156   if (ConstantExpr *CE = dyn_cast<ConstantExpr>(neg)) {
00157     if (!CE->getConstantValue()) {
00158       result = (Assignment*) 0;
00159       return true;
00160     }
00161   } else {
00162     key.insert(neg);
00163   }
00164 
00165   if (!searchForAssignment(key, result)) {
00166     // need to solve
00167     
00168     std::vector<const Array*> objects;
00169     findSymbolicObjects(key.begin(), key.end(), objects);
00170 
00171     std::vector< std::vector<unsigned char> > values;
00172     bool hasSolution;
00173     if (!solver->impl->computeInitialValues(query, objects, values, 
00174                                             hasSolution))
00175       return false;
00176     
00177     Assignment *binding;
00178     if (hasSolution) {
00179       binding = new Assignment(objects, values);
00180 
00181       // memoization
00182       std::pair<assignmentsTable_ty::iterator, bool>
00183         res = assignmentsTable.insert(binding);
00184       if (!res.second) {
00185         delete binding;
00186         binding = *res.first;
00187       }
00188 
00189       if (DebugCexCacheCheckBinding)
00190         assert(binding->satisfies(key.begin(), key.end()));
00191     } else {
00192       binding = (Assignment*) 0;
00193     }
00194 
00195     result = binding;
00196     cache.insert(key, binding);
00197   }
00198 
00199   return true;
00200 }
00201 
00203 
00204 CexCachingSolver::~CexCachingSolver() {
00205   cache.clear();
00206   delete solver;
00207   for (assignmentsTable_ty::iterator it = assignmentsTable.begin(), 
00208          ie = assignmentsTable.end(); it != ie; ++it)
00209     delete *it;
00210 }
00211 
00212 bool CexCachingSolver::computeValidity(const Query& query,
00213                                        Solver::Validity &result) {
00214   TimerStatIncrementer t(stats::cexCacheTime);
00215   Assignment *a;
00216   if (!getAssignment(query.withFalse(), a))
00217     return false;
00218   assert(a && "computeValidity() must have assignment");
00219   ref<Expr> q = a->evaluate(query.expr);
00220   assert(isa<ConstantExpr>(q) && 
00221          "assignment evaluation did not result in constant");
00222 
00223   if (cast<ConstantExpr>(q)->getConstantValue()) {
00224     if (!getAssignment(query, a))
00225       return false;
00226     result = !a ? Solver::True : Solver::Unknown;
00227   } else {
00228     if (!getAssignment(query.negateExpr(), a))
00229       return false;
00230     result = !a ? Solver::False : Solver::Unknown;
00231   }
00232   
00233   return true;
00234 }
00235 
00236 bool CexCachingSolver::computeTruth(const Query& query,
00237                                     bool &isValid) {
00238   TimerStatIncrementer t(stats::cexCacheTime);
00239 
00240   // There is a small amount of redundancy here. We only need to know
00241   // truth and do not really need to compute an assignment. This means
00242   // that we could check the cache to see if we already know that
00243   // state ^ query has no assignment. In that case, by the validity of
00244   // state, we know that state ^ !query must have an assignment, and
00245   // so query cannot be true (valid). This does get hits, but doesn't
00246   // really seem to be worth the overhead.
00247 
00248   if (CexCacheExperimental) {
00249     Assignment *a;
00250     if (lookupAssignment(query.negateExpr(), a) && !a)
00251       return false;
00252   }
00253 
00254   Assignment *a;
00255   if (!getAssignment(query, a))
00256     return false;
00257 
00258   isValid = !a;
00259 
00260   return true;
00261 }
00262 
00263 bool CexCachingSolver::computeValue(const Query& query,
00264                                     ref<Expr> &result) {
00265   TimerStatIncrementer t(stats::cexCacheTime);
00266 
00267   Assignment *a;
00268   if (!getAssignment(query.withFalse(), a))
00269     return false;
00270   assert(a && "computeValue() must have assignment");
00271   result = a->evaluate(query.expr);  
00272   assert(isa<ConstantExpr>(result) && 
00273          "assignment evaluation did not result in constant");
00274   return true;
00275 }
00276 
00277 bool 
00278 CexCachingSolver::computeInitialValues(const Query& query,
00279                                        const std::vector<const Array*> 
00280                                          &objects,
00281                                        std::vector< std::vector<unsigned char> >
00282                                          &values,
00283                                        bool &hasSolution) {
00284   TimerStatIncrementer t(stats::cexCacheTime);
00285   Assignment *a;
00286   if (!getAssignment(query, a))
00287     return false;
00288   hasSolution = !!a;
00289   
00290   if (!a)
00291     return true;
00292 
00293   // FIXME: We should use smarter assignment for result so we don't
00294   // need redundant copy.
00295   values = std::vector< std::vector<unsigned char> >(objects.size());
00296   for (unsigned i=0; i < objects.size(); ++i) {
00297     const Array *os = objects[i];
00298     Assignment::bindings_ty::iterator it = a->bindings.find(os);
00299     
00300     if (it == a->bindings.end()) {
00301       values[i] = std::vector<unsigned char>(os->size, 0);
00302     } else {
00303       values[i] = it->second;
00304     }
00305   }
00306   
00307   return true;
00308 }
00309 
00311 
00312 Solver *klee::createCexCachingSolver(Solver *_solver) {
00313   return new Solver(new CexCachingSolver(_solver));
00314 }

Generated on Fri Jun 5 03:31:32 2009 for klee by  doxygen 1.5.8