Skip to content

Commit e440a25

Browse files
author
Daniel Kroening
committed
introduce INCOMPLETE_GOTO and turn guarded goto into a stateless pass
1 parent 360fabe commit e440a25

File tree

9 files changed

+109
-111
lines changed

9 files changed

+109
-111
lines changed

regression/cbmc/goto5/main.c

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <assert.h>
2+
3+
int main(void)
4+
{
5+
int r = 0;
6+
7+
if(r == 0)
8+
{
9+
goto l1;
10+
r = 1;
11+
}
12+
13+
l1:
14+
assert(r != 0); // expected to fail
15+
}

regression/cbmc/goto5/test.desc

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
--
8+
^warning: ignoring

src/analyses/goto_rw.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,7 @@ void goto_rw(goto_programt::const_targett target,
719719
switch(target->type)
720720
{
721721
case NO_INSTRUCTION_TYPE:
722+
case INCOMPLETE_GOTO:
722723
UNREACHABLE;
723724
break;
724725

src/goto-instrument/goto_program2code.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ goto_programt::const_targett goto_program2codet::convert_instruction(
245245
return convert_catch(target, upper_bound, dest);
246246

247247
case NO_INSTRUCTION_TYPE:
248+
case INCOMPLETE_GOTO:
248249
UNREACHABLE;
249250
}
250251

src/goto-programs/cfg.h

+1
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ void cfg_baset<T, P, I>::compute_edges(
418418
break;
419419

420420
case NO_INSTRUCTION_TYPE:
421+
case INCOMPLETE_GOTO:
421422
UNREACHABLE;
422423
break;
423424
}

src/goto-programs/goto_convert.cpp

+42-80
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,7 @@ void goto_convertt::finish_gotos(goto_programt &dest, const irep_idt &mode)
138138
throw 0;
139139
}
140140

141-
i.type=GOTO;
142-
i.targets.clear();
143-
i.targets.push_back(l_it->second.first);
141+
i.complete_goto(l_it->second.first);
144142

145143
// If the goto recorded a destructor stack, execute as much as is
146144
// appropriate for however many automatic variables leave scope.
@@ -234,45 +232,49 @@ void goto_convertt::finish_computed_gotos(goto_programt &goto_program)
234232
targets.computed_gotos.clear();
235233
}
236234

237-
/// For each if(x) goto z; goto y; z: emitted, see if any destructor statements
238-
/// were inserted between goto z and z, and if not, simplify into if(!x) goto y;
235+
/// Rewrite "if(x) goto z; goto y; z:" into "if(!x) goto y;"
236+
/// This only works if the "goto y" is not a branch target.
239237
/// \par parameters: Destination goto program
240-
void goto_convertt::finish_guarded_gotos(goto_programt &dest)
238+
void goto_convertt::optimize_guarded_gotos(goto_programt &dest)
241239
{
242-
for(auto &gg : guarded_gotos)
240+
// We cannot use a set of targets, as target iterators
241+
// cannot be compared at this stage.
242+
243+
// collect targets: reset marking
244+
for(auto &i : dest.instructions)
245+
i.target_number = goto_programt::instructiont::nil_target;
246+
247+
// mark the goto targets
248+
unsigned cnt = 0;
249+
for(const auto &i : dest.instructions)
250+
if(i.is_goto())
251+
i.get_target()->target_number = (++cnt);
252+
253+
for(auto it = dest.instructions.begin(); it != dest.instructions.end(); it++)
243254
{
244-
// Check if any destructor code has been inserted:
245-
bool destructor_present=false;
246-
for(auto it=gg.ifiter;
247-
it!=gg.gotoiter && !destructor_present;
248-
++it)
249-
{
250-
if(!(it->is_goto() || it->is_skip()))
251-
destructor_present=true;
252-
}
255+
if(!it->is_goto())
256+
continue;
257+
258+
auto it_goto_y = std::next(it);
253259

254-
// If so, can't simplify.
255-
if(destructor_present)
260+
if(
261+
it_goto_y == dest.instructions.end() || !it_goto_y->is_goto() ||
262+
!it_goto_y->guard.is_true() || it_goto_y->is_target())
256263
continue;
257264

258-
// Simplify: remove whatever code was generated for the condition
259-
// and attach the original guard to the goto instruction.
260-
gg.gotoiter->guard=gg.guard;
261-
// inherit the source location (otherwise the guarded goto will
262-
// have the source location of the else branch)
263-
gg.gotoiter->source_location=gg.ifiter->source_location;
264-
// goto_programt doesn't provide an erase operation,
265-
// perhaps for a good reason, so let's be cautious and just
266-
// flatten the unneeded instructions into skips.
267-
for(auto it=gg.ifiter, itend=gg.gotoiter; it!=itend; ++it)
268-
it->make_skip();
269-
}
265+
auto it_z = std::next(it_goto_y);
270266

271-
// Must clear this, as future functions may be converted
272-
// using the same instance of goto_convertt, typically via
273-
// goto_convert_functions.
267+
if(it_z == dest.instructions.end())
268+
continue;
274269

275-
guarded_gotos.clear();
270+
// cannot compare iterators, so compare target number instead
271+
if(it->get_target()->target_number == it_z->target_number)
272+
{
273+
it->set_target(it_goto_y->get_target());
274+
it->guard = boolean_negate(it->guard);
275+
it_goto_y->make_skip();
276+
}
277+
}
276278
}
277279

278280
void goto_convertt::goto_convert(
@@ -288,14 +290,11 @@ void goto_convertt::goto_convert_rec(
288290
goto_programt &dest,
289291
const irep_idt &mode)
290292
{
291-
// Check that guarded_gotos was cleared after any previous use of this
292-
// converter instance:
293-
PRECONDITION(guarded_gotos.empty());
294293
convert(code, dest, mode);
295294

296295
finish_gotos(dest, mode);
297296
finish_computed_gotos(dest);
298-
finish_guarded_gotos(dest);
297+
optimize_guarded_gotos(dest);
299298
finish_catch_push_targets(dest);
300299
}
301300

@@ -493,13 +492,11 @@ void goto_convertt::convert(
493492
else if(statement==ID_continue)
494493
convert_continue(to_code_continue(code), dest, mode);
495494
else if(statement==ID_goto)
496-
convert_goto(code, dest);
495+
convert_goto(to_code_goto(code), dest);
497496
else if(statement==ID_gcc_computed_goto)
498497
convert_gcc_computed_goto(code, dest);
499498
else if(statement==ID_skip)
500499
convert_skip(code, dest);
501-
else if(statement=="non-deterministic-goto")
502-
convert_non_deterministic_goto(code, dest);
503500
else if(statement==ID_ifthenelse)
504501
convert_ifthenelse(to_code_ifthenelse(code), dest, mode);
505502
else if(statement==ID_start_thread)
@@ -1456,16 +1453,12 @@ void goto_convertt::convert_continue(
14561453
t->source_location=code.source_location();
14571454
}
14581455

1459-
void goto_convertt::convert_goto(
1460-
const codet &code,
1461-
goto_programt &dest)
1456+
void goto_convertt::convert_goto(const code_gotot &code, goto_programt &dest)
14621457
{
14631458
// this instruction will be completed during post-processing
1464-
// it is required to mark this as GOTO in order to enable
1465-
// simplifications in generate_ifthenelse
1466-
goto_programt::targett t = dest.add_instruction(GOTO);
1459+
goto_programt::targett t = dest.add_instruction();
1460+
t->make_incomplete_goto(code);
14671461
t->source_location=code.source_location();
1468-
t->code=code;
14691462

14701463
// remember it to do the target later
14711464
targets.gotos.push_back(std::make_pair(t, targets.destructor_stack));
@@ -1484,13 +1477,6 @@ void goto_convertt::convert_gcc_computed_goto(
14841477
targets.computed_gotos.push_back(t);
14851478
}
14861479

1487-
void goto_convertt::convert_non_deterministic_goto(
1488-
const codet &code,
1489-
goto_programt &dest)
1490-
{
1491-
convert_goto(code, dest);
1492-
}
1493-
14941480
void goto_convertt::convert_start_thread(
14951481
const codet &code,
14961482
goto_programt &dest)
@@ -1649,24 +1635,7 @@ void goto_convertt::generate_ifthenelse(
16491635
return;
16501636
}
16511637

1652-
bool is_guarded_goto=false;
1653-
1654-
// do guarded gotos directly
1655-
if(is_empty(false_case) &&
1656-
is_size_one(true_case) &&
1657-
true_case.instructions.back().is_goto() &&
1658-
true_case.instructions.back().guard.is_true() &&
1659-
true_case.instructions.back().labels.empty())
1660-
{
1661-
// The above conjunction deliberately excludes the instance
1662-
// if(some) { label: goto somewhere; }
1663-
// Don't perform the transformation here, as code might get inserted into
1664-
// the true case to perform destructors.
1665-
// This will be attempted in finish_guarded_gotos.
1666-
is_guarded_goto=true;
1667-
}
1668-
1669-
// similarly, do guarded assertions directly
1638+
// do guarded assertions directly
16701639
if(is_size_one(true_case) &&
16711640
true_case.instructions.back().is_assert() &&
16721641
true_case.instructions.back().guard.is_false() &&
@@ -1779,13 +1748,6 @@ void goto_convertt::generate_ifthenelse(
17791748
assert(!tmp_w.instructions.empty());
17801749
x->source_location=tmp_w.instructions.back().source_location;
17811750

1782-
// See if we can simplify this guarded goto later.
1783-
// Note this depends on the fact that `instructions` is a std::list
1784-
// and so goto-program-destructive-append preserves iterator validity.
1785-
if(is_guarded_goto)
1786-
guarded_gotos.push_back(
1787-
{tmp_v.instructions.begin(), tmp_w.instructions.begin(), guard});
1788-
17891751
dest.destructive_append(tmp_v);
17901752
dest.destructive_append(tmp_w);
17911753

src/goto-programs/goto_convert_class.h

+2-12
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,9 @@ class goto_convertt:public messaget
268268
const irep_idt &mode);
269269
void
270270
convert_init(const codet &code, goto_programt &dest, const irep_idt &mode);
271-
void convert_goto(const codet &code, goto_programt &dest);
271+
void convert_goto(const code_gotot &code, goto_programt &dest);
272272
void convert_gcc_computed_goto(const codet &code, goto_programt &dest);
273273
void convert_skip(const codet &code, goto_programt &dest);
274-
void convert_non_deterministic_goto(const codet &code, goto_programt &dest);
275274
void convert_label(
276275
const code_labelt &code,
277276
goto_programt &dest,
@@ -355,7 +354,7 @@ class goto_convertt:public messaget
355354

356355
void finish_gotos(goto_programt &dest, const irep_idt &mode);
357356
void finish_computed_gotos(goto_programt &dest);
358-
void finish_guarded_gotos(goto_programt &dest);
357+
void optimize_guarded_gotos(goto_programt &dest);
359358

360359
typedef std::map<irep_idt,
361360
std::pair<goto_programt::targett, destructor_stackt>>
@@ -545,15 +544,6 @@ class goto_convertt:public messaget
545544
std::size_t leave_stack_size;
546545
};
547546

548-
struct guarded_gotot
549-
{
550-
goto_programt::targett ifiter;
551-
goto_programt::targett gotoiter;
552-
exprt guard;
553-
};
554-
typedef std::list<guarded_gotot> guarded_gotost;
555-
guarded_gotost guarded_gotos;
556-
557547
exprt case_guard(
558548
const exprt &value,
559549
const caset &case_op);

src/goto-programs/goto_program.h

+37-19
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,26 @@ Author: Daniel Kroening, [email protected]
2828
/// The type of an instruction in a GOTO program.
2929
enum goto_program_instruction_typet
3030
{
31-
NO_INSTRUCTION_TYPE=0,
32-
GOTO=1, // branch, possibly guarded
33-
ASSUME=2, // non-failing guarded self loop
34-
ASSERT=3, // assertions
35-
OTHER=4, // anything else
36-
SKIP=5, // just advance the PC
37-
START_THREAD=6, // spawns an asynchronous thread
38-
END_THREAD=7, // end the current thread
39-
LOCATION=8, // semantically like SKIP
40-
END_FUNCTION=9, // exit point of a function
41-
ATOMIC_BEGIN=10, // marks a block without interleavings
42-
ATOMIC_END=11, // end of a block without interleavings
43-
RETURN=12, // set function return value (no control-flow change)
44-
ASSIGN=13, // assignment lhs:=rhs
45-
DECL=14, // declare a local variable
46-
DEAD=15, // marks the end-of-live of a local variable
47-
FUNCTION_CALL=16, // call a function
48-
THROW=17, // throw an exception
49-
CATCH=18 // push, pop or enter an exception handler
31+
NO_INSTRUCTION_TYPE = 0,
32+
GOTO = 1, // branch, possibly guarded
33+
ASSUME = 2, // non-failing guarded self loop
34+
ASSERT = 3, // assertions
35+
OTHER = 4, // anything else
36+
SKIP = 5, // just advance the PC
37+
START_THREAD = 6, // spawns an asynchronous thread
38+
END_THREAD = 7, // end the current thread
39+
LOCATION = 8, // semantically like SKIP
40+
END_FUNCTION = 9, // exit point of a function
41+
ATOMIC_BEGIN = 10, // marks a block without interleavings
42+
ATOMIC_END = 11, // end of a block without interleavings
43+
RETURN = 12, // set function return value (no control-flow change)
44+
ASSIGN = 13, // assignment lhs:=rhs
45+
DECL = 14, // declare a local variable
46+
DEAD = 15, // marks the end-of-live of a local variable
47+
FUNCTION_CALL = 16, // call a function
48+
THROW = 17, // throw an exception
49+
CATCH = 18, // push, pop or enter an exception handler
50+
INCOMPLETE_GOTO = 19 // goto where target is yet to be determined
5051
};
5152

5253
std::ostream &operator<<(std::ostream &, goto_program_instruction_typet);
@@ -253,6 +254,12 @@ class goto_programt
253254
void make_atomic_end() { clear(ATOMIC_END); }
254255
void make_end_function() { clear(END_FUNCTION); }
255256

257+
void make_incomplete_goto(const code_gotot &_code)
258+
{
259+
clear(INCOMPLETE_GOTO);
260+
code = _code;
261+
}
262+
256263
void make_goto(targett _target)
257264
{
258265
clear(GOTO);
@@ -265,6 +272,13 @@ class goto_programt
265272
guard=g;
266273
}
267274

275+
void complete_goto(targett _target)
276+
{
277+
PRECONDITION(type == INCOMPLETE_GOTO);
278+
targets.push_back(_target);
279+
type = GOTO;
280+
}
281+
268282
void make_assignment(const codet &_code)
269283
{
270284
clear(ASSIGN);
@@ -301,6 +315,10 @@ class goto_programt
301315
bool is_start_thread () const { return type==START_THREAD; }
302316
bool is_end_thread () const { return type==END_THREAD; }
303317
bool is_end_function () const { return type==END_FUNCTION; }
318+
bool is_incomplete_goto() const
319+
{
320+
return type == INCOMPLETE_GOTO;
321+
}
304322

305323
instructiont():
306324
instructiont(NO_INSTRUCTION_TYPE) // NOLINT(runtime/explicit)

src/goto-programs/string_abstraction.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,8 @@ goto_programt::targett string_abstractiont::abstract(
490490
case OTHER:
491491
case LOCATION:
492492
break;
493+
494+
case INCOMPLETE_GOTO:
493495
case NO_INSTRUCTION_TYPE:
494496
UNREACHABLE;
495497
break;

0 commit comments

Comments
 (0)