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