CBMC
unwind.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Loop unwinding
4 
5 Author: Daniel Kroening, kroening@kroening.com
6  Daniel Poetzl
7 
8 \*******************************************************************/
9 
12 
13 #include "unwind.h"
14 
15 #ifdef DEBUG
16 #include <iostream>
17 #endif
18 
19 #include <util/expr_util.h>
20 #include <util/std_expr.h>
21 
23 
24 #include "unwindset.h"
25 
27  const goto_programt::const_targett start,
28  const goto_programt::const_targett end, // exclusive
29  goto_programt &goto_program) // result
30 {
31  assert(start->location_number<end->location_number);
32  assert(goto_program.empty());
33 
34  // build map for branch targets inside the loop
35  typedef std::map<goto_programt::const_targett, unsigned> target_mapt;
36  target_mapt target_map;
37 
38  unsigned i=0;
39 
40  for(goto_programt::const_targett t=start; t!=end; t++, i++)
41  target_map[t]=i;
42 
43  // make a copy
44  std::vector<goto_programt::targett> target_vector;
45  target_vector.reserve(target_map.size());
46  assert(target_vector.empty());
47 
48  for(goto_programt::const_targett t=start; t!=end; t++)
49  {
50  // copy the instruction
52  goto_program.add(goto_programt::instructiont(*t));
53  unwind_log.insert(t_new, t->location_number);
54  target_vector.push_back(t_new); // store copied instruction
55  }
56 
57  assert(goto_program.instructions.size()==target_vector.size());
58 
59  // adjust intra-segment gotos
60  for(std::size_t target_index = 0; target_index < target_vector.size();
61  target_index++)
62  {
63  goto_programt::targett t = target_vector[target_index];
64 
65  if(!t->is_goto())
66  continue;
67 
68  goto_programt::const_targett tgt=t->get_target();
69 
70  target_mapt::const_iterator m_it=target_map.find(tgt);
71 
72  if(m_it!=target_map.end())
73  {
74  unsigned j=m_it->second;
75 
76  assert(j<target_vector.size());
77  t->set_target(target_vector[j]);
78  }
79  }
80 }
81 
83  const irep_idt &function_id,
84  goto_programt &goto_program,
85  const goto_programt::const_targett loop_head,
86  const goto_programt::const_targett loop_exit,
87  const unsigned k,
88  const unwind_strategyt unwind_strategy)
89 {
90  std::vector<goto_programt::targett> iteration_points;
91  unwind(
92  function_id,
93  goto_program,
94  loop_head,
95  loop_exit,
96  k,
97  unwind_strategy,
98  iteration_points);
99 }
100 
102  const irep_idt &function_id,
103  goto_programt &goto_program,
104  const goto_programt::const_targett loop_head,
105  const goto_programt::const_targett loop_exit,
106  const unsigned k,
107  const unwind_strategyt unwind_strategy,
108  std::vector<goto_programt::targett> &iteration_points)
109 {
110  assert(iteration_points.empty());
111  assert(loop_head->location_number<loop_exit->location_number);
112 
113  // rest program after unwound part
114  goto_programt rest_program;
115 
116  if(unwind_strategy==unwind_strategyt::PARTIAL)
117  {
119  rest_program.add(goto_programt::make_skip(loop_head->source_location()));
120 
121  t->location_number=loop_head->location_number;
122 
123  unwind_log.insert(t, loop_head->location_number);
124  }
125  else if(unwind_strategy==unwind_strategyt::CONTINUE)
126  {
127  copy_segment(loop_head, loop_exit, rest_program);
128  }
129  else
130  {
131  PRECONDITION(
132  unwind_strategy == unwind_strategyt::ASSERT_ASSUME ||
133  unwind_strategy == unwind_strategyt::ASSERT_PARTIAL ||
134  unwind_strategy == unwind_strategyt::ASSUME);
135 
136  goto_programt::const_targett t=loop_exit;
137  t--;
138  assert(t->is_backwards_goto());
139 
140  exprt exit_cond = false_exprt(); // default is false
141 
142  if(!t->condition().is_true()) // cond in backedge
143  {
144  exit_cond = boolean_negate(t->condition());
145  }
146  else if(loop_head->is_goto())
147  {
148  if(loop_head->get_target()==loop_exit) // cond in forward edge
149  exit_cond = loop_head->condition();
150  }
151 
152  if(
153  unwind_strategy == unwind_strategyt::ASSERT_ASSUME ||
154  unwind_strategy == unwind_strategyt::ASSERT_PARTIAL)
155  {
156  goto_programt::targett assertion = rest_program.add(
157  goto_programt::make_assertion(exit_cond, loop_head->source_location()));
158  unwind_log.insert(assertion, loop_head->location_number);
159  }
160 
161  if(
162  unwind_strategy == unwind_strategyt::ASSUME ||
163  unwind_strategy == unwind_strategyt::ASSERT_ASSUME)
164  {
165  goto_programt::targett assumption =
166  rest_program.add(goto_programt::make_assumption(
167  exit_cond, loop_head->source_location()));
168  unwind_log.insert(assumption, loop_head->location_number);
169  }
170 
171  }
172 
173  assert(!rest_program.empty());
174 
175  // to be filled with copies of the loop body
176  goto_programt copies;
177 
178  if(k!=0)
179  {
180  iteration_points.resize(k);
181 
182  // add a goto before the loop exit to guard against 'fall-out'
183 
184  goto_programt::const_targett t_before=loop_exit;
185  t_before--;
186 
187  if(!t_before->is_goto() || !t_before->condition().is_true())
188  {
189  goto_programt::targett t_goto = goto_program.insert_before(
190  loop_exit,
192  goto_program.const_cast_target(loop_exit),
193  true_exprt(),
194  loop_exit->source_location()));
195  t_goto->location_number=loop_exit->location_number;
196 
197  unwind_log.insert(t_goto, loop_exit->location_number);
198  }
199 
200  // add a skip before the loop exit
201 
202  goto_programt::targett t_skip = goto_program.insert_before(
203  loop_exit, goto_programt::make_skip(loop_head->source_location()));
204  t_skip->location_number=loop_head->location_number;
205 
206  unwind_log.insert(t_skip, loop_exit->location_number);
207 
208  // where to go for the next iteration
209  goto_programt::targett loop_iter=t_skip;
210 
211  // record the exit point of first iteration
212  iteration_points[0]=loop_iter;
213 
214  // re-direct any branches that go to loop_head to loop_iter
215 
217  t=goto_program.const_cast_target(loop_head);
218  t!=loop_iter; t++)
219  {
220  if(!t->is_goto())
221  continue;
222 
223  if(t->get_target()==loop_head)
224  t->set_target(loop_iter);
225  }
226 
227  // k-1 additional copies
228  for(unsigned i=1; i<k; i++)
229  {
230  goto_programt tmp_program;
231  copy_segment(loop_head, loop_exit, tmp_program);
232  assert(!tmp_program.instructions.empty());
233 
234  iteration_points[i]=--tmp_program.instructions.end();
235 
236  copies.destructive_append(tmp_program);
237  }
238  }
239  else
240  {
241  // insert skip for loop body
242 
243  goto_programt::targett t_skip = goto_program.insert_before(
244  loop_head, goto_programt::make_skip(loop_head->source_location()));
245  t_skip->location_number=loop_head->location_number;
246 
247  unwind_log.insert(t_skip, loop_head->location_number);
248 
249  // redirect gotos into loop body
250  for(auto &instruction : goto_program.instructions)
251  {
252  if(!instruction.is_goto())
253  continue;
254 
255  goto_programt::const_targett t = instruction.get_target();
256 
257  if(t->location_number>=loop_head->location_number &&
258  t->location_number<loop_exit->location_number)
259  {
260  instruction.set_target(t_skip);
261  }
262  }
263 
264  // delete loop body
265  goto_program.instructions.erase(loop_head, loop_exit);
266  }
267 
268  // after unwound part
269  copies.destructive_append(rest_program);
270 
271  // now insert copies before loop_exit
272  goto_program.destructive_insert(loop_exit, copies);
273 }
274 
276  const irep_idt &function_id,
277  goto_programt &goto_program,
278  const unwindsett &unwindset,
279  const unwind_strategyt unwind_strategy)
280 {
281  for(goto_programt::const_targett i_it=goto_program.instructions.begin();
282  i_it!=goto_program.instructions.end();)
283  {
284 #ifdef DEBUG
285  std::cout << "Instruction:\n";
286  i_it->output(std::cout);
287 #endif
288 
289  if(!i_it->is_backwards_goto())
290  {
291  i_it++;
292  continue;
293  }
294 
295  PRECONDITION(!function_id.empty());
296  const irep_idt loop_id = goto_programt::loop_id(function_id, *i_it);
297 
298  auto limit=unwindset.get_limit(loop_id, 0);
299 
300  if(!limit.has_value())
301  {
302  // no unwinding given
303  i_it++;
304  continue;
305  }
306 
307  goto_programt::const_targett loop_head=i_it->get_target();
308  goto_programt::const_targett loop_exit=i_it;
309  loop_exit++;
310  assert(loop_exit!=goto_program.instructions.end());
311 
312  unwind(
313  function_id, goto_program, loop_head, loop_exit, *limit, unwind_strategy);
314 
315  // necessary as we change the goto program in the previous line
316  i_it=loop_exit;
317  }
318 }
319 
322  const unwindsett &unwindset,
323  const unwind_strategyt unwind_strategy)
324 {
325  for(auto &gf_entry : goto_functions.function_map)
326  {
327  goto_functionst::goto_functiont &goto_function = gf_entry.second;
328 
329  if(!goto_function.body_available())
330  continue;
331 
332 #ifdef DEBUG
333  std::cout << "Function: " << gf_entry.first << '\n';
334 #endif
335 
336  goto_programt &goto_program=goto_function.body;
337 
338  unwind(gf_entry.first, goto_program, unwindset, unwind_strategy);
339  }
340 }
341 
342 // call after calling goto_functions.update()!
344 {
345  json_objectt json_result;
346  json_arrayt &json_unwound=json_result["unwound"].make_array();
347 
348  for(location_mapt::const_iterator it=location_map.begin();
349  it!=location_map.end(); it++)
350  {
351  goto_programt::const_targett target=it->first;
352  unsigned location_number=it->second;
353 
354  json_objectt object{
355  {"originalLocationNumber", json_numbert(std::to_string(location_number))},
356  {"newLocationNumber",
357  json_numbert(std::to_string(target->location_number))}};
358 
359  json_unwound.push_back(std::move(object));
360  }
361 
362  return std::move(json_result);
363 }
dstringt
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition: dstring.h:36
json_numbert
Definition: json.h:290
goto_unwindt::unwind_strategyt
unwind_strategyt
Definition: unwind.h:24
goto_unwindt::unwind_strategyt::ASSERT_PARTIAL
@ ASSERT_PARTIAL
goto_programt::add
targett add(instructiont &&instruction)
Adds a given instruction at the end.
Definition: goto_program.h:709
goto_unwindt::unwind_strategyt::CONTINUE
@ CONTINUE
goto_programt::empty
bool empty() const
Is the program empty?
Definition: goto_program.h:790
exprt
Base class for all expressions.
Definition: expr.h:55
to_string
std::string to_string(const string_not_contains_constraintt &expr)
Used for debug printing.
Definition: string_constraint.cpp:58
goto_unwindt::unwind_strategyt::ASSERT_ASSUME
@ ASSERT_ASSUME
goto_functionst::function_map
function_mapt function_map
Definition: goto_functions.h:29
goto_unwindt::unwind
void unwind(const irep_idt &function_id, goto_programt &goto_program, const goto_programt::const_targett loop_head, const goto_programt::const_targett loop_exit, const unsigned k, const unwind_strategyt unwind_strategy)
Definition: unwind.cpp:82
jsont
Definition: json.h:26
goto_programt::make_goto
static instructiont make_goto(targett _target, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:1030
json_arrayt
Definition: json.h:164
goto_unwindt::unwind_strategyt::PARTIAL
@ PARTIAL
json_objectt
Definition: json.h:299
unwindsett::get_limit
optionalt< unsigned > get_limit(const irep_idt &loop, unsigned thread_id) const
Definition: unwindset.cpp:186
goto_programt::loop_id
static irep_idt loop_id(const irep_idt &function_id, const instructiont &instruction)
Human-readable loop name.
Definition: goto_program.h:783
goto_unwindt::unwind_strategyt::ASSUME
@ ASSUME
goto_programt::insert_before
targett insert_before(const_targett target)
Insertion before the instruction pointed-to by the given instruction iterator target.
Definition: goto_program.h:662
goto_programt::make_assertion
static instructiont make_assertion(const exprt &g, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:924
goto_unwindt::unwind_logt::insert
void insert(const goto_programt::const_targett target, const unsigned location_number)
Definition: unwind.h:99
goto_programt::destructive_insert
void destructive_insert(const_targett target, goto_programt &p)
Inserts the given program p before target.
Definition: goto_program.h:700
unwindsett
Definition: unwindset.h:23
goto_unwindt::unwind_logt::output_log_json
jsont output_log_json() const
Definition: unwind.cpp:343
goto_programt::make_skip
static instructiont make_skip(const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:882
unwind.h
PRECONDITION
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
boolean_negate
exprt boolean_negate(const exprt &src)
negate a Boolean expression, possibly removing a not_exprt, and swapping false and true
Definition: expr_util.cpp:127
goto_unwindt::operator()
void operator()(goto_functionst &, const unwindsett &unwindset, const unwind_strategyt unwind_strategy=unwind_strategyt::PARTIAL)
Definition: unwind.cpp:320
dstringt::empty
bool empty() const
Definition: dstring.h:88
jsont::make_array
json_arrayt & make_array()
Definition: json.h:420
false_exprt
The Boolean constant false.
Definition: std_expr.h:3016
goto_unwindt::unwind_logt::location_map
location_mapt location_map
Definition: unwind.h:108
goto_programt::destructive_append
void destructive_append(goto_programt &p)
Appends the given program p to *this. p is destroyed.
Definition: goto_program.h:692
goto_functionst::goto_functiont
::goto_functiont goto_functiont
Definition: goto_functions.h:27
goto_programt::instructions
instructionst instructions
The list of instructions in the goto program.
Definition: goto_program.h:592
expr_util.h
Deprecated expression utility functions.
goto_unwindt::copy_segment
void copy_segment(const goto_programt::const_targett start, const goto_programt::const_targett end, goto_programt &goto_program)
Definition: unwind.cpp:26
goto_functionst
A collection of goto functions.
Definition: goto_functions.h:24
goto_modelt::goto_functions
goto_functionst goto_functions
GOTO functions.
Definition: goto_model.h:33
goto_programt::const_cast_target
targett const_cast_target(const_targett t)
Convert a const_targett to a targett - use with care and avoid whenever possible.
Definition: goto_program.h:596
goto_functions.h
goto_programt
A generic container class for the GOTO intermediate representation of one function.
Definition: goto_program.h:72
goto_programt::const_targett
instructionst::const_iterator const_targett
Definition: goto_program.h:587
goto_programt::make_assumption
static instructiont make_assumption(const exprt &g, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:936
unwindset.h
true_exprt
The Boolean constant true.
Definition: std_expr.h:3007
goto_programt::instructiont
This class represents an instruction in the GOTO intermediate representation.
Definition: goto_program.h:180
std_expr.h
exprt::source_location
const source_locationt & source_location() const
Definition: expr.h:211
json_arrayt::push_back
jsont & push_back(const jsont &json)
Definition: json.h:212
goto_programt::targett
instructionst::iterator targett
Definition: goto_program.h:586
goto_unwindt::unwind_log
unwind_logt unwind_log
Definition: unwind.h:111