13
13
#include < util/pointer_expr.h>
14
14
#include < util/std_code.h>
15
15
16
+ #include < goto-programs/goto_inline.h>
17
+
16
18
#include < analyses/goto_rw.h>
17
19
#include < goto-instrument/contracts/utils.h>
18
20
#include < goto-instrument/havoc_utils.h>
19
21
22
+ #include " dfcc_loop_nesting_graph.h"
20
23
#include " dfcc_root_object.h"
21
24
22
25
// / Builds a call expression `object_whole(expr)`
@@ -154,10 +157,67 @@ std::unordered_set<irep_idt> gen_loop_locals_set(
154
157
return loop_locals;
155
158
}
156
159
157
- assignst dfcc_infer_loop_assigns (
160
+ // / Find all identifiers in `src`.
161
+ static std::unordered_set<irep_idt>
162
+ find_symbol_identifiers (const goto_programt &src)
163
+ {
164
+ std::unordered_set<irep_idt> identifiers;
165
+ for (const auto &instruction : src.instructions )
166
+ {
167
+ // compute forward edges first
168
+ switch (instruction.type ())
169
+ {
170
+ case ASSERT:
171
+ case ASSUME:
172
+ case GOTO:
173
+ find_symbols (instruction.condition (), identifiers);
174
+ break ;
175
+
176
+ case FUNCTION_CALL:
177
+ find_symbols (instruction.call_lhs (), identifiers);
178
+ for (const auto &e : instruction.call_arguments ())
179
+ find_symbols (e, identifiers);
180
+ break ;
181
+ case ASSIGN:
182
+ case OTHER:
183
+
184
+ case SET_RETURN_VALUE:
185
+ case DECL:
186
+ case DEAD:
187
+ for (const auto &e : instruction.code ().operands ())
188
+ {
189
+ find_symbols (e, identifiers);
190
+ }
191
+ break ;
192
+
193
+ case END_THREAD:
194
+ case END_FUNCTION:
195
+ case ATOMIC_BEGIN:
196
+ case ATOMIC_END:
197
+ case SKIP:
198
+ case LOCATION:
199
+ case CATCH:
200
+ case THROW:
201
+ case START_THREAD:
202
+ break ;
203
+ case NO_INSTRUCTION_TYPE:
204
+ case INCOMPLETE_GOTO:
205
+ UNREACHABLE;
206
+ break ;
207
+ }
208
+ }
209
+ return identifiers;
210
+ }
211
+
212
+ // / Infer loop assigns in the given `loop`. Loop assigns should depend
213
+ // / on some identifiers in `candidate_targets`. `function_assigns_map`
214
+ // / contains the function contracts used to infer loop assigns of
215
+ // / function_call instructions.
216
+ static assignst dfcc_infer_loop_assigns_for_loop (
158
217
const local_may_aliast &local_may_alias,
159
218
goto_functiont &goto_function,
160
219
const dfcc_loop_nesting_graph_nodet &loop,
220
+ const std::unordered_set<irep_idt> &candidate_targets,
161
221
message_handlert &message_handler,
162
222
const namespacet &ns)
163
223
{
@@ -176,6 +236,12 @@ assignst dfcc_infer_loop_assigns(
176
236
assignst result;
177
237
for (const auto &expr : assigns)
178
238
{
239
+ // Skip targets that only depend on non-visible identifiers.
240
+ if (!depends_on (expr, candidate_targets))
241
+ {
242
+ continue ;
243
+ }
244
+
179
245
if (depends_on (expr, loop_locals))
180
246
{
181
247
// Target depends on loop locals, attempt widening to the root object
@@ -210,3 +276,103 @@ assignst dfcc_infer_loop_assigns(
210
276
211
277
return result;
212
278
}
279
+
280
+ // / Remove assignments to `__CPROVER_dead_object` to avoid aliasing all targets
281
+ // / that are assigned to `__CPROVER_dead_object`.
282
+ static void remove_dead_object_assignment (goto_functiont &goto_function)
283
+ {
284
+ Forall_goto_program_instructions (i_it, goto_function.body )
285
+ {
286
+ if (i_it->is_assign ())
287
+ {
288
+ auto &lhs = i_it->assign_lhs ();
289
+
290
+ if (
291
+ lhs.id () == ID_symbol &&
292
+ to_symbol_expr (lhs).get_identifier () == CPROVER_PREFIX " dead_object" )
293
+ {
294
+ i_it->turn_into_skip ();
295
+ }
296
+ }
297
+ }
298
+ }
299
+
300
+ void dfcc_infer_loop_assigns_for_function (
301
+ std::map<std::size_t , assignst> &inferred_loop_assigns_map,
302
+ goto_functionst &goto_functions,
303
+ const goto_functiont &goto_function,
304
+ message_handlert &message_handler,
305
+ const namespacet &ns)
306
+ {
307
+ messaget log (message_handler);
308
+
309
+ // Collect all candidate targets---identifiers visible in `goto_function`.
310
+ const auto candidate_targets = find_symbol_identifiers (goto_function.body );
311
+
312
+ // We infer loop assigns based on the copy of `goto_function`.
313
+ goto_functiont goto_function_copy;
314
+ goto_function_copy.copy_from (goto_function);
315
+
316
+ // Build the loop id map before inlining attempt. So that we can later
317
+ // distinguish loops in the original body and loops added by inlining.
318
+ const auto loop_nesting_graph =
319
+ build_loop_nesting_graph (goto_function_copy.body );
320
+ auto topsorted = loop_nesting_graph.topsort ();
321
+ // skip function without loop.
322
+ if (topsorted.empty ())
323
+ return ;
324
+
325
+ // Map from targett in `goto_function_copy` to loop number.
326
+ std::
327
+ unordered_map<goto_programt::const_targett, std::size_t , const_target_hash>
328
+ loop_number_map;
329
+
330
+ for (const auto id : topsorted)
331
+ {
332
+ loop_number_map.emplace (
333
+ loop_nesting_graph[id].head , loop_nesting_graph[id].latch ->loop_number );
334
+ }
335
+
336
+ // We avoid inlining `malloc` and `free` whose variables are not assigns.
337
+ auto malloc_body = goto_functions.function_map .extract (irep_idt (" malloc" ));
338
+ auto free_body = goto_functions.function_map .extract (irep_idt (" free" ));
339
+
340
+ // Inline all function calls in goto_function_copy.
341
+ goto_program_inline (
342
+ goto_functions, goto_function_copy.body , ns, log .get_message_handler ());
343
+ // Update the body to make sure all goto correctly jump to valid targets.
344
+ goto_function_copy.body .update ();
345
+ // Build the loop graph after inlining.
346
+ const auto inlined_loop_nesting_graph =
347
+ build_loop_nesting_graph (goto_function_copy.body );
348
+
349
+ // Alias analysis.
350
+ remove_dead_object_assignment (goto_function_copy);
351
+ local_may_aliast local_may_alias (goto_function_copy);
352
+
353
+ auto inlined_topsorted = inlined_loop_nesting_graph.topsort ();
354
+
355
+ for (const auto inlined_id : inlined_topsorted)
356
+ {
357
+ // We only infer loop assigns for loops in the original function.
358
+ if (
359
+ loop_number_map.find (inlined_loop_nesting_graph[inlined_id].head ) !=
360
+ loop_number_map.end ())
361
+ {
362
+ const auto loop_number =
363
+ loop_number_map[inlined_loop_nesting_graph[inlined_id].head ];
364
+ const auto inlined_loop = inlined_loop_nesting_graph[inlined_id];
365
+
366
+ inferred_loop_assigns_map[loop_number] = dfcc_infer_loop_assigns_for_loop (
367
+ local_may_alias,
368
+ goto_function_copy,
369
+ inlined_loop,
370
+ candidate_targets,
371
+ message_handler,
372
+ ns);
373
+ }
374
+ }
375
+ // Restore the function boyd of `malloc` and `free`.
376
+ goto_functions.function_map .insert (std::move (malloc_body));
377
+ goto_functions.function_map .insert (std::move (free_body));
378
+ }
0 commit comments