//===-- Constraints.cpp ---------------------------------------------------===// // // The KLEE Symbolic Virtual Machine // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "klee/Expr/Constraints.h" #include "klee/Expr/ExprVisitor.h" #include "klee/Module/KModule.h" #include "klee/Support/OptionCategories.h" #include "llvm/IR/Function.h" #include "llvm/Support/CommandLine.h" #include using namespace klee; namespace { llvm::cl::opt RewriteEqualities( "rewrite-equalities", llvm::cl::desc("Rewrite existing constraints when an equality with a " "constant is added (default=true)"), llvm::cl::init(true), llvm::cl::cat(SolvingCat)); } // namespace class ExprReplaceVisitor : public ExprVisitor { private: ref src, dst; public: ExprReplaceVisitor(const ref &_src, const ref &_dst) : src(_src), dst(_dst) {} Action visitExpr(const Expr &e) override { if (e == *src) { return Action::changeTo(dst); } return Action::doChildren(); } Action visitExprPost(const Expr &e) override { if (e == *src) { return Action::changeTo(dst); } return Action::doChildren(); } }; class ExprReplaceVisitor2 : public ExprVisitor { private: const std::map< ref, ref > &replacements; public: explicit ExprReplaceVisitor2( const std::map, ref> &_replacements) : ExprVisitor(true), replacements(_replacements) {} Action visitExprPost(const Expr &e) override { auto it = replacements.find(ref(const_cast(&e))); if (it!=replacements.end()) { return Action::changeTo(it->second); } return Action::doChildren(); } }; bool ConstraintManager::rewriteConstraints(ExprVisitor &visitor) { ConstraintSet old; bool changed = false; std::swap(constraints, old); for (auto &ce : old) { ref e = visitor.visit(ce); if (e!=ce) { addConstraintInternal(e); // enable further reductions changed = true; } else { constraints.push_back(ce); } } return changed; } ref ConstraintManager::simplifyExpr(const ConstraintSet &constraints, const ref &e) { if (isa(e)) return e; std::map< ref, ref > equalities; for (auto &constraint : constraints) { if (const EqExpr *ee = dyn_cast(constraint)) { if (isa(ee->left)) { equalities.insert(std::make_pair(ee->right, ee->left)); } else { equalities.insert( std::make_pair(constraint, ConstantExpr::alloc(1, Expr::Bool))); } } else { equalities.insert( std::make_pair(constraint, ConstantExpr::alloc(1, Expr::Bool))); } } return ExprReplaceVisitor2(equalities).visit(e); } void ConstraintManager::addConstraintInternal(const ref &e) { // rewrite any known equalities and split Ands into different conjuncts switch (e->getKind()) { case Expr::Constant: assert(cast(e)->isTrue() && "attempt to add invalid (false) constraint"); break; // split to enable finer grained independence and other optimizations case Expr::And: { BinaryExpr *be = cast(e); addConstraintInternal(be->left); addConstraintInternal(be->right); break; } case Expr::Eq: { if (RewriteEqualities) { // XXX: should profile the effects of this and the overhead. // traversing the constraints looking for equalities is hardly the // slowest thing we do, but it is probably nicer to have a // ConstraintSet ADT which efficiently remembers obvious patterns // (byte-constant comparison). BinaryExpr *be = cast(e); if (isa(be->left)) { ExprReplaceVisitor visitor(be->right, be->left); rewriteConstraints(visitor); } } constraints.push_back(e); break; } default: constraints.push_back(e); break; } } void ConstraintManager::addConstraint(const ref &e) { ref simplified = simplifyExpr(constraints, e); addConstraintInternal(simplified); } ConstraintManager::ConstraintManager(ConstraintSet &_constraints) : constraints(_constraints) {} bool ConstraintSet::empty() const { return constraints.empty(); } klee::ConstraintSet::constraint_iterator ConstraintSet::begin() const { return constraints.begin(); } klee::ConstraintSet::constraint_iterator ConstraintSet::end() const { return constraints.end(); } size_t ConstraintSet::size() const noexcept { return constraints.size(); } void ConstraintSet::push_back(const ref &e) { constraints.push_back(e); }