CBMC
Loading...
Searching...
No Matches
reachability_slicer.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Slicer
4
5Author: Daniel Kroening, kroening@kroening.com
6
7\*******************************************************************/
8
15
16#include "full_slicer_class.h"
18
20
21#include <goto-programs/cfg.h>
25
27
28#include "reachability_slicer.h"
29
31 goto_functionst &goto_functions,
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.
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 {
47 cfg[cfg.entry_map[i_it]].function_id = gf_entry.first;
48 }
49
50 is_threadedt is_threaded(goto_functions);
54 slice(goto_functions);
55}
56
62std::vector<reachability_slicert::cfgt::node_indext>
64 const is_threadedt &is_threaded,
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
104std::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,
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:
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
241 {
242 // Real call -- store the callee head node:
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(
252 }
253 else
254 {
255 // Bodyless function -- mark the successor instruction only.
257 }
258}
259
266std::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.
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
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,
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:
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 {
370 {
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,
400 message_handlert &message_handler)
401{
404 s(goto_model.goto_functions,
405 a,
407 message_handler);
408}
409
419 goto_modelt &goto_model,
420 const std::list<std::string> &properties,
422 message_handlert &message_handler)
423{
425 properties_criteriont p(properties);
426 s(goto_model.goto_functions,
427 p,
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 {
447 slicer(
448 goto_model.goto_functions, matching_criterion, true, message_handler);
449 }
450
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.
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
const entry_mapt & entries() const
Get a map from program points to their corresponding node indices.
Definition cfg.h:259
nodet & get_node(const goto_programt::const_targett &program_point)
Get the CFG graph node relating to program_point.
Definition cfg.h:245
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
The Boolean constant false.
Definition std_expr.h:3199
A collection of goto functions.
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())
instructionst::const_iterator const_targett
N nodet
Definition graph.h:169
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
Program Transformation.
void remove_unreachable(goto_programt &goto_program)
remove unreachable code
Program Transformation.
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423