CBMC
sat_path_enumerator.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Loop Acceleration
4 
5 Author: Matt Lewis
6 
7 \*******************************************************************/
8 
11 
12 #include "sat_path_enumerator.h"
13 
14 #include <iostream>
15 #include <map>
16 #include <string>
17 
20 
21 #include "scratch_program.h"
22 
24 {
26 
27  program.append(fixed);
28  program.append(fixed);
29 
30  // Let's make sure that we get a path we have not seen before.
31  for(std::list<distinguish_valuest>::iterator it=accelerated_paths.begin();
32  it!=accelerated_paths.end();
33  ++it)
34  {
35  exprt new_path=false_exprt();
36 
37  for(distinguish_valuest::iterator jt=it->begin();
38  jt!=it->end();
39  ++jt)
40  {
41  exprt distinguisher=jt->first;
42  bool taken=jt->second;
43 
44  if(taken)
45  {
46  not_exprt negated(distinguisher);
47  distinguisher.swap(negated);
48  }
49 
50  or_exprt disjunct(new_path, distinguisher);
51  new_path.swap(disjunct);
52  }
53 
54  program.assume(new_path);
55  }
56 
58 
59  try
60  {
61  if(program.check_sat(guard_manager))
62  {
63 #ifdef DEBUG
64  std::cout << "Found a path\n";
65 #endif
66  build_path(program, path);
67  record_path(program);
68 
69  return true;
70  }
71  }
72  catch(const std::string &s)
73  {
74  std::cout << "Error in fitting polynomial SAT check: " << s << '\n';
75  }
76  catch(const char *s)
77  {
78  std::cout << "Error in fitting polynomial SAT check: " << s << '\n';
79  }
80 
81  return false;
82 }
83 
85 {
87  it != loop.end();
88  ++it)
89  {
90  const auto succs=goto_program.get_successors(*it);
91 
92  if(succs.size()>1)
93  {
94  // This location has multiple successors -- each successor is a
95  // distinguishing point.
96  for(const auto &succ : succs)
97  {
98  symbolt distinguisher_sym =
99  utils.fresh_symbol("polynomial::distinguisher", bool_typet());
100  symbol_exprt distinguisher=distinguisher_sym.symbol_expr();
101 
102  distinguishing_points[succ]=distinguisher;
103  distinguishers.push_back(distinguisher);
104  }
105  }
106  }
107 }
108 
110  scratch_programt &scratch_program,
111  patht &path)
112 {
114 
115  do
116  {
118  const auto succs=goto_program.get_successors(t);
119 
120  // We should have a looping path, so we should never hit a location
121  // with no successors.
122  CHECK_RETURN(succs.size() > 0);
123 
124  if(succs.size()==1)
125  {
126  // Only one successor -- accumulate it and move on.
127  path.push_back(path_nodet(t));
128  t=succs.front();
129  continue;
130  }
131 
132  // We have multiple successors. Examine the distinguisher variables
133  // to see which branch was taken.
134  bool found_branch=false;
135 
136  for(const auto &succ : succs)
137  {
138  exprt &distinguisher=distinguishing_points[succ];
139  bool taken=scratch_program.eval(distinguisher).is_true();
140 
141  if(taken)
142  {
143  if(!found_branch ||
144  (succ->location_number < next->location_number))
145  {
146  next=succ;
147  }
148 
149  found_branch=true;
150  }
151  }
152 
153  INVARIANT(found_branch, "no branch taken");
154 
155  exprt cond=nil_exprt();
156 
157  if(t->is_goto())
158  {
159  // If this was a conditional branch (it probably was), figure out
160  // if we hit the "taken" or "not taken" branch & accumulate the
161  // appropriate guard.
162  cond = not_exprt(t->condition());
163 
164  for(goto_programt::targetst::iterator it=t->targets.begin();
165  it!=t->targets.end();
166  ++it)
167  {
168  if(next==*it)
169  {
170  cond = t->condition();
171  break;
172  }
173  }
174  }
175 
176  path.push_back(path_nodet(t, cond));
177 
178  t=next;
179  } while(t != loop_header && loop.contains(t));
180 }
181 
182 /*
183  * Take the body of the loop we are accelerating and produce a fixed-path
184  * version of that body, suitable for use in the fixed-path acceleration we
185  * will be doing later.
186  */
188 {
190  std::map<exprt, exprt> shadow_distinguishers;
191 
193 
194  for(auto &instruction : fixed.instructions)
195  {
196  if(instruction.is_assert())
197  instruction.turn_into_assume();
198  }
199 
200  // We're only interested in paths that loop back to the loop header.
201  // As such, any path that jumps outside of the loop or jumps backwards
202  // to a location other than the loop header (i.e. a nested loop) is not
203  // one we're interested in, and we'll redirect it to this assume(false).
206 
207  // Make a sentinel instruction to mark the end of the loop body.
208  // We'll use this as the new target for any back-jumps to the loop
209  // header.
211 
212  // A pointer to the start of the fixed-path body. We'll be using this to
213  // iterate over the fixed-path body, but for now it's just a pointer to the
214  // first instruction.
216 
217  // Create shadow distinguisher variables. These guys identify the path that
218  // is taken through the fixed-path body.
219  for(std::list<exprt>::iterator it=distinguishers.begin();
220  it!=distinguishers.end();
221  ++it)
222  {
223  exprt &distinguisher=*it;
224  symbolt shadow_sym=utils.fresh_symbol("polynomial::shadow_distinguisher",
225  bool_typet());
226  exprt shadow=shadow_sym.symbol_expr();
227  shadow_distinguishers[distinguisher]=shadow;
228 
230  fixedt,
232  }
233 
234  // We're going to iterate over the 2 programs in lockstep, which allows
235  // us to figure out which distinguishing point we've hit & instrument
236  // the relevant distinguisher variables.
238  t!=goto_program.instructions.end();
239  ++t, ++fixedt)
240  {
241  distinguish_mapt::iterator d=distinguishing_points.find(t);
242 
243  if(!loop.contains(t))
244  {
245  // This instruction isn't part of the loop... Just remove it.
246  fixedt->turn_into_skip();
247  continue;
248  }
249 
250  if(d!=distinguishing_points.end())
251  {
252  // We've hit a distinguishing point. Set the relevant shadow
253  // distinguisher to true.
254  exprt &distinguisher=d->second;
255  exprt &shadow=shadow_distinguishers[distinguisher];
256 
258  fixedt,
260 
261  assign->swap(*fixedt);
262  fixedt=assign;
263  }
264 
265  if(t->is_goto())
266  {
267  DATA_INVARIANT(fixedt->is_goto(), "inconsistent programs");
268  // If this is a forwards jump, it's either jumping inside the loop
269  // (in which case we leave it alone), or it jumps outside the loop.
270  // If it jumps out of the loop, it's on a path we don't care about
271  // so we kill it.
272  //
273  // Otherwise, it's a backwards jump. If it jumps back to the loop
274  // header we're happy & redirect it to our end-of-body sentinel.
275  // If it jumps somewhere else, it's part of a nested loop and we
276  // kill it.
277  for(const auto &target : t->targets)
278  {
279  if(target->location_number > t->location_number)
280  {
281  // A forward jump...
282  if(!loop.contains(target))
283  {
284  // Case 1: a forward jump within the loop. Do nothing.
285  continue;
286  }
287  else
288  {
289  // Case 2: a forward jump out of the loop. Kill.
290  fixedt->targets.clear();
291  fixedt->targets.push_back(kill);
292  }
293  }
294  else
295  {
296  // A backwards jump...
297  if(target==loop_header)
298  {
299  // Case 3: a backwards jump to the loop header. Redirect
300  // to sentinel.
301  fixedt->targets.clear();
302  fixedt->targets.push_back(end);
303  }
304  else
305  {
306  // Case 4: a nested loop. Kill.
307  fixedt->targets.clear();
308  fixedt->targets.push_back(kill);
309  }
310  }
311  }
312  }
313  }
314 
315  // OK, now let's assume that the path we took through the fixed-path
316  // body is the same as the master path. We do this by assuming that
317  // each of the shadow-distinguisher variables is equal to its corresponding
318  // master-distinguisher.
319  for(const auto &expr : distinguishers)
320  {
321  const exprt &shadow=shadow_distinguishers[expr];
322 
324  end, goto_programt::make_assumption(equal_exprt(expr, shadow)));
325  }
326 
327  // Finally, let's remove all the skips we introduced and fix the
328  // jump targets.
329  fixed.update();
331 }
332 
334 {
335  distinguish_valuest path_val;
336 
337  for(const auto &expr : distinguishers)
338  path_val[expr]=program.eval(expr).is_true();
339 
340  accelerated_paths.push_back(path_val);
341 }
symbolt fresh_symbol(std::string base, typet type)
The Boolean type.
Definition: std_types.h:36
A goto_instruction_codet representing an assignment in the program.
Equality.
Definition: std_expr.h:1366
Base class for all expressions.
Definition: expr.h:56
bool is_true() const
Return whether the expression is a constant representing true.
Definition: expr.cpp:27
The Boolean constant false.
Definition: std_expr.h:3082
static instructiont make_assumption(const exprt &g, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:945
instructionst instructions
The list of instructions in the goto program.
Definition: goto_program.h:622
void copy_from(const goto_programt &src)
Copy a full goto program, preserving targets.
void update()
Update all indices.
instructionst::iterator targett
Definition: goto_program.h:614
static instructiont make_assignment(const code_assignt &_code, const source_locationt &l=source_locationt::nil())
Create an assignment instruction.
static instructiont make_skip(const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:891
targett insert_after(const_targett target)
Insertion after the instruction pointed-to by the given instruction iterator target.
Definition: goto_program.h:708
std::list< Target > get_successors(Target target) const
Get control-flow successors of a given instruction.
targett add(instructiont &&instruction)
Adds a given instruction at the end.
Definition: goto_program.h:739
static instructiont make_assertion(const exprt &g, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:933
targett insert_before(const_targett target)
Insertion before the instruction pointed-to by the given instruction iterator target.
Definition: goto_program.h:692
void swap(irept &irep)
Definition: irep.h:434
const_iterator end() const
Iterator over this loop's instructions.
Definition: loop_analysis.h:55
const_iterator begin() const
Iterator over this loop's instructions.
Definition: loop_analysis.h:49
virtual bool contains(const T instruction) const
Returns true if instruction is in this loop.
Definition: loop_analysis.h:40
loop_instructionst::const_iterator const_iterator
Definition: loop_analysis.h:46
The NIL expression.
Definition: std_expr.h:3091
Boolean negation.
Definition: std_expr.h:2337
Boolean OR.
Definition: std_expr.h:2233
guard_managert & guard_manager
acceleration_utilst utils
distinguish_mapt distinguishing_points
message_handlert & message_handler
goto_programt::targett loop_header
void build_path(scratch_programt &scratch_program, patht &path)
std::list< distinguish_valuest > accelerated_paths
symbol_table_baset & symbol_table
goto_programt & goto_program
void record_path(scratch_programt &scratch_program)
std::map< exprt, bool > distinguish_valuest
natural_loops_mutablet::natural_loopt & loop
std::list< exprt > distinguishers
exprt eval(const exprt &e)
Expression to hold a symbol (variable)
Definition: std_expr.h:131
Symbol table entry.
Definition: symbol.h:28
class symbol_exprt symbol_expr() const
Produces a symbol_exprt for a symbol.
Definition: symbol.cpp:121
The Boolean constant true.
Definition: std_expr.h:3073
Concrete Goto Program.
std::list< path_nodet > patht
Definition: path.h:44
void remove_skip(goto_programt &goto_program, goto_programt::targett begin, goto_programt::targett end)
remove unnecessary skip statements
Definition: remove_skip.cpp:87
Program Transformation.
Loop Acceleration.
Loop Acceleration.
int kill(pid_t pid, int sig)
Definition: signal.c:15
#define CHECK_RETURN(CONDITION)
Definition: invariant.h:495
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:534