CBMC
flow_insensitive_analysis.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Flow Insensitive Static Analysis
4 
5 Author: Daniel Kroening, kroening@kroening.com
6  CM Wintersteiger
7 
8 \*******************************************************************/
9 
12 
14 
15 #include <util/expr_util.h>
16 #include <util/pointer_expr.h>
17 #include <util/std_code.h>
18 
20  locationt from,
21  locationt to) const
22 {
23  if(!from->is_goto())
24  return true_exprt();
25  else if(std::next(from) == to)
26  return boolean_negate(from->condition());
27  else
28  return from->condition();
29 }
30 
32 {
33  // get predecessor of "to"
34  to--;
35 
36  if(to->is_end_function())
37  return static_cast<const exprt &>(get_nil_irep());
38 
39  // must be the function call
40  return to->call_lhs();
41 }
42 
44  const goto_functionst &goto_functions)
45 {
46  initialize(goto_functions);
47  fixedpoint(goto_functions);
48 }
49 
51 operator()(const irep_idt &function_id, const goto_programt &goto_program)
52 {
53  initialize(goto_program);
54  goto_functionst goto_functions;
55  fixedpoint(function_id, goto_program, goto_functions);
56 }
57 
59  const goto_functionst &goto_functions,
60  std::ostream &out)
61 {
62  for(const auto &gf_entry : goto_functions.function_map)
63  {
64  out << "////\n"
65  << "//// Function: " << gf_entry.first << "\n////\n\n";
66 
67  output(gf_entry.first, gf_entry.second.body, out);
68  }
69 }
70 
72  const irep_idt &,
73  const goto_programt &,
74  std::ostream &out)
75 {
76  get_state().output(ns, out);
77 }
78 
81  working_sett &working_set)
82 {
83  PRECONDITION(!working_set.empty());
84 
85  // working_sett::iterator i=working_set.begin();
86  // locationt l=i->second;
87  // working_set.erase(i);
88 
89  // pop_heap(working_set.begin(), working_set.end());
90  locationt l=working_set.top();
91  working_set.pop();
92 
93  return l;
94 }
95 
97  const irep_idt &function_id,
98  const goto_programt &goto_program,
99  const goto_functionst &goto_functions)
100 {
101  if(goto_program.instructions.empty())
102  return false;
103 
104  working_sett working_set;
105 
106 // make_heap(working_set.begin(), working_set.end());
107 
109  working_set,
110  goto_program.instructions.begin());
111 
112  bool new_data=false;
113 
114  while(!working_set.empty())
115  {
116  locationt l=get_next(working_set);
117 
118  if(visit(function_id, l, working_set, goto_program, goto_functions))
119  new_data=true;
120  }
121 
122  return new_data;
123 }
124 
126  const irep_idt &function_id,
127  locationt l,
128  working_sett &working_set,
129  const goto_programt &goto_program,
130  const goto_functionst &goto_functions)
131 {
132  bool new_data=false;
133 
134  #if 0
135  std::cout << "Visiting: " << l->function << " " <<
136  l->location_number << '\n';
137  #endif
138 
139  seen_locations.insert(l);
140  if(statistics.find(l)==statistics.end())
141  statistics[l]=1;
142  else
143  statistics[l]++;
144 
145  for(const auto &to_l : goto_program.get_successors(l))
146  {
147  if(to_l==goto_program.instructions.end())
148  continue;
149 
150  bool changed=false;
151 
152  if(l->is_function_call())
153  {
154  // this is a big special case
155  changed = do_function_call_rec(
156  function_id,
157  l,
158  l->call_function(),
159  l->call_arguments(),
160  get_state(),
161  goto_functions);
162  }
163  else
164  changed = get_state().transform(ns, function_id, l, function_id, to_l);
165 
166  if(changed || !seen(to_l))
167  {
168  new_data=true;
169  put_in_working_set(working_set, to_l);
170  }
171  }
172 
173 // if (id2string(l->function).find("debug")!=std::string::npos)
174 // std::cout << l->function << '\n'; //=="messages::debug")
175 
176  // {
177  // static unsigned state_cntr=0;
178  // std::string s("pastate"); s += std::to_string(state_cntr);
179  // std::ofstream f(s.c_str());
180  // l->output(f);
181  // f << '\n';
182  // get_state().output(ns, f);
183  // f.close();
184  // state_cntr++;
185  // }
186 
187  return new_data;
188 }
189 
191  const irep_idt &calling_function,
192  locationt l_call,
193  const goto_functionst &goto_functions,
194  const goto_functionst::function_mapt::const_iterator f_it,
195  const exprt::operandst &,
196  statet &state)
197 {
198  const goto_functionst::goto_functiont &goto_function=f_it->second;
199 
200  if(!goto_function.body_available())
201  {
202  goto_programt temp;
203 
206  l_call->call_lhs().type(), l_call->source_location())));
207  r->location_number=0;
208 
210  t->location_number=1;
211 
212  locationt l_next=l_call; l_next++;
213  // do the edge from the call site to the simulated function (the artificial
214  // return statement that we just generated)
215  bool new_data =
216  state.transform(ns, calling_function, l_call, f_it->first, r);
217  // do the edge from the return to the artificial end-of-function
218  new_data = state.transform(ns, f_it->first, r, f_it->first, t) || new_data;
219  // do edge from (artificial) end of function to instruction after call
220  new_data =
221  state.transform(ns, f_it->first, t, calling_function, l_next) || new_data;
222 
223  return new_data;
224  }
225 
227  !goto_function.body.instructions.empty(), "body must be present");
228 
229  bool new_data=false;
230 
231  {
232  // get the state at the beginning of the function
233  locationt l_begin=goto_function.body.instructions.begin();
234 
235  // do the edge from the call site to the beginning of the function
236  new_data =
237  state.transform(ns, calling_function, l_call, f_it->first, l_begin);
238 
239  // do each function at least once
240  if(functions_done.find(f_it->first)==
241  functions_done.end())
242  {
243  new_data=true;
244  functions_done.insert(f_it->first);
245  }
246 
247  // do we need to do the fixedpoint of the body?
248  if(new_data)
249  {
250  // recursive call
251  fixedpoint(f_it->first, goto_function.body, goto_functions);
252  new_data=true; // could be reset by fixedpoint
253  }
254  }
255 
256  {
257  // get location at end of procedure
258  locationt l_end=--goto_function.body.instructions.end();
259 
260  DATA_INVARIANT(l_end->is_end_function(), "must be end of function");
261 
262  // do edge from end of function to instruction after call
263  locationt l_next=l_call;
264  l_next++;
265  new_data =
266  state.transform(ns, f_it->first, l_end, calling_function, l_next) ||
267  new_data;
268  }
269 
270  return new_data;
271 }
272 
274  const irep_idt &calling_function,
275  locationt l_call,
276  const exprt &function,
277  const exprt::operandst &arguments,
278  statet &state,
279  const goto_functionst &goto_functions)
280 {
281  bool new_data = false;
282 
283  if(function.id()==ID_symbol)
284  {
285  const irep_idt &identifier = to_symbol_expr(function).get_identifier();
286 
287  if(recursion_set.find(identifier)!=recursion_set.end())
288  {
289  // recursion detected!
290  return false;
291  }
292  else
293  recursion_set.insert(identifier);
294 
295  goto_functionst::function_mapt::const_iterator it=
296  goto_functions.function_map.find(identifier);
297 
298  if(it==goto_functions.function_map.end())
299  throw "failed to find function "+id2string(identifier);
300 
301  new_data = do_function_call(
302  calling_function, l_call, goto_functions, it, arguments, state);
303 
304  recursion_set.erase(identifier);
305  }
306  else if(function.id()==ID_if)
307  {
308  const auto &if_expr = to_if_expr(function);
309 
310  new_data = do_function_call_rec(
311  calling_function,
312  l_call,
313  if_expr.true_case(),
314  arguments,
315  state,
316  goto_functions);
317 
318  new_data = do_function_call_rec(
319  calling_function,
320  l_call,
321  if_expr.false_case(),
322  arguments,
323  state,
324  goto_functions) ||
325  new_data;
326  }
327  else if(function.id()==ID_dereference)
328  {
329  // get value set
330  expr_sett values;
331 
332  get_reference_set(function, values);
333 
334  // now call all of these
335  for(const auto &v : values)
336  {
337  if(v.id()==ID_object_descriptor)
338  {
340 
341  // ... but only if they are actually functions.
342  goto_functionst::function_mapt::const_iterator it=
343  goto_functions.function_map.find(o.object().get(ID_identifier));
344 
345  if(it!=goto_functions.function_map.end())
346  {
347  new_data = do_function_call_rec(
348  calling_function,
349  l_call,
350  o.object(),
351  arguments,
352  state,
353  goto_functions) ||
354  new_data;
355  }
356  }
357  }
358  }
359  else if(function.id() == ID_null_object)
360  {
361  // ignore, can't be a function
362  }
363  else if(function.id()==ID_member || function.id()==ID_index)
364  {
365  // ignore, can't be a function
366  }
367  else if(function.id()=="builtin-function")
368  {
369  // ignore
370  }
371  else
372  {
373  throw "unexpected function_call argument: "+
374  function.id_string();
375  }
376  return new_data;
377 }
378 
380  const goto_functionst &goto_functions)
381 {
382  // do each function at least once
383 
384  for(const auto &gf_entry : goto_functions.function_map)
385  {
386  if(functions_done.find(gf_entry.first) == functions_done.end())
387  {
388  functions_done.insert(gf_entry.first);
389  fixedpoint(gf_entry.first, gf_entry.second.body, goto_functions);
390  }
391  }
392 }
393 
395 {
396  // no need to copy value sets around
397 }
398 
400 {
401  // no need to copy value sets around
402 }
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition: dstring.h:38
Base class for all expressions.
Definition: expr.h:56
std::vector< exprt > operandst
Definition: expr.h:58
exprt get_guard(locationt from, locationt to) const
virtual void output(const namespacet &, std::ostream &) const
virtual bool transform(const namespacet &ns, const irep_idt &function_from, locationt from, const irep_idt &function_to, locationt to)=0
std::map< locationt, unsigned, goto_programt::target_less_than > statistics
virtual void output(const goto_functionst &goto_functions, std::ostream &out)
std::priority_queue< locationt, std::vector< locationt >, goto_programt::target_less_than > working_sett
virtual void operator()(const irep_idt &function_id, const goto_programt &goto_program)
virtual void get_reference_set(const exprt &expr, expr_sett &expr_set)=0
locationt get_next(working_sett &working_set)
std::set< locationt, goto_programt::target_less_than > seen_locations
bool fixedpoint(const irep_idt &function_id, const goto_programt &goto_program, const goto_functionst &goto_functions)
virtual statet & get_state()=0
bool visit(const irep_idt &function_id, locationt l, working_sett &working_set, const goto_programt &goto_program, const goto_functionst &goto_functions)
virtual void update(const goto_programt &goto_program)
flow_insensitive_abstract_domain_baset::expr_sett expr_sett
bool do_function_call_rec(const irep_idt &calling_function, locationt l_call, const exprt &function, const exprt::operandst &arguments, statet &new_state, const goto_functionst &goto_functions)
goto_programt::const_targett locationt
virtual void initialize(const goto_programt &)
void put_in_working_set(working_sett &working_set, locationt l)
bool do_function_call(const irep_idt &calling_function, locationt l_call, const goto_functionst &goto_functions, const goto_functionst::function_mapt::const_iterator f_it, const exprt::operandst &arguments, statet &new_state)
A collection of goto functions.
function_mapt function_map
::goto_functiont goto_functiont
A generic container class for the GOTO intermediate representation of one function.
Definition: goto_program.h:73
instructionst instructions
The list of instructions in the goto program.
Definition: goto_program.h:622
static instructiont make_set_return_value(exprt return_value, const source_locationt &l=source_locationt::nil())
Definition: goto_program.h:874
static instructiont make_end_function(const source_locationt &l=source_locationt::nil())
instructionst::iterator targett
Definition: goto_program.h:614
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
const irep_idt & get(const irep_idt &name) const
Definition: irep.cpp:44
Split an expression into a base object and a (byte) offset.
Definition: pointer_expr.h:181
A side_effect_exprt that returns a non-deterministically chosen value.
Definition: std_code.h:1520
const irep_idt & get_identifier() const
Definition: std_expr.h:160
The Boolean constant true.
Definition: std_expr.h:3063
exprt boolean_negate(const exprt &src)
negate a Boolean expression, possibly removing a not_exprt, and swapping false and true
Definition: expr_util.cpp:103
Deprecated expression utility functions.
Flow Insensitive Static Analysis.
const irept & get_nil_irep()
Definition: irep.cpp:19
const std::string & id2string(const irep_idt &d)
Definition: irep.h:44
static int8_t r
Definition: irep_hash.h:60
API to expression classes for Pointers.
const object_descriptor_exprt & to_object_descriptor_expr(const exprt &expr)
Cast an exprt to an object_descriptor_exprt.
Definition: pointer_expr.h:252
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:534
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
const if_exprt & to_if_expr(const exprt &expr)
Cast an exprt to an if_exprt.
Definition: std_expr.h:2450
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition: std_expr.h:272