Skip to content

Commit ea16fd4

Browse files
committed
Restore the guarded-goto construct
Instead of building it in the first pass, make a pessimistic multi-instruction branch and then promote it in the post-pass if no destructors got added.
1 parent 8e98eaa commit ea16fd4

File tree

2 files changed

+91
-17
lines changed

2 files changed

+91
-17
lines changed

src/goto-programs/goto_convert.cpp

+82-17
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ void goto_convertt::finish_gotos(goto_programt &dest)
118118

119119
// If the goto recorded a destructor stack, execute as much as is
120120
// appropriate for however many automatic variables leave scope.
121-
// We don't currently handle variables *entering* 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.
122123
auto goto_stack=g_it.second;
123124
const auto& label_stack=l_it->second.second;
124125
bool stack_is_prefix=true;
@@ -221,6 +222,50 @@ void goto_convertt::finish_computed_gotos(goto_programt &goto_program)
221222

222223
/*******************************************************************\
223224
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+
224269
Function: goto_convertt::goto_convert
225270
226271
Inputs:
@@ -256,6 +301,7 @@ void goto_convertt::goto_convert_rec(
256301

257302
finish_gotos(dest);
258303
finish_computed_gotos(dest);
304+
finish_guarded_gotos(dest);
259305
}
260306

261307
/*******************************************************************\
@@ -1662,14 +1708,6 @@ void goto_convertt::convert_goto(
16621708
const codet &code,
16631709
goto_programt &dest)
16641710
{
1665-
// Precede with a 'skip', which will be replaced by any pre-departure
1666-
// destructor code if appropriate. Without this the goto can be amalgamated
1667-
// into a control-flow structure, such as IF x THEN GOTO 1;, leaving
1668-
// nowhere for the destructors to go.
1669-
goto_programt::targett skip=dest.add_instruction(SKIP);
1670-
skip->source_location=code.source_location();
1671-
skip->code=code_skipt();
1672-
16731711
goto_programt::targett t=dest.add_instruction();
16741712
t->make_goto();
16751713
t->source_location=code.source_location();
@@ -2178,6 +2216,24 @@ void goto_convertt::collect_operands(
21782216

21792217
/*******************************************************************\
21802218
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+
21812237
Function: goto_convertt::generate_ifthenelse
21822238
21832239
Inputs:
@@ -2209,24 +2265,24 @@ void goto_convertt::generate_ifthenelse(
22092265
return;
22102266
}
22112267

2268+
bool is_guarded_goto=false;
2269+
22122270
// do guarded gotos directly
22132271
if(is_empty(false_case) &&
2214-
// true_case.instructions.size()==1 optimised
2215-
!true_case.instructions.empty() &&
2216-
++true_case.instructions.begin()==true_case.instructions.end() &&
2272+
is_size_one(true_case) &&
22172273
true_case.instructions.back().is_goto() &&
22182274
true_case.instructions.back().guard.is_true() &&
22192275
true_case.instructions.back().labels.empty())
22202276
{
22212277
// The above conjunction deliberately excludes the instance
22222278
// if(some) { label: goto somewhere; }
2223-
true_case.instructions.back().guard=guard;
2224-
dest.destructive_append(true_case);
2225-
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;
22262282
}
22272283

22282284
// similarly, do guarded assertions directly
2229-
if(true_case.instructions.size()==1 &&
2285+
if(is_size_one(true_case) &&
22302286
true_case.instructions.back().is_assert() &&
22312287
true_case.instructions.back().guard.is_false() &&
22322288
true_case.instructions.back().labels.empty())
@@ -2239,7 +2295,7 @@ void goto_convertt::generate_ifthenelse(
22392295
}
22402296

22412297
// similarly, do guarded assertions directly
2242-
if(false_case.instructions.size()==1 &&
2298+
if(is_size_one(false_case) &&
22432299
false_case.instructions.back().is_assert() &&
22442300
false_case.instructions.back().guard.is_false() &&
22452301
false_case.instructions.back().labels.empty())
@@ -2306,6 +2362,15 @@ void goto_convertt::generate_ifthenelse(
23062362
assert(!tmp_w.instructions.empty());
23072363
x->source_location=tmp_w.instructions.back().source_location;
23082364

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+
23092374
dest.destructive_append(tmp_v);
23102375
dest.destructive_append(tmp_w);
23112376

src/goto-programs/goto_convert_class.h

+9
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ class goto_convertt:public messaget
235235

236236
void finish_gotos(goto_programt &dest);
237237
void finish_computed_gotos(goto_programt &dest);
238+
void finish_guarded_gotos(goto_programt &dest);
238239

239240
typedef std::map<irep_idt, std::pair<goto_programt::targett, destructor_stackt> > labelst;
240241
typedef std::list<std::pair<goto_programt::targett, destructor_stackt> > gotost;
@@ -418,6 +419,14 @@ class goto_convertt:public messaget
418419
std::size_t leave_stack_size;
419420
};
420421

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+
421430
exprt case_guard(
422431
const exprt &value,
423432
const caset &case_op);

0 commit comments

Comments
 (0)