//===-- ExprSMTLIBPrinter.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 <iostream>
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "klee/util/ExprSMTLIBPrinter.h"
using namespace std;
namespace ExprSMTLIBOptions
{
//Command line options
llvm::cl::opt<klee::ExprSMTLIBPrinter::ConstantDisplayMode> argConstantDisplayMode
("smtlib-display-constants", llvm::cl::desc("Sets how bitvector constants are written in generated SMT-LIBv2 files (default=dec)"),
llvm::cl::values( clEnumValN(klee::ExprSMTLIBPrinter::BINARY, "bin","Use binary form (e.g. #b00101101)"),
clEnumValN(klee::ExprSMTLIBPrinter::HEX, "hex","Use Hexadecimal form (e.g. #x2D)"),
clEnumValN(klee::ExprSMTLIBPrinter::DECIMAL, "dec","Use decimal form (e.g. (_ bv45 8) )"),
clEnumValEnd
),
llvm::cl::init(klee::ExprSMTLIBPrinter::DECIMAL)
);
llvm::cl::opt<bool> humanReadableSMTLIB("smtlib-human-readable",
llvm::cl::desc("Enables generated SMT-LIBv2 files to be human readable (default=off)"),
llvm::cl::init(false)
);
}
namespace klee
{
ExprSMTLIBPrinter::ExprSMTLIBPrinter() :
usedArrays(), o(NULL), query(NULL), p(NULL), haveConstantArray(false), logicToUse(QF_AUFBV),
humanReadable(ExprSMTLIBOptions::humanReadableSMTLIB), smtlibBoolOptions(), arraysToCallGetValueOn(NULL)
{
setConstantDisplayMode(ExprSMTLIBOptions::argConstantDisplayMode);
}
ExprSMTLIBPrinter::~ExprSMTLIBPrinter()
{
if(p!=NULL)
delete p;
}
void ExprSMTLIBPrinter::setOutput(std::ostream& output)
{
o = &output;
if(p!=NULL)
delete p;
p = new PrintContext(output);
}
void ExprSMTLIBPrinter::setQuery(const Query& q)
{
query = &q;
reset(); // clear the data structures
scanAll();
negateQueryExpression();
}
void ExprSMTLIBPrinter::reset()
{
usedArrays.clear();
haveConstantArray=false;
/* Clear the PRODUCE_MODELS option if it was automatically set.
* We need to do this because the next query might not need the
* (get-value) SMT-LIBv2 command.
*/
if(arraysToCallGetValueOn !=NULL)
setSMTLIBboolOption(PRODUCE_MODELS,OPTION_DEFAULT);
arraysToCallGetValueOn=NULL;
}
bool ExprSMTLIBPrinter::isHumanReadable()
{
return humanReadable;
}
bool ExprSMTLIBPrinter::setConstantDisplayMode(ConstantDisplayMode cdm)
{
if(cdm > DECIMAL)
return false;
this->cdm = cdm;
return true;
}
void ExprSMTLIBPrinter::printConstant(const ref<ConstantExpr>& e)
{
/* Handle simple boolean constants */
if(e->isTrue())
{
*p << "true";
return;
}
if(e->isFalse())
{
*p << "false";
return;
}
/* Handle bitvector constants */
std::string value;
/* SMTLIBv2 deduces the bit-width (should be 8-bits in our case)
* from the length of the string (e.g. zero is #b00000000). LLVM
* doesn't know about this so we need to pad the printed output
* with the appropriate number of zeros (zeroPad)
*/
unsigned int zeroPad=0;
switch(cdm)
{
case BINARY:
e->toString(value,2);
*p << "#b";
zeroPad = e->getWidth() - value.length();
for(unsigned int count=0; count < zeroPad; count++)
*p << "0";
*p << value ;
break;
case HEX:
e->toString(value,16);
*p << "#x";
zeroPad = (e->getWidth() / 4) - value.length();
for(unsigned int count=0; count < zeroPad; count++)
*p << "0";
*p << value ;
break;
case DECIMAL:
e->toString(value,10);
*p << "(_ bv" << value<< " " << e->getWidth() << ")";
break;
default:
std::cerr << "ExprSMTLIBPrinter::printConstant() : Unexpected Constant display mode" << std::endl;
}
}
void ExprSMTLIBPrinter::printExpression(const ref<Expr>& e, ExprSMTLIBPrinter::SMTLIB_SORT expectedSort)
{
//check if casting might be necessary
if(getSort(e) != expectedSort)
{
printCastToSort(e,expectedSort);
return;
}
switch(e->getKind())
{
case Expr::Constant:
printConstant(cast<ConstantExpr>(e));
return; //base case
case Expr::NotOptimized:
//skip to child
printExpression(e->getKid(0),expectedSort);
return;
case Expr::Read:
printReadExpr(cast<ReadExpr>(e));
return;
case Expr::Extract:
printExtractExpr(cast<ExtractExpr>(e));
return;
case Expr::SExt:
case Expr::ZExt:
printCastExpr(cast<CastExpr>(e));
return;
case Expr::Ne:
printNotEqualExpr(cast<NeExpr>(e));
return;
case Expr::Select:
//the if-then-else expression.
printSelectExpr(cast<SelectExpr>(e),expectedSort);
return;
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
* by enforcing the second argument is of the same type as the first.
*/
printSortArgsExpr(e,getSort(e->getKid(0)));
return;
case Expr::And:
case Expr::Or:
case Expr::Xor:
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.
*/
printLogicalOrBitVectorExpr(e,expectedSort);
return;
default:
/* The remaining operators (Add,Sub...,Ult,Ule,..)
* Expect SORT_BITVECTOR arguments
*/
printSortArgsExpr(e,SORT_BITVECTOR);
return;
}
}
void ExprSMTLIBPrinter::printReadExpr(const ref<ReadExpr>& e)
{
*p << "(" << getSMTLIBKeyword(e) << " ";
p->pushIndent();
printSeperator();
//print array with updates recursively
printUpdatesAndArray(e->updates.head,e->updates.root);
//print index
printSeperator();
printExpression(e->index,SORT_BITVECTOR);
p->popIndent();
printSeperator();
*p << ")";
}
void ExprSMTLIBPrinter::printExtractExpr(const ref<ExtractExpr>& e)
{
unsigned int lowIndex= e->offset;
unsigned int highIndex= lowIndex + e->width -1;
*p << "((_ " << getSMTLIBKeyword(e) << " " << highIndex << " " << lowIndex << ") ";
p->pushIndent(); //add indent for recursive call
printSeperator();
//recurse
printExpression(e->getKid(0),SORT_BITVECTOR);
p->popIndent(); //pop indent added for the recursive call
printSeperator();
*p << ")";
}
void ExprSMTLIBPrinter::printCastExpr(const ref<CastExpr>& e)
{
/* 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
*
* e.g
* ((_ sign_extend 64) (_ bv5 32))
*
* gives a (_ BitVec 96) instead of (_ BitVec 64)
*
* So we must work out how many bits we need to add.
*
* (e->width) is the desired number of bits
* (e->src->getWidth()) is the number of bits in the child
*/
unsigned int numExtraBits= (e->width) - (e->src->getWidth());
*p << "((_ " << getSMTLIBKeyword(e) << " " <<
numExtraBits << ") ";
p->pushIndent(); //add indent for recursive call
printSeperator();
//recurse
printExpression(e->src,SORT_BITVECTOR);
p->popIndent(); //pop indent added for recursive call
printSeperator();
*p << ")";
}
void ExprSMTLIBPrinter::printNotEqualExpr(const ref<NeExpr>& e)
{
*p << "(not (";
p->pushIndent();
*p << "=" << " ";
p->pushIndent();
printSeperator();
/* The "=" operators allows both sorts. We assume
* that the second argument sort should be forced to be the same sort as the
* first argument
*/
SMTLIB_SORT s = getSort(e->getKid(0));
printExpression(e->getKid(0),s);
printSeperator();
printExpression(e->getKid(1),s);
p->popIndent();
printSeperator();
*p << ")";
p->popIndent();
printSeperator();
*p << ")";
}
const char* ExprSMTLIBPrinter::getSMTLIBKeyword(const ref<Expr>& e)
{
switch(e->getKind())
{
case Expr::Read: return "select";
case Expr::Select: return "ite";
case Expr::Concat: return "concat";
case Expr::Extract: return "extract";
case Expr::ZExt: return "zero_extend";
case Expr::SExt: return "sign_extend";
case Expr::Add: return "bvadd";
case Expr::Sub: return "bvsub";
case Expr::Mul: return "bvmul";
case Expr::UDiv: return "bvudiv";
case Expr::SDiv: return "bvsdiv";
case Expr::URem: return "bvurem";
case Expr::SRem: return "bvsrem";
/* And, Xor, Not and Or are not handled here because there different versions
* for different sorts. See printLogicalOrBitVectorExpr()
*/
case Expr::Shl: return "bvshl";
case Expr::LShr: return "bvlshr";
case Expr::AShr: return "bvashr";
case Expr::Eq: return "=";
//Not Equal does not exist directly in SMTLIBv2
case Expr::Ult: return "bvult";
case Expr::Ule: return "bvule";
case Expr::Ugt: return "bvugt";
case Expr::Uge: return "bvuge";
case Expr::Slt: return "bvslt";
case Expr::Sle: return "bvsle";
case Expr::Sgt: return "bvsgt";
case Expr::Sge: return "bvsge";
default:
return "<error>";
}
}
void ExprSMTLIBPrinter::printUpdatesAndArray(const UpdateNode* un, const Array* root)
{
if(un!=NULL)
{
*p << "(store ";
p->pushIndent();
printSeperator();
//recurse to get the array or update that this store operations applies to
printUpdatesAndArray(un->next,root);
printSeperator();
//print index
printExpression(un->index,SORT_BITVECTOR);
printSeperator();
//print value that is assigned to this index of the array
printExpression(un->value,SORT_BITVECTOR);
p->popIndent();
printSeperator();
*p << ")";
}
else
{
//The base case of the recursion
*p << root->name;
}
}
void ExprSMTLIBPrinter::scanAll()
{
//perform scan of all expressions
for(ConstraintManager::const_iterator i= query->constraints.begin(); i != query->constraints.end(); i++)
scan(*i);
//Scan the query too
scan(query->expr);
}
void ExprSMTLIBPrinter::generateOutput()
{
if(p==NULL || query == NULL || o ==NULL)
{
std::cerr << "ExprSMTLIBPrinter::generateOutput() Can't print SMTLIBv2. Output or query bad!" << std::endl;
return;
}
if(humanReadable) printNotice();
printOptions();
printSetLogic();
printArrayDeclarations();
printConstraints();
printQuery();
printAction();
printExit();
}
void ExprSMTLIBPrinter::printSetLogic()
{
*o << "(set-logic ";
switch(logicToUse)
{
case QF_ABV: *o << "QF_ABV"; break;
case QF_AUFBV: *o << "QF_AUFBV" ; break;
}
*o << " )" << std::endl;
}
void ExprSMTLIBPrinter::printArrayDeclarations()
{
//Assume scan() has been called
if(humanReadable)
*o << "; Array declarations" << endl;
//declare arrays
for(set<const Array*>::iterator it = usedArrays.begin(); it != usedArrays.end(); it++)
{
*o << "(declare-fun " << (*it)->name << " () "
"(Array (_ BitVec " << (*it)->getDomain() << ") "
"(_ BitVec " << (*it)->getRange() << ") ) )" << endl;
}
//Set array values for constant values
if(haveConstantArray)
{
if(humanReadable)
*o << "; Constant Array Definitions" << endl;
const Array* array;
//loop over found arrays
for(set<const Array*>::iterator it = usedArrays.begin(); it != usedArrays.end(); it++)
{
array= *it;
int byteIndex=0;
if(array->isConstantArray())
{
/*loop over elements in the array and generate an assert statement
for each one
*/
for(vector< ref<ConstantExpr> >::const_iterator ce= array->constantValues.begin();
ce != array->constantValues.end(); ce++, byteIndex++)
{
*p << "(assert (";
p->pushIndent();
*p << "= ";
p->pushIndent();
printSeperator();
*p << "(select " << array->name << " (_ bv" << byteIndex << " " << array->getDomain() << ") )";
printSeperator();
printConstant((*ce));
p->popIndent();
printSeperator();
*p << ")";
p->popIndent();
printSeperator();
*p << ")";
p->breakLineI();
}
}
}
}
}
void ExprSMTLIBPrinter::printConstraints()
{
if(humanReadable)
*o << "; Constraints" << endl;
//Generate assert statements for each constraint
for(ConstraintManager::const_iterator i= query->constraints.begin(); i != query->constraints.end(); i++)
{
*p << "(assert ";
p->pushIndent();
printSeperator();
//recurse into Expression
printExpression(*i,SORT_BOOL);
p->popIndent();
printSeperator();
*p << ")"; p->breakLineI();
}
}
void ExprSMTLIBPrinter::printAction()
{
//Ask solver to check for satisfiability
*o << "(check-sat)" << endl;
/* If we has 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())
{
const Array* theArray=0;
//loop over the array names
for(vector<const Array*>::const_iterator it = arraysToCallGetValueOn->begin(); it != arraysToCallGetValueOn->end(); it++)
{
theArray=*it;
//Loop over the array indices
for(unsigned int index=0; index < theArray->size; ++index)
{
*o << "(get-value ( (select " << (**it).name <<
" (_ bv" << index << " " << theArray->getDomain() << ") ) ) )" << endl;
}
}
}
}
void ExprSMTLIBPrinter::scan(const ref<Expr>& e)
{
if(e.isNull())
{
std::cerr << "ExprSMTLIBPrinter::scan() : Found NULL expression!" << std::endl;
return;
}
if(isa<ConstantExpr>(e))
return; //we don't need to scan simple constants
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));
}
void ExprSMTLIBPrinter::scanUpdates(const UpdateNode* un)
{
while(un != NULL)
{
scan(un->index);
scan(un->value);
un= un->next;
}
}
void ExprSMTLIBPrinter::printExit()
{
*o << "(exit)" << endl;
}
bool ExprSMTLIBPrinter::setLogic(SMTLIBv2Logic l)
{
if(l > QF_AUFBV)
return false;
logicToUse=l;
return true;
}
void ExprSMTLIBPrinter::printSeperator()
{
if(humanReadable)
p->breakLineI();
else
p->write(" ");
}
void ExprSMTLIBPrinter::printNotice()
{
*o << "; This file conforms to SMTLIBv2 and was generated by KLEE" << endl;
}
void ExprSMTLIBPrinter::setHumanReadable(bool hr)
{
humanReadable=hr;
}
void ExprSMTLIBPrinter::printOptions()
{
//Print out SMTLIBv2 boolean options
for(std::map<SMTLIBboolOptions,bool>::const_iterator i= smtlibBoolOptions.begin(); i!= smtlibBoolOptions.end(); i++)
{
*o << "(set-option :" << getSMTLIBOptionString(i->first) <<
" " << ((i->second)?"true":"false") << ")" << endl;
}
}
void ExprSMTLIBPrinter::printQuery()
{
if(humanReadable)
{
*p << "; Query from solver turned into an assert";
p->breakLineI();
}
p->pushIndent();
*p << "(assert";
p->pushIndent();
printSeperator();
printExpression(queryAssert,SORT_BOOL);
p->popIndent();
printSeperator();
*p << ")";
p->popIndent();
p->breakLineI();
}
ExprSMTLIBPrinter::SMTLIB_SORT ExprSMTLIBPrinter::getSort(const ref<Expr>& e)
{
/* We could handle every operator in a large switch statement,
* but this seems more elegant.
*/
if(e->getKind() == Expr::Extract)
{
/* This is a special corner case. In most cases if a node in the expression tree
* is of width 1 it should be considered as SORT_BOOL. However it is possible to
* perform an extract operation on a SORT_BITVECTOR and produce a SORT_BITVECTOR of length 1.
* The ((_ extract i j) () ) operation in SMTLIBv2 always produces SORT_BITVECTOR
*/
return SORT_BITVECTOR;
}
else
return (e->getWidth() == Expr::Bool)?(SORT_BOOL):(SORT_BITVECTOR);
}
void ExprSMTLIBPrinter::printCastToSort(const ref<Expr>& e, ExprSMTLIBPrinter::SMTLIB_SORT sort)
{
switch(sort)
{
case SORT_BITVECTOR:
if(humanReadable)
{
p->breakLineI(); *p << ";Performing implicit bool to bitvector cast"; p->breakLine();
}
//We assume the e is a bool that we need to cast to a bitvector sort.
*p << "(ite"; p->pushIndent(); printSeperator();
printExpression(e,SORT_BOOL); printSeperator();
*p << "(_ bv1 1)" ; printSeperator(); //printing the "true" bitvector
*p << "(_ bv0 1)" ; p->popIndent(); printSeperator(); //printing the "false" bitvector
*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.
*
* This may not be the interpretation we actually want!
*/
Expr::Width bitWidth=e->getWidth();
if(humanReadable)
{
p->breakLineI(); *p << ";Performing implicit bitvector to bool cast"; p->breakLine();
}
*p << "(bvugt"; p->pushIndent(); printSeperator();
// We assume is e is a bitvector
printExpression(e,SORT_BITVECTOR); printSeperator();
*p << "(_ bv0 " << bitWidth << ")"; p->popIndent(); printSeperator(); //Zero bitvector of required width
*p << ")";
if(bitWidth!=Expr::Bool)
std::cerr << "ExprSMTLIBPrinter : Warning. Casting a bitvector (length " << bitWidth << ") to bool!" << std::endl;
}
break;
default:
assert(0 && "Unsupported cast!");
}
}
void ExprSMTLIBPrinter::printSelectExpr(const ref<SelectExpr>& e, ExprSMTLIBPrinter::SMTLIB_SORT s)
{
//This is the if-then-else expression
*p << "(" << getSMTLIBKeyword(e) << " ";
p->pushIndent(); //add indent for recursive call
//The condition
printSeperator();
printExpression(e->getKid(0),SORT_BOOL);
/* This operator is special in that the remaining children
* can be of any sort.
*/
//if true
printSeperator();
printExpression(e->getKid(1),s);
//if false
printSeperator();
printExpression(e->getKid(2),s);
p->popIndent(); //pop indent added for recursive call
printSeperator();
*p << ")";
}
void ExprSMTLIBPrinter::printSortArgsExpr(const ref<Expr>& e, ExprSMTLIBPrinter::SMTLIB_SORT s)
{
*p << "(" << getSMTLIBKeyword(e) << " ";
p->pushIndent(); //add indent for recursive call
//loop over children and recurse into each expecting they are of sort "s"
for(unsigned int i=0; i < e->getNumKids(); i++)
{
printSeperator();
printExpression(e->getKid(i),s);
}
p->popIndent(); //pop indent added for recursive call
printSeperator();
*p << ")";
}
void ExprSMTLIBPrinter::printLogicalOrBitVectorExpr(const ref<Expr>& e, ExprSMTLIBPrinter::SMTLIB_SORT s)
{
/* For these operators it is the case that the expected sort is the same as the sorts
* of the arguments.
*/
*p << "(";
switch(e->getKind())
{
case Expr::And:
*p << ((s==SORT_BITVECTOR)?"bvand":"and");
break;
case Expr::Not:
*p << ((s==SORT_BITVECTOR)?"bvnot":"not");
break;
case Expr::Or:
*p << ((s==SORT_BITVECTOR)?"bvor":"or");
break;
case Expr::Xor:
*p << ((s==SORT_BITVECTOR)?"bvxor":"xor");
break;
default:
*p << "ERROR"; // this shouldn't happen
}
*p << " ";
p->pushIndent(); //add indent for recursive call
//loop over children and recurse into each expecting they are of sort "s"
for(unsigned int i=0; i < e->getNumKids(); i++)
{
printSeperator();
printExpression(e->getKid(i),s);
}
p->popIndent(); //pop indent added for recursive call
printSeperator();
*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;
bool theValue= (value==OPTION_TRUE)?true:false;
switch(option)
{
case PRINT_SUCCESS:
case PRODUCE_MODELS:
case INTERACTIVE_MODE:
thePair=smtlibBoolOptions.insert(std::pair<SMTLIBboolOptions,bool>(option,theValue));
if(value== OPTION_DEFAULT)
{
//we should unset (by removing from map) this option so the solver uses its default
smtlibBoolOptions.erase(thePair.first);
return true;
}
if(!thePair.second)
{
//option was already present so modify instead.
thePair.first->second=value;
}
return true;
default:
return false;
}
}
void ExprSMTLIBPrinter::setArrayValuesToGet(const std::vector<const Array*>& a)
{
arraysToCallGetValueOn = &a;
//This option must be set in order to use the SMTLIBv2 command (get-value () )
if(!a.empty())
setSMTLIBboolOption(PRODUCE_MODELS,OPTION_TRUE);
/* There is a risk that users will ask about array values that aren't
* even in the query. We should add them to the usedArrays list and hope
* that the solver knows what to do when we ask for the values of arrays
* that don't feature in our query!
*/
for(vector<const Array*>::const_iterator i = a.begin(); i!= a.end() ; ++i)
{
usedArrays.insert(*i);
}
}
const char* ExprSMTLIBPrinter::getSMTLIBOptionString(ExprSMTLIBPrinter::SMTLIBboolOptions option)
{
switch(option)
{
case PRINT_SUCCESS: return "print-success";
case PRODUCE_MODELS: return "produce-models";
case INTERACTIVE_MODE: return "interactive-mode";
default:
return "unknown-option";
}
}
}