diff options
Diffstat (limited to 'lib/Expr')
-rw-r--r-- | lib/Expr/ExprSMTLIBLetPrinter.cpp | 232 | ||||
-rw-r--r-- | lib/Expr/ExprSMTLIBPrinter.cpp | 348 |
2 files changed, 288 insertions, 292 deletions
diff --git a/lib/Expr/ExprSMTLIBLetPrinter.cpp b/lib/Expr/ExprSMTLIBLetPrinter.cpp deleted file mode 100644 index d4243452..00000000 --- a/lib/Expr/ExprSMTLIBLetPrinter.cpp +++ /dev/null @@ -1,232 +0,0 @@ -//===-- ExprSMTLIBLetPrinter.cpp ------------------------------------------*- -//C++ -*-===// -// -// The KLEE Symbolic Virtual Machine -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/CommandLine.h" -#include "klee/util/ExprSMTLIBLetPrinter.h" - -namespace ExprSMTLIBOptions { -llvm::cl::opt<bool> -useLetExpressions("smtlib-use-let-expressions", - llvm::cl::desc("Enables generated SMT-LIBv2 files to use " - "(let) expressions (default=on)"), - llvm::cl::init(true)); -} - -namespace klee { -const char ExprSMTLIBLetPrinter::BINDING_PREFIX[] = "?B"; - -ExprSMTLIBLetPrinter::ExprSMTLIBLetPrinter() - : bindings(), firstEO(), twoOrMoreEO(), disablePrintedAbbreviations(false) { -} - -void ExprSMTLIBLetPrinter::generateOutput() { - if (p == NULL || query == NULL || o == NULL) { - llvm::errs() << "Can't print SMTLIBv2. Output or query bad!\n"; - return; - } - - generateBindings(); - - if (isHumanReadable()) - printNotice(); - printOptions(); - printSetLogic(); - printArrayDeclarations(); - printLetExpression(); - printAction(); - printExit(); -} - -void ExprSMTLIBLetPrinter::scan(const ref<Expr> &e) { - if (isa<ConstantExpr>(e)) - return; // we don't need to scan simple constants - - if (firstEO.insert(e).second) { - // We've not seen this expression before - - if (const ReadExpr *re = dyn_cast<ReadExpr>(e)) { - - // Attempt to insert array and if array wasn't present before do more - // things - if (usedArrays.insert(re->updates.root).second) { - - // check if the array is constant - if (re->updates.root->isConstantArray()) - haveConstantArray = true; - - // scan the update list - scanUpdates(re->updates.head); - } - } - - // recurse into the children - Expr *ep = e.get(); - for (unsigned int i = 0; i < ep->getNumKids(); i++) - scan(ep->getKid(i)); - } else { - /* We must of seen the expression before. Add it to - * the set of twoOrMoreOccurances. We don't need to - * check if the insertion fails. - */ - twoOrMoreEO.insert(e); - } -} - -void ExprSMTLIBLetPrinter::generateBindings() { - // Assign a number to each binding that will be used - unsigned int counter = 0; - for (std::set<ref<Expr> >::const_iterator i = twoOrMoreEO.begin(); - i != twoOrMoreEO.end(); ++i) { - bindings.insert(std::make_pair(*i, counter)); - ++counter; - } -} - -void ExprSMTLIBLetPrinter::printExpression( - const ref<Expr> &e, ExprSMTLIBPrinter::SMTLIB_SORT expectedSort) { - std::map<const ref<Expr>, unsigned int>::const_iterator i = bindings.find(e); - - if (disablePrintedAbbreviations || i == bindings.end()) { - /*There is no abbreviation for this expression so print it normally. - * Do this by using the parent method. - */ - ExprSMTLIBPrinter::printExpression(e, expectedSort); - } else { - // Use binding name e.g. "?B1" - - /* Handle the corner case where the expectedSort - * doesn't match the sort of the abbreviation. Not really very efficient - * (calls bindings.find() twice), - * we'll cast and call ourself again but with the correct expectedSort. - */ - if (getSort(e) != expectedSort) { - printCastToSort(e, expectedSort); - return; - } - - // No casting is needed in this depth of recursion, just print the - // abbreviation - *p << BINDING_PREFIX << i->second; - } -} - -void ExprSMTLIBLetPrinter::reset() { - // Let parent clean up first - ExprSMTLIBPrinter::reset(); - - firstEO.clear(); - twoOrMoreEO.clear(); - bindings.clear(); -} - -void ExprSMTLIBLetPrinter::printLetExpression() { - *p << "(assert"; - p->pushIndent(); - printSeperator(); - - if (bindings.size() != 0) { - // Only print let expression if we have bindings to use. - *p << "(let"; - p->pushIndent(); - printSeperator(); - *p << "( "; - p->pushIndent(); - - // Print each binding - for (std::map<const ref<Expr>, unsigned int>::const_iterator i = - bindings.begin(); - i != bindings.end(); ++i) { - printSeperator(); - *p << "(" << BINDING_PREFIX << i->second << " "; - p->pushIndent(); - - // Disable abbreviations so none are used here. - disablePrintedAbbreviations = true; - - // We can abbreviate SORT_BOOL or SORT_BITVECTOR in let expressions - printExpression(i->first, getSort(i->first)); - - p->popIndent(); - printSeperator(); - *p << ")"; - } - - p->popIndent(); - printSeperator(); - *p << ")"; - printSeperator(); - - // Re-enable printing abbreviations. - disablePrintedAbbreviations = false; - } - - // print out Expressions with abbreviations. - unsigned int numberOfItems = query->constraints.size() + 1; //+1 for query - unsigned int itemsLeft = numberOfItems; - std::vector<ref<Expr> >::const_iterator constraint = - query->constraints.begin(); - - /* Produce nested (and () () statements. If the constraint set - * is empty then we will only print the "queryAssert". - */ - for (; itemsLeft != 0; --itemsLeft) { - if (itemsLeft >= 2) { - *p << "(and"; - p->pushIndent(); - printSeperator(); - printExpression(*constraint, - SORT_BOOL); // We must and together bool expressions - printSeperator(); - ++constraint; - continue; - } else { - // must have 1 item left (i.e. the "queryAssert") - if (isHumanReadable()) { - *p << "; query"; - p->breakLineI(); - } - printExpression(queryAssert, - SORT_BOOL); // The query must be a bool expression - } - } - - /* Produce closing brackets for nested "and statements". - * Number of "nested ands" = numberOfItems -1 - */ - itemsLeft = numberOfItems - 1; - for (; itemsLeft != 0; --itemsLeft) { - p->popIndent(); - printSeperator(); - *p << ")"; - } - - if (bindings.size() != 0) { - // end let expression - p->popIndent(); - printSeperator(); - *p << ")"; - printSeperator(); - } - - // end assert - p->popIndent(); - printSeperator(); - *p << ")"; - p->breakLineI(); -} - -ExprSMTLIBPrinter *createSMTLIBPrinter() { - if (ExprSMTLIBOptions::useLetExpressions) - return new ExprSMTLIBLetPrinter(); - else - return new ExprSMTLIBPrinter(); -} -} diff --git a/lib/Expr/ExprSMTLIBPrinter.cpp b/lib/Expr/ExprSMTLIBPrinter.cpp index bbb82d0d..c4a6876e 100644 --- a/lib/Expr/ExprSMTLIBPrinter.cpp +++ b/lib/Expr/ExprSMTLIBPrinter.cpp @@ -8,8 +8,11 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" #include "klee/util/ExprSMTLIBPrinter.h" +#include <stack> + namespace ExprSMTLIBOptions { // Command line options llvm::cl::opt<klee::ExprSMTLIBPrinter::ConstantDisplayMode> @@ -31,6 +34,19 @@ llvm::cl::opt<bool> humanReadableSMTLIB( llvm::cl::desc( "Enables generated SMT-LIBv2 files to be human readable (default=off)"), llvm::cl::init(false)); + +llvm::cl::opt<klee::ExprSMTLIBPrinter::AbbreviationMode> abbreviationMode( + "smtlib-abbreviation-mode", + llvm::cl::desc( + "Choose abbreviation mode to use in SMT-LIBv2 files (default=let)"), + llvm::cl::values(clEnumValN(klee::ExprSMTLIBPrinter::ABBR_NONE, "none", + "Do not abbreviate"), + clEnumValN(klee::ExprSMTLIBPrinter::ABBR_LET, "let", + "Abbreviate with let"), + clEnumValN(klee::ExprSMTLIBPrinter::ABBR_NAMED, "named", + "Abbreviate with :named annotations"), + clEnumValEnd), + llvm::cl::init(klee::ExprSMTLIBPrinter::ABBR_LET)); } namespace klee { @@ -41,6 +57,7 @@ ExprSMTLIBPrinter::ExprSMTLIBPrinter() humanReadable(ExprSMTLIBOptions::humanReadableSMTLIB), smtlibBoolOptions(), arraysToCallGetValueOn(NULL) { setConstantDisplayMode(ExprSMTLIBOptions::argConstantDisplayMode); + setAbbreviationMode(ExprSMTLIBOptions::abbreviationMode); } ExprSMTLIBPrinter::~ExprSMTLIBPrinter() { @@ -60,10 +77,12 @@ void ExprSMTLIBPrinter::setQuery(const Query &q) { query = &q; reset(); // clear the data structures scanAll(); - negateQueryExpression(); } void ExprSMTLIBPrinter::reset() { + bindings.clear(); + orderedBindings.clear(); + seenExprs.clear(); usedArrays.clear(); haveConstantArray = false; @@ -141,8 +160,7 @@ void ExprSMTLIBPrinter::printConstant(const ref<ConstantExpr> &e) { break; default: - llvm::errs() << "ExprSMTLIBPrinter::printConstant() : Unexpected Constant " - "display mode\n"; + llvm_unreachable("Unexpected constant display mode"); } } @@ -154,6 +172,41 @@ void ExprSMTLIBPrinter::printExpression( return; } + switch (abbrMode) { + case ABBR_NONE: + break; + + case ABBR_LET: { + BindingMap::iterator i = bindings.find(e); + if (i != bindings.end()) { + *p << "?B" << i->second; + return; + } + break; + } + + case ABBR_NAMED: { + BindingMap::iterator i = bindings.find(e); + if (i != bindings.end()) { + if (i->second > 0) { + *p << "(! "; + printFullExpression(e, expectedSort); + *p << " :named ?B" << i->second << ")"; + i->second = -i->second; + } else { + *p << "?B" << -i->second; + } + return; + } + break; + } + } + + printFullExpression(e, expectedSort); +} + +void ExprSMTLIBPrinter::printFullExpression( + const ref<Expr> &e, ExprSMTLIBPrinter::SMTLIB_SORT expectedSort) { switch (e->getKind()) { case Expr::Constant: printConstant(cast<ConstantExpr>(e)); @@ -188,7 +241,7 @@ void ExprSMTLIBPrinter::printExpression( case Expr::Eq: /* The "=" operator is special in that it can take any sort but we must - * enforce that both arguments are the same type. We do this a lazy way + * enforce that both arguments are the same sort. We do this in a lazy way * by enforcing the second argument is of the same type as the first. */ printSortArgsExpr(e, getSort(e->getKid(0))); @@ -201,8 +254,8 @@ void ExprSMTLIBPrinter::printExpression( case Expr::Not: /* These operators have a bitvector version and a bool version. * For these operators only (e.g. wouldn't apply to bvult) if the expected - * sort the - * expression is T then that implies the arguments are also of type T. + * sort of the expression is T then that implies the arguments are also of + * type T. */ printLogicalOrBitVectorExpr(e, expectedSort); @@ -254,7 +307,7 @@ void ExprSMTLIBPrinter::printExtractExpr(const ref<ExtractExpr> &e) { } void ExprSMTLIBPrinter::printCastExpr(const ref<CastExpr> &e) { - /* sign_extend and zero_extend behave slightly unusually in SMTLIBv2 + /* sign_extend and zero_extend behave slightly unusually in SMTLIBv2, * instead of specifying of what bit-width we would like to extend to * we specify how many bits to add to the child expression * @@ -376,7 +429,7 @@ const char *ExprSMTLIBPrinter::getSMTLIBKeyword(const ref<Expr> &e) { return "bvsge"; default: - return "<error>"; + llvm_unreachable("Conversion from Expr to SMTLIB keyword failed"); } } @@ -416,6 +469,10 @@ void ExprSMTLIBPrinter::scanAll() { // Scan the query too scan(query->expr); + + // Scan bindings for expression intra-dependencies + if (abbrMode == ABBR_LET) + scanBindingExprDeps(); } void ExprSMTLIBPrinter::generateOutput() { @@ -430,8 +487,12 @@ void ExprSMTLIBPrinter::generateOutput() { printOptions(); printSetLogic(); printArrayDeclarations(); - printConstraints(); - printQuery(); + + if (humanReadable) + printHumanReadableQuery(); + else + printMachineReadableQuery(); + printAction(); printExit(); } @@ -520,32 +581,57 @@ void ExprSMTLIBPrinter::printArrayDeclarations() { } } -void ExprSMTLIBPrinter::printConstraints() { - if (humanReadable) - *o << "; Constraints\n"; +void ExprSMTLIBPrinter::printHumanReadableQuery() { + assert(humanReadable && "method must be called in humanReadable mode"); + *o << "; Constraints\n"; - // Generate assert statements for each constraint - for (ConstraintManager::const_iterator i = query->constraints.begin(); - i != query->constraints.end(); i++) { - *p << "(assert "; - p->pushIndent(); - printSeperator(); + if (abbrMode != ABBR_LET) { + // Generate assert statements for each constraint + for (ConstraintManager::const_iterator i = query->constraints.begin(); + i != query->constraints.end(); i++) { + printAssert(*i); + } - // recurse into Expression - printExpression(*i, SORT_BOOL); + *o << "; QueryExpr\n"; - p->popIndent(); - printSeperator(); - *p << ")"; - p->breakLineI(); + // We negate the Query Expr because in KLEE queries are solved + // in terms of validity, but SMT-LIB works in terms of satisfiability + ref<Expr> queryAssert = Expr::createIsZero(query->expr); + printAssert(queryAssert); + } else { + // let bindings are only scoped within a single (assert ...) so + // the entire query must be printed within a single assert + *o << "; Constraints and QueryExpr\n"; + printQueryInSingleAssert(); } } +void ExprSMTLIBPrinter::printMachineReadableQuery() { + assert(!humanReadable && "method should not be called in humanReadable mode"); + printQueryInSingleAssert(); +} + + +void ExprSMTLIBPrinter::printQueryInSingleAssert() { + // We negate the Query Expr because in KLEE queries are solved + // in terms of validity, but SMT-LIB works in terms of satisfiability + ref<Expr> queryAssert = Expr::createIsZero(query->expr); + + // Print constraints inside the main query to reuse the Expr bindings + for (std::vector<ref<Expr> >::const_iterator i = query->constraints.begin(), + e = query->constraints.end(); + i != e; ++i) { + queryAssert = AndExpr::create(queryAssert, *i); + } + + // print just a single (assert ...) containing entire query + printAssert(queryAssert); +} void ExprSMTLIBPrinter::printAction() { // Ask solver to check for satisfiability *o << "(check-sat)\n"; - /* If we has arrays to find the values of then we'll + /* If we have arrays to find the values of then we'll * ask the solver for the value of each bitvector in each array */ if (arraysToCallGetValueOn != NULL && !arraysToCallGetValueOn->empty()) { @@ -567,33 +653,129 @@ void ExprSMTLIBPrinter::printAction() { } void ExprSMTLIBPrinter::scan(const ref<Expr> &e) { - if (e.isNull()) { - llvm::errs() << "ExprSMTLIBPrinter::scan() : Found NULL expression!" - << "\n"; - return; - } + assert(!(e.isNull()) && "found NULL expression"); if (isa<ConstantExpr>(e)) return; // we don't need to scan simple constants - if (const ReadExpr *re = dyn_cast<ReadExpr>(e)) { + if (seenExprs.insert(e).second) { + // We've not seen this expression before + + if (const ReadExpr *re = dyn_cast<ReadExpr>(e)) { - // Attempt to insert array and if array wasn't present before do more things - if (usedArrays.insert(re->updates.root).second) { + if (usedArrays.insert(re->updates.root).second) { + // Array was not recorded before - // check if the array is constant - if (re->updates.root->isConstantArray()) - haveConstantArray = true; + // check if the array is constant + if (re->updates.root->isConstantArray()) + haveConstantArray = true; - // scan the update list - scanUpdates(re->updates.head); + // scan the update list + scanUpdates(re->updates.head); + } } + + // recurse into the children + Expr *ep = e.get(); + for (unsigned int i = 0; i < ep->getNumKids(); i++) + scan(ep->getKid(i)); + } else { + // Add the expression to the binding map. The semantics of std::map::insert + // are such that it will not be inserted twice. + bindings.insert(std::make_pair(e, bindings.size()+1)); } +} - // recurse into the children - Expr *ep = e.get(); - for (unsigned int i = 0; i < ep->getNumKids(); i++) - scan(ep->getKid(i)); +void ExprSMTLIBPrinter::scanBindingExprDeps() { + if (!bindings.size()) + return; + + // Mutual dependency storage + typedef std::map<const ref<Expr>, std::set<ref<Expr> > > ExprDepMap; + + // A map from binding Expr (need abbreviating) "e" to the set of binding Expr + // that are sub expressions of "e" (i.e. "e" uses these sub expressions). + // usesSubExprMap[a].count(b) == 1 means that binding Expr "a" uses + // sub Expr "b" (also a binding Expr). + // One can think of this map representing the "depends on" edges + // in a "dependency graph" where nodes are binding Expr roots + ExprDepMap usesSubExprMap; + + // A map from Binding Expr "e" to the set of binding Expr that use "e" + // subExprOfMap[a].count(b) == 1 means that binding Expr "a" is a sub Expr + // of binding Expr "b". + // One can think of this map as representing the edges in the previously + // mentioned "dependency graph" except the direction of the edges + // have been reversed + ExprDepMap subExprOfMap; + + // Working queue holding expressions with no dependencies + std::vector<ref<Expr> > nonDepBindings; + + // Iterate over bindings and collect dependencies + for (BindingMap::const_iterator it = bindings.begin(); + it != bindings.end(); ++it) { + std::stack<ref<Expr> > childQueue; + childQueue.push(it->first); + // Non-recursive expression parsing + while (childQueue.size()) { + Expr *ep = childQueue.top().get(); + childQueue.pop(); + for (unsigned i = 0; i < ep->getNumKids(); ++i) { + ref<Expr> e = ep->getKid(i); + if (isa<ConstantExpr>(e)) + continue; + // Are there any dependencies in the bindings? + if (bindings.count(e)) { + usesSubExprMap[it->first].insert(e); + subExprOfMap[e].insert(it->first); + } else { + childQueue.push(e); + } + } + } + // Store expressions with zero deps + if (!usesSubExprMap.count(it->first)) + nonDepBindings.push_back(it->first); + } + assert(nonDepBindings.size() && "there must be expr bindings with no deps"); + + // Unroll the dependency tree starting with zero-dep expressions + // and redefine bindings + unsigned counter = 1; + // nonDepBindings always holds expressions with no dependencies + while (nonDepBindings.size()) { + BindingMap levelExprs; + std::vector<ref<Expr> > tmp(nonDepBindings); + nonDepBindings.clear(); + for (std::vector<ref<Expr> >::const_iterator nonDepExprIt = tmp.begin(); + nonDepExprIt != tmp.end(); ++nonDepExprIt) { + // Save to the level expression bindings + levelExprs.insert(std::make_pair(*nonDepExprIt, counter++)); + // Who is dependent on me? + ExprDepMap::iterator depsIt = subExprOfMap.find(*nonDepExprIt); + if (depsIt != subExprOfMap.end()) { + for (std::set<ref<Expr> >::iterator exprIt = depsIt->second.begin(); + exprIt != depsIt->second.end(); ) { + // Erase dependency + ExprDepMap::iterator subExprIt = usesSubExprMap.find(*exprIt); + assert(subExprIt != usesSubExprMap.end()); + assert(subExprIt->second.count(*nonDepExprIt)); + subExprIt->second.erase(*nonDepExprIt); + // If the expression *exprIt does not have any + // dependencies anymore, add it to the queue + if (!subExprIt->second.size()) { + nonDepBindings.push_back(*exprIt); + depsIt->second.erase(exprIt++); + } else { + ++exprIt; + } + } + } + } + // Store level bindings + orderedBindings.push_back(levelExprs); + } } void ExprSMTLIBPrinter::scanUpdates(const UpdateNode *un) { @@ -637,18 +819,69 @@ void ExprSMTLIBPrinter::printOptions() { } } -void ExprSMTLIBPrinter::printQuery() { - if (humanReadable) { - *p << "; Query from solver turned into an assert"; - p->breakLineI(); - } - +void ExprSMTLIBPrinter::printAssert(const ref<Expr> &e) { p->pushIndent(); *p << "(assert"; p->pushIndent(); printSeperator(); - printExpression(queryAssert, SORT_BOOL); + if (abbrMode == ABBR_LET && orderedBindings.size() != 0) { + // Only print let expression if we have bindings to use. + *p << "(let"; + p->pushIndent(); + printSeperator(); + *p << "("; + p->pushIndent(); + + // Clear original bindings, we'll be using orderedBindings + // to print nested let expressions + bindings.clear(); + + // Print each binding on its level + for (unsigned i = 0; i < orderedBindings.size(); ++i) { + BindingMap levelBindings = orderedBindings[i]; + for (BindingMap::const_iterator j = levelBindings.begin(); + j != levelBindings.end(); ++j) { + printSeperator(); + *p << "(?B" << j->second; + p->pushIndent(); + printSeperator(); + + // We can abbreviate SORT_BOOL or SORT_BITVECTOR in let expressions + printExpression(j->first, getSort(j->first)); + + p->popIndent(); + printSeperator(); + *p << ")"; + } + p->popIndent(); + printSeperator(); + *p << ")"; + printSeperator(); + + // Add nested let expressions (if any) + if (i < orderedBindings.size()-1) { + *p << "(let"; + p->pushIndent(); + printSeperator(); + *p << "("; + p->pushIndent(); + } + // Insert current level bindings so that they can be used + // in the next level during expression printing + bindings.insert(levelBindings.begin(), levelBindings.end()); + } + + printExpression(e, SORT_BOOL); + + for (unsigned i = 0; i < orderedBindings.size(); ++i) { + p->popIndent(); + printSeperator(); + *p << ")"; + } + } else { + printExpression(e, SORT_BOOL); + } p->popIndent(); printSeperator(); @@ -713,9 +946,9 @@ void ExprSMTLIBPrinter::printCastToSort(const ref<Expr> &e, *p << ")"; break; case SORT_BOOL: { - /* We make the assumption (might be wrong) that any bitvector whos unsigned - *decimal value is - * is zero is interpreted as "false", otherwise it is true. + /* We make the assumption (might be wrong) that any bitvector whose unsigned + * decimal value is is zero is interpreted as "false", otherwise it is + * true. * * This may not be the interpretation we actually want! */ @@ -743,7 +976,7 @@ void ExprSMTLIBPrinter::printCastToSort(const ref<Expr> &e, } break; default: - assert(0 && "Unsupported cast!"); + llvm_unreachable("Unsupported cast"); } } @@ -814,7 +1047,7 @@ void ExprSMTLIBPrinter::printLogicalOrBitVectorExpr( *p << ((s == SORT_BITVECTOR) ? "bvxor" : "xor"); break; default: - *p << "ERROR"; // this shouldn't happen + llvm_unreachable("Conversion from Expr to SMTLIB keyword failed"); } *p << " "; @@ -831,11 +1064,6 @@ void ExprSMTLIBPrinter::printLogicalOrBitVectorExpr( *p << ")"; } -void ExprSMTLIBPrinter::negateQueryExpression() { - // Negating the query - queryAssert = Expr::createIsZero(query->expr); -} - bool ExprSMTLIBPrinter::setSMTLIBboolOption(SMTLIBboolOptions option, SMTLIBboolValues value) { std::pair<std::map<SMTLIBboolOptions, bool>::iterator, bool> thePair; |