Skip to content

Commit 7f7e3c4

Browse files
author
tautschnig
committed
Fix (and cleanup) bounded thread spawn from loops or recursive calls with --unwind
- Also fixes bounded loop unwinding when loops contain recursive calls git-svn-id: svn+ssh://svn.cprover.org/srv/svn/cbmc/trunk@3161 6afb6bc1-c8e4-404c-8f48-9ae832c5b171
1 parent 4ac53e0 commit 7f7e3c4

File tree

13 files changed

+156
-43
lines changed

13 files changed

+156
-43
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
void rec_spawn()
2+
{
3+
__CPROVER_ASYNC_1: rec_spawn();
4+
}
5+
6+
int main()
7+
{
8+
start:
9+
(void)0;
10+
__CPROVER_ASYNC_1:
11+
({(void)0;
12+
goto start;});
13+
14+
start2:
15+
(void)0;
16+
__CPROVER_ASYNC_1:
17+
goto start2;
18+
19+
rec_spawn();
20+
21+
__CPROVER_assert(0, "should be reachable when using --partial-loops");
22+
23+
return 0;
24+
}
25+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--unwind 1 --partial-loops --depth 100
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
^Unwinding loop c::main.0 iteration 1 file main.c line 12 function main thread 1
8+
^Unwinding loop c::main.1 iteration 1 file main.c line 17 function main thread 2
9+
^Unwinding recursion rec_spawn iteration 1
10+
--
11+
^warning: ignoring
12+
^Unwinding recursion rec_spawn iteration [0-9][0-9]

regression/cbmc/Recursion5/main.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
void loop();
2+
3+
void rec()
4+
{
5+
loop();
6+
}
7+
8+
void loop()
9+
{
10+
while(nondet_bool())
11+
rec();
12+
}
13+
14+
int main()
15+
{
16+
rec();
17+
18+
__CPROVER_assert(0, "");
19+
20+
return 0;
21+
}
22+

regression/cbmc/Recursion5/test.desc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--unwind 2 --no-unwinding-assertions
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
^warning: ignoring

src/cbmc/bmc.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,11 +549,22 @@ void bmct::setup_unwind()
549549
std::string::size_type next=set.find(",", idx);
550550
std::string val=set.substr(idx, next-idx);
551551

552+
long thread_nr=-1;
553+
if(!val.empty() &&
554+
isdigit(val[0]) &&
555+
val.find(":")!=std::string::npos)
556+
{
557+
std::string nr=val.substr(0, val.find(":"));
558+
thread_nr=atol(nr.c_str());
559+
val.erase(0, nr.size()+1);
560+
}
561+
552562
if(val.rfind(":")!=std::string::npos)
553563
{
554564
std::string id=val.substr(0, val.rfind(":"));
555-
unsigned long uw=atol(val.substr(val.rfind(":")+1).c_str());
556-
symex.unwind_set[id]=uw;
565+
long uw=atol(val.substr(val.rfind(":")+1).c_str());
566+
567+
symex.set_unwind_limit(id, thread_nr, uw);
557568
}
558569

559570
if(next==std::string::npos) break;

src/cbmc/symex_bmc.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,11 @@ bool symex_bmct::get_unwind(
7878
const symex_targett::sourcet &source,
7979
unsigned unwind)
8080
{
81-
irep_idt id=(source.thread_nr!=0?(i2string(source.thread_nr)+":"):"")+
82-
id2string(source.pc->function)+"."+
83-
i2string(source.pc->loop_number);
84-
unsigned long this_loop_max_unwind=max_unwind;
85-
86-
if(unwind_set.count(id)!=0)
87-
this_loop_max_unwind=unwind_set[id];
81+
const irep_idt id=goto_programt::loop_id(source.pc);
82+
const long this_loop_max_unwind=
83+
std::max(max_unwind,
84+
std::max(thread_loop_limits[(unsigned)-1][id],
85+
thread_loop_limits[source.thread_nr][id]));
8886

8987
#if 1
9088
statistics() << "Unwinding loop " << id << " iteration "
@@ -109,15 +107,19 @@ Function: symex_bmct::get_unwind_recursion
109107
\*******************************************************************/
110108

111109
bool symex_bmct::get_unwind_recursion(
112-
const irep_idt &identifier,
110+
const irep_idt &id,
111+
const unsigned thread_nr,
113112
unsigned unwind)
114113
{
115-
unsigned long this_loop_max_unwind=max_unwind;
114+
const long this_loop_max_unwind=
115+
std::max(max_unwind,
116+
std::max(thread_loop_limits[(unsigned)-1][id],
117+
thread_loop_limits[thread_nr][id]));
116118

117119
#if 1
118120
if(unwind!=0)
119121
{
120-
const symbolt &symbol=ns.lookup(identifier);
122+
const symbolt &symbol=ns.lookup(id);
121123

122124
statistics() << "Unwinding recursion "
123125
<< symbol.display_name()

src/cbmc/symex_bmc.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,23 @@ class symex_bmct:
2828
irept last_location;
2929

3030
// control unwinding
31-
unsigned long max_unwind;
32-
std::map<irep_idt, long> unwind_set;
31+
long max_unwind;
32+
33+
void set_unwind_limit(
34+
const irep_idt &id,
35+
long thread_nr,
36+
long limit)
37+
{
38+
unsigned t=thread_nr>=0 ? thread_nr : (unsigned)-1;
39+
40+
thread_loop_limits[t][id]=limit;
41+
}
3342

3443
protected:
44+
typedef hash_map_cont<irep_idt, long, irep_id_hash> loop_limitst;
45+
typedef std::map<unsigned, loop_limitst> thread_loop_limitst;
46+
thread_loop_limitst thread_loop_limits;
47+
3548
//
3649
// overloaded from goto_symext
3750
//
@@ -46,6 +59,7 @@ class symex_bmct:
4659

4760
virtual bool get_unwind_recursion(
4861
const irep_idt &identifier,
62+
const unsigned thread_nr,
4963
unsigned unwind);
5064

5165
virtual void no_body(const irep_idt &identifier);

src/goto-programs/goto_convert.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,17 +1721,6 @@ void goto_convertt::convert_start_thread(
17211721

17221722
start_thread->location=code.location();
17231723

1724-
// see if op0 is an unconditional goto
1725-
1726-
if(code.op0().get(ID_statement)==ID_goto)
1727-
{
1728-
start_thread->code.set(ID_destination,
1729-
code.op0().get(ID_destination));
1730-
1731-
// remember it to do target later
1732-
targets.gotos.push_back(start_thread);
1733-
}
1734-
else
17351724
{
17361725
// start_thread label;
17371726
// goto tmp;

src/goto-programs/goto_program_template.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,13 @@ class goto_program_templatet
381381

382382
//! Update all indices
383383
void update();
384+
385+
//! Human-readable loop name
386+
inline static irep_idt loop_id(const_targett target)
387+
{
388+
return id2string(target->function)+"."+
389+
i2string(target->loop_number);
390+
}
384391

385392
//! Is the program empty?
386393
inline bool empty() const

src/goto-symex/goto_symex.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ class goto_symext
209209

210210
virtual bool get_unwind_recursion(
211211
const irep_idt &identifier,
212+
const unsigned thread_nr,
212213
unsigned unwind);
213214

214215
void argument_assignments(
@@ -225,9 +226,6 @@ class goto_symext
225226
void add_end_of_function(
226227
exprt &code,
227228
const irep_idt &identifier);
228-
229-
std::map<irep_idt, unsigned> function_unwind;
230-
std::map<symex_targett::sourcet, unsigned> unwind_map;
231229

232230
// exceptions
233231

src/goto-symex/goto_symex_state.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,22 @@ class goto_symex_statet
291291
// exceptions
292292
typedef std::map<irep_idt, goto_programt::targett> catch_mapt;
293293
catch_mapt catch_map;
294+
295+
// loop and recursion unwinding
296+
struct loop_infot
297+
{
298+
loop_infot():
299+
count(0),
300+
is_recursion(false)
301+
{
302+
}
303+
304+
unsigned count;
305+
bool is_recursion;
306+
};
307+
typedef hash_map_cont<irep_idt, loop_infot, irep_id_hash>
308+
loop_iterationst;
309+
loop_iterationst loop_iterations;
294310
};
295311

296312
typedef std::vector<framet> call_stackt;

src/goto-symex/symex_function_call.cpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Function: goto_symext::get_unwind_recursion
3535

3636
bool goto_symext::get_unwind_recursion(
3737
const irep_idt &identifier,
38+
const unsigned thread_nr,
3839
unsigned unwind)
3940
{
4041
return false;
@@ -263,10 +264,13 @@ void goto_symext::symex_function_call_code(
263264

264265
const goto_functionst::goto_functiont &goto_function=it->second;
265266

266-
unsigned &unwinding_counter=function_unwind[identifier];
267+
const bool stop_recursing=get_unwind_recursion(
268+
identifier,
269+
state.source.thread_nr,
270+
state.top().loop_iterations[identifier].count);
267271

268272
// see if it's too much
269-
if(get_unwind_recursion(identifier, unwinding_counter))
273+
if(stop_recursing)
270274
{
271275
if(options.get_bool_option("partial-loops"))
272276
{
@@ -314,9 +318,6 @@ void goto_symext::symex_function_call_code(
314318
for(unsigned i=0; i<arguments.size(); i++)
315319
state.rename(arguments[i], ns);
316320

317-
// increase unwinding counter
318-
unwinding_counter++;
319-
320321
// produce a new frame
321322
assert(!state.call_stack().empty());
322323
goto_symex_statet::framet &frame=state.new_frame();
@@ -332,6 +333,17 @@ void goto_symext::symex_function_call_code(
332333
frame.calling_location=state.source;
333334
frame.function_identifier=identifier;
334335

336+
const goto_symex_statet::framet &p_frame=state.previous_frame();
337+
for(goto_symex_statet::framet::loop_iterationst::const_iterator
338+
it=p_frame.loop_iterations.begin();
339+
it!=p_frame.loop_iterations.end();
340+
++it)
341+
if(it->second.is_recursion)
342+
frame.loop_iterations.insert(*it);
343+
// increase unwinding counter
344+
frame.loop_iterations[identifier].is_recursion=true;
345+
frame.loop_iterations[identifier].count++;
346+
335347
state.source.is_set=true;
336348
state.source.pc=goto_function.body.instructions.begin();
337349
}
@@ -367,10 +379,6 @@ void goto_symext::pop_frame(statet &state)
367379
it!=frame.local_variables.end();
368380
it++)
369381
state.level2.remove(*it);
370-
371-
// decrease recursion unwinding counter
372-
if(frame.function_identifier!="")
373-
function_unwind[frame.function_identifier]--;
374382
}
375383

376384
state.pop_frame();

src/goto-symex/symex_goto.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Function: goto_symext::symex_goto
2929
void goto_symext::symex_goto(statet &state)
3030
{
3131
const goto_programt::instructiont &instruction=*state.source.pc;
32+
statet::framet &frame=state.top();
3233

3334
exprt old_guard=instruction.guard;
3435
clean_expr(old_guard, state, false);
@@ -43,7 +44,8 @@ void goto_symext::symex_goto(statet &state)
4344
state.guard.is_false())
4445
{
4546
// reset unwinding counter
46-
unwind_map[state.source]=0;
47+
if(instruction.is_backwards_goto())
48+
frame.loop_iterations[goto_programt::loop_id(state.source.pc)].count=0;
4749

4850
// next instruction
4951
state.source.pc++;
@@ -59,21 +61,20 @@ void goto_symext::symex_goto(statet &state)
5961
goto_programt::const_targett goto_target=
6062
instruction.get_target();
6163

62-
bool forward=
63-
state.source.pc->location_number<
64-
goto_target->location_number;
64+
bool forward=!instruction.is_backwards_goto();
6565

6666
if(!forward) // backwards?
6767
{
68-
unsigned &unwind=unwind_map[state.source];
68+
unsigned &unwind=
69+
frame.loop_iterations[goto_programt::loop_id(state.source.pc)].count;
6970
unwind++;
7071

7172
if(get_unwind(state.source, unwind))
7273
{
7374
loop_bound_exceeded(state, new_guard);
7475

7576
// reset unwinding
76-
unwind_map[state.source]=0;
77+
unwind=0;
7778

7879
// next instruction
7980
state.source.pc++;

0 commit comments

Comments
 (0)