@@ -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+
87108bool 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