CBMC
reachability_slicer.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Slicer
4 
5 Author: Daniel Kroening, kroening@kroening.com
6 
7 \*******************************************************************/
8 
15 
16 #include "full_slicer_class.h"
18 
19 #include <util/exception_utils.h>
20 
21 #include <goto-programs/cfg.h>
25 
26 #include <analyses/is_threaded.h>
27 
28 #include "reachability_slicer.h"
29 
31  goto_functionst &goto_functions,
32  const slicing_criteriont &criterion,
33  bool include_forward_reachability,
34  message_handlert &message_handler)
35 {
36  // Replace function calls without body by non-deterministic return values to
37  // ensure the CFG does not consider instructions after such a call to be
38  // unreachable.
39  remove_calls_no_bodyt remove_calls_no_body;
40  remove_calls_no_body(goto_functions, message_handler);
41  goto_functions.update();
42 
43  cfg(goto_functions);
44  for(const auto &gf_entry : goto_functions.function_map)
45  {
46  forall_goto_program_instructions(i_it, gf_entry.second.body)
47  cfg[cfg.entry_map[i_it]].function_id = gf_entry.first;
48  }
49 
50  is_threadedt is_threaded(goto_functions);
51  fixedpoint_to_assertions(is_threaded, criterion);
52  if(include_forward_reachability)
53  fixedpoint_from_assertions(is_threaded, criterion);
54  slice(goto_functions);
55 }
56 
62 std::vector<reachability_slicert::cfgt::node_indext>
64  const is_threadedt &is_threaded,
65  const slicing_criteriont &criterion)
66 {
67  std::vector<cfgt::node_indext> sources;
68  for(const auto &e_it : cfg.entries())
69  {
70  if(
71  criterion(cfg[e_it.second].function_id, e_it.first) ||
72  is_threaded(e_it.first))
73  sources.push_back(e_it.second);
74  }
75 
76  if(sources.empty())
77  {
79  "no slicing criterion found",
80  "--property",
81  "provide at least one property using --property <property>"};
82  }
83 
84  return sources;
85 }
86 
90 {
91  // Avoid comparing iterators belonging to different functions, and therefore
92  // different std::lists.
93  const auto &node1 = cfg.get_node(it1);
94  const auto &node2 = cfg.get_node(it2);
95  return node1.function_id == node2.function_id && it1 == it2;
96 }
97 
104 std::vector<reachability_slicert::cfgt::node_indext>
106  std::vector<cfgt::node_indext> stack)
107 {
108  std::vector<cfgt::node_indext> return_sites;
109 
110  while(!stack.empty())
111  {
112  auto &node = cfg[stack.back()];
113  stack.pop_back();
114 
115  if(node.reaches_assertion)
116  continue;
117  node.reaches_assertion = true;
118 
119  for(const auto &edge : node.in)
120  {
121  const auto &pred_node = cfg[edge.first];
122 
123  if(pred_node.PC->is_end_function())
124  {
125  // This is an end-of-function -> successor-of-callsite edge.
126  // Record the return site for later investigation and step over it:
127  return_sites.push_back(edge.first);
128 
129  INVARIANT(
130  std::prev(node.PC)->is_function_call(),
131  "all function return edges should point to the successor of a "
132  "FUNCTION_CALL instruction");
133 
134  stack.push_back(cfg.get_node_index(std::prev(node.PC)));
135  }
136  else
137  {
138  stack.push_back(edge.first);
139  }
140  }
141  }
142 
143  return return_sites;
144 }
145 
156  std::vector<cfgt::node_indext> stack)
157 {
158  while(!stack.empty())
159  {
160  auto &node = cfg[stack.back()];
161  stack.pop_back();
162 
163  if(node.reaches_assertion)
164  continue;
165  node.reaches_assertion = true;
166 
167  for(const auto &edge : node.in)
168  {
169  const auto &pred_node = cfg[edge.first];
170 
171  if(pred_node.PC->is_end_function())
172  {
173  // This is an end-of-function -> successor-of-callsite edge.
174  // Walk into the called function, and then walk from the callsite
175  // backward:
176  stack.push_back(edge.first);
177 
178  INVARIANT(
179  std::prev(node.PC)->is_function_call(),
180  "all function return edges should point to the successor of a "
181  "FUNCTION_CALL instruction");
182 
183  stack.push_back(cfg.get_node_index(std::prev(node.PC)));
184  }
185  else if(pred_node.PC->is_function_call())
186  {
187  // Skip -- the callsite relevant to this function was already queued
188  // when we processed the return site.
189  }
190  else
191  {
192  stack.push_back(edge.first);
193  }
194  }
195  }
196 }
197 
205  const is_threadedt &is_threaded,
206  const slicing_criteriont &criterion)
207 {
208  std::vector<cfgt::node_indext> sources = get_sources(is_threaded, criterion);
209 
210  // First walk outwards towards __CPROVER_start, visiting all possible callers
211  // and stepping over but recording callees as we go:
212  std::vector<cfgt::node_indext> return_sites =
214 
215  // Now walk into those callees, restricting our walk to the known callsites:
216  backward_inwards_walk_from(return_sites);
217 }
218 
228  const cfgt::nodet &call_node,
229  std::vector<cfgt::node_indext> &callsite_successor_stack,
230  std::vector<cfgt::node_indext> &callee_head_stack)
231 {
232  // Get the instruction's natural successor (function head, or next
233  // instruction if the function is bodyless)
234  INVARIANT(call_node.out.size() == 1, "Call sites should have one successor");
235  const auto successor_index = call_node.out.begin()->first;
236 
237  auto callsite_successor_pc = std::next(call_node.PC);
238 
239  auto successor_pc = cfg[successor_index].PC;
240  if(!is_same_target(successor_pc, callsite_successor_pc))
241  {
242  // Real call -- store the callee head node:
243  callee_head_stack.push_back(successor_index);
244 
245  // Check if it can return, and if so store the callsite's successor:
246  while(!successor_pc->is_end_function())
247  ++successor_pc;
248 
249  if(!cfg.get_node(successor_pc).out.empty())
250  callsite_successor_stack.push_back(
251  cfg.get_node_index(callsite_successor_pc));
252  }
253  else
254  {
255  // Bodyless function -- mark the successor instruction only.
256  callsite_successor_stack.push_back(successor_index);
257  }
258 }
259 
266 std::vector<reachability_slicert::cfgt::node_indext>
268  std::vector<cfgt::node_indext> stack)
269 {
270  std::vector<cfgt::node_indext> called_function_heads;
271 
272  while(!stack.empty())
273  {
274  auto &node = cfg[stack.back()];
275  stack.pop_back();
276 
277  if(node.reachable_from_assertion)
278  continue;
279  node.reachable_from_assertion = true;
280 
281  if(node.PC->is_function_call())
282  {
283  // Store the called function head for the later inwards walk;
284  // visit the call instruction's local successor now.
285  forward_walk_call_instruction(node, stack, called_function_heads);
286  }
287  else
288  {
289  // General case, including end of function: queue all successors.
290  for(const auto &edge : node.out)
291  stack.push_back(edge.first);
292  }
293  }
294 
295  return called_function_heads;
296 }
297 
306  std::vector<cfgt::node_indext> stack)
307 {
308  while(!stack.empty())
309  {
310  auto &node = cfg[stack.back()];
311  stack.pop_back();
312 
313  if(node.reachable_from_assertion)
314  continue;
315  node.reachable_from_assertion = true;
316 
317  if(node.PC->is_function_call())
318  {
319  // Visit both the called function head and the callsite successor:
320  forward_walk_call_instruction(node, stack, stack);
321  }
322  else if(node.PC->is_end_function())
323  {
324  // Special case -- the callsite successor was already queued when entering
325  // this function, more precisely than we can see from the function return
326  // edges (which lead to all possible callers), so nothing to do here.
327  }
328  else
329  {
330  // General case: queue all successors.
331  for(const auto &edge : node.out)
332  stack.push_back(edge.first);
333  }
334  }
335 }
336 
344  const is_threadedt &is_threaded,
345  const slicing_criteriont &criterion)
346 {
347  std::vector<cfgt::node_indext> sources = get_sources(is_threaded, criterion);
348 
349  // First walk outwards towards __CPROVER_start, visiting all possible callers
350  // and stepping over but recording callees as we go:
351  std::vector<cfgt::node_indext> return_sites =
353 
354  // Now walk into those callees, restricting our walk to the known callsites:
355  forward_inwards_walk_from(return_sites);
356 }
357 
361 {
362  // now replace those instructions that do not reach any assertions
363  // by assume(false)
364 
365  for(auto &gf_entry : goto_functions.function_map)
366  {
367  if(gf_entry.second.body_available())
368  {
369  Forall_goto_program_instructions(i_it, gf_entry.second.body)
370  {
371  cfgt::nodet &e = cfg.get_node(i_it);
372  if(
373  !e.reaches_assertion && !e.reachable_from_assertion &&
374  !i_it->is_end_function())
375  {
377  false_exprt(), i_it->source_location());
378  }
379  }
380 
381  // replace unreachable code by skip
382  remove_unreachable(gf_entry.second.body);
383  }
384  }
385 
386  // remove the skips
387  remove_skip(goto_functions);
388 }
389 
398  goto_modelt &goto_model,
399  const bool include_forward_reachability,
400  message_handlert &message_handler)
401 {
404  s(goto_model.goto_functions,
405  a,
406  include_forward_reachability,
407  message_handler);
408 }
409 
419  goto_modelt &goto_model,
420  const std::list<std::string> &properties,
421  const bool include_forward_reachability,
422  message_handlert &message_handler)
423 {
425  properties_criteriont p(properties);
426  s(goto_model.goto_functions,
427  p,
428  include_forward_reachability,
429  message_handler);
430 }
431 
439  goto_modelt &goto_model,
440  const std::list<std::string> &functions_list,
441  message_handlert &message_handler)
442 {
443  for(const auto &function : functions_list)
444  {
445  in_function_criteriont matching_criterion(function);
446  reachability_slicert slicer;
447  slicer(
448  goto_model.goto_functions, matching_criterion, true, message_handler);
449  }
450 
451  remove_calls_no_bodyt remove_calls_no_body;
452  remove_calls_no_body(goto_model.goto_functions, message_handler);
453 
454  goto_model.goto_functions.update();
456 }
457 
464  goto_modelt &goto_model,
465  message_handlert &message_handler)
466 {
467  reachability_slicer(goto_model, false, message_handler);
468 }
469 
477  goto_modelt &goto_model,
478  const std::list<std::string> &properties,
479  message_handlert &message_handler)
480 {
481  reachability_slicer(goto_model, properties, false, message_handler);
482 }
Control Flow Graph.
const entry_mapt & entries() const
Get a map from program points to their corresponding node indices.
Definition: cfg.h:259
base_grapht::nodet nodet
Definition: cfg.h:92
entryt get_node_index(const goto_programt::const_targett &program_point) const
Get the graph node index for program_point.
Definition: cfg.h:239
entry_mapt entry_map
Definition: cfg.h:152
nodet & get_node(const goto_programt::const_targett &program_point)
Get the CFG graph node relating to program_point.
Definition: cfg.h:245
const source_locationt & source_location() const
Definition: expr.h:231
The Boolean constant false.
Definition: std_expr.h:3064
A collection of goto functions.
void compute_loop_numbers()
function_mapt function_map
goto_functionst goto_functions
GOTO functions.
Definition: goto_model.h:34
static instructiont make_assumption(const exprt &g, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:945
instructionst::const_iterator const_targett
Definition: goto_program.h:615
edgest out
Definition: graph.h:42
Thrown when users pass incorrect command line arguments, for example passing no files to analysis or ...
std::vector< cfgt::node_indext > backward_outwards_walk_from(std::vector< cfgt::node_indext >)
Perform backward depth-first search of the control-flow graph of the goto program,...
bool is_same_target(goto_programt::const_targett it1, goto_programt::const_targett it2) const
std::vector< cfgt::node_indext > forward_outwards_walk_from(std::vector< cfgt::node_indext >)
Perform forwards depth-first search of the control-flow graph of the goto program,...
void backward_inwards_walk_from(std::vector< cfgt::node_indext >)
Perform backward depth-first search of the control-flow graph of the goto program,...
std::vector< cfgt::node_indext > get_sources(const is_threadedt &is_threaded, const slicing_criteriont &criterion)
Get the set of nodes that correspond to the given criterion, or that can appear in concurrent executi...
void operator()(goto_functionst &goto_functions, const slicing_criteriont &criterion, bool include_forward_reachability, message_handlert &)
void fixedpoint_to_assertions(const is_threadedt &is_threaded, const slicing_criteriont &criterion)
Perform backward depth-first search of the control-flow graph of the goto program,...
void fixedpoint_from_assertions(const is_threadedt &is_threaded, const slicing_criteriont &criterion)
Perform forwards depth-first search of the control-flow graph of the goto program,...
void forward_inwards_walk_from(std::vector< cfgt::node_indext >)
Perform forwards depth-first search of the control-flow graph of the goto program,...
void slice(goto_functionst &goto_functions)
This function removes all instructions that have the flag reaches_assertion or reachable_from_asserti...
void forward_walk_call_instruction(const cfgt::nodet &call_node, std::vector< cfgt::node_indext > &callsite_successor_stack, std::vector< cfgt::node_indext > &callee_head_stack)
Process a call instruction during a forwards reachability walk.
Goto Program Slicing.
#define forall_goto_program_instructions(it, program)
#define Forall_goto_program_instructions(it, program)
Over-approximate Concurrency for Threaded Goto Programs.
void reachability_slicer(goto_modelt &goto_model, const bool include_forward_reachability, message_handlert &message_handler)
Perform reachability slicing on goto_model, with respect to the criterion given by all properties.
void function_path_reachability_slicer(goto_modelt &goto_model, const std::list< std::string > &functions_list, message_handlert &message_handler)
Perform reachability slicing on goto_model for selected functions.
Goto Program Slicing.
Remove calls to functions without a body.
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.
void remove_unreachable(goto_programt &goto_program)
remove unreachable code
Program Transformation.