14
14
#include < cassert>
15
15
#include < memory>
16
16
#include < sstream>
17
+ #include < type_traits>
17
18
18
19
#include < util/invariant.h>
19
20
#include < util/std_code.h>
@@ -170,7 +171,8 @@ void ai_baset::entry_state(const goto_functionst &goto_functions)
170
171
void ai_baset::entry_state (const goto_programt &goto_program)
171
172
{
172
173
// The first instruction of 'goto_program' is the entry point
173
- get_state (goto_program.instructions .begin ()).make_entry ();
174
+ trace_ptrt p = history_factory->epoch (goto_program.instructions .begin ());
175
+ get_state (p).make_entry ();
174
176
}
175
177
176
178
void ai_baset::initialize (
@@ -197,16 +199,22 @@ void ai_baset::finalize()
197
199
// Nothing to do per default
198
200
}
199
201
200
- ai_baset::locationt ai_baset::get_next (
201
- working_sett &working_set)
202
+ ai_baset::trace_ptrt ai_baset::get_next (working_sett &working_set)
202
203
{
203
204
PRECONDITION (!working_set.empty ());
204
205
205
- working_sett::iterator i=working_set.begin ();
206
- locationt l=i->second ;
207
- working_set.erase (i);
206
+ static_assert (
207
+ std::is_same<
208
+ working_sett,
209
+ std::set<trace_ptrt, ai_history_baset::compare_historyt>>::value,
210
+ " begin must return the minimal entry" );
211
+ auto first = working_set.begin ();
212
+
213
+ trace_ptrt t = *first;
214
+
215
+ working_set.erase (first);
208
216
209
- return l ;
217
+ return t ;
210
218
}
211
219
212
220
bool ai_baset::fixedpoint (
@@ -219,18 +227,19 @@ bool ai_baset::fixedpoint(
219
227
220
228
// Put the first location in the working set
221
229
if (!goto_program.empty ())
230
+ {
222
231
put_in_working_set (
223
- working_set,
224
- goto_program. instructions . begin ());
232
+ working_set, history_factory-> bang (goto_program. instructions . begin ()));
233
+ }
225
234
226
235
bool new_data=false ;
227
236
228
237
while (!working_set.empty ())
229
238
{
230
- locationt l= get_next (working_set);
239
+ trace_ptrt p = get_next (working_set);
231
240
232
241
// goto_program is really only needed for iterator manipulation
233
- if (visit (function_id, l , working_set, goto_program, goto_functions, ns))
242
+ if (visit (function_id, p , working_set, goto_program, goto_functions, ns))
234
243
new_data=true ;
235
244
}
236
245
@@ -239,13 +248,14 @@ bool ai_baset::fixedpoint(
239
248
240
249
bool ai_baset::visit (
241
250
const irep_idt &function_id,
242
- locationt l ,
251
+ trace_ptrt p ,
243
252
working_sett &working_set,
244
253
const goto_programt &goto_program,
245
254
const goto_functionst &goto_functions,
246
255
const namespacet &ns)
247
256
{
248
257
bool new_data=false ;
258
+ locationt l = p->current_location ();
249
259
250
260
// Function call and end are special cases
251
261
if (l->is_function_call ())
@@ -259,7 +269,7 @@ bool ai_baset::visit(
259
269
" function call successor / return location must be the next instruction" );
260
270
261
271
new_data = visit_function_call (
262
- function_id, l , working_set, goto_program, goto_functions, ns);
272
+ function_id, p , working_set, goto_program, goto_functions, ns);
263
273
}
264
274
else if (l->is_end_function ())
265
275
{
@@ -268,7 +278,7 @@ bool ai_baset::visit(
268
278
" The end function instruction should have no successors." );
269
279
270
280
new_data = visit_end_function (
271
- function_id, l , working_set, goto_program, goto_functions, ns);
281
+ function_id, p , working_set, goto_program, goto_functions, ns);
272
282
}
273
283
else
274
284
{
@@ -280,7 +290,7 @@ bool ai_baset::visit(
280
290
continue ;
281
291
282
292
new_data |=
283
- visit_edge (function_id, l , function_id, to_l, ns, working_set);
293
+ visit_edge (function_id, p , function_id, to_l, ns, working_set);
284
294
}
285
295
}
286
296
@@ -289,27 +299,36 @@ bool ai_baset::visit(
289
299
290
300
bool ai_baset::visit_edge (
291
301
const irep_idt &function_id,
292
- locationt l ,
302
+ trace_ptrt p ,
293
303
const irep_idt &to_function_id,
294
- const locationt & to_l,
304
+ locationt to_l,
295
305
const namespacet &ns,
296
306
working_sett &working_set)
297
307
{
308
+ // Has history taught us not to step here...
309
+ auto next = p->step (to_l, *(storage->abstract_traces_before (to_l)));
310
+ if (next.first == ai_history_baset::step_statust::BLOCKED)
311
+ return false ;
312
+ trace_ptrt to_p = next.second ;
313
+
298
314
// Abstract domains are mutable so we must copy before we transform
299
- statet ¤t = get_state (l );
315
+ statet ¤t = get_state (p );
300
316
301
317
std::unique_ptr<statet> tmp_state (make_temporary_state (current));
302
318
statet &new_values = *tmp_state;
303
319
304
320
// Apply transformer
305
- new_values.transform (function_id, l, to_function_id, to_l, *this , ns);
306
-
307
- // Initialize state(s), if necessary
308
- get_state (to_l);
309
-
310
- if (merge (new_values, l, to_l))
321
+ new_values.transform (function_id, p, to_function_id, to_p, *this , ns);
322
+
323
+ // Expanding a domain means that it has to be analysed again
324
+ // Likewise if the history insists that it is a new trace
325
+ // (assuming it is actually reachable).
326
+ if (
327
+ merge (new_values, p, to_p) ||
328
+ (next.first == ai_history_baset::step_statust::NEW &&
329
+ !new_values.is_bottom ()))
311
330
{
312
- put_in_working_set (working_set, to_l );
331
+ put_in_working_set (working_set, to_p );
313
332
return true ;
314
333
}
315
334
@@ -318,7 +337,7 @@ bool ai_baset::visit_edge(
318
337
319
338
bool ai_baset::visit_edge_function_call (
320
339
const irep_idt &calling_function_id,
321
- locationt l_call ,
340
+ trace_ptrt p_call ,
322
341
locationt l_return,
323
342
const irep_idt &,
324
343
working_sett &working_set,
@@ -330,7 +349,7 @@ bool ai_baset::visit_edge_function_call(
330
349
// so the effects of the call are approximated but nothing else
331
350
return visit_edge (
332
351
calling_function_id,
333
- l_call ,
352
+ p_call ,
334
353
calling_function_id,
335
354
l_return,
336
355
ns,
@@ -339,12 +358,14 @@ bool ai_baset::visit_edge_function_call(
339
358
340
359
bool ai_baset::visit_function_call (
341
360
const irep_idt &calling_function_id,
342
- locationt l_call ,
361
+ trace_ptrt p_call ,
343
362
working_sett &working_set,
344
363
const goto_programt &caller,
345
364
const goto_functionst &goto_functions,
346
365
const namespacet &ns)
347
366
{
367
+ locationt l_call = p_call->current_location ();
368
+
348
369
PRECONDITION (l_call->is_function_call ());
349
370
350
371
locationt l_return = std::next (l_call);
@@ -374,7 +395,7 @@ bool ai_baset::visit_function_call(
374
395
{
375
396
return visit_edge_function_call (
376
397
calling_function_id,
377
- l_call ,
398
+ p_call ,
378
399
l_return,
379
400
callee_function_id,
380
401
working_set,
@@ -401,7 +422,7 @@ bool ai_baset::visit_function_call(
401
422
// in the caller. Domains should over-approximate what the function might do.
402
423
return visit_edge (
403
424
calling_function_id,
404
- l_call ,
425
+ p_call ,
405
426
calling_function_id,
406
427
l_return,
407
428
ns,
@@ -410,12 +431,13 @@ bool ai_baset::visit_function_call(
410
431
411
432
bool ai_baset::visit_end_function (
412
433
const irep_idt &function_id,
413
- locationt l ,
434
+ trace_ptrt p ,
414
435
working_sett &working_set,
415
436
const goto_programt &goto_program,
416
437
const goto_functionst &goto_functions,
417
438
const namespacet &ns)
418
439
{
440
+ locationt l = p->current_location ();
419
441
PRECONDITION (l->is_end_function ());
420
442
421
443
// Do nothing
@@ -435,7 +457,7 @@ void ai_baset::fixedpoint(
435
457
436
458
bool ai_recursive_interproceduralt::visit_edge_function_call (
437
459
const irep_idt &calling_function_id,
438
- locationt l_call ,
460
+ trace_ptrt p_call ,
439
461
locationt l_return,
440
462
const irep_idt &callee_function_id,
441
463
working_sett &working_set,
@@ -452,7 +474,7 @@ bool ai_recursive_interproceduralt::visit_edge_function_call(
452
474
// Do the edge from the call site to the beginning of the function
453
475
bool new_data = visit_edge (
454
476
calling_function_id,
455
- l_call ,
477
+ p_call ,
456
478
callee_function_id,
457
479
l_begin,
458
480
ns,
@@ -464,26 +486,42 @@ bool ai_recursive_interproceduralt::visit_edge_function_call(
464
486
}
465
487
466
488
// This is the edge from function end to return site.
467
-
468
489
{
469
490
// get location at end of the procedure we have called
470
491
locationt l_end = --callee.instructions .end ();
471
492
DATA_INVARIANT (
472
493
l_end->is_end_function (),
473
494
" The last instruction of a goto_program must be END_FUNCTION" );
474
495
475
- // do edge from end of function to instruction after call
476
- const statet &end_state = get_state (l_end);
496
+ // Construct a history from a location
497
+ auto traces = storage-> abstract_traces_before (l_end);
477
498
478
- if (end_state.is_bottom ())
479
- return false ; // function exit point not reachable
499
+ bool new_data = false ;
480
500
481
- return visit_edge (
482
- callee_function_id,
483
- l_end,
484
- calling_function_id,
485
- l_return,
486
- ns,
487
- working_set);
501
+ // The history used may mean there are multiple histories at the end of the
502
+ // function. Some of these can be progressed (for example, if the history
503
+ // tracks paths within functions), some can't be (for example, not all
504
+ // calling contexts will be appropriate). We rely on the history's step,
505
+ // called from visit_edge to prune as applicable.
506
+ for (auto p_end : *traces)
507
+ {
508
+ // do edge from end of function to instruction after call
509
+ const statet &end_state = get_state (p_end);
510
+
511
+ if (!end_state.is_bottom ())
512
+ {
513
+ // function exit point reachable in that history
514
+
515
+ new_data |= visit_edge (
516
+ callee_function_id,
517
+ p_end,
518
+ calling_function_id,
519
+ l_return,
520
+ ns,
521
+ working_set);
522
+ }
523
+ }
524
+
525
+ return new_data;
488
526
}
489
527
}
0 commit comments