@@ -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,14 +106,18 @@ 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
114
else
88
115
{
89
116
// it's an assignment, but we don't really know what object is being written
90
117
// to on the left-hand side - bail and set all values to top to be on the
91
118
// safe side in terms of soundness
92
- dest_values.set_to_top ();
119
+ if (is_assignment)
120
+ dest_values.set_to_top ();
93
121
}
94
122
}
95
123
@@ -137,7 +165,7 @@ void constant_propagator_domaint::transform(
137
165
const code_assignt &assignment=to_code_assign (from->code );
138
166
const exprt &lhs=assignment.lhs ();
139
167
const exprt &rhs=assignment.rhs ();
140
- assign_rec (values, lhs, rhs, ns, cp);
168
+ assign_rec (values, lhs, rhs, ns, cp, true );
141
169
}
142
170
else if (from->is_assume ())
143
171
{
@@ -157,15 +185,7 @@ void constant_propagator_domaint::transform(
157
185
if (g.is_false ())
158
186
values.set_to_bottom ();
159
187
else
160
- {
161
188
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
189
}
170
190
else if (from->is_dead ())
171
191
{
@@ -223,7 +243,7 @@ void constant_propagator_domaint::transform(
223
243
break ;
224
244
225
245
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 );
227
247
228
248
++p_it;
229
249
}
@@ -264,6 +284,23 @@ void constant_propagator_domaint::transform(
264
284
#endif
265
285
}
266
286
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
+ }
267
304
268
305
// / handles equalities and conjunctions containing equalities
269
306
bool constant_propagator_domaint::two_way_propagate_rec (
@@ -275,31 +312,87 @@ bool constant_propagator_domaint::two_way_propagate_rec(
275
312
std::cout << " two_way_propagate_rec: " << format (expr) << ' \n ' ;
276
313
#endif
277
314
278
- bool change= false ;
315
+ bool change = false ;
279
316
280
317
if (expr.id ()==ID_and)
281
318
{
282
319
// need a fixed point here to get the most out of it
320
+ bool change_this_time;
283
321
do
284
322
{
285
- change = false ;
323
+ change_this_time = false ;
286
324
287
325
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
+ }
290
331
}
291
- while (change );
332
+ while (change_this_time );
292
333
}
293
- else if (expr.id ()==ID_equal )
334
+ else if (expr.id () == ID_not )
294
335
{
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);
297
390
298
391
// two-way propagation
299
392
valuest copy_values=values;
300
- assign_rec (copy_values, lhs, rhs, ns, cp);
393
+ assign_rec (copy_values, lhs, rhs, ns, cp, false );
301
394
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 );
303
396
change = values.meet (copy_values, ns);
304
397
}
305
398
0 commit comments