Skip to content

Commit eb19783

Browse files
author
Daniel Kroening
authored
Merge pull request #395 from smowton/call_destructors_on_goto_out_of_scope
Call destructors on goto out of scope
2 parents 0353de5 + cfbe2ac commit eb19783

File tree

6 files changed

+190
-27
lines changed

6 files changed

+190
-27
lines changed
443 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
public class Break {
3+
4+
public static void main() {
5+
6+
int j = 0;
7+
++j;
8+
for(int i = 0; i < 10; ++i)
9+
if(i == 5) break;
10+
for(j = 0; j < 10; ++j)
11+
if(j == 5) break;
12+
}
13+
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
Break.class
3+
--show-goto-functions
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
dead i;
7+
--
8+
GOTO 10

src/goto-programs/goto_convert.cpp

+131-18
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ Function: goto_convertt::finish_gotos
5757
5858
\*******************************************************************/
5959

60-
void goto_convertt::finish_gotos()
60+
void goto_convertt::finish_gotos(goto_programt &dest)
6161
{
6262
for(const auto &g_it : targets.gotos)
6363
{
64-
goto_programt::instructiont &i=*g_it;
64+
goto_programt::instructiont &i=*(g_it.first);
6565

6666
if(i.code.get_statement()=="non-deterministic-goto")
6767
{
@@ -81,7 +81,7 @@ void goto_convertt::finish_gotos()
8181
throw 0;
8282
}
8383

84-
i.targets.push_back(l_it->second);
84+
i.targets.push_back(l_it->second.first);
8585
}
8686
}
8787
else if(i.is_start_thread())
@@ -98,7 +98,7 @@ void goto_convertt::finish_gotos()
9898
throw 0;
9999
}
100100

101-
i.targets.push_back(l_it->second);
101+
i.targets.push_back(l_it->second.first);
102102
}
103103
else if(i.code.get_statement()==ID_goto)
104104
{
@@ -114,7 +114,49 @@ void goto_convertt::finish_gotos()
114114
}
115115

116116
i.targets.clear();
117-
i.targets.push_back(l_it->second);
117+
i.targets.push_back(l_it->second.first);
118+
119+
// If the goto recorded a destructor stack, execute as much as is
120+
// appropriate for however many automatic variables leave scope.
121+
// We don't currently handle variables *entering* scope, which is illegal
122+
// for C++ non-pod types and impossible in Java in any case.
123+
auto goto_stack=g_it.second;
124+
const auto& label_stack=l_it->second.second;
125+
bool stack_is_prefix=true;
126+
if(label_stack.size()>goto_stack.size())
127+
stack_is_prefix=false;
128+
for(unsigned i=0, ilim=label_stack.size();
129+
i!=ilim && stack_is_prefix;
130+
++i)
131+
{
132+
if(goto_stack[i]!=label_stack[i])
133+
stack_is_prefix=false;
134+
}
135+
136+
if(!stack_is_prefix)
137+
{
138+
warning() << "Encountered goto (" << goto_label <<
139+
") that enters one or more lexical blocks;" <<
140+
"omitting constructors and destructors." << eom;
141+
}
142+
else
143+
{
144+
auto unwind_to_size=label_stack.size();
145+
if(unwind_to_size<goto_stack.size())
146+
{
147+
status() << "Adding goto-destructor code on jump to " <<
148+
goto_label << eom;
149+
goto_programt destructor_code;
150+
unwind_destructor_stack(
151+
i.code.add_source_location(),
152+
unwind_to_size,
153+
destructor_code,
154+
goto_stack);
155+
dest.destructive_insert(g_it.first, destructor_code);
156+
// This should leave iterators intact, as long as
157+
// goto_programt::instructionst is std::list.
158+
}
159+
}
118160
}
119161
else
120162
{
@@ -169,7 +211,7 @@ void goto_convertt::finish_computed_gotos(goto_programt &goto_program)
169211
goto_programt::targett t=
170212
goto_program.insert_after(g_it);
171213

172-
t->make_goto(label.second);
214+
t->make_goto(label.second.first);
173215
t->source_location=i.source_location;
174216
t->guard=guard;
175217
}
@@ -180,6 +222,50 @@ void goto_convertt::finish_computed_gotos(goto_programt &goto_program)
180222

181223
/*******************************************************************\
182224
225+
Function: goto_convertt::finish_guarded_gotos
226+
227+
Inputs: Destination goto program
228+
229+
Outputs:
230+
231+
Purpose: For each if(x) goto z; goto y; z: emitted,
232+
see if any destructor statements were inserted
233+
between goto z and z, and if not, simplify into
234+
if(!x) goto y;
235+
236+
\*******************************************************************/
237+
238+
void goto_convertt::finish_guarded_gotos(goto_programt &dest)
239+
{
240+
for(auto& gg : guarded_gotos)
241+
{
242+
// Check if any destructor code has been inserted:
243+
bool destructor_present=false;
244+
for(auto it=gg.ifiter;
245+
it!=gg.gotoiter && !destructor_present;
246+
++it)
247+
{
248+
if(!(it->is_goto() || it->is_skip()))
249+
destructor_present=true;
250+
}
251+
252+
// If so, can't simplify.
253+
if(destructor_present)
254+
continue;
255+
256+
// Simplify: remove whatever code was generated for the condition
257+
// and attach the original guard to the goto instruction.
258+
gg.gotoiter->guard=gg.guard;
259+
// goto_programt doesn't provide an erase operation,
260+
// perhaps for a good reason, so let's be cautious and just
261+
// flatten the un-needed instructions into skips.
262+
for(auto it=gg.ifiter, itend=gg.gotoiter; it!=itend; ++it)
263+
it->make_skip();
264+
}
265+
}
266+
267+
/*******************************************************************\
268+
183269
Function: goto_convertt::goto_convert
184270
185271
Inputs:
@@ -213,8 +299,9 @@ void goto_convertt::goto_convert_rec(
213299
{
214300
convert(code, dest);
215301

216-
finish_gotos();
302+
finish_gotos(dest);
217303
finish_computed_gotos(dest);
304+
finish_guarded_gotos(dest);
218305
}
219306

220307
/*******************************************************************\
@@ -282,8 +369,7 @@ void goto_convertt::convert_label(
282369
goto_programt::targett target=tmp.instructions.begin();
283370
dest.destructive_append(tmp);
284371

285-
targets.labels.insert(std::pair<irep_idt, goto_programt::targett>
286-
(label, target));
372+
targets.labels.insert({label, {target, targets.destructor_stack}});
287373
target->labels.push_front(label);
288374
}
289375

@@ -1628,7 +1714,7 @@ void goto_convertt::convert_goto(
16281714
t->code=code;
16291715

16301716
// remember it to do target later
1631-
targets.gotos.push_back(t);
1717+
targets.gotos.push_back(std::make_pair(t,targets.destructor_stack));
16321718
}
16331719

16341720
/*******************************************************************\
@@ -2130,6 +2216,24 @@ void goto_convertt::collect_operands(
21302216

21312217
/*******************************************************************\
21322218
2219+
Function: is_size_one
2220+
2221+
Inputs: Goto program 'g'
2222+
2223+
Outputs: True if 'g' has one instruction
2224+
2225+
Purpose: This is (believed to be) faster than using std::list.size
2226+
2227+
\*******************************************************************/
2228+
2229+
static inline bool is_size_one(const goto_programt &g)
2230+
{
2231+
return (!g.instructions.empty()) &&
2232+
++g.instructions.begin()==g.instructions.end();
2233+
}
2234+
2235+
/*******************************************************************\
2236+
21332237
Function: goto_convertt::generate_ifthenelse
21342238
21352239
Inputs:
@@ -2161,24 +2265,24 @@ void goto_convertt::generate_ifthenelse(
21612265
return;
21622266
}
21632267

2268+
bool is_guarded_goto=false;
2269+
21642270
// do guarded gotos directly
21652271
if(is_empty(false_case) &&
2166-
// true_case.instructions.size()==1 optimised
2167-
!true_case.instructions.empty() &&
2168-
++true_case.instructions.begin()==true_case.instructions.end() &&
2272+
is_size_one(true_case) &&
21692273
true_case.instructions.back().is_goto() &&
21702274
true_case.instructions.back().guard.is_true() &&
21712275
true_case.instructions.back().labels.empty())
21722276
{
21732277
// The above conjunction deliberately excludes the instance
21742278
// if(some) { label: goto somewhere; }
2175-
true_case.instructions.back().guard=guard;
2176-
dest.destructive_append(true_case);
2177-
return;
2279+
// Don't perform the transformation here, as code might get inserted into
2280+
// the true case to perform destructors. This will be attempted in finish_guarded_gotos.
2281+
is_guarded_goto=true;
21782282
}
21792283

21802284
// similarly, do guarded assertions directly
2181-
if(true_case.instructions.size()==1 &&
2285+
if(is_size_one(true_case) &&
21822286
true_case.instructions.back().is_assert() &&
21832287
true_case.instructions.back().guard.is_false() &&
21842288
true_case.instructions.back().labels.empty())
@@ -2191,7 +2295,7 @@ void goto_convertt::generate_ifthenelse(
21912295
}
21922296

21932297
// similarly, do guarded assertions directly
2194-
if(false_case.instructions.size()==1 &&
2298+
if(is_size_one(false_case) &&
21952299
false_case.instructions.back().is_assert() &&
21962300
false_case.instructions.back().guard.is_false() &&
21972301
false_case.instructions.back().labels.empty())
@@ -2258,6 +2362,15 @@ void goto_convertt::generate_ifthenelse(
22582362
assert(!tmp_w.instructions.empty());
22592363
x->source_location=tmp_w.instructions.back().source_location;
22602364

2365+
// See if we can simplify this guarded goto later.
2366+
// Note this depends on the fact that `instructions` is a std::list
2367+
// and so goto-program-destructive-append preserves iterator validity.
2368+
if(is_guarded_goto)
2369+
guarded_gotos.push_back({
2370+
tmp_v.instructions.begin(),
2371+
tmp_w.instructions.begin(),
2372+
guard});
2373+
22612374
dest.destructive_append(tmp_v);
22622375
dest.destructive_append(tmp_w);
22632376

src/goto-programs/goto_convert_class.h

+19-4
Original file line numberDiff line numberDiff line change
@@ -216,22 +216,29 @@ class goto_convertt:public messaget
216216
// exceptions
217217
//
218218

219+
typedef std::vector<codet> destructor_stackt;
220+
219221
symbol_exprt exception_flag();
220222
void unwind_destructor_stack(
221223
const source_locationt &,
222224
std::size_t stack_size,
223225
goto_programt &dest);
226+
void unwind_destructor_stack(
227+
const source_locationt &,
228+
std::size_t stack_size,
229+
goto_programt &dest,
230+
destructor_stackt &stack);
224231

225232
//
226233
// gotos
227234
//
228235

229-
void finish_gotos();
236+
void finish_gotos(goto_programt &dest);
230237
void finish_computed_gotos(goto_programt &dest);
238+
void finish_guarded_gotos(goto_programt &dest);
231239

232-
typedef std::vector<codet> destructor_stackt;
233-
typedef std::map<irep_idt, goto_programt::targett> labelst;
234-
typedef std::list<goto_programt::targett> gotost;
240+
typedef std::map<irep_idt, std::pair<goto_programt::targett, destructor_stackt> > labelst;
241+
typedef std::list<std::pair<goto_programt::targett, destructor_stackt> > gotost;
235242
typedef std::list<goto_programt::targett> computed_gotost;
236243
typedef exprt::operandst caset;
237244
typedef std::list<std::pair<goto_programt::targett, caset> > casest;
@@ -412,6 +419,14 @@ class goto_convertt:public messaget
412419
std::size_t leave_stack_size;
413420
};
414421

422+
struct guarded_gotot {
423+
goto_programt::targett ifiter;
424+
goto_programt::targett gotoiter;
425+
exprt guard;
426+
};
427+
typedef std::list<guarded_gotot> guarded_gotost;
428+
guarded_gotost guarded_gotos;
429+
415430
exprt case_guard(
416431
const exprt &value,
417432
const caset &case_op);

src/goto-programs/goto_convert_exceptions.cpp

+18-5
Original file line numberDiff line numberDiff line change
@@ -374,23 +374,36 @@ void goto_convertt::unwind_destructor_stack(
374374
const source_locationt &source_location,
375375
std::size_t final_stack_size,
376376
goto_programt &dest)
377+
{
378+
unwind_destructor_stack(
379+
source_location,
380+
final_stack_size,
381+
dest,
382+
targets.destructor_stack);
383+
}
384+
385+
void goto_convertt::unwind_destructor_stack(
386+
const source_locationt &source_location,
387+
std::size_t final_stack_size,
388+
goto_programt &dest,
389+
destructor_stackt &destructor_stack)
377390
{
378391
// There might be exceptions happening in the exception
379392
// handler. We thus pop off the stack, and then later
380393
// one restore the original stack.
381-
destructor_stackt old_stack=targets.destructor_stack;
394+
destructor_stackt old_stack=destructor_stack;
382395

383-
while(targets.destructor_stack.size()>final_stack_size)
396+
while(destructor_stack.size()>final_stack_size)
384397
{
385-
codet d_code=targets.destructor_stack.back();
398+
codet d_code=destructor_stack.back();
386399
d_code.add_source_location()=source_location;
387400

388401
// pop now to avoid doing this again
389-
targets.destructor_stack.pop_back();
402+
destructor_stack.pop_back();
390403

391404
convert(d_code, dest);
392405
}
393406

394407
// Now restore old stack.
395-
old_stack.swap(targets.destructor_stack);
408+
old_stack.swap(destructor_stack);
396409
}

0 commit comments

Comments
 (0)