@@ -334,15 +334,33 @@ bool ai_baset::visit(
334334
335335 statet ¤t=get_state (l);
336336
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)
338345 {
339346 if (to_l==goto_program.instructions .end ())
340347 continue ;
341348
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;
344352
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;
346364
347365 bool have_new_values=false ;
348366
@@ -367,8 +385,11 @@ bool ai_baset::visit(
367385
368386 new_values.transform (l, to_l, *this , ns);
369387
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);
372393 }
373394
374395 if (have_new_values)
@@ -378,6 +399,12 @@ bool ai_baset::visit(
378399 }
379400 }
380401
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+
381408 return new_data;
382409}
383410
@@ -400,7 +427,7 @@ bool ai_baset::do_function_call(
400427 std::unique_ptr<statet> tmp_state (make_temporary_state (get_state (l_call)));
401428 tmp_state->transform (l_call, l_return, *this , ns);
402429
403- return merge (*tmp_state, l_call, l_return);
430+ return merge (std::move ( *tmp_state) , l_call, l_return);
404431 }
405432
406433 assert (!goto_function.body .instructions .empty ());
@@ -420,7 +447,7 @@ bool ai_baset::do_function_call(
420447 bool new_data=false ;
421448
422449 // merge the new stuff
423- if (merge (*tmp_state, l_call, l_begin))
450+ if (merge (std::move ( *tmp_state) , l_call, l_begin))
424451 new_data=true ;
425452
426453 // do we need to do/re-do the fixedpoint of the body?
@@ -445,7 +472,7 @@ bool ai_baset::do_function_call(
445472 tmp_state->transform (l_end, l_return, *this , ns);
446473
447474 // Propagate those
448- return merge (*tmp_state, l_end, l_return);
475+ return merge (std::move ( *tmp_state) , l_end, l_return);
449476 }
450477}
451478
@@ -523,6 +550,42 @@ bool ai_baset::do_function_call_rec(
523550 return new_data;
524551}
525552
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+
526589void ai_baset::sequential_fixedpoint (
527590 const goto_functionst &goto_functions,
528591 const namespacet &ns)
0 commit comments