17
17
#include < util/exception_utils.h>
18
18
#include < util/expr_iterator.h>
19
19
#include < util/expr_util.h>
20
+ #include < util/fresh_symbol.h>
20
21
#include < util/invariant.h>
21
22
#include < util/pointer_offset_size.h>
22
23
23
24
#include < pointer-analysis/value_set_dereference.h>
24
25
26
+ #include " expr_skeleton.h"
27
+ #include " symex_assign.h"
25
28
#include " symex_dereference_state.h"
26
29
27
30
// / Transforms an lvalue expression by replacing any dereference operations it
@@ -71,7 +74,7 @@ exprt goto_symext::address_arithmetic(
71
74
72
75
// there could be further dereferencing in the offset
73
76
exprt offset=be.offset ();
74
- dereference_rec (offset, state, false );
77
+ dereference_rec (offset, state, false , false );
75
78
76
79
result=plus_exprt (result, offset);
77
80
@@ -107,14 +110,14 @@ exprt goto_symext::address_arithmetic(
107
110
// just grab the pointer, but be wary of further dereferencing
108
111
// in the pointer itself
109
112
result=to_dereference_expr (expr).pointer ();
110
- dereference_rec (result, state, false );
113
+ dereference_rec (result, state, false , false );
111
114
}
112
115
else if (expr.id ()==ID_if)
113
116
{
114
117
if_exprt if_expr=to_if_expr (expr);
115
118
116
119
// the condition is not an address
117
- dereference_rec (if_expr.cond (), state, false );
120
+ dereference_rec (if_expr.cond (), state, false , false );
118
121
119
122
// recursive call
120
123
if_expr.true_case () =
@@ -131,7 +134,7 @@ exprt goto_symext::address_arithmetic(
131
134
{
132
135
// give up, just dereference
133
136
result=expr;
134
- dereference_rec (result, state, false );
137
+ dereference_rec (result, state, false , false );
135
138
136
139
// turn &array into &array[0]
137
140
if (result.type ().id () == ID_array && !keep_array)
@@ -191,14 +194,68 @@ exprt goto_symext::address_arithmetic(
191
194
return result;
192
195
}
193
196
197
+ symbol_exprt
198
+ goto_symext::cache_dereference (exprt &dereference_result, statet &state)
199
+ {
200
+ auto const cache_key = [&] {
201
+ auto cache_key =
202
+ state.field_sensitivity .apply (ns, state, dereference_result, false );
203
+ if (auto let_expr = expr_try_dynamic_cast<let_exprt>(dereference_result))
204
+ {
205
+ let_expr->value () = state.rename <L2>(let_expr->value (), ns).get ();
206
+ }
207
+ else
208
+ {
209
+ cache_key = state.rename <L2>(cache_key, ns).get ();
210
+ }
211
+ return cache_key;
212
+ }();
213
+
214
+ if (auto cached = state.dereference_cache .lookup (cache_key))
215
+ {
216
+ return *cached;
217
+ }
218
+
219
+ auto const &cache_symbol = get_fresh_aux_symbol (
220
+ cache_key.type (),
221
+ " symex" ,
222
+ " dereference_cache" ,
223
+ dereference_result.source_location (),
224
+ ID_C,
225
+ ns,
226
+ state.symbol_table );
227
+
228
+ // we need to lift possible lets
229
+ // (come from the value set to avoid repeating complex pointer comparisons)
230
+ auto cache_value = cache_key;
231
+ lift_lets (state, cache_value);
232
+
233
+ exprt::operandst guard{};
234
+ auto assign = symex_assignt{
235
+ state, symex_targett::assignment_typet::STATE, ns, symex_config, target};
236
+
237
+ assign.assign_symbol (
238
+ to_ssa_expr (state.rename <L1>(cache_symbol.symbol_expr (), ns).get ()),
239
+ expr_skeletont{},
240
+ cache_value,
241
+ guard);
242
+
243
+ state.dereference_cache .insert (cache_key, cache_symbol.symbol_expr ());
244
+ return cache_symbol.symbol_expr ();
245
+ }
246
+
194
247
// / If \p expr is a \ref dereference_exprt, replace it with explicit references
195
248
// / to the objects it may point to. Otherwise recursively apply this function to
196
249
// / \p expr's operands, with special cases for address-of (handled by \ref
197
250
// / goto_symext::address_arithmetic) and certain common expression patterns
198
251
// / such as `&struct.flexible_array[0]` (see inline comments in code).
199
252
// / For full details of this method's pointer replacement and potential side-
200
253
// / effects see \ref goto_symext::dereference
201
- void goto_symext::dereference_rec (exprt &expr, statet &state, bool write)
254
+ void goto_symext::dereference_rec (
255
+ exprt &expr,
256
+ statet &state,
257
+ bool write,
258
+ bool is_in_quantifier)
202
259
{
203
260
if (expr.id ()==ID_dereference)
204
261
{
@@ -221,7 +278,7 @@ void goto_symext::dereference_rec(exprt &expr, statet &state, bool write)
221
278
tmp1.swap (to_dereference_expr (expr).pointer ());
222
279
223
280
// first make sure there are no dereferences in there
224
- dereference_rec (tmp1, state, false );
281
+ dereference_rec (tmp1, state, false , is_in_quantifier );
225
282
226
283
// Depending on the nature of the pointer expression, the recursive deref
227
284
// operation might have introduced a construct such as
@@ -271,10 +328,29 @@ void goto_symext::dereference_rec(exprt &expr, statet &state, bool write)
271
328
dereference.dereference (tmp1, symex_config.show_points_to_sets );
272
329
// std::cout << "**** " << format(tmp2) << '\n';
273
330
274
- expr.swap (tmp2);
275
331
276
332
// this may yield a new auto-object
277
- trigger_auto_object (expr, state);
333
+ trigger_auto_object (tmp2, state);
334
+
335
+ // If the dereference result is not a complicated expression
336
+ // (i.e. of the form
337
+ // [let p = <expr> in ]
338
+ // (p == &something ? something : ...))
339
+ // we should just return it unchanged.
340
+ // also if we are on the lhs of an assignment we should also not attempt to
341
+ // go to the cache we cannot do this for quantified expressions because this
342
+ // would result in us referencing quantifier variables outside the scope of
343
+ // the quantifier.
344
+ if (
345
+ !write && !is_in_quantifier &&
346
+ (tmp2.id () == ID_if || tmp2.id () == ID_let))
347
+ {
348
+ expr = cache_dereference (tmp2, state);
349
+ }
350
+ else
351
+ {
352
+ expr = tmp2;
353
+ }
278
354
}
279
355
else if (
280
356
expr.id () == ID_index && to_index_expr (expr).array ().id () == ID_member &&
@@ -293,7 +369,7 @@ void goto_symext::dereference_rec(exprt &expr, statet &state, bool write)
293
369
tmp.add_source_location ()=expr.source_location ();
294
370
295
371
// recursive call
296
- dereference_rec (tmp, state, write );
372
+ dereference_rec (tmp, state, write , is_in_quantifier );
297
373
298
374
expr.swap (tmp);
299
375
}
@@ -331,17 +407,18 @@ void goto_symext::dereference_rec(exprt &expr, statet &state, bool write)
331
407
to_address_of_expr (tc_op).object (),
332
408
from_integer (0 , index_type ())));
333
409
334
- dereference_rec (expr, state, write );
410
+ dereference_rec (expr, state, write , is_in_quantifier );
335
411
}
336
412
else
337
413
{
338
- dereference_rec (tc_op, state, write );
414
+ dereference_rec (tc_op, state, write , is_in_quantifier );
339
415
}
340
416
}
341
417
else
342
418
{
419
+ bool is_quantifier = expr.id () == ID_forall || expr.id () == ID_exists;
343
420
Forall_operands (it, expr)
344
- dereference_rec (*it, state, write );
421
+ dereference_rec (*it, state, write , is_in_quantifier || is_quantifier );
345
422
}
346
423
}
347
424
@@ -409,7 +486,7 @@ void goto_symext::dereference(exprt &expr, statet &state, bool write)
409
486
});
410
487
411
488
// start the recursion!
412
- dereference_rec (expr, state, write );
489
+ dereference_rec (expr, state, write , false );
413
490
// dereferencing may introduce new symbol_exprt
414
491
// (like __CPROVER_memory)
415
492
expr = state.rename <L1>(std::move (expr), ns).get ();
0 commit comments