@@ -18,6 +18,7 @@ Author: Peter Schrammel
18
18
19
19
#include < util/arith_tools.h>
20
20
#include < util/base_type.h>
21
+ #include < util/c_types.h>
21
22
#include < util/cprover_prefix.h>
22
23
#include < util/expr_util.h>
23
24
#include < util/find_symbols.h>
@@ -29,40 +30,64 @@ Author: Peter Schrammel
29
30
#include < algorithm>
30
31
#include < array>
31
32
33
+ // / Assign value `rhs` to `lhs`, recording any newly-known constants in
34
+ // / `dest_values`.
35
+ // / \param [out] dest_values: results of the assignment are recorded here. We
36
+ // / might add extra entries (if we determine some symbol is constant), or
37
+ // / might remove existing ones (if the lhs expression is unknown), except if
38
+ // / `is_assignment` is false, in which case only the former is done.
39
+ // / \param lhs: lhs expression to assign
40
+ // / \param rhs: rhs expression to assign to lhs
41
+ // / \param ns: namespace, used to check for type mismatches
42
+ // / \param cp: owning constant propagator instance, used to filter out symbols
43
+ // / that the user doesn't want tracked
44
+ // / \param is_assignment: if true, assign_rec may remove entries from
45
+ // / dest_values when a constant assignment cannot be determined. This is used
46
+ // / when an actual assignment instruction is processed. If false, new entries
47
+ // / can be added but existing ones will not be removed; this is used when the
48
+ // / "assignment" is actually implied by a read-only operation, such as passing
49
+ // / "IF x == y" -- if we know what 'y' is that tells us the value for x, but
50
+ // / if we don't there is no reason to discard pre-existing knowledge about x.
32
51
void constant_propagator_domaint::assign_rec (
33
52
valuest &dest_values,
34
53
const exprt &lhs,
35
54
const exprt &rhs,
36
55
const namespacet &ns,
37
- const constant_propagator_ait *cp)
56
+ const constant_propagator_ait *cp,
57
+ bool is_assignment)
38
58
{
39
59
if (lhs.id () == ID_dereference)
40
60
{
41
61
exprt eval_lhs = lhs;
42
62
if (partial_evaluate (dest_values, eval_lhs, ns))
43
63
{
44
- const bool have_dirty = (cp != nullptr );
64
+ if (is_assignment)
65
+ {
66
+ const bool have_dirty = (cp != nullptr );
45
67
46
- if (have_dirty)
47
- dest_values.set_dirty_to_top (cp->dirty , ns);
48
- else
49
- dest_values.set_to_top ();
68
+ if (have_dirty)
69
+ dest_values.set_dirty_to_top (cp->dirty , ns);
70
+ else
71
+ dest_values.set_to_top ();
72
+ }
73
+ // Otherwise disregard this unknown deref in a read-only context.
50
74
}
51
75
else
52
- assign_rec (dest_values, eval_lhs, rhs, ns, cp);
76
+ assign_rec (dest_values, eval_lhs, rhs, ns, cp, is_assignment );
53
77
}
54
78
else if (lhs.id () == ID_index)
55
79
{
56
80
const index_exprt &index_expr = to_index_expr (lhs);
57
81
with_exprt new_rhs (index_expr.array (), index_expr.index (), rhs);
58
- assign_rec (dest_values, index_expr.array (), new_rhs, ns, cp);
82
+ assign_rec (dest_values, index_expr.array (), new_rhs, ns, cp, is_assignment );
59
83
}
60
84
else if (lhs.id () == ID_member)
61
85
{
62
86
const member_exprt &member_expr = to_member_expr (lhs);
63
87
with_exprt new_rhs (member_expr.compound (), exprt (ID_member_name), rhs);
64
88
new_rhs.where ().set (ID_component_name, member_expr.get_component_name ());
65
- assign_rec (dest_values, member_expr.compound (), new_rhs, ns, cp);
89
+ assign_rec (
90
+ dest_values, member_expr.compound (), new_rhs, ns, cp, is_assignment);
66
91
}
67
92
else if (lhs.id () == ID_symbol)
68
93
{
@@ -82,13 +107,15 @@ void constant_propagator_domaint::assign_rec(
82
107
dest_values.set_to (s, tmp);
83
108
}
84
109
else
85
- dest_values.set_to_top (s);
110
+ {
111
+ if (is_assignment)
112
+ dest_values.set_to_top (s);
113
+ }
86
114
}
87
- else
115
+ else if (is_assignment)
88
116
{
89
117
// 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
118
+ // to: assume it may write to anything.
92
119
dest_values.set_to_top ();
93
120
}
94
121
}
@@ -137,7 +164,7 @@ void constant_propagator_domaint::transform(
137
164
const code_assignt &assignment=to_code_assign (from->code );
138
165
const exprt &lhs=assignment.lhs ();
139
166
const exprt &rhs=assignment.rhs ();
140
- assign_rec (values, lhs, rhs, ns, cp);
167
+ assign_rec (values, lhs, rhs, ns, cp, true );
141
168
}
142
169
else if (from->is_assume ())
143
170
{
@@ -157,15 +184,7 @@ void constant_propagator_domaint::transform(
157
184
if (g.is_false ())
158
185
values.set_to_bottom ();
159
186
else
160
- {
161
187
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
188
}
170
189
else if (from->is_dead ())
171
190
{
@@ -223,7 +242,7 @@ void constant_propagator_domaint::transform(
223
242
break ;
224
243
225
244
const symbol_exprt parameter_expr (p_it->get_identifier (), arg.type ());
226
- assign_rec (values, parameter_expr, arg, ns, cp);
245
+ assign_rec (values, parameter_expr, arg, ns, cp, true );
227
246
228
247
++p_it;
229
248
}
@@ -264,6 +283,26 @@ void constant_propagator_domaint::transform(
264
283
#endif
265
284
}
266
285
286
+ static void
287
+ replace_typecast_of_bool (exprt &lhs, exprt &rhs, const namespacet &ns)
288
+ {
289
+ // (int)var xx 0 ==> var xx (_Bool)0 or similar (xx is == or !=)
290
+ // Note this is restricted to bools because in general turning a widening
291
+ // into a narrowing typecast is not correct.
292
+ if (lhs.id () != ID_typecast)
293
+ return ;
294
+
295
+ const exprt &lhs_underlying = to_typecast_expr (lhs).op ();
296
+ if (
297
+ lhs_underlying.type () == bool_typet () ||
298
+ lhs_underlying.type () == c_bool_type ())
299
+ {
300
+ rhs = typecast_exprt (rhs, lhs_underlying.type ());
301
+ simplify (rhs, ns);
302
+
303
+ lhs = lhs_underlying;
304
+ }
305
+ }
267
306
268
307
// / handles equalities and conjunctions containing equalities
269
308
bool constant_propagator_domaint::two_way_propagate_rec (
@@ -280,26 +319,81 @@ bool constant_propagator_domaint::two_way_propagate_rec(
280
319
if (expr.id ()==ID_and)
281
320
{
282
321
// need a fixed point here to get the most out of it
322
+ bool change_this_time;
283
323
do
284
324
{
285
- change = false ;
325
+ change_this_time = false ;
286
326
287
327
forall_operands (it, expr)
288
- if (two_way_propagate_rec (*it, ns, cp))
289
- change=true ;
328
+ {
329
+ change_this_time |= two_way_propagate_rec (*it, ns, cp);
330
+ if (change_this_time)
331
+ change = true ;
332
+ }
333
+ } while (change_this_time);
334
+ }
335
+ else if (expr.id () == ID_not)
336
+ {
337
+ if (expr.op0 ().id () == ID_equal || expr.op0 ().id () == ID_notequal)
338
+ {
339
+ exprt subexpr = expr.op0 ();
340
+ subexpr.id (subexpr.id () == ID_equal ? ID_notequal : ID_equal);
341
+ change = two_way_propagate_rec (subexpr, ns, cp);
342
+ }
343
+ else if (expr.op0 ().id () == ID_symbol && expr.type () == bool_typet ())
344
+ {
345
+ // Treat `IF !x` like `IF x == FALSE`:
346
+ change =
347
+ two_way_propagate_rec (equal_exprt (expr.op0 (), false_exprt ()), ns, cp);
348
+ }
349
+ }
350
+ else if (expr.id () == ID_symbol)
351
+ {
352
+ if (expr.type () == bool_typet ())
353
+ {
354
+ // Treat `IF x` like `IF x == TRUE`:
355
+ change = two_way_propagate_rec (equal_exprt (expr, true_exprt ()), ns, cp);
290
356
}
291
- while (change);
292
357
}
293
- else if (expr.id ()==ID_equal )
358
+ else if (expr.id () == ID_notequal )
294
359
{
295
- const exprt &lhs=expr.op0 ();
296
- const exprt &rhs=expr.op1 ();
360
+ // Treat "symbol != constant" like "symbol == !constant" when the constant
361
+ // is a boolean.
362
+ exprt lhs = expr.op0 ();
363
+ exprt rhs = expr.op1 ();
364
+
365
+ if (lhs.is_constant () && !rhs.is_constant ())
366
+ std::swap (lhs, rhs);
367
+
368
+ if (lhs.is_constant () || !rhs.is_constant ())
369
+ return false ;
370
+
371
+ replace_typecast_of_bool (lhs, rhs, ns);
372
+
373
+ if (lhs.type () != bool_typet () && lhs.type () != c_bool_type ())
374
+ return false ;
375
+
376
+ // x != FALSE ==> x == TRUE
377
+
378
+ if (rhs.is_zero () || rhs.is_false ())
379
+ rhs = from_integer (1 , rhs.type ());
380
+ else
381
+ rhs = from_integer (0 , rhs.type ());
382
+
383
+ change = two_way_propagate_rec (equal_exprt (lhs, rhs), ns, cp);
384
+ }
385
+ else if (expr.id () == ID_equal)
386
+ {
387
+ exprt lhs = expr.op0 ();
388
+ exprt rhs = expr.op1 ();
389
+
390
+ replace_typecast_of_bool (lhs, rhs, ns);
297
391
298
392
// two-way propagation
299
393
valuest copy_values=values;
300
- assign_rec (copy_values, lhs, rhs, ns, cp);
394
+ assign_rec (copy_values, lhs, rhs, ns, cp, false );
301
395
if (!values.is_constant (rhs) || values.is_constant (lhs))
302
- assign_rec (values, rhs, lhs, ns, cp);
396
+ assign_rec (values, rhs, lhs, ns, cp, false );
303
397
change = values.meet (copy_values, ns);
304
398
}
305
399
0 commit comments