@@ -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,7 @@ 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
+ have_new_values |= merge (std::move (new_values), l, to_l);
372
389
}
373
390
374
391
if (have_new_values)
@@ -378,6 +395,12 @@ bool ai_baset::visit(
378
395
}
379
396
}
380
397
398
+ if (!must_retain_current_state)
399
+ {
400
+ // If this state isn't needed any longer, destroy it now:
401
+ current.make_bottom ();
402
+ }
403
+
381
404
return new_data;
382
405
}
383
406
@@ -400,7 +423,7 @@ bool ai_baset::do_function_call(
400
423
std::unique_ptr<statet> tmp_state (make_temporary_state (get_state (l_call)));
401
424
tmp_state->transform (l_call, l_return, *this , ns);
402
425
403
- return merge (*tmp_state, l_call, l_return);
426
+ return merge (std::move ( *tmp_state) , l_call, l_return);
404
427
}
405
428
406
429
assert (!goto_function.body .instructions .empty ());
@@ -420,7 +443,7 @@ bool ai_baset::do_function_call(
420
443
bool new_data=false ;
421
444
422
445
// merge the new stuff
423
- if (merge (*tmp_state, l_call, l_begin))
446
+ if (merge (std::move ( *tmp_state) , l_call, l_begin))
424
447
new_data=true ;
425
448
426
449
// do we need to do/re-do the fixedpoint of the body?
@@ -445,7 +468,7 @@ bool ai_baset::do_function_call(
445
468
tmp_state->transform (l_end, l_return, *this , ns);
446
469
447
470
// Propagate those
448
- return merge (*tmp_state, l_end, l_return);
471
+ return merge (std::move ( *tmp_state) , l_end, l_return);
449
472
}
450
473
}
451
474
@@ -523,6 +546,42 @@ bool ai_baset::do_function_call_rec(
523
546
return new_data;
524
547
}
525
548
549
+ // / Determine whether we must retain the state for program point `l` even after
550
+ // / fixpoint analysis completes (e.g. for our user to inspect), or if it solely
551
+ // / for internal use and can be thrown away as and when suits us.
552
+ // / \param l: program point
553
+ // / \param l_program: program that l belongs to
554
+ // / \return true if we must retain the state for that program point
555
+ bool ai_baset::must_retain_state (
556
+ locationt l, const goto_programt &l_program) const
557
+ {
558
+ // If the derived class doesn't specify otherwise, assume the old default
559
+ // behaviour: always retain state.
560
+ if (!must_retain_state_callback)
561
+ return true ;
562
+
563
+ // Regardless, always keep states with multiple predecessors, which is
564
+ // required for termination when loops are present, and saves redundant
565
+ // re-investigation of successors for other kinds of convergence.
566
+ if (l->incoming_edges .size () > 1 )
567
+ return true ;
568
+
569
+ // Similarly retain function head instructions, which may have multiple
570
+ // incoming call-graph edges:
571
+ if (l == l_program.instructions .begin ())
572
+ return true ;
573
+
574
+ // ...and retain function end states, as they will be propagated to many
575
+ // different callers (they have multiple successors, even though they appear
576
+ // to have none intraprocedurally).
577
+ if (l->is_end_function ())
578
+ return true ;
579
+
580
+ // Finally, retain states where the particular subclass specifies they
581
+ // should be kept:
582
+ return must_retain_state_callback (l);
583
+ }
584
+
526
585
void ai_baset::sequential_fixedpoint (
527
586
const goto_functionst &goto_functions,
528
587
const namespacet &ns)
0 commit comments