Skip to content

Commit 06e6573

Browse files
committed
Constant propagator: improve boolean propagation
This supports propagation of booleans in more situations, including propagating "x != false" and "IF x" like "IF x == TRUE".
1 parent 053ae9c commit 06e6573

File tree

3 files changed

+393
-45
lines changed

3 files changed

+393
-45
lines changed

src/analyses/constant_propagator.cpp

Lines changed: 121 additions & 32 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,13 +106,15 @@ 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
}
87-
else
114+
else if(is_assignment)
88115
{
89116
// it's an assignment, but we don't really know what object is being written
90-
// to on the left-hand side - bail and set all values to top to be on the
91-
// safe side in terms of soundness
117+
// to: assume it may write to anything.
92118
dest_values.set_to_top();
93119
}
94120
}
@@ -137,7 +163,7 @@ void constant_propagator_domaint::transform(
137163
const code_assignt &assignment=to_code_assign(from->code);
138164
const exprt &lhs=assignment.lhs();
139165
const exprt &rhs=assignment.rhs();
140-
assign_rec(values, lhs, rhs, ns, cp);
166+
assign_rec(values, lhs, rhs, ns, cp, true);
141167
}
142168
else if(from->is_assume())
143169
{
@@ -157,15 +183,7 @@ void constant_propagator_domaint::transform(
157183
if(g.is_false())
158184
values.set_to_bottom();
159185
else
160-
{
161186
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-
}
169187
}
170188
else if(from->is_dead())
171189
{
@@ -223,7 +241,7 @@ void constant_propagator_domaint::transform(
223241
break;
224242

225243
const symbol_exprt parameter_expr(p_it->get_identifier(), arg.type());
226-
assign_rec(values, parameter_expr, arg, ns, cp);
244+
assign_rec(values, parameter_expr, arg, ns, cp, true);
227245

228246
++p_it;
229247
}
@@ -264,6 +282,22 @@ void constant_propagator_domaint::transform(
264282
#endif
265283
}
266284

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

268302
/// handles equalities and conjunctions containing equalities
269303
bool constant_propagator_domaint::two_way_propagate_rec(
@@ -280,26 +314,81 @@ bool constant_propagator_domaint::two_way_propagate_rec(
280314
if(expr.id()==ID_and)
281315
{
282316
// need a fixed point here to get the most out of it
317+
bool change_this_time;
283318
do
284319
{
285-
change = false;
320+
change_this_time = false;
286321

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

298387
// two-way propagation
299388
valuest copy_values=values;
300-
assign_rec(copy_values, lhs, rhs, ns, cp);
389+
assign_rec(copy_values, lhs, rhs, ns, cp, false);
301390
if(!values.is_constant(rhs) || values.is_constant(lhs))
302-
assign_rec(values, rhs, lhs, ns, cp);
391+
assign_rec(values, rhs, lhs, ns, cp, false);
303392
change = values.meet(copy_values, ns);
304393
}
305394

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)