Skip to content

Commit 20535ad

Browse files
authored
Merge pull request #7321 from tautschnig/feature/natural-to-lexical-loop
Ensure one backedge per target: restore lexical loops
2 parents 7a82951 + c127049 commit 20535ad

File tree

4 files changed

+122
-1
lines changed

4 files changed

+122
-1
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
_Bool nondet_bool();
2+
3+
int main()
4+
{
5+
int j = 0;
6+
int i;
7+
L2:
8+
++j;
9+
if(j == 2)
10+
return 0;
11+
int extra_counter = 0;
12+
for(int i = nondet_bool() ? -1 : -2; extra_counter < 10; ++i, ++extra_counter)
13+
{
14+
// The following causes a surprising loop unwinding failure (and an equally
15+
// surprising sequence of loop unwinding status output) when using
16+
// --unwind 4 --unwinding-assertions
17+
// No such failure can be observed when using "goto L2X" instead and
18+
// enabling the below label/goto pair.
19+
if(i >= 1)
20+
goto L2;
21+
}
22+
//L2X:
23+
// goto L2;
24+
return 0;
25+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
main.c
3+
--ensure-one-backedge-per-target --show-lexical-loops
4+
^3 is head of \{ 3, 4, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 \(backedge\) \}$
5+
^16 is head of \{ 16, 17, 22, 23, 24, 25 \(backedge\) \}$
6+
^EXIT=0$
7+
^SIGNAL=0$
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
main.c
3+
--show-lexical-loops
4+
^16 is head of \{ 16, 17, 22, 23, 24, 25 \(backedge\) \}$
5+
Note not all loops were in lexical loop form
6+
^EXIT=0$
7+
^SIGNAL=0$

src/goto-programs/ensure_one_backedge_per_target.cpp

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Author: Diffblue Ltd.
1111

1212
#include "ensure_one_backedge_per_target.h"
1313

14+
#include <analyses/natural_loops.h>
15+
1416
#include "goto_model.h"
1517

1618
#include <algorithm>
@@ -84,9 +86,89 @@ bool ensure_one_backedge_per_target(
8486
return true;
8587
}
8688

89+
struct instruction_location_numbert : public goto_programt::targett
90+
{
91+
// Implicit conversion from a goto_programt::targett is permitted.
92+
// NOLINTNEXTLINE(runtime/explicit)
93+
instruction_location_numbert(goto_programt::targett target)
94+
: goto_programt::targett(target)
95+
{
96+
}
97+
98+
instruction_location_numbert() = default;
99+
};
100+
101+
inline bool operator<(
102+
const instruction_location_numbert &i1,
103+
const instruction_location_numbert &i2)
104+
{
105+
return i1->location_number < i2->location_number;
106+
}
107+
87108
bool ensure_one_backedge_per_target(goto_programt &goto_program)
88109
{
89-
bool any_change = false;
110+
natural_loops_templatet<goto_programt, instruction_location_numbert>
111+
natural_loops{goto_program};
112+
std::set<instruction_location_numbert> modified_loops;
113+
114+
for(auto it1 = natural_loops.loop_map.begin();
115+
it1 != natural_loops.loop_map.end();
116+
++it1)
117+
{
118+
DATA_INVARIANT(!it1->second.empty(), "loops cannot have no instructions");
119+
// skip over lexical loops
120+
if(
121+
(*std::prev(it1->second.end()))->is_goto() &&
122+
(*std::prev(it1->second.end()))->get_target() == it1->first)
123+
continue;
124+
if(modified_loops.find(it1->first) != modified_loops.end())
125+
continue;
126+
// it1 refers to a loop that isn't a lexical loop, now see whether any other
127+
// loop is nested within it1
128+
for(auto it2 = natural_loops.loop_map.begin();
129+
it2 != natural_loops.loop_map.end();
130+
++it2)
131+
{
132+
if(it1 == it2)
133+
continue;
134+
135+
if(std::includes(
136+
it1->second.begin(),
137+
it1->second.end(),
138+
it2->second.begin(),
139+
it2->second.end()))
140+
{
141+
// make sure that loops with overlapping body are properly nested by a
142+
// back edge; this will be a disconnected edge, which
143+
// ensure_one_backedge_per_target connects
144+
if(
145+
modified_loops.find(it2->first) == modified_loops.end() &&
146+
(!(*std::prev(it2->second.end()))->is_goto() ||
147+
(*std::prev(it2->second.end()))->get_target() != it2->first))
148+
{
149+
auto new_goto = goto_program.insert_after(
150+
*std::prev(it2->second.end()),
151+
goto_programt::make_goto(
152+
it2->first, (*std::prev(it2->second.end()))->source_location()));
153+
it2->second.insert_instruction(new_goto);
154+
it1->second.insert_instruction(new_goto);
155+
modified_loops.insert(it2->first);
156+
}
157+
158+
goto_program.insert_after(
159+
*std::prev(it1->second.end()),
160+
goto_programt::make_goto(
161+
it1->first, (*std::prev(it1->second.end()))->source_location()));
162+
modified_loops.insert(it1->first);
163+
break;
164+
}
165+
}
166+
}
167+
168+
if(!modified_loops.empty())
169+
goto_program.update();
170+
171+
bool any_change = !modified_loops.empty();
90172

91173
for(auto it = goto_program.instructions.begin();
92174
it != goto_program.instructions.end();

0 commit comments

Comments
 (0)