@@ -118,7 +118,8 @@ void goto_convertt::finish_gotos(goto_programt &dest)
118
118
119
119
// If the goto recorded a destructor stack, execute as much as is
120
120
// 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.
122
123
auto goto_stack=g_it.second ;
123
124
const auto & label_stack=l_it->second .second ;
124
125
bool stack_is_prefix=true ;
@@ -221,6 +222,50 @@ void goto_convertt::finish_computed_gotos(goto_programt &goto_program)
221
222
222
223
/* ******************************************************************\
223
224
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
+
224
269
Function: goto_convertt::goto_convert
225
270
226
271
Inputs:
@@ -256,6 +301,7 @@ void goto_convertt::goto_convert_rec(
256
301
257
302
finish_gotos (dest);
258
303
finish_computed_gotos (dest);
304
+ finish_guarded_gotos (dest);
259
305
}
260
306
261
307
/* ******************************************************************\
@@ -1662,14 +1708,6 @@ void goto_convertt::convert_goto(
1662
1708
const codet &code,
1663
1709
goto_programt &dest)
1664
1710
{
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
-
1673
1711
goto_programt::targett t=dest.add_instruction ();
1674
1712
t->make_goto ();
1675
1713
t->source_location =code.source_location ();
@@ -2178,6 +2216,24 @@ void goto_convertt::collect_operands(
2178
2216
2179
2217
/* ******************************************************************\
2180
2218
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
+
2181
2237
Function: goto_convertt::generate_ifthenelse
2182
2238
2183
2239
Inputs:
@@ -2209,24 +2265,24 @@ void goto_convertt::generate_ifthenelse(
2209
2265
return ;
2210
2266
}
2211
2267
2268
+ bool is_guarded_goto=false ;
2269
+
2212
2270
// do guarded gotos directly
2213
2271
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) &&
2217
2273
true_case.instructions .back ().is_goto () &&
2218
2274
true_case.instructions .back ().guard .is_true () &&
2219
2275
true_case.instructions .back ().labels .empty ())
2220
2276
{
2221
2277
// The above conjunction deliberately excludes the instance
2222
2278
// 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 ;
2226
2282
}
2227
2283
2228
2284
// similarly, do guarded assertions directly
2229
- if (true_case. instructions . size ()== 1 &&
2285
+ if (is_size_one (true_case) &&
2230
2286
true_case.instructions .back ().is_assert () &&
2231
2287
true_case.instructions .back ().guard .is_false () &&
2232
2288
true_case.instructions .back ().labels .empty ())
@@ -2239,7 +2295,7 @@ void goto_convertt::generate_ifthenelse(
2239
2295
}
2240
2296
2241
2297
// similarly, do guarded assertions directly
2242
- if (false_case. instructions . size ()== 1 &&
2298
+ if (is_size_one (false_case) &&
2243
2299
false_case.instructions .back ().is_assert () &&
2244
2300
false_case.instructions .back ().guard .is_false () &&
2245
2301
false_case.instructions .back ().labels .empty ())
@@ -2306,6 +2362,15 @@ void goto_convertt::generate_ifthenelse(
2306
2362
assert (!tmp_w.instructions .empty ());
2307
2363
x->source_location =tmp_w.instructions .back ().source_location ;
2308
2364
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
+
2309
2374
dest.destructive_append (tmp_v);
2310
2375
dest.destructive_append (tmp_w);
2311
2376
0 commit comments