klee
ArrayExprVisitor.cpp
Go to the documentation of this file.
1//===-- ArrayExprVisitor.cpp ----------------------------------------------===//
2//
3// The KLEE Symbolic Virtual Machine
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
11
13
14#include <algorithm>
15
16using namespace klee;
17
18//------------------------------ HELPER FUNCTIONS ---------------------------//
19bool ArrayExprHelper::isReadExprAtOffset(ref<Expr> e, const ReadExpr *base,
20 ref<Expr> offset) {
21 const ReadExpr *re = dyn_cast<ReadExpr>(e.get());
22 if (!re || (re->getWidth() != Expr::Int8))
23 return false;
24 return SubExpr::create(re->index, base->index) == offset;
25}
26
28 const ReadExpr *base = dyn_cast<ReadExpr>(ce.getKid(0));
29
30 // right now, all Reads are byte reads but some
31 // transformations might change this
32 if (!base || base->getWidth() != Expr::Int8)
33 return nullptr;
34
35 // Get stride expr in proper index width.
36 Expr::Width idxWidth = base->index->getWidth();
37 ref<Expr> strideExpr = ConstantExpr::alloc(-1, idxWidth);
38 ref<Expr> offset = ConstantExpr::create(0, idxWidth);
39
40 ref<Expr> e = ce.getKid(1);
41
42 // concat chains are unbalanced to the right
43 while (e->getKind() == Expr::Concat) {
44 offset = AddExpr::create(offset, strideExpr);
45 if (!isReadExprAtOffset(e->getKid(0), base, offset))
46 return nullptr;
47 e = e->getKid(1);
48 }
49
50 offset = AddExpr::create(offset, strideExpr);
51 if (!isReadExprAtOffset(e, base, offset))
52 return nullptr;
53
54 return cast<ReadExpr>(e.get());
55}
56
57//--------------------------- INDEX-BASED OPTIMIZATION-----------------------//
61 if (base) {
62 // It is an interesting ReadExpr if it contains a concrete array
63 // that is read at a symbolic index
64 if (base->updates.root->isConstantArray() &&
65 !isa<ConstantExpr>(base->index)) {
66 for (const auto *un = base->updates.head.get(); un; un = un->next.get()) {
67 if (!isa<ConstantExpr>(un->index) || !isa<ConstantExpr>(un->value)) {
68 incompatible = true;
69 return Action::skipChildren();
70 }
71 }
73 compatible.visit(base->index);
74 if (compatible.isCompatible() &&
75 addedIndexes.find(base->index.get()->hash()) == addedIndexes.end()) {
76 if (arrays.find(base->updates.root) == arrays.end()) {
77 arrays.insert(
78 std::make_pair(base->updates.root, std::vector<ref<Expr> >()));
79 } else {
80 // Another possible index to resolve, currently unsupported
81 incompatible = true;
82 return Action::skipChildren();
83 }
84 arrays.find(base->updates.root)->second.push_back(base->index);
85 addedIndexes.insert(base->index.get()->hash());
86 } else if (compatible.hasInnerReads()) {
87 // This Read has an inner Read, we want to optimize the inner one
88 // to create a cascading effect during assignment evaluation
89 return Action::doChildren();
90 }
91 return Action::skipChildren();
92 }
93 }
94 return Action::doChildren();
95}
97 // It is an interesting ReadExpr if it contains a concrete array
98 // that is read at a symbolic index
99 if (re.updates.root->isConstantArray() && !isa<ConstantExpr>(re.index)) {
100 for (const auto *un = re.updates.head.get(); un; un = un->next.get()) {
101 if (!isa<ConstantExpr>(un->index) || !isa<ConstantExpr>(un->value)) {
102 incompatible = true;
103 return Action::skipChildren();
104 }
105 }
107 compatible.visit(re.index);
108 if (compatible.isCompatible() &&
109 addedIndexes.find(re.index.get()->hash()) == addedIndexes.end()) {
110 if (arrays.find(re.updates.root) == arrays.end()) {
111 arrays.insert(
112 std::make_pair(re.updates.root, std::vector<ref<Expr> >()));
113 } else {
114 // Another possible index to resolve, currently unsupported
115 incompatible = true;
116 return Action::skipChildren();
117 }
118 arrays.find(re.updates.root)->second.push_back(re.index);
119 addedIndexes.insert(re.index.get()->hash());
120 } else if (compatible.hasInnerReads()) {
121 // This Read has an inner Read, we want to optimize the inner one
122 // to create a cascading effect during assignment evaluation
123 return Action::doChildren();
124 }
125 return Action::skipChildren();
126 } else if (re.updates.root->isSymbolicArray()) {
127 incompatible = true;
128 }
129
130 return Action::doChildren();
131}
132
135 if (re.updates.head) {
136 compatible = false;
137 return Action::skipChildren();
138 } else if (re.updates.root->isConstantArray() &&
139 !isa<ConstantExpr>(re.index)) {
140 compatible = false;
141 inner = true;
142 return Action::skipChildren();
143 }
144 return Action::doChildren();
145}
147 compatible = false;
148 return Action::skipChildren();
149}
151 compatible = false;
152 return Action::skipChildren();
153}
155 compatible = false;
156 return Action::skipChildren();
157}
158
161 if (ReadExpr *re = dyn_cast<ReadExpr>(ce.getKid(0))) {
162 if (re->updates.root->hash() == array->hash() && width < ce.getWidth()) {
164 width = ce.getWidth();
165 }
166 } else if (ReadExpr *re = dyn_cast<ReadExpr>(ce.getKid(1))) {
167 if (re->updates.root->hash() == array->hash() && width < ce.getWidth()) {
169 width = ce.getWidth();
170 }
171 }
172 return Action::doChildren();
173}
175 if (isa<ConstantExpr>(e.getKid(0)))
176 mul = e.getKid(0);
177 else if (isa<ConstantExpr>(e.getKid(0)))
178 mul = e.getKid(1);
179 return Action::doChildren();
180}
181
182//-------------------------- VALUE-BASED OPTIMIZATION------------------------//
185 if (base) {
186 return inspectRead(const_cast<ConcatExpr *>(&ce), ce.getWidth(), *base);
187 }
188 return Action::doChildren();
189}
191 return inspectRead(const_cast<ReadExpr *>(&re), re.getWidth(), re);
192}
193// This method is a mess because I want to avoid looping over the UpdateList
194// values twice
196 Expr::Width width,
197 const ReadExpr &re) {
198 // pre(*): index is symbolic
199 if (!isa<ConstantExpr>(re.index)) {
200 if (readInfo.find(&re) == readInfo.end()) {
201 if (re.updates.root->isSymbolicArray() && !re.updates.head) {
202 return Action::doChildren();
203 }
204 if (re.updates.head) {
205 // Check preconditions on UpdateList nodes
206 bool hasConcreteValues = false;
207 for (const auto *un = re.updates.head.get(); un; un = un->next.get()) {
208 // Symbolic case - \inv(update): index is concrete
209 if (!isa<ConstantExpr>(un->index)) {
210 incompatible = true;
211 break;
212 } else if (!isa<ConstantExpr>(un->value)) {
213 // We tell the optimization that there is a symbolic value,
214 // otherwise we rely on the concrete optimization procedure
215 symbolic = true;
216 } else if (re.updates.root->isSymbolicArray() &&
217 isa<ConstantExpr>(un->value)) {
218 // We can optimize symbolic array, but only if they have
219 // at least one concrete value
220 hasConcreteValues = true;
221 }
222 }
223 // Symbolic case - if array is symbolic, then we need at least one
224 // concrete value
225 if (re.updates.root->isSymbolicArray()) {
226 if (hasConcreteValues)
227 symbolic = true;
228 else
229 incompatible = true;
230 }
231
232 if (incompatible)
233 return Action::skipChildren();
234 }
236 reads.push_back(&re);
237 readInfo.emplace(&re, std::make_pair(hash, width));
238 }
239 return Action::skipChildren();
240 }
241 return Action::doChildren();
242}
243
246 auto found = optimized.find(const_cast<ConcatExpr *>(&ce));
247 if (found != optimized.end()) {
248 return Action::changeTo((*found).second.get());
249 }
250 return Action::doChildren();
251}
253 auto found = optimized.find(const_cast<ReadExpr *>(&re));
254 if (found != optimized.end()) {
255 return Action::changeTo((*found).second.get());
256 }
257 return Action::doChildren();
258}
static bool isReadExprAtOffset(ref< Expr > e, const ReadExpr *base, ref< Expr > offset)
static ReadExpr * hasOrderedReads(const ConcatExpr &ce)
std::map< const ReadExpr *, std::pair< ref< Expr >, Expr::Width > > & readInfo
Action visitConcat(const ConcatExpr &) override
Action inspectRead(ref< Expr > hash, Expr::Width width, const ReadExpr &)
std::vector< const ReadExpr * > & reads
Action visitRead(const ReadExpr &) override
Action visitConcat(const ConcatExpr &) override
Action visitRead(const ReadExpr &re) override
ExprHashMap< ref< Expr > > optimized
bool isConstantArray() const
Definition: Expr.h:525
bool isSymbolicArray() const
Definition: Expr.h:524
unsigned hash() const
Definition: Expr.h:534
ref< Expr > getKid(unsigned i) const
Definition: Expr.h:697
Width getWidth() const
Definition: Expr.h:691
Action visitConcat(const ConcatExpr &) override
Action visitRead(const ReadExpr &) override
std::unordered_set< unsigned > addedIndexes
static ref< ConstantExpr > create(uint64_t v, Width w)
Definition: Expr.h:1079
static ref< ConstantExpr > alloc(const llvm::APInt &v)
Definition: Expr.h:1065
static Action changeTo(const ref< Expr > &expr)
Definition: ExprVisitor.h:36
static Action doChildren()
Definition: ExprVisitor.h:39
static Action skipChildren()
Definition: ExprVisitor.h:40
ref< Expr > visit(const ref< Expr > &e)
Definition: ExprVisitor.cpp:26
static const Width InvalidWidth
Definition: Expr.h:99
virtual Kind getKind() const =0
virtual unsigned hash() const
Returns the pre-computed hash of the current expression.
Definition: Expr.h:222
virtual Width getWidth() const =0
@ Concat
Definition: Expr.h:125
unsigned Width
The type of an expression is simply its width, in bits.
Definition: Expr.h:97
virtual ref< Expr > getKid(unsigned i) const =0
static const Width Int8
Definition: Expr.h:101
Action visitRead(const ReadExpr &) override
Action visitURem(const URemExpr &) override
Action visitOr(const OrExpr &) override
Action visitSRem(const SRemExpr &) override
Action visitConcat(const ConcatExpr &) override
Action visitMul(const MulExpr &) override
Class representing a one byte read from an array.
Definition: Expr.h:565
ref< Expr > index
Definition: Expr.h:572
Width getWidth() const
Definition: Expr.h:583
UpdateList updates
Definition: Expr.h:571
ref< UpdateNode > head
pointer to the most recent update node
Definition: Expr.h:546
const Array * root
Definition: Expr.h:543
T * get() const
Definition: Ref.h:129
Definition: main.cpp:291