Skip to content

Commit b12023d

Browse files
committed
Ensure one backedge per target: restore lexical loops
Figure-of-eight loop pairs may indeed have just one backedge, but still confused symex' unwinding counters. Re-create properly nested lexical loops from such loop pairs to avoid this problem.
1 parent 0060417 commit b12023d

File tree

1 file changed

+81
-1
lines changed

1 file changed

+81
-1
lines changed

src/goto-programs/ensure_one_backedge_per_target.cpp

Lines changed: 81 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,87 @@ bool ensure_one_backedge_per_target(
8486
return true;
8587
}
8688

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

91171
for(auto it = goto_program.instructions.begin();
92172
it != goto_program.instructions.end();

0 commit comments

Comments
 (0)