//===-- ArrayExprVisitor.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/ArrayExprVisitor.h" #include "klee/Support/ErrorHandling.h" #include using namespace klee; //------------------------------ HELPER FUNCTIONS ---------------------------// bool ArrayExprHelper::isReadExprAtOffset(ref e, const ReadExpr *base, ref offset) { const ReadExpr *re = dyn_cast(e.get()); if (!re || (re->getWidth() != Expr::Int8)) return false; return SubExpr::create(re->index, base->index) == offset; } ReadExpr *ArrayExprHelper::hasOrderedReads(const ConcatExpr &ce) { const ReadExpr *base = dyn_cast(ce.getKid(0)); // right now, all Reads are byte reads but some // transformations might change this if (!base || base->getWidth() != Expr::Int8) return nullptr; // Get stride expr in proper index width. Expr::Width idxWidth = base->index->getWidth(); ref strideExpr = ConstantExpr::alloc(-1, idxWidth); ref offset = ConstantExpr::create(0, idxWidth); ref e = ce.getKid(1); // concat chains are unbalanced to the right while (e->getKind() == Expr::Concat) { offset = AddExpr::create(offset, strideExpr); if (!isReadExprAtOffset(e->getKid(0), base, offset)) return nullptr; e = e->getKid(1); } offset = AddExpr::create(offset, strideExpr); if (!isReadExprAtOffset(e, base, offset)) return nullptr; return cast(e.get()); } //--------------------------- INDEX-BASED OPTIMIZATION-----------------------// ExprVisitor::Action ConstantArrayExprVisitor::visitConcat(const ConcatExpr &ce) { ReadExpr *base = ArrayExprHelper::hasOrderedReads(ce); if (base) { // It is an interesting ReadExpr if it contains a concrete array // that is read at a symbolic index if (base->updates.root->isConstantArray() && !isa(base->index)) { for (const auto *un = base->updates.head.get(); un; un = un->next.get()) { if (!isa(un->index) || !isa(un->value)) { incompatible = true; return Action::skipChildren(); } } IndexCompatibilityExprVisitor compatible; compatible.visit(base->index); if (compatible.isCompatible() && addedIndexes.find(base->index.get()->hash()) == addedIndexes.end()) { if (arrays.find(base->updates.root) == arrays.end()) { arrays.insert( std::make_pair(base->updates.root, std::vector >())); } else { // Another possible index to resolve, currently unsupported incompatible = true; return Action::skipChildren(); } arrays.find(base->updates.root)->second.push_back(base->index); addedIndexes.insert(base->index.get()->hash()); } else if (compatible.hasInnerReads()) { // This Read has an inner Read, we want to optimize the inner one // to create a cascading effect during assignment evaluation return Action::doChildren(); } return Action::skipChildren(); } } return Action::doChildren(); } ExprVisitor::Action ConstantArrayExprVisitor::visitRead(const ReadExpr &re) { // It is an interesting ReadExpr if it contains a concrete array // that is read at a symbolic index if (re.updates.root->isConstantArray() && !isa(re.index)) { for (const auto *un = re.updates.head.get(); un; un = un->next.get()) { if (!isa(un->index) || !isa(un->value)) { incompatible = true; return Action::skipChildren(); } } IndexCompatibilityExprVisitor compatible; compatible.visit(re.index); if (compatible.isCompatible() && addedIndexes.find(re.index.get()->hash()) == addedIndexes.end()) { if (arrays.find(re.updates.root) == arrays.end()) { arrays.insert( std::make_pair(re.updates.root, std::vector >())); } else { // Another possible index to resolve, currently unsupported incompatible = true; return Action::skipChildren(); } arrays.find(re.updates.root)->second.push_back(re.index); addedIndexes.insert(re.index.get()->hash()); } else if (compatible.hasInnerReads()) { // This Read has an inner Read, we want to optimize the inner one // to create a cascading effect during assignment evaluation return Action::doChildren(); } return Action::skipChildren(); } else if (re.updates.root->isSymbolicArray()) { incompatible = true; } return Action::doChildren(); } ExprVisitor::Action IndexCompatibilityExprVisitor::visitRead(const ReadExpr &re) { if (!re.updates.head.isNull()) { compatible = false; return Action::skipChildren(); } else if (re.updates.root->isConstantArray() && !isa(re.index)) { compatible = false; inner = true; return Action::skipChildren(); } return Action::doChildren(); } ExprVisitor::Action IndexCompatibilityExprVisitor::visitURem(const URemExpr &) { compatible = false; return Action::skipChildren(); } ExprVisitor::Action IndexCompatibilityExprVisitor::visitSRem(const SRemExpr &) { compatible = false; return Action::skipChildren(); } ExprVisitor::Action IndexCompatibilityExprVisitor::visitOr(const OrExpr &) { compatible = false; return Action::skipChildren(); } ExprVisitor::Action IndexTransformationExprVisitor::visitConcat(const ConcatExpr &ce) { if (ReadExpr *re = dyn_cast(ce.getKid(0))) { if (re->updates.root->hash() == array->hash() && width < ce.getWidth()) { if (width == Expr::InvalidWidth) width = ce.getWidth(); } } else if (ReadExpr *re = dyn_cast(ce.getKid(1))) { if (re->updates.root->hash() == array->hash() && width < ce.getWidth()) { if (width == Expr::InvalidWidth) width = ce.getWidth(); } } return Action::doChildren(); } ExprVisitor::Action IndexTransformationExprVisitor::visitMul(const MulExpr &e) { if (isa(e.getKid(0))) mul = e.getKid(0); else if (isa(e.getKid(0))) mul = e.getKid(1); return Action::doChildren(); } //-------------------------- VALUE-BASED OPTIMIZATION------------------------// ExprVisitor::Action ArrayReadExprVisitor::visitConcat(const ConcatExpr &ce) { ReadExpr *base = ArrayExprHelper::hasOrderedReads(ce); if (base) { return inspectRead(const_cast(&ce), ce.getWidth(), *base); } return Action::doChildren(); } ExprVisitor::Action ArrayReadExprVisitor::visitRead(const ReadExpr &re) { return inspectRead(const_cast(&re), re.getWidth(), re); } // This method is a mess because I want to avoid looping over the UpdateList // values twice ExprVisitor::Action ArrayReadExprVisitor::inspectRead(ref hash, Expr::Width width, const ReadExpr &re) { // pre(*): index is symbolic if (!isa(re.index)) { if (readInfo.find(&re) == readInfo.end()) { if (re.updates.root->isSymbolicArray() && re.updates.head.isNull()) { return Action::doChildren(); } if (!re.updates.head.isNull()) { // Check preconditions on UpdateList nodes bool hasConcreteValues = false; for (const auto *un = re.updates.head.get(); un; un = un->next.get()) { // Symbolic case - \inv(update): index is concrete if (!isa(un->index)) { incompatible = true; break; } else if (!isa(un->value)) { // We tell the optimization that there is a symbolic value, // otherwise we rely on the concrete optimization procedure symbolic = true; } else if (re.updates.root->isSymbolicArray() && isa(un->value)) { // We can optimize symbolic array, but only if they have // at least one concrete value hasConcreteValues = true; } } // Symbolic case - if array is symbolic, then we need at least one // concrete value if (re.updates.root->isSymbolicArray()) { if (hasConcreteValues) symbolic = true; else incompatible = true; } if (incompatible) return Action::skipChildren(); } symbolic |= re.updates.root->isSymbolicArray(); reads.push_back(&re); readInfo.emplace(&re, std::make_pair(hash, width)); } return Action::skipChildren(); } return Action::doChildren(); } ExprVisitor::Action ArrayValueOptReplaceVisitor::visitConcat(const ConcatExpr &ce) { auto found = optimized.find(const_cast(&ce)); if (found != optimized.end()) { return Action::changeTo((*found).second.get()); } return Action::doChildren(); } ExprVisitor::Action ArrayValueOptReplaceVisitor::visitRead(const ReadExpr &re) { auto found = optimized.find(const_cast(&re)); if (found != optimized.end()) { return Action::changeTo((*found).second.get()); } return Action::doChildren(); }