@@ -334,15 +334,33 @@ bool ai_baset::visit(
334
334
335
335
statet ¤t=get_state (l);
336
336
337
- for (const auto &to_l : goto_program.get_successors (l))
337
+ // Check if our user wants to be able to inspect this state after the
338
+ // fixpoint algorithm terminates.
339
+ // Default if the instantiating code hasn't supplied any specific advice is
340
+ // to retain all states.
341
+ bool must_retain_current_state = must_retain_state (l, goto_program);
342
+
343
+ std::list<locationt> successors = goto_program.get_successors (l);
344
+ for (const auto &to_l : successors)
338
345
{
339
346
if (to_l==goto_program.instructions .end ())
340
347
continue ;
341
348
342
- std::unique_ptr<statet> tmp_state (
343
- make_temporary_state (current));
349
+ // If permissible, alter the existing instruction's state in place;
350
+ // otherwise use a temporary copy:
351
+ std::unique_ptr<statet> tmp_state;
344
352
345
- statet &new_values=*tmp_state;
353
+ // Note this condition is stricter than `!must_retain_current_state` because
354
+ // when we have multiple successors `transform` may do something different
355
+ // for different successors, in which case we must use a clean copy of the
356
+ // pre-existing state each time.
357
+ bool may_work_in_place =
358
+ successors.size () == 1 && !must_retain_current_state;
359
+
360
+ if (!may_work_in_place)
361
+ tmp_state = make_temporary_state (current);
362
+
363
+ statet &new_values = may_work_in_place ? current : *tmp_state;
346
364
347
365
bool have_new_values=false ;
348
366
@@ -367,8 +385,11 @@ bool ai_baset::visit(
367
385
368
386
new_values.transform (l, to_l, *this , ns);
369
387
370
- if (merge (new_values, l, to_l))
371
- have_new_values=true ;
388
+ // Note new_values is moved regardless of `may_work_in_place`:
389
+ // In one case we're working in place and the source domain (`current`)
390
+ // is about to be `make_bottom`'d below, and in the other we've made
391
+ // a temporary copy `tmp_state` which is about to die.
392
+ have_new_values |= merge (std::move (new_values), l, to_l);
372
393
}
373
394
374
395
if (have_new_values)
@@ -378,6 +399,12 @@ bool ai_baset::visit(
378
399
}
379
400
}
380
401
402
+ if (!must_retain_current_state)
403
+ {
404
+ // If this state isn't needed any longer, destroy it now:
405
+ current.make_bottom ();
406
+ }
407
+
381
408
return new_data;
382
409
}
383
410
@@ -400,7 +427,7 @@ bool ai_baset::do_function_call(
400
427
std::unique_ptr<statet> tmp_state (make_temporary_state (get_state (l_call)));
401
428
tmp_state->transform (l_call, l_return, *this , ns);
402
429
403
- return merge (*tmp_state, l_call, l_return);
430
+ return merge (std::move ( *tmp_state) , l_call, l_return);
404
431
}
405
432
406
433
assert (!goto_function.body .instructions .empty ());
@@ -420,7 +447,7 @@ bool ai_baset::do_function_call(
420
447
bool new_data=false ;
421
448
422
449
// merge the new stuff
423
- if (merge (*tmp_state, l_call, l_begin))
450
+ if (merge (std::move ( *tmp_state) , l_call, l_begin))
424
451
new_data=true ;
425
452
426
453
// do we need to do/re-do the fixedpoint of the body?
@@ -445,7 +472,7 @@ bool ai_baset::do_function_call(
445
472
tmp_state->transform (l_end, l_return, *this , ns);
446
473
447
474
// Propagate those
448
- return merge (*tmp_state, l_end, l_return);
475
+ return merge (std::move ( *tmp_state) , l_end, l_return);
449
476
}
450
477
}
451
478
@@ -523,6 +550,42 @@ bool ai_baset::do_function_call_rec(
523
550
return new_data;
524
551
}
525
552
553
+ // / Determine whether we must retain the state for program point `l` even after
554
+ // / fixpoint analysis completes (e.g. for our user to inspect), or if it solely
555
+ // / for internal use and can be thrown away as and when suits us.
556
+ // / \param l: program point
557
+ // / \param l_program: program that l belongs to
558
+ // / \return true if we must retain the state for that program point
559
+ bool ai_baset::must_retain_state (
560
+ locationt l, const goto_programt &l_program) const
561
+ {
562
+ // If the derived class doesn't specify otherwise, assume the old default
563
+ // behaviour: always retain state.
564
+ if (!must_retain_state_callback)
565
+ return true ;
566
+
567
+ // Regardless, always keep states with multiple predecessors, which is
568
+ // required for termination when loops are present, and saves redundant
569
+ // re-investigation of successors for other kinds of convergence.
570
+ if (l->incoming_edges .size () > 1 )
571
+ return true ;
572
+
573
+ // Similarly retain function head instructions, which may have multiple
574
+ // incoming call-graph edges:
575
+ if (l == l_program.instructions .begin ())
576
+ return true ;
577
+
578
+ // ...and retain function end states, as they will be propagated to many
579
+ // different callers (they have multiple successors, even though they appear
580
+ // to have none intraprocedurally).
581
+ if (l->is_end_function ())
582
+ return true ;
583
+
584
+ // Finally, retain states where the particular subclass specifies they
585
+ // should be kept:
586
+ return must_retain_state_callback (l);
587
+ }
588
+
526
589
void ai_baset::sequential_fixedpoint (
527
590
const goto_functionst &goto_functions,
528
591
const namespacet &ns)
0 commit comments