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,72 @@ 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 .find (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
+ language_mode,
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
+ auto assign = symex_assignt{
234
+ state, symex_targett::assignment_typet::STATE, ns, symex_config, target};
235
+
236
+ auto cache_symbol_expr = cache_symbol.symbol_expr ();
237
+ assign.assign_symbol (
238
+ to_ssa_expr (state.rename <L1>(cache_symbol_expr, ns).get ()),
239
+ expr_skeletont{},
240
+ cache_value,
241
+ {});
242
+
243
+ state.dereference_cache .insert (cache_key, cache_symbol_expr);
244
+ return cache_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).
252
+ // / Note that \p write is used to alter behaviour when this function is
253
+ // / operating on the LHS of an assignment. Similarly \p is_in_quantifier
254
+ // / indicates when the dereference is inside a quantifier (related to scoping
255
+ // / when dereference caching is enabled).
199
256
// / For full details of this method's pointer replacement and potential side-
200
257
// / effects see \ref goto_symext::dereference
201
- void goto_symext::dereference_rec (exprt &expr, statet &state, bool write)
258
+ void goto_symext::dereference_rec (
259
+ exprt &expr,
260
+ statet &state,
261
+ bool write,
262
+ bool is_in_quantifier)
202
263
{
203
264
if (expr.id ()==ID_dereference)
204
265
{
@@ -221,7 +282,7 @@ void goto_symext::dereference_rec(exprt &expr, statet &state, bool write)
221
282
tmp1.swap (to_dereference_expr (expr).pointer ());
222
283
223
284
// first make sure there are no dereferences in there
224
- dereference_rec (tmp1, state, false );
285
+ dereference_rec (tmp1, state, false , is_in_quantifier );
225
286
226
287
// Depending on the nature of the pointer expression, the recursive deref
227
288
// operation might have introduced a construct such as
@@ -271,10 +332,29 @@ void goto_symext::dereference_rec(exprt &expr, statet &state, bool write)
271
332
dereference.dereference (tmp1, symex_config.show_points_to_sets );
272
333
// std::cout << "**** " << format(tmp2) << '\n';
273
334
274
- expr.swap (tmp2);
275
335
276
336
// this may yield a new auto-object
277
- trigger_auto_object (expr, state);
337
+ trigger_auto_object (tmp2, state);
338
+
339
+ // Check various conditions for when we should try to cache the expression.
340
+ // 1. Caching dereferences must be enabled.
341
+ // 2. Do not cache inside LHS of writes.
342
+ // 3. Do not cache inside quantifiers (references variables outside their
343
+ // scope).
344
+ // 4. Only cache "complicated" expressions, i.e. of the form
345
+ // [let p = <expr> in ]
346
+ // (p == &something ? something : ...))
347
+ // Otherwise we should just return it unchanged.
348
+ if (
349
+ symex_config.cache_dereferences && !write && !is_in_quantifier &&
350
+ (tmp2.id () == ID_if || tmp2.id () == ID_let))
351
+ {
352
+ expr = cache_dereference (tmp2, state);
353
+ }
354
+ else
355
+ {
356
+ expr = std::move (tmp2);
357
+ }
278
358
}
279
359
else if (
280
360
expr.id () == ID_index && to_index_expr (expr).array ().id () == ID_member &&
@@ -293,7 +373,7 @@ void goto_symext::dereference_rec(exprt &expr, statet &state, bool write)
293
373
tmp.add_source_location ()=expr.source_location ();
294
374
295
375
// recursive call
296
- dereference_rec (tmp, state, write );
376
+ dereference_rec (tmp, state, write , is_in_quantifier );
297
377
298
378
expr.swap (tmp);
299
379
}
@@ -331,17 +411,18 @@ void goto_symext::dereference_rec(exprt &expr, statet &state, bool write)
331
411
to_address_of_expr (tc_op).object (),
332
412
from_integer (0 , index_type ())));
333
413
334
- dereference_rec (expr, state, write );
414
+ dereference_rec (expr, state, write , is_in_quantifier );
335
415
}
336
416
else
337
417
{
338
- dereference_rec (tc_op, state, write );
418
+ dereference_rec (tc_op, state, write , is_in_quantifier );
339
419
}
340
420
}
341
421
else
342
422
{
423
+ bool is_quantifier = expr.id () == ID_forall || expr.id () == ID_exists;
343
424
Forall_operands (it, expr)
344
- dereference_rec (*it, state, write );
425
+ dereference_rec (*it, state, write , is_in_quantifier || is_quantifier );
345
426
}
346
427
}
347
428
@@ -409,7 +490,7 @@ void goto_symext::dereference(exprt &expr, statet &state, bool write)
409
490
});
410
491
411
492
// start the recursion!
412
- dereference_rec (expr, state, write );
493
+ dereference_rec (expr, state, write , false );
413
494
// dereferencing may introduce new symbol_exprt
414
495
// (like __CPROVER_memory)
415
496
expr = state.rename <L1>(std::move (expr), ns).get ();
0 commit comments