Skip to content

Commit 06a220a

Browse files
committed
Reimplement remove-static-init-loops to avoid need to inspect GOTO program
This was supplying synthetic --unwind parameters for each loop found in the GOTO program, but that required early access to the GOTO code, which will not be available under symex- driven lazy loading. Therefore we replace that pass with a handler mechanism that allows the driver program to provide its own callback to make unwinding decisions, and use it in JBMC to ensure that enumeration type static initialisers are unwound sufficiently.
1 parent 2bb98d9 commit 06a220a

12 files changed

+232
-200
lines changed

src/cbmc/bmc.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ class bmct:public safety_checkert
7171
return run(goto_functions);
7272
}
7373

74+
void add_loop_unwind_handler(symex_bmct::loop_unwind_handlert handler)
75+
{
76+
symex.add_loop_unwind_handler(handler);
77+
}
78+
79+
void add_unwind_recursion_handler(
80+
symex_bmct::recursion_unwind_handlert handler)
81+
{
82+
symex.add_recursion_unwind_handler(handler);
83+
}
84+
7485
protected:
7586
const optionst &options;
7687
symbol_tablet new_symbol_table;

src/cbmc/cbmc_parse_options.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ Author: Daniel Kroening, kroening@kroening.com
4545
#include <goto-programs/remove_asm.h>
4646
#include <goto-programs/remove_unused_functions.h>
4747
#include <goto-programs/remove_skip.h>
48-
#include <goto-programs/remove_static_init_loops.h>
4948
#include <goto-programs/set_properties.h>
5049
#include <goto-programs/show_goto_functions.h>
5150
#include <goto-programs/show_symbol_table.h>

src/cbmc/symex_bmc.cpp

Lines changed: 67 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -110,27 +110,49 @@ bool symex_bmct::get_unwind(
110110
{
111111
const irep_idt id=goto_programt::loop_id(*source.pc);
112112

113-
// We use the most specific limit we have,
114-
// and 'infinity' when we have none.
115-
113+
tvt abort_unwind_decision;
116114
unsigned this_loop_limit=std::numeric_limits<unsigned>::max();
117115

118-
loop_limitst &this_thread_limits=
119-
thread_loop_limits[source.thread_nr];
116+
for(auto handler : loop_unwind_handlers)
117+
{
118+
abort_unwind_decision =
119+
handler(
120+
source.pc->function,
121+
source.pc->loop_number,
122+
unwind,
123+
this_loop_limit);
124+
if(abort_unwind_decision.is_known())
125+
break;
126+
}
120127

121-
loop_limitst::const_iterator l_it=this_thread_limits.find(id);
122-
if(l_it!=this_thread_limits.end())
123-
this_loop_limit=l_it->second;
124-
else
128+
// If no handler gave an opinion, use standard command-line --unwindset
129+
// / --unwind options to decide:
130+
if(abort_unwind_decision.is_unknown())
125131
{
126-
l_it=loop_limits.find(id);
127-
if(l_it!=loop_limits.end())
132+
// We use the most specific limit we have,
133+
// and 'infinity' when we have none.
134+
135+
loop_limitst &this_thread_limits=
136+
thread_loop_limits[source.thread_nr];
137+
138+
loop_limitst::const_iterator l_it=this_thread_limits.find(id);
139+
if(l_it!=this_thread_limits.end())
128140
this_loop_limit=l_it->second;
129-
else if(max_unwind_is_set)
130-
this_loop_limit=max_unwind;
141+
else
142+
{
143+
l_it=loop_limits.find(id);
144+
if(l_it!=loop_limits.end())
145+
this_loop_limit=l_it->second;
146+
else if(max_unwind_is_set)
147+
this_loop_limit=max_unwind;
148+
}
149+
150+
abort_unwind_decision = tvt(unwind >= this_loop_limit);
131151
}
132152

133-
bool abort=unwind>=this_loop_limit;
153+
INVARIANT(
154+
abort_unwind_decision.is_known(), "unwind decision should be taken by now");
155+
bool abort = abort_unwind_decision.is_true();
134156

135157
log.statistics() << (abort ? "Not unwinding" : "Unwinding") << " loop " << id
136158
<< " iteration " << unwind;
@@ -149,27 +171,44 @@ bool symex_bmct::get_unwind_recursion(
149171
const unsigned thread_nr,
150172
unsigned unwind)
151173
{
152-
// We use the most specific limit we have,
153-
// and 'infinity' when we have none.
154-
174+
tvt abort_unwind_decision;
155175
unsigned this_loop_limit=std::numeric_limits<unsigned>::max();
156176

157-
loop_limitst &this_thread_limits=
158-
thread_loop_limits[thread_nr];
177+
for(auto handler : recursion_unwind_handlers)
178+
{
179+
abort_unwind_decision = handler(id, unwind, this_loop_limit);
180+
if(abort_unwind_decision.is_known())
181+
break;
182+
}
159183

160-
loop_limitst::const_iterator l_it=this_thread_limits.find(id);
161-
if(l_it!=this_thread_limits.end())
162-
this_loop_limit=l_it->second;
163-
else
184+
// If no handler gave an opinion, use standard command-line --unwindset
185+
// / --unwind options to decide:
186+
if(abort_unwind_decision.is_unknown())
164187
{
165-
l_it=loop_limits.find(id);
166-
if(l_it!=loop_limits.end())
188+
// We use the most specific limit we have,
189+
// and 'infinity' when we have none.
190+
191+
loop_limitst &this_thread_limits=
192+
thread_loop_limits[thread_nr];
193+
194+
loop_limitst::const_iterator l_it=this_thread_limits.find(id);
195+
if(l_it!=this_thread_limits.end())
167196
this_loop_limit=l_it->second;
168-
else if(max_unwind_is_set)
169-
this_loop_limit=max_unwind;
197+
else
198+
{
199+
l_it=loop_limits.find(id);
200+
if(l_it!=loop_limits.end())
201+
this_loop_limit=l_it->second;
202+
else if(max_unwind_is_set)
203+
this_loop_limit=max_unwind;
204+
}
205+
206+
abort_unwind_decision = tvt(unwind>this_loop_limit);
170207
}
171208

172-
bool abort=unwind>this_loop_limit;
209+
INVARIANT(
210+
abort_unwind_decision.is_known(), "unwind decision should be taken by now");
211+
bool abort = abort_unwind_decision.is_true();
173212

174213
if(unwind>0 || abort)
175214
{

src/cbmc/symex_bmc.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Author: Daniel Kroening, kroening@kroening.com
1313
#define CPROVER_CBMC_SYMEX_BMC_H
1414

1515
#include <util/message.h>
16+
#include <util/threeval.h>
1617

1718
#include <goto-symex/goto_symex.h>
1819

@@ -53,6 +54,43 @@ class symex_bmct: public goto_symext
5354
loop_limits[id]=limit;
5455
}
5556

57+
/// Loop unwind handlers take the function ID and loop number, the unwind
58+
/// count so far, and an out-parameter specifying an advisory maximum, which
59+
/// they may set. If set the advisory maximum is set it is *only* used to
60+
/// print useful information for the user (e.g. "unwinding iteration N, max
61+
/// M"), and is not enforced. They return true to halt unwinding, false to
62+
/// authorise unwinding, or Unknown to indicate they have no opinion.
63+
typedef
64+
std::function<tvt(const irep_idt &, unsigned, unsigned, unsigned &)>
65+
loop_unwind_handlert;
66+
67+
/// Recursion unwind handlers take the function ID, the unwind count so far,
68+
/// and an out-parameter specifying an advisory maximum, which they may set.
69+
/// If set the advisory maximum is set it is *only* used to print useful
70+
/// information for the user (e.g. "unwinding iteration N, max M"),
71+
/// and is not enforced. They return true to halt unwinding, false to
72+
/// authorise unwinding, or Unknown to indicate they have no opinion.
73+
typedef std::function<tvt(const irep_idt &, unsigned, unsigned &)>
74+
recursion_unwind_handlert;
75+
76+
/// Add a callback function that will be called to determine whether to unwind
77+
/// loops. The first function added will get the first chance to answer, and
78+
/// the first authoratitive (true or false) answer is final.
79+
/// \param handler: new callback
80+
void add_loop_unwind_handler(loop_unwind_handlert handler)
81+
{
82+
loop_unwind_handlers.push_back(handler);
83+
}
84+
85+
/// Add a callback function that will be called to determine whether to unwind
86+
/// recursion. The first function added will get the first chance to answer,
87+
/// and the first authoratitive (true or false) answer is final.
88+
/// \param handler: new callback
89+
void add_recursion_unwind_handler(recursion_unwind_handlert handler)
90+
{
91+
recursion_unwind_handlers.push_back(handler);
92+
}
93+
5694
bool output_coverage_report(
5795
const goto_functionst &goto_functions,
5896
const std::string &path) const
@@ -67,6 +105,8 @@ class symex_bmct: public goto_symext
67105
// 1) a global limit (max_unwind)
68106
// 2) a limit per loop, all threads
69107
// 3) a limit for a particular thread.
108+
// 4) zero or more handler functions that can special-case particular
109+
// functions or loops
70110
// We use the most specific of the above.
71111

72112
unsigned max_unwind;
@@ -78,6 +118,12 @@ class symex_bmct: public goto_symext
78118
typedef std::map<unsigned, loop_limitst> thread_loop_limitst;
79119
thread_loop_limitst thread_loop_limits;
80120

121+
/// Callbacks that may provide an unwind/do-not-unwind decision for a loop
122+
std::vector<loop_unwind_handlert> loop_unwind_handlers;
123+
/// Callbacks that may provide an unwind/do-not-unwind decision for a
124+
/// recursive call
125+
std::vector<recursion_unwind_handlert> recursion_unwind_handlers;
126+
81127
//
82128
// overloaded from goto_symext
83129
//

src/goto-programs/Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ SRC = basic_blocks.cpp \
4747
remove_instanceof.cpp \
4848
remove_returns.cpp \
4949
remove_skip.cpp \
50-
remove_static_init_loops.cpp \
5150
remove_unreachable.cpp \
5251
remove_unused_functions.cpp \
5352
remove_vector.cpp \

src/goto-programs/remove_static_init_loops.cpp

Lines changed: 0 additions & 128 deletions
This file was deleted.

src/goto-programs/remove_static_init_loops.h

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)