@@ -57,11 +57,11 @@ Function: goto_convertt::finish_gotos
57
57
58
58
\*******************************************************************/
59
59
60
- void goto_convertt::finish_gotos ()
60
+ void goto_convertt::finish_gotos (goto_programt &dest )
61
61
{
62
62
for (const auto &g_it : targets.gotos )
63
63
{
64
- goto_programt::instructiont &i=*g_it;
64
+ goto_programt::instructiont &i=*( g_it. first ) ;
65
65
66
66
if (i.code .get_statement ()==" non-deterministic-goto" )
67
67
{
@@ -81,7 +81,7 @@ void goto_convertt::finish_gotos()
81
81
throw 0 ;
82
82
}
83
83
84
- i.targets .push_back (l_it->second );
84
+ i.targets .push_back (l_it->second . first );
85
85
}
86
86
}
87
87
else if (i.is_start_thread ())
@@ -98,7 +98,7 @@ void goto_convertt::finish_gotos()
98
98
throw 0 ;
99
99
}
100
100
101
- i.targets .push_back (l_it->second );
101
+ i.targets .push_back (l_it->second . first );
102
102
}
103
103
else if (i.code .get_statement ()==ID_goto)
104
104
{
@@ -114,7 +114,49 @@ void goto_convertt::finish_gotos()
114
114
}
115
115
116
116
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
+ }
118
160
}
119
161
else
120
162
{
@@ -169,7 +211,7 @@ void goto_convertt::finish_computed_gotos(goto_programt &goto_program)
169
211
goto_programt::targett t=
170
212
goto_program.insert_after (g_it);
171
213
172
- t->make_goto (label.second );
214
+ t->make_goto (label.second . first );
173
215
t->source_location =i.source_location ;
174
216
t->guard =guard;
175
217
}
@@ -180,6 +222,50 @@ void goto_convertt::finish_computed_gotos(goto_programt &goto_program)
180
222
181
223
/* ******************************************************************\
182
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
+
183
269
Function: goto_convertt::goto_convert
184
270
185
271
Inputs:
@@ -213,8 +299,9 @@ void goto_convertt::goto_convert_rec(
213
299
{
214
300
convert (code, dest);
215
301
216
- finish_gotos ();
302
+ finish_gotos (dest );
217
303
finish_computed_gotos (dest);
304
+ finish_guarded_gotos (dest);
218
305
}
219
306
220
307
/* ******************************************************************\
@@ -282,8 +369,7 @@ void goto_convertt::convert_label(
282
369
goto_programt::targett target=tmp.instructions .begin ();
283
370
dest.destructive_append (tmp);
284
371
285
- targets.labels .insert (std::pair<irep_idt, goto_programt::targett>
286
- (label, target));
372
+ targets.labels .insert ({label, {target, targets.destructor_stack }});
287
373
target->labels .push_front (label);
288
374
}
289
375
@@ -1628,7 +1714,7 @@ void goto_convertt::convert_goto(
1628
1714
t->code =code;
1629
1715
1630
1716
// remember it to do target later
1631
- targets.gotos .push_back (t );
1717
+ targets.gotos .push_back (std::make_pair (t,targets. destructor_stack ) );
1632
1718
}
1633
1719
1634
1720
/* ******************************************************************\
@@ -2130,6 +2216,24 @@ void goto_convertt::collect_operands(
2130
2216
2131
2217
/* ******************************************************************\
2132
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
+
2133
2237
Function: goto_convertt::generate_ifthenelse
2134
2238
2135
2239
Inputs:
@@ -2161,24 +2265,24 @@ void goto_convertt::generate_ifthenelse(
2161
2265
return ;
2162
2266
}
2163
2267
2268
+ bool is_guarded_goto=false ;
2269
+
2164
2270
// do guarded gotos directly
2165
2271
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) &&
2169
2273
true_case.instructions .back ().is_goto () &&
2170
2274
true_case.instructions .back ().guard .is_true () &&
2171
2275
true_case.instructions .back ().labels .empty ())
2172
2276
{
2173
2277
// The above conjunction deliberately excludes the instance
2174
2278
// 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 ;
2178
2282
}
2179
2283
2180
2284
// similarly, do guarded assertions directly
2181
- if (true_case. instructions . size ()== 1 &&
2285
+ if (is_size_one (true_case) &&
2182
2286
true_case.instructions .back ().is_assert () &&
2183
2287
true_case.instructions .back ().guard .is_false () &&
2184
2288
true_case.instructions .back ().labels .empty ())
@@ -2191,7 +2295,7 @@ void goto_convertt::generate_ifthenelse(
2191
2295
}
2192
2296
2193
2297
// similarly, do guarded assertions directly
2194
- if (false_case. instructions . size ()== 1 &&
2298
+ if (is_size_one (false_case) &&
2195
2299
false_case.instructions .back ().is_assert () &&
2196
2300
false_case.instructions .back ().guard .is_false () &&
2197
2301
false_case.instructions .back ().labels .empty ())
@@ -2258,6 +2362,15 @@ void goto_convertt::generate_ifthenelse(
2258
2362
assert (!tmp_w.instructions .empty ());
2259
2363
x->source_location =tmp_w.instructions .back ().source_location ;
2260
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
+
2261
2374
dest.destructive_append (tmp_v);
2262
2375
dest.destructive_append (tmp_w);
2263
2376
0 commit comments