@@ -29,40 +29,64 @@ Author: Peter Schrammel
29
29
#include < algorithm>
30
30
#include < array>
31
31
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.
32
50
void constant_propagator_domaint::assign_rec (
33
51
valuest &dest_values,
34
52
const exprt &lhs,
35
53
const exprt &rhs,
36
54
const namespacet &ns,
37
- const constant_propagator_ait *cp)
55
+ const constant_propagator_ait *cp,
56
+ bool is_assignment)
38
57
{
39
58
if (lhs.id () == ID_dereference)
40
59
{
41
60
exprt eval_lhs = lhs;
42
61
if (partial_evaluate (dest_values, eval_lhs, ns))
43
62
{
44
- const bool have_dirty = (cp != nullptr );
63
+ if (is_assignment)
64
+ {
65
+ const bool have_dirty = (cp != nullptr );
45
66
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.
50
73
}
51
74
else
52
- assign_rec (dest_values, eval_lhs, rhs, ns, cp);
75
+ assign_rec (dest_values, eval_lhs, rhs, ns, cp, is_assignment );
53
76
}
54
77
else if (lhs.id () == ID_index)
55
78
{
56
79
const index_exprt &index_expr = to_index_expr (lhs);
57
80
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 );
59
82
}
60
83
else if (lhs.id () == ID_member)
61
84
{
62
85
const member_exprt &member_expr = to_member_expr (lhs);
63
86
with_exprt new_rhs (member_expr.compound (), exprt (ID_member_name), rhs);
64
87
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);
66
90
}
67
91
else if (lhs.id () == ID_symbol)
68
92
{
@@ -82,13 +106,15 @@ void constant_propagator_domaint::assign_rec(
82
106
dest_values.set_to (s, tmp);
83
107
}
84
108
else
85
- dest_values.set_to_top (s);
109
+ {
110
+ if (is_assignment)
111
+ dest_values.set_to_top (s);
112
+ }
86
113
}
87
- else
114
+ else if (is_assignment)
88
115
{
89
116
// 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.
92
118
dest_values.set_to_top ();
93
119
}
94
120
}
@@ -137,7 +163,7 @@ void constant_propagator_domaint::transform(
137
163
const code_assignt &assignment=to_code_assign (from->code );
138
164
const exprt &lhs=assignment.lhs ();
139
165
const exprt &rhs=assignment.rhs ();
140
- assign_rec (values, lhs, rhs, ns, cp);
166
+ assign_rec (values, lhs, rhs, ns, cp, true );
141
167
}
142
168
else if (from->is_assume ())
143
169
{
@@ -157,15 +183,7 @@ void constant_propagator_domaint::transform(
157
183
if (g.is_false ())
158
184
values.set_to_bottom ();
159
185
else
160
- {
161
186
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
- }
169
187
}
170
188
else if (from->is_dead ())
171
189
{
@@ -223,7 +241,7 @@ void constant_propagator_domaint::transform(
223
241
break ;
224
242
225
243
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 );
227
245
228
246
++p_it;
229
247
}
@@ -264,6 +282,22 @@ void constant_propagator_domaint::transform(
264
282
#endif
265
283
}
266
284
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
+ }
267
301
268
302
// / handles equalities and conjunctions containing equalities
269
303
bool constant_propagator_domaint::two_way_propagate_rec (
@@ -280,26 +314,81 @@ bool constant_propagator_domaint::two_way_propagate_rec(
280
314
if (expr.id ()==ID_and)
281
315
{
282
316
// need a fixed point here to get the most out of it
317
+ bool change_this_time;
283
318
do
284
319
{
285
- change = false ;
320
+ change_this_time = false ;
286
321
287
322
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);
290
343
}
291
- while (change);
292
344
}
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)
294
381
{
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);
297
386
298
387
// two-way propagation
299
388
valuest copy_values=values;
300
- assign_rec (copy_values, lhs, rhs, ns, cp);
389
+ assign_rec (copy_values, lhs, rhs, ns, cp, false );
301
390
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 );
303
392
change = values.meet (copy_values, ns);
304
393
}
305
394
0 commit comments