//===-- SolverTest.cpp ----------------------------------------------------===// // // The KLEE Symbolic Virtual Machine // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "gtest/gtest.h" #include "klee/Expr/ArrayCache.h" #include "klee/Expr/Constraints.h" #include "klee/Expr/Expr.h" #include "klee/Solver/Solver.h" #include "klee/Solver/SolverCmdLine.h" #include "llvm/ADT/StringExtras.h" #include using namespace klee; namespace { const int g_constants[] = { -1, 1, 4, 17, 0 }; const Expr::Width g_types[] = { Expr::Bool, Expr::Int8, Expr::Int16, Expr::Int32, Expr::Int64 }; ref getConstant(int value, Expr::Width width) { int64_t ext = value; uint64_t trunc = ext & (((uint64_t) -1LL) >> (64 - width)); return ConstantExpr::create(trunc, width); } // We have to have the cache globally scopped (and not in ``testOperation``) // because the Solver (i.e. in STP's case the STPBuilder) holds on to pointers // to allocated Arrays. ArrayCache ac; template void testOperation(Solver &solver, int value, Expr::Width operandWidth, Expr::Width resultWidth) { std::vector symbolicArgs; for (unsigned i = 0; i < T::numKids; i++) { if (!T::isValidKidWidth(i, operandWidth)) return; unsigned size = Expr::getMinBytesForWidth(operandWidth); static uint64_t id = 0; const Array *array = ac.CreateArray("arr" + llvm::utostr(++id), size); symbolicArgs.push_back(Expr::CreateArg(Expr::createTempRead(array, operandWidth))); } if (T::needsResultType()) symbolicArgs.push_back(Expr::CreateArg(resultWidth)); ref fullySymbolicExpr = Expr::createFromKind(T::kind, symbolicArgs); // For each kid, replace the kid with a constant value and verify // that the fully symbolic expression is equivalent to it when the // replaced value is appropriated constrained. for (unsigned kid = 0; kid < T::numKids; kid++) { std::vector partiallyConstantArgs(symbolicArgs); partiallyConstantArgs[kid] = getConstant(value, operandWidth); ref expr = NotOptimizedExpr::create(EqExpr::create(partiallyConstantArgs[kid].expr, symbolicArgs[kid].expr)); ref partiallyConstantExpr = Expr::createFromKind(T::kind, partiallyConstantArgs); ref queryExpr = EqExpr::create(fullySymbolicExpr, partiallyConstantExpr); ConstraintSet constraints; ConstraintManager cm(constraints); cm.addConstraint(expr); bool res; bool success = solver.mustBeTrue(Query(constraints, queryExpr), res); EXPECT_EQ(true, success) << "Constraint solving failed"; if (success) { EXPECT_EQ(true, res) << "Evaluation failed!\n" << "query " << queryExpr << " with " << expr; } } } template void testOpcode(Solver &solver, bool tryBool = true, bool tryZero = true, unsigned maxWidth = 64) { for (unsigned j=0; j maxWidth) continue; for (unsigned i=0; i(solver, value, type, type); continue; } for (unsigned k=0; k= Expr::getMinBytesForWidth(resultType)) continue; } testOperation(solver, value, type, resultType); } } } } TEST(SolverTest, Evaluation) { auto solver = klee::createCoreSolver(CoreSolverToUse); solver = createCexCachingSolver(std::move(solver)); solver = createCachingSolver(std::move(solver)); solver = createIndependentSolver(std::move(solver)); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver, false, true, 8); testOpcode(*solver, false, false, 8); testOpcode(*solver, false, false, 8); testOpcode(*solver, false, false, 8); testOpcode(*solver, false, false, 8); testOpcode(*solver, false); testOpcode(*solver, false); testOpcode(*solver, false); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); testOpcode(*solver); } }