Skip to content

Commit dbeb187

Browse files
authored
Merge pull request #6290 from jezhiggins/vsd-eval-within-our-lifetime
VSD - constant expression evaluation speedup
2 parents 3092e7b + bec4cb6 commit dbeb187

File tree

3 files changed

+105
-30
lines changed

3 files changed

+105
-30
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
int nondet_int(void);
2+
3+
struct MH
4+
{
5+
int s;
6+
int t;
7+
int to;
8+
int su;
9+
};
10+
11+
int main(void)
12+
{
13+
int l = nondet_int();
14+
int s = nondet_int();
15+
int n = nondet_int();
16+
int o = nondet_int();
17+
int nt = nondet_int();
18+
int ns = nondet_int();
19+
20+
struct MH mh;
21+
mh.s = nondet_int();
22+
mh.t = nondet_int();
23+
mh.to = nondet_int();
24+
mh.su = nondet_int();
25+
26+
int result =
27+
((l >= mh.s ? 1 : 0) &
28+
(((mh.s >= s ? 1 : 0) &
29+
(((n >= mh.t ? 1 : 0) &
30+
(((mh.t >= o ? 1 : 0) & ((((int)mh.to == (int)nt ? 0 : 1) &
31+
((int)mh.su == (int)ns ? 0 : 1)) == 0
32+
? 0
33+
: 1)) == 0
34+
? 0
35+
: 1)) == 0
36+
? 0
37+
: 1)) == 0
38+
? 0
39+
: 1)) == 0;
40+
41+
assert(result = 0);
42+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--vsd
4+
^SIGNAL=0$
5+
^EXIT=0$
6+
--
7+
--
8+
This expression is derived from a real system under test. Due to inadvertent reevaluation of expression operands, all expression evaluations had geometric complexity (even more so in the presence of TOP values). This particular expression took nearly two hours to evaluate. Reworking to prevent repeated reevaluation brings this down to a few tenths.
9+
See https://github.com/diffblue/cbmc/pull/6290
10+
If this test appears to hang, the constant_evaluator is probably your first port of call.

src/analyses/variable-sensitivity/abstract_value_object.cpp

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,10 @@ class constants_evaluator
225225
public:
226226
constants_evaluator(
227227
const exprt &e,
228+
const std::vector<abstract_object_pointert> &ops,
228229
const abstract_environmentt &env,
229230
const namespacet &n)
230-
: expression(e), environment(env), ns(n)
231+
: expression(e), operands(ops), environment(env), ns(n)
231232
{
232233
}
233234

@@ -244,28 +245,26 @@ class constants_evaluator
244245
private:
245246
abstract_object_pointert transform() const
246247
{
247-
exprt expr = adjust_expression_for_rounding_mode();
248-
auto operands = expr.operands();
249-
expr.operands().clear();
248+
auto expr = adjust_expression_for_rounding_mode();
250249

251-
// Two passes over the expression - one for simplification,
252-
// another to check if there are any top subexpressions left
253-
for(const exprt &op : operands)
250+
auto operand_is_top = false;
251+
for(size_t i = 0; i != operands.size(); ++i)
254252
{
255-
auto lhs_value = eval_constant(op);
253+
auto lhs_value = operands[i]->to_constant();
256254

257255
// do not give up if a sub-expression is not a constant,
258256
// because the whole expression may still be simplified in some cases
259-
expr.operands().push_back(lhs_value.is_nil() ? op : lhs_value);
257+
// (eg multiplication by zero)
258+
if(lhs_value.is_not_nil())
259+
expr.operands()[i] = lhs_value;
260+
else
261+
operand_is_top = true;
260262
}
261263

262-
exprt simplified = simplify_expr(expr, ns);
263-
for(const exprt &op : simplified.operands())
264-
{
265-
auto lhs_value = eval_constant(op);
266-
if(lhs_value.is_nil())
267-
return top(simplified.type());
268-
}
264+
auto simplified = simplify_expr(expr, ns);
265+
266+
if(simplified.has_operands() && operand_is_top)
267+
return top(simplified.type());
269268

270269
// the expression is fully simplified
271270
return std::make_shared<constant_abstract_valuet>(
@@ -274,22 +273,31 @@ class constants_evaluator
274273

275274
abstract_object_pointert try_transform_expr_with_all_rounding_modes() const
276275
{
277-
std::vector<abstract_object_pointert> possible_results;
276+
abstract_object_pointert last_result;
277+
278+
auto results_differ = [](
279+
const abstract_object_pointert &prev,
280+
const abstract_object_pointert &cur) {
281+
if(prev == nullptr)
282+
return false;
283+
return prev->to_constant() != cur->to_constant();
284+
};
285+
278286
for(auto rounding_mode : all_rounding_modes)
279287
{
280288
auto child_env(environment_with_rounding_mode(rounding_mode));
281-
possible_results.push_back(
282-
constants_evaluator(expression, child_env, ns)());
283-
}
289+
auto child_operands =
290+
reeval_operands(expression.operands(), child_env, ns);
284291

285-
auto first = possible_results.front()->to_constant();
286-
for(auto const &possible_result : possible_results)
287-
{
288-
auto current = possible_result->to_constant();
289-
if(current.is_nil() || current != first)
292+
auto result =
293+
constants_evaluator(expression, child_operands, child_env, ns)();
294+
295+
if(result->is_top() || results_differ(last_result, result))
290296
return top(expression.type());
297+
last_result = result;
291298
}
292-
return possible_results.front();
299+
300+
return last_result;
293301
}
294302

295303
abstract_environmentt
@@ -311,12 +319,25 @@ class constants_evaluator
311319
{
312320
exprt adjusted_expr = expression;
313321
adjust_float_expressions(adjusted_expr, ns);
322+
323+
if(adjusted_expr != expression)
324+
operands = reeval_operands(adjusted_expr.operands(), environment, ns);
325+
314326
return adjusted_expr;
315327
}
316328

317-
exprt eval_constant(const exprt &op) const
329+
static std::vector<abstract_object_pointert> reeval_operands(
330+
const exprt::operandst &ops,
331+
const abstract_environmentt &env,
332+
const namespacet &ns)
318333
{
319-
return environment.eval(op, ns)->to_constant();
334+
auto reevaled_operands = std::vector<abstract_object_pointert>{};
335+
std::transform(
336+
ops.cbegin(),
337+
ops.end(),
338+
std::back_inserter(reevaled_operands),
339+
[&env, &ns](const exprt &op) { return env.eval(op, ns); });
340+
return reevaled_operands;
320341
}
321342

322343
abstract_object_pointert top(const typet &type) const
@@ -326,11 +347,13 @@ class constants_evaluator
326347

327348
bool rounding_mode_is_not_set() const
328349
{
329-
auto rounding_mode = eval_constant(rounding_mode_symbol);
350+
auto rounding_mode =
351+
environment.eval(rounding_mode_symbol, ns)->to_constant();
330352
return rounding_mode.is_nil();
331353
}
332354

333355
const exprt &expression;
356+
mutable std::vector<abstract_object_pointert> operands;
334357
const abstract_environmentt &environment;
335358
const namespacet &ns;
336359

@@ -355,7 +378,7 @@ abstract_object_pointert constants_expression_transform(
355378
const abstract_environmentt &environment,
356379
const namespacet &ns)
357380
{
358-
auto evaluator = constants_evaluator(expr, environment, ns);
381+
auto evaluator = constants_evaluator(expr, operands, environment, ns);
359382
return evaluator();
360383
}
361384

0 commit comments

Comments
 (0)