diff --git a/regression/cbmc-concurrency/recursion1/main.c b/regression/cbmc-concurrency/recursion1/main.c new file mode 100644 index 0000000000..f4af36c6c4 --- /dev/null +++ b/regression/cbmc-concurrency/recursion1/main.c @@ -0,0 +1,25 @@ +void rec_spawn() +{ +__CPROVER_ASYNC_1: rec_spawn(); +} + +int main() +{ +start: + (void)0; +__CPROVER_ASYNC_1: + ({(void)0; + goto start;}); + +start2: + (void)0; +__CPROVER_ASYNC_1: + goto start2; + + rec_spawn(); + + __CPROVER_assert(0, "should be reachable when using --partial-loops"); + + return 0; +} + diff --git a/regression/cbmc-concurrency/recursion1/test.desc b/regression/cbmc-concurrency/recursion1/test.desc new file mode 100644 index 0000000000..607e2e5b21 --- /dev/null +++ b/regression/cbmc-concurrency/recursion1/test.desc @@ -0,0 +1,12 @@ +CORE +main.c +--unwind 1 --partial-loops --depth 100 +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^Unwinding loop c::main.0 iteration 1 file main.c line 12 function main thread 1 +^Unwinding loop c::main.1 iteration 1 file main.c line 17 function main thread 2 +^Unwinding recursion rec_spawn iteration 1 +-- +^warning: ignoring +^Unwinding recursion rec_spawn iteration [0-9][0-9] diff --git a/regression/cbmc/Recursion5/main.c b/regression/cbmc/Recursion5/main.c new file mode 100644 index 0000000000..135e39ea14 --- /dev/null +++ b/regression/cbmc/Recursion5/main.c @@ -0,0 +1,22 @@ +void loop(); + +void rec() +{ + loop(); +} + +void loop() +{ + while(nondet_bool()) + rec(); +} + +int main() +{ + rec(); + + __CPROVER_assert(0, ""); + + return 0; +} + diff --git a/regression/cbmc/Recursion5/test.desc b/regression/cbmc/Recursion5/test.desc new file mode 100644 index 0000000000..bb6455323c --- /dev/null +++ b/regression/cbmc/Recursion5/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +--unwind 2 --no-unwinding-assertions +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +-- +^warning: ignoring diff --git a/src/cbmc/bmc.cpp b/src/cbmc/bmc.cpp index 06e25e41c2..380e3f56fc 100644 --- a/src/cbmc/bmc.cpp +++ b/src/cbmc/bmc.cpp @@ -549,11 +549,22 @@ void bmct::setup_unwind() std::string::size_type next=set.find(",", idx); std::string val=set.substr(idx, next-idx); + long thread_nr=-1; + if(!val.empty() && + isdigit(val[0]) && + val.find(":")!=std::string::npos) + { + std::string nr=val.substr(0, val.find(":")); + thread_nr=atol(nr.c_str()); + val.erase(0, nr.size()+1); + } + if(val.rfind(":")!=std::string::npos) { std::string id=val.substr(0, val.rfind(":")); - unsigned long uw=atol(val.substr(val.rfind(":")+1).c_str()); - symex.unwind_set[id]=uw; + long uw=atol(val.substr(val.rfind(":")+1).c_str()); + + symex.set_unwind_limit(id, thread_nr, uw); } if(next==std::string::npos) break; diff --git a/src/cbmc/symex_bmc.cpp b/src/cbmc/symex_bmc.cpp index 7d243723cc..fecb95635b 100644 --- a/src/cbmc/symex_bmc.cpp +++ b/src/cbmc/symex_bmc.cpp @@ -78,13 +78,11 @@ bool symex_bmct::get_unwind( const symex_targett::sourcet &source, unsigned unwind) { - irep_idt id=(source.thread_nr!=0?(i2string(source.thread_nr)+":"):"")+ - id2string(source.pc->function)+"."+ - i2string(source.pc->loop_number); - unsigned long this_loop_max_unwind=max_unwind; - - if(unwind_set.count(id)!=0) - this_loop_max_unwind=unwind_set[id]; + const irep_idt id=goto_programt::loop_id(source.pc); + const long this_loop_max_unwind= + std::max(max_unwind, + std::max(thread_loop_limits[(unsigned)-1][id], + thread_loop_limits[source.thread_nr][id])); #if 1 statistics() << "Unwinding loop " << id << " iteration " @@ -109,15 +107,19 @@ Function: symex_bmct::get_unwind_recursion \*******************************************************************/ bool symex_bmct::get_unwind_recursion( - const irep_idt &identifier, + const irep_idt &id, + const unsigned thread_nr, unsigned unwind) { - unsigned long this_loop_max_unwind=max_unwind; + const long this_loop_max_unwind= + std::max(max_unwind, + std::max(thread_loop_limits[(unsigned)-1][id], + thread_loop_limits[thread_nr][id])); #if 1 if(unwind!=0) { - const symbolt &symbol=ns.lookup(identifier); + const symbolt &symbol=ns.lookup(id); statistics() << "Unwinding recursion " << symbol.display_name() diff --git a/src/cbmc/symex_bmc.h b/src/cbmc/symex_bmc.h index e33c6c571a..5006a4aaa6 100644 --- a/src/cbmc/symex_bmc.h +++ b/src/cbmc/symex_bmc.h @@ -28,10 +28,23 @@ public: irept last_location; // control unwinding - unsigned long max_unwind; - std::map unwind_set; + long max_unwind; + + void set_unwind_limit( + const irep_idt &id, + long thread_nr, + long limit) + { + unsigned t=thread_nr>=0 ? thread_nr : (unsigned)-1; + + thread_loop_limits[t][id]=limit; + } protected: + typedef hash_map_cont loop_limitst; + typedef std::map thread_loop_limitst; + thread_loop_limitst thread_loop_limits; + // // overloaded from goto_symext // @@ -46,6 +59,7 @@ protected: virtual bool get_unwind_recursion( const irep_idt &identifier, + const unsigned thread_nr, unsigned unwind); virtual void no_body(const irep_idt &identifier); diff --git a/src/goto-programs/goto_convert.cpp b/src/goto-programs/goto_convert.cpp index 06ae472930..5c7c21ac88 100644 --- a/src/goto-programs/goto_convert.cpp +++ b/src/goto-programs/goto_convert.cpp @@ -1721,17 +1721,6 @@ void goto_convertt::convert_start_thread( start_thread->location=code.location(); - // see if op0 is an unconditional goto - - if(code.op0().get(ID_statement)==ID_goto) - { - start_thread->code.set(ID_destination, - code.op0().get(ID_destination)); - - // remember it to do target later - targets.gotos.push_back(start_thread); - } - else { // start_thread label; // goto tmp; diff --git a/src/goto-programs/goto_program_template.h b/src/goto-programs/goto_program_template.h index 05061fa5a7..1fde86b1ad 100644 --- a/src/goto-programs/goto_program_template.h +++ b/src/goto-programs/goto_program_template.h @@ -381,6 +381,13 @@ public: //! Update all indices void update(); + + //! Human-readable loop name + inline static irep_idt loop_id(const_targett target) + { + return id2string(target->function)+"."+ + i2string(target->loop_number); + } //! Is the program empty? inline bool empty() const diff --git a/src/goto-symex/goto_symex.h b/src/goto-symex/goto_symex.h index 1aa792a11b..0fb292c794 100644 --- a/src/goto-symex/goto_symex.h +++ b/src/goto-symex/goto_symex.h @@ -209,6 +209,7 @@ protected: virtual bool get_unwind_recursion( const irep_idt &identifier, + const unsigned thread_nr, unsigned unwind); void argument_assignments( @@ -225,9 +226,6 @@ protected: void add_end_of_function( exprt &code, const irep_idt &identifier); - - std::map function_unwind; - std::map unwind_map; // exceptions diff --git a/src/goto-symex/goto_symex_state.h b/src/goto-symex/goto_symex_state.h index 637294a121..82f27c528c 100644 --- a/src/goto-symex/goto_symex_state.h +++ b/src/goto-symex/goto_symex_state.h @@ -291,6 +291,22 @@ public: // exceptions typedef std::map catch_mapt; catch_mapt catch_map; + + // loop and recursion unwinding + struct loop_infot + { + loop_infot(): + count(0), + is_recursion(false) + { + } + + unsigned count; + bool is_recursion; + }; + typedef hash_map_cont + loop_iterationst; + loop_iterationst loop_iterations; }; typedef std::vector call_stackt; diff --git a/src/goto-symex/symex_function_call.cpp b/src/goto-symex/symex_function_call.cpp index 9bcef714dd..60e1174495 100644 --- a/src/goto-symex/symex_function_call.cpp +++ b/src/goto-symex/symex_function_call.cpp @@ -35,6 +35,7 @@ Function: goto_symext::get_unwind_recursion bool goto_symext::get_unwind_recursion( const irep_idt &identifier, + const unsigned thread_nr, unsigned unwind) { return false; @@ -263,10 +264,13 @@ void goto_symext::symex_function_call_code( const goto_functionst::goto_functiont &goto_function=it->second; - unsigned &unwinding_counter=function_unwind[identifier]; + const bool stop_recursing=get_unwind_recursion( + identifier, + state.source.thread_nr, + state.top().loop_iterations[identifier].count); // see if it's too much - if(get_unwind_recursion(identifier, unwinding_counter)) + if(stop_recursing) { if(options.get_bool_option("partial-loops")) { @@ -314,9 +318,6 @@ void goto_symext::symex_function_call_code( for(unsigned i=0; isecond.is_recursion) + frame.loop_iterations.insert(*it); + // increase unwinding counter + frame.loop_iterations[identifier].is_recursion=true; + frame.loop_iterations[identifier].count++; + state.source.is_set=true; state.source.pc=goto_function.body.instructions.begin(); } @@ -367,10 +379,6 @@ void goto_symext::pop_frame(statet &state) it!=frame.local_variables.end(); it++) state.level2.remove(*it); - - // decrease recursion unwinding counter - if(frame.function_identifier!="") - function_unwind[frame.function_identifier]--; } state.pop_frame(); diff --git a/src/goto-symex/symex_goto.cpp b/src/goto-symex/symex_goto.cpp index 9899e67b7b..fba97b2f68 100644 --- a/src/goto-symex/symex_goto.cpp +++ b/src/goto-symex/symex_goto.cpp @@ -29,6 +29,7 @@ Function: goto_symext::symex_goto void goto_symext::symex_goto(statet &state) { const goto_programt::instructiont &instruction=*state.source.pc; + statet::framet &frame=state.top(); exprt old_guard=instruction.guard; clean_expr(old_guard, state, false); @@ -43,7 +44,8 @@ void goto_symext::symex_goto(statet &state) state.guard.is_false()) { // reset unwinding counter - unwind_map[state.source]=0; + if(instruction.is_backwards_goto()) + frame.loop_iterations[goto_programt::loop_id(state.source.pc)].count=0; // next instruction state.source.pc++; @@ -59,13 +61,12 @@ void goto_symext::symex_goto(statet &state) goto_programt::const_targett goto_target= instruction.get_target(); - bool forward= - state.source.pc->location_number< - goto_target->location_number; + bool forward=!instruction.is_backwards_goto(); if(!forward) // backwards? { - unsigned &unwind=unwind_map[state.source]; + unsigned &unwind= + frame.loop_iterations[goto_programt::loop_id(state.source.pc)].count; unwind++; if(get_unwind(state.source, unwind)) @@ -73,7 +74,7 @@ void goto_symext::symex_goto(statet &state) loop_bound_exceeded(state, new_guard); // reset unwinding - unwind_map[state.source]=0; + unwind=0; // next instruction state.source.pc++;