//===-- IndependentSolver.cpp ---------------------------------------------===// // // The KLEE Symbolic Virtual Machine // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "independent-solver" #include "klee/Solver/Solver.h" #include "klee/Expr/Assignment.h" #include "klee/Expr/Constraints.h" #include "klee/Expr/Expr.h" #include "klee/Expr/ExprUtil.h" #include "klee/Support/Debug.h" #include "klee/Solver/SolverImpl.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include using namespace klee; using namespace llvm; template class DenseSet { typedef std::set set_ty; set_ty s; public: DenseSet() {} void add(T x) { s.insert(x); } void add(T start, T end) { for (; start::iterator begin(){ return s.begin(); } std::set::iterator end(){ return s.end(); } void print(llvm::raw_ostream &os) const { bool first = true; os << "{"; for (typename set_ty::iterator it = s.begin(), ie = s.end(); it != ie; ++it) { if (first) { first = false; } else { os << ","; } os << *it; } os << "}"; } }; template inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const ::DenseSet &dis) { dis.print(os); return os; } class IndependentElementSet { public: typedef std::map > elements_ty; elements_ty elements; // Represents individual elements of array accesses (arr[1]) std::set wholeObjects; // Represents symbolically accessed arrays (arr[x]) std::vector > exprs; // All expressions that are associated with this factor // Although order doesn't matter, we use a vector to match // the ConstraintManager constructor that will eventually // be invoked. IndependentElementSet() {} IndependentElementSet(ref e) { exprs.push_back(e); // Track all reads in the program. Determines whether reads are // concrete or symbolic. If they are symbolic, "collapses" array // by adding it to wholeObjects. Otherwise, creates a mapping of // the form Map> which tracks which parts of the // array are being accessed. std::vector< ref > reads; findReads(e, /* visitUpdates= */ true, reads); for (unsigned i = 0; i != reads.size(); ++i) { ReadExpr *re = reads[i].get(); const Array *array = re->updates.root; // Reads of a constant array don't alias. if (re->updates.root->isConstantArray() && !re->updates.head) continue; if (!wholeObjects.count(array)) { if (ConstantExpr *CE = dyn_cast(re->index)) { // if index constant, then add to set of constraints operating // on that array (actually, don't add constraint, just set index) ::DenseSet &dis = elements[array]; dis.add((unsigned) CE->getZExtValue(32)); } else { elements_ty::iterator it2 = elements.find(array); if (it2!=elements.end()) elements.erase(it2); wholeObjects.insert(array); } } } } IndependentElementSet(const IndependentElementSet &ies) : elements(ies.elements), wholeObjects(ies.wholeObjects), exprs(ies.exprs) {} IndependentElementSet &operator=(const IndependentElementSet &ies) { elements = ies.elements; wholeObjects = ies.wholeObjects; exprs = ies.exprs; return *this; } void print(llvm::raw_ostream &os) const { os << "{"; bool first = true; for (std::set::iterator it = wholeObjects.begin(), ie = wholeObjects.end(); it != ie; ++it) { const Array *array = *it; if (first) { first = false; } else { os << ", "; } os << "MO" << array->name; } for (elements_ty::const_iterator it = elements.begin(), ie = elements.end(); it != ie; ++it) { const Array *array = it->first; const ::DenseSet &dis = it->second; if (first) { first = false; } else { os << ", "; } os << "MO" << array->name << " : " << dis; } os << "}"; } // more efficient when this is the smaller set bool intersects(const IndependentElementSet &b) { // If there are any symbolic arrays in our query that b accesses for (std::set::iterator it = wholeObjects.begin(), ie = wholeObjects.end(); it != ie; ++it) { const Array *array = *it; if (b.wholeObjects.count(array) || b.elements.find(array) != b.elements.end()) return true; } for (elements_ty::iterator it = elements.begin(), ie = elements.end(); it != ie; ++it) { const Array *array = it->first; // if the array we access is symbolic in b if (b.wholeObjects.count(array)) return true; elements_ty::const_iterator it2 = b.elements.find(array); // if any of the elements we access are also accessed by b if (it2 != b.elements.end()) { if (it->second.intersects(it2->second)) return true; } } return false; } // returns true iff set is changed by addition bool add(const IndependentElementSet &b) { for(unsigned i = 0; i < b.exprs.size(); i ++){ ref expr = b.exprs[i]; exprs.push_back(expr); } bool modified = false; for (std::set::const_iterator it = b.wholeObjects.begin(), ie = b.wholeObjects.end(); it != ie; ++it) { const Array *array = *it; elements_ty::iterator it2 = elements.find(array); if (it2!=elements.end()) { modified = true; elements.erase(it2); wholeObjects.insert(array); } else { if (!wholeObjects.count(array)) { modified = true; wholeObjects.insert(array); } } } for (elements_ty::const_iterator it = b.elements.begin(), ie = b.elements.end(); it != ie; ++it) { const Array *array = it->first; if (!wholeObjects.count(array)) { elements_ty::iterator it2 = elements.find(array); if (it2==elements.end()) { modified = true; elements.insert(*it); } else { // Now need to see if there are any (z=?)'s if (it2->second.add(it->second)) modified = true; } } } return modified; } }; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const IndependentElementSet &ies) { ies.print(os); return os; } // Breaks down a constraint into all of it's individual pieces, returning a // list of IndependentElementSets or the independent factors. // // Caller takes ownership of returned std::list. static std::list* getAllIndependentConstraintsSets(const Query &query) { std::list *factors = new std::list(); ConstantExpr *CE = dyn_cast(query.expr); if (CE) { assert(CE && CE->isFalse() && "the expr should always be false and " "therefore not included in factors"); } else { ref neg = Expr::createIsZero(query.expr); factors->push_back(IndependentElementSet(neg)); } for (const auto &constraint : query.constraints) { // iterate through all the previously separated constraints. Until we // actually return, factors is treated as a queue of expressions to be // evaluated. If the queue property isn't maintained, then the exprs // could be returned in an order different from how they came it, negatively // affecting later stages. factors->push_back(IndependentElementSet(constraint)); } bool doneLoop = false; do { doneLoop = true; std::list *done = new std::list; while (factors->size() > 0) { IndependentElementSet current = factors->front(); factors->pop_front(); // This list represents the set of factors that are separate from current. // Those that are not inserted into this list (queue) intersect with // current. std::list *keep = new std::list; while (factors->size() > 0) { IndependentElementSet compare = factors->front(); factors->pop_front(); if (current.intersects(compare)) { if (current.add(compare)) { // Means that we have added (z=y)added to (x=y) // Now need to see if there are any (z=?)'s doneLoop = false; } } else { keep->push_back(compare); } } done->push_back(current); delete factors; factors = keep; } delete factors; factors = done; } while (!doneLoop); return factors; } static IndependentElementSet getIndependentConstraints(const Query& query, std::vector< ref > &result) { IndependentElementSet eltsClosure(query.expr); std::vector< std::pair, IndependentElementSet> > worklist; for (const auto &constraint : query.constraints) worklist.push_back( std::make_pair(constraint, IndependentElementSet(constraint))); // XXX This should be more efficient (in terms of low level copy stuff). bool done = false; do { done = true; std::vector< std::pair, IndependentElementSet> > newWorklist; for (std::vector< std::pair, IndependentElementSet> >::iterator it = worklist.begin(), ie = worklist.end(); it != ie; ++it) { if (it->second.intersects(eltsClosure)) { if (eltsClosure.add(it->second)) done = false; result.push_back(it->first); // Means that we have added (z=y)added to (x=y) // Now need to see if there are any (z=?)'s } else { newWorklist.push_back(*it); } } worklist.swap(newWorklist); } while (!done); KLEE_DEBUG( std::set< ref > reqset(result.begin(), result.end()); errs() << "--\n"; errs() << "Q: " << query.expr << "\n"; errs() << "\telts: " << IndependentElementSet(query.expr) << "\n"; int i = 0; for (const auto &constraint: query.constraints) { errs() << "C" << i++ << ": " << constraint; errs() << " " << (reqset.count(constraint) ? "(required)" : "(independent)") << "\n"; errs() << "\telts: " << IndependentElementSet(constraint) << "\n"; } errs() << "elts closure: " << eltsClosure << "\n"; ); return eltsClosure; } // Extracts which arrays are referenced from a particular independent set. Examines both // the actual known array accesses arr[1] plus the undetermined accesses arr[x]. static void calculateArrayReferences(const IndependentElementSet & ie, std::vector &returnVector){ std::set thisSeen; for(std::map >::const_iterator it = ie.elements.begin(); it != ie.elements.end(); it ++){ thisSeen.insert(it->first); } for(std::set::iterator it = ie.wholeObjects.begin(); it != ie.wholeObjects.end(); it ++){ thisSeen.insert(*it); } for(std::set::iterator it = thisSeen.begin(); it != thisSeen.end(); it ++){ returnVector.push_back(*it); } } class IndependentSolver : public SolverImpl { private: std::unique_ptr solver; public: IndependentSolver(std::unique_ptr solver) : solver(std::move(solver)) {} bool computeTruth(const Query&, bool &isValid); bool computeValidity(const Query&, Solver::Validity &result); bool computeValue(const Query&, ref &result); bool computeInitialValues(const Query& query, const std::vector &objects, std::vector< std::vector > &values, bool &hasSolution); SolverRunStatus getOperationStatusCode(); std::string getConstraintLog(const Query&) override; void setCoreSolverTimeout(time::Span timeout); }; bool IndependentSolver::computeValidity(const Query& query, Solver::Validity &result) { std::vector< ref > required; IndependentElementSet eltsClosure = getIndependentConstraints(query, required); ConstraintSet tmp(required); return solver->impl->computeValidity(Query(tmp, query.expr), result); } bool IndependentSolver::computeTruth(const Query& query, bool &isValid) { std::vector< ref > required; IndependentElementSet eltsClosure = getIndependentConstraints(query, required); ConstraintSet tmp(required); return solver->impl->computeTruth(Query(tmp, query.expr), isValid); } bool IndependentSolver::computeValue(const Query& query, ref &result) { std::vector< ref > required; IndependentElementSet eltsClosure = getIndependentConstraints(query, required); ConstraintSet tmp(required); return solver->impl->computeValue(Query(tmp, query.expr), result); } // Helper function used only for assertions to make sure point created // during computeInitialValues is in fact correct. The ``retMap`` is used // in the case ``objects`` doesn't contain all the assignments needed. bool assertCreatedPointEvaluatesToTrue( const Query &query, const std::vector &objects, std::vector> &values, std::map> &retMap) { // _allowFreeValues is set to true so that if there are missing bytes in the // assignment we will end up with a non ConstantExpr after evaluating the // assignment and fail Assignment assign = Assignment(objects, values, /*_allowFreeValues=*/true); // Add any additional bindings. // The semantics of std::map should be to not insert a (key, value) // pair if it already exists so we should continue to use the assignment // from ``objects`` and ``values``. if (retMap.size() > 0) assign.bindings.insert(retMap.begin(), retMap.end()); for (auto const &constraint : query.constraints) { ref ret = assign.evaluate(constraint); assert(isa(ret) && "assignment evaluation did not result in constant"); ref evaluatedConstraint = dyn_cast(ret); if (evaluatedConstraint->isFalse()) { return false; } } ref neg = Expr::createIsZero(query.expr); ref q = assign.evaluate(neg); assert(isa(q) && "assignment evaluation did not result in constant"); return cast(q)->isTrue(); } bool IndependentSolver::computeInitialValues(const Query& query, const std::vector &objects, std::vector< std::vector > &values, bool &hasSolution){ // We assume the query has a solution except proven differently // This is important in case we don't have any constraints but // we need initial values for requested array objects. hasSolution = true; // FIXME: When we switch to C++11 this should be a std::unique_ptr so we don't need // to remember to manually call delete std::list *factors = getAllIndependentConstraintsSets(query); //Used to rearrange all of the answers into the correct order std::map > retMap; for (std::list::iterator it = factors->begin(); it != factors->end(); ++it) { std::vector arraysInFactor; calculateArrayReferences(*it, arraysInFactor); // Going to use this as the "fresh" expression for the Query() invocation below assert(it->exprs.size() >= 1 && "No null/empty factors"); if (arraysInFactor.size() == 0){ continue; } ConstraintSet tmp(it->exprs); std::vector > tempValues; if (!solver->impl->computeInitialValues(Query(tmp, ConstantExpr::alloc(0, Expr::Bool)), arraysInFactor, tempValues, hasSolution)){ values.clear(); delete factors; return false; } else if (!hasSolution){ values.clear(); delete factors; return true; } else { assert(tempValues.size() == arraysInFactor.size() && "Should be equal number arrays and answers"); for (unsigned i = 0; i < tempValues.size(); i++){ if (retMap.count(arraysInFactor[i])){ // We already have an array with some partially correct answers, // so we need to place the answers to the new query into the right // spot while avoiding the undetermined values also in the array std::vector * tempPtr = &retMap[arraysInFactor[i]]; assert(tempPtr->size() == tempValues[i].size() && "we're talking about the same array here"); ::DenseSet * ds = &(it->elements[arraysInFactor[i]]); for (std::set::iterator it2 = ds->begin(); it2 != ds->end(); it2++){ unsigned index = * it2; (* tempPtr)[index] = tempValues[i][index]; } } else { // Dump all the new values into the array retMap[arraysInFactor[i]] = tempValues[i]; } } } } for (std::vector::const_iterator it = objects.begin(); it != objects.end(); it++){ const Array * arr = * it; if (!retMap.count(arr)){ // this means we have an array that is somehow related to the // constraint, but whose values aren't actually required to // satisfy the query. std::vector ret(arr->size); values.push_back(ret); } else { values.push_back(retMap[arr]); } } assert(assertCreatedPointEvaluatesToTrue(query, objects, values, retMap) && "should satisfy the equation"); delete factors; return true; } SolverImpl::SolverRunStatus IndependentSolver::getOperationStatusCode() { return solver->impl->getOperationStatusCode(); } std::string IndependentSolver::getConstraintLog(const Query& query) { return solver->impl->getConstraintLog(query); } void IndependentSolver::setCoreSolverTimeout(time::Span timeout) { solver->impl->setCoreSolverTimeout(timeout); } std::unique_ptr klee::createIndependentSolver(std::unique_ptr s) { return std::make_unique( std::make_unique(std::move(s))); }