Skip to content

Commit f988768

Browse files
committed
Constant propagator: improve GOTO condition propagation
This was disabled due to unspecified problems with two-way constant prop (e.g. trying to propagate x => y and y => x when we have ASSUME(x == y) or IF x == y THEN GOTO ... Rather than try to fix those bugs, this provides a simpler implementation that only propagates from constants.
1 parent 053ae9c commit f988768

File tree

3 files changed

+405
-35
lines changed

3 files changed

+405
-35
lines changed

src/analyses/constant_propagator.cpp

Lines changed: 124 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,40 +29,64 @@ Author: Peter Schrammel
2929
#include <algorithm>
3030
#include <array>
3131

32+
/// Assign value `rhs` to `lhs`, recording any newly-known constants in
33+
/// `dest_values`.
34+
/// \param [out] dest_values: results of the assignment are recorded here. We
35+
/// might add extra entries (if we determine some symbol is constant), or
36+
/// might remove existing ones (if the lhs expression is unknown), except if
37+
/// `is_assignment` is false, in which case only the former is done.
38+
/// \param lhs: lhs expression to assign
39+
/// \param rhs: rhs expression to assign to lhs
40+
/// \param ns: namespace, used to check for type mismatches
41+
/// \param cp: owning constant propagator instance, used to filter out symbols
42+
/// that the user doesn't want tracked
43+
/// \param is_assignment: if true, assign_rec may remove entries from
44+
/// dest_values when a constant assignment cannot be determined. This is used
45+
/// when an actual assignment instruction is processed. If false, new entries
46+
/// can be added but existing ones will not be removed; this is used when the
47+
/// "assignment" is actually implied by a read-only operation, such as passing
48+
/// "IF x == y" -- if we know what 'y' is that tells us the value for x, but
49+
/// if we don't there is no reason to discard pre-existing knowledge about x.
3250
void constant_propagator_domaint::assign_rec(
3351
valuest &dest_values,
3452
const exprt &lhs,
3553
const exprt &rhs,
3654
const namespacet &ns,
37-
const constant_propagator_ait *cp)
55+
const constant_propagator_ait *cp,
56+
bool is_assignment)
3857
{
3958
if(lhs.id() == ID_dereference)
4059
{
4160
exprt eval_lhs = lhs;
4261
if(partial_evaluate(dest_values, eval_lhs, ns))
4362
{
44-
const bool have_dirty = (cp != nullptr);
63+
if(is_assignment)
64+
{
65+
const bool have_dirty = (cp != nullptr);
4566

46-
if(have_dirty)
47-
dest_values.set_dirty_to_top(cp->dirty, ns);
48-
else
49-
dest_values.set_to_top();
67+
if(have_dirty)
68+
dest_values.set_dirty_to_top(cp->dirty, ns);
69+
else
70+
dest_values.set_to_top();
71+
}
72+
// Otherwise disregard this unknown deref in a read-only context.
5073
}
5174
else
52-
assign_rec(dest_values, eval_lhs, rhs, ns, cp);
75+
assign_rec(dest_values, eval_lhs, rhs, ns, cp, is_assignment);
5376
}
5477
else if(lhs.id() == ID_index)
5578
{
5679
const index_exprt &index_expr = to_index_expr(lhs);
5780
with_exprt new_rhs(index_expr.array(), index_expr.index(), rhs);
58-
assign_rec(dest_values, index_expr.array(), new_rhs, ns, cp);
81+
assign_rec(dest_values, index_expr.array(), new_rhs, ns, cp, is_assignment);
5982
}
6083
else if(lhs.id() == ID_member)
6184
{
6285
const member_exprt &member_expr = to_member_expr(lhs);
6386
with_exprt new_rhs(member_expr.compound(), exprt(ID_member_name), rhs);
6487
new_rhs.where().set(ID_component_name, member_expr.get_component_name());
65-
assign_rec(dest_values, member_expr.compound(), new_rhs, ns, cp);
88+
assign_rec(
89+
dest_values, member_expr.compound(), new_rhs, ns, cp, is_assignment);
6690
}
6791
else if(lhs.id() == ID_symbol)
6892
{
@@ -82,14 +106,18 @@ void constant_propagator_domaint::assign_rec(
82106
dest_values.set_to(s, tmp);
83107
}
84108
else
85-
dest_values.set_to_top(s);
109+
{
110+
if(is_assignment)
111+
dest_values.set_to_top(s);
112+
}
86113
}
87114
else
88115
{
89116
// it's an assignment, but we don't really know what object is being written
90117
// to on the left-hand side - bail and set all values to top to be on the
91118
// safe side in terms of soundness
92-
dest_values.set_to_top();
119+
if(is_assignment)
120+
dest_values.set_to_top();
93121
}
94122
}
95123

@@ -137,7 +165,7 @@ void constant_propagator_domaint::transform(
137165
const code_assignt &assignment=to_code_assign(from->code);
138166
const exprt &lhs=assignment.lhs();
139167
const exprt &rhs=assignment.rhs();
140-
assign_rec(values, lhs, rhs, ns, cp);
168+
assign_rec(values, lhs, rhs, ns, cp, true);
141169
}
142170
else if(from->is_assume())
143171
{
@@ -157,15 +185,7 @@ void constant_propagator_domaint::transform(
157185
if(g.is_false())
158186
values.set_to_bottom();
159187
else
160-
{
161188
two_way_propagate_rec(g, ns, cp);
162-
// If two way propagate is enabled then it may be possible to detect
163-
// that the branch condition is infeasible and thus the domain should
164-
// be set to bottom. Without it the domain will only be set to bottom
165-
// if the guard expression is trivially (i.e. without context) false.
166-
INVARIANT(!values.is_bottom,
167-
"Without two-way propagation this should be impossible.");
168-
}
169189
}
170190
else if(from->is_dead())
171191
{
@@ -223,7 +243,7 @@ void constant_propagator_domaint::transform(
223243
break;
224244

225245
const symbol_exprt parameter_expr(p_it->get_identifier(), arg.type());
226-
assign_rec(values, parameter_expr, arg, ns, cp);
246+
assign_rec(values, parameter_expr, arg, ns, cp, true);
227247

228248
++p_it;
229249
}
@@ -264,6 +284,23 @@ void constant_propagator_domaint::transform(
264284
#endif
265285
}
266286

287+
static void replace_typecast_of_bool(
288+
exprt &lhs,
289+
exprt &rhs,
290+
const namespacet &ns)
291+
{
292+
// (int)var xx 0 ==> var xx (_Bool)0 or similar (xx is == or !=)
293+
// Note this is restricted to bools because in general turning a widening
294+
// into a narrowing typecast is not correct.
295+
if(lhs.id() == ID_typecast &&
296+
(lhs.op0().type().id() == ID_bool || lhs.op0().type().id() == ID_c_bool))
297+
{
298+
rhs = typecast_exprt(rhs, lhs.op0().type());
299+
simplify(rhs, ns);
300+
301+
lhs = lhs.op0();
302+
}
303+
}
267304

268305
/// handles equalities and conjunctions containing equalities
269306
bool constant_propagator_domaint::two_way_propagate_rec(
@@ -275,31 +312,87 @@ bool constant_propagator_domaint::two_way_propagate_rec(
275312
std::cout << "two_way_propagate_rec: " << format(expr) << '\n';
276313
#endif
277314

278-
bool change=false;
315+
bool change = false;
279316

280317
if(expr.id()==ID_and)
281318
{
282319
// need a fixed point here to get the most out of it
320+
bool change_this_time;
283321
do
284322
{
285-
change = false;
323+
change_this_time = false;
286324

287325
forall_operands(it, expr)
288-
if(two_way_propagate_rec(*it, ns, cp))
289-
change=true;
326+
{
327+
change_this_time |= two_way_propagate_rec(*it, ns, cp);
328+
if(change_this_time)
329+
change = true;
330+
}
290331
}
291-
while(change);
332+
while(change_this_time);
292333
}
293-
else if(expr.id()==ID_equal)
334+
else if(expr.id() == ID_not)
294335
{
295-
const exprt &lhs=expr.op0();
296-
const exprt &rhs=expr.op1();
336+
if(expr.op0().id() == ID_equal || expr.op0().id() == ID_notequal)
337+
{
338+
exprt subexpr = expr.op0();
339+
subexpr.id(subexpr.id() == ID_equal ? ID_notequal : ID_equal);
340+
change = two_way_propagate_rec(subexpr, ns, cp);
341+
}
342+
else if(expr.op0().id() == ID_symbol && expr.type() == bool_typet())
343+
{
344+
// Treat `IF !x` like `IF x == FALSE`:
345+
change =
346+
two_way_propagate_rec(equal_exprt(expr.op0(), false_exprt()), ns, cp);
347+
}
348+
}
349+
else if(expr.id() == ID_symbol)
350+
{
351+
if(expr.type() == bool_typet())
352+
{
353+
// Treat `IF x` like `IF x == TRUE`:
354+
change = two_way_propagate_rec(equal_exprt(expr, true_exprt()), ns, cp);
355+
}
356+
}
357+
else if(expr.id() == ID_notequal)
358+
{
359+
// Treat "symbol != constant" like "symbol == !constant" when the constant
360+
// is a boolean.
361+
exprt lhs = expr.op0();
362+
exprt rhs = expr.op1();
363+
364+
if(lhs.is_constant() && !rhs.is_constant())
365+
std::swap(lhs, rhs);
366+
367+
if(lhs.is_constant() || !rhs.is_constant())
368+
return false;
369+
370+
replace_typecast_of_bool(lhs, rhs, ns);
371+
372+
if(lhs.type().id() != ID_bool && lhs.type().id() != ID_c_bool)
373+
return false;
374+
375+
// x != FALSE ==> x == TRUE
376+
377+
if(rhs.is_zero() || rhs.is_false())
378+
rhs = from_integer(1, rhs.type());
379+
else
380+
rhs = from_integer(0, rhs.type());
381+
382+
change = two_way_propagate_rec(equal_exprt(lhs, rhs), ns, cp);
383+
}
384+
else if(expr.id() == ID_equal)
385+
{
386+
exprt lhs = expr.op0();
387+
exprt rhs = expr.op1();
388+
389+
replace_typecast_of_bool(lhs, rhs, ns);
297390

298391
// two-way propagation
299392
valuest copy_values=values;
300-
assign_rec(copy_values, lhs, rhs, ns, cp);
393+
assign_rec(copy_values, lhs, rhs, ns, cp, false);
301394
if(!values.is_constant(rhs) || values.is_constant(lhs))
302-
assign_rec(values, rhs, lhs, ns, cp);
395+
assign_rec(values, rhs, lhs, ns, cp, false);
303396
change = values.meet(copy_values, ns);
304397
}
305398

src/analyses/constant_propagator.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ class constant_propagator_domaint:public ai_domain_baset
146146
const exprt &lhs,
147147
const exprt &rhs,
148148
const namespacet &ns,
149-
const constant_propagator_ait *cp);
149+
const constant_propagator_ait *cp,
150+
bool is_assignment);
150151

151152
bool two_way_propagate_rec(
152153
const exprt &expr,

0 commit comments

Comments
 (0)