//===-- ImpliedValue.cpp --------------------------------------------------===// // // The KLEE Symbolic Virtual Machine // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ImpliedValue.h" #include "klee/Constraints.h" #include "klee/Expr.h" #include "klee/Solver.h" // FIXME: Use APInt. #include "klee/Internal/Support/IntEvaluation.h" #include "klee/util/ExprUtil.h" #include #include #include using namespace klee; // XXX we really want to do some sort of canonicalization of exprs // globally so that cases below become simpler static void _getImpliedValue(ref e, uint64_t value, ImpliedValueList &results) { switch (e->getKind()) { case Expr::Constant: { assert(value == e->getConstantValue() && "error in implied value calculation"); break; } // Special case Expr::NotOptimized: break; case Expr::Read: { // XXX in theory it is possible to descend into a symbolic index // under certain circumstances (all values known, known value // unique, or range known, max / min hit). Seems unlikely this // would work often enough to be worth the effort. ReadExpr *re = static_ref_cast(e); results.push_back(std::make_pair(re, ConstantExpr::create(value, e->getWidth()))); break; } case Expr::Select: { // not much to do, could improve with range analysis SelectExpr *se = static_ref_cast(e); if (se->trueExpr.isConstant()) { if (se->falseExpr.isConstant()) { if (se->trueExpr->getConstantValue() != se->falseExpr->getConstantValue()) { if (value == se->trueExpr->getConstantValue()) { _getImpliedValue(se->cond, 1, results); } else { assert(value == se->falseExpr->getConstantValue() && "err in implied value calculation"); _getImpliedValue(se->cond, 0, results); } } } } break; } case Expr::Concat: { ConcatExpr *ce = static_ref_cast(e); _getImpliedValue(ce->getKid(0), (value >> ce->getKid(1)->getWidth()) & ((1 << ce->getKid(0)->getWidth()) - 1), results); _getImpliedValue(ce->getKid(1), value & ((1 << ce->getKid(1)->getWidth()) - 1), results); break; } case Expr::Extract: { // XXX, could do more here with "some bits" mask break; } // Casting case Expr::ZExt: case Expr::SExt: { CastExpr *ce = static_ref_cast(e); _getImpliedValue(ce->src, bits64::truncateToNBits(value, ce->src->getWidth()), results); break; } // Arithmetic case Expr::Add: { // constants on left BinaryExpr *be = static_ref_cast(e); if (be->left.isConstant()) { uint64_t nvalue = ints::sub(value, be->left->getConstantValue(), be->left->getWidth()); _getImpliedValue(be->right, nvalue, results); } break; } case Expr::Sub: { // constants on left BinaryExpr *be = static_ref_cast(e); if (be->left.isConstant()) { uint64_t nvalue = ints::sub(be->left->getConstantValue(), value, be->left->getWidth()); _getImpliedValue(be->right, nvalue, results); } break; } case Expr::Mul: { // XXX can do stuff here, but need valid mask and other things // because of bits that might be lost break; } case Expr::UDiv: case Expr::SDiv: case Expr::URem: case Expr::SRem: // no, no, no break; // Binary case Expr::And: { BinaryExpr *be = static_ref_cast(e); if (be->getWidth() == Expr::Bool) { if (value) { _getImpliedValue(be->left, value, results); _getImpliedValue(be->right, value, results); } } else { // XXX, we can basically propogate a mask here // where we know "some bits". may or may not be // useful. } break; } case Expr::Or: { BinaryExpr *be = static_ref_cast(e); if (!value) { _getImpliedValue(be->left, 0, results); _getImpliedValue(be->right, 0, results); } else { // XXX, can do more? } break; } case Expr::Xor: { // constants on left BinaryExpr *be = static_ref_cast(e); if (be->left.isConstant()) { _getImpliedValue(be->right, value ^ be->left->getConstantValue(), results); } break; } // Comparison case Expr::Ne: value = !value; /* fallthru */ case Expr::Eq: { EqExpr *ee = static_ref_cast(e); if (value) { if (ee->left.isConstant()) _getImpliedValue(ee->right, ee->left->getConstantValue(), results); } else { // look for limited value range, woohoo // // in general anytime one side was restricted to two values we // can apply this trick. the only obvious case where this // occurs, aside from booleans, is as the result of a select // expression where the true and false branches are single // valued and distinct. if (ee->left.isConstant()) { if (ee->left->getWidth() == Expr::Bool) { _getImpliedValue(ee->right, !ee->left->getConstantValue(), results); } } } break; } default: break; } } void ImpliedValue::getImpliedValues(ref e, ref value, ImpliedValueList &results) { assert(value.isConstant() && "non-constant in place of constant"); _getImpliedValue(e, value->getConstantValue(), results); } void ImpliedValue::checkForImpliedValues(Solver *S, ref e, ref value) { assert(value.isConstant() && "non-constant in place of constant"); std::vector > reads; std::map, ref > found; ImpliedValueList results; getImpliedValues(e, value, results); for (ImpliedValueList::iterator i = results.begin(), ie = results.end(); i != ie; ++i) { std::map, ref >::iterator it = found.find(i->first); if (it != found.end()) { assert(it->second->getConstantValue() == i->second->getConstantValue() && "I don't think so Scott"); } else { found.insert(std::make_pair(i->first, i->second)); } } findReads(e, false, reads); std::set< ref > readsSet(reads.begin(), reads.end()); reads = std::vector< ref >(readsSet.begin(), readsSet.end()); std::vector > assumption; assumption.push_back(EqExpr::create(e, value)); // obscure... we need to make sure that all the read indices are // bounds checked. if we don't do this we can end up constructing // invalid counterexamples because STP will happily make out of // bounds indices which will not get picked up. this is of utmost // importance if we are being backed by the CexCachingSolver. for (std::vector< ref >::iterator i = reads.begin(), ie = reads.end(); i != ie; ++i) { ReadExpr *re = i->get(); assumption.push_back(UltExpr::create(re->index, ConstantExpr::alloc(re->updates.root->size, kMachinePointerType))); } ConstraintManager assume(assumption); for (std::vector< ref >::iterator i = reads.begin(), ie = reads.end(); i != ie; ++i) { ref var = *i; ref possible; bool success = S->getValue(Query(assume, var), possible); assert(success && "FIXME: Unhandled solver failure"); std::map, ref >::iterator it = found.find(var); bool res; success = S->mustBeTrue(Query(assume, EqExpr::create(var, possible)), res); assert(success && "FIXME: Unhandled solver failure"); if (res) { if (it != found.end()) { assert(possible->getConstantValue() == it->second->getConstantValue()); found.erase(it); } } else { if (it!=found.end()) { ref binding = it->second; llvm::cerr << "checkForImpliedValues: " << e << " = " << value << "\n" << "\t\t implies " << var << " == " << binding << " (error)\n"; assert(0); } } } assert(found.empty()); }