CBMC
variable_sensitivity_domain.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Abstract Interpretation
4 
5 Author: Martin Brain
6 
7 Date: April 2016
8 
9 \*******************************************************************/
10 
12 
13 #include <util/cprover_prefix.h>
14 #include <util/namespace.h>
15 #include <util/pointer_expr.h>
16 #include <util/symbol_table_base.h>
17 
18 #include <algorithm>
19 
20 #ifdef DEBUG
21 # include <iostream>
22 #endif
23 
25  const irep_idt &function_from,
26  trace_ptrt trace_from,
27  const irep_idt &function_to,
28  trace_ptrt trace_to,
29  ai_baset &ai,
30  const namespacet &ns)
31 {
32  locationt from{trace_from->current_location()};
33  locationt to{trace_to->current_location()};
34 
35 #ifdef DEBUG
36  std::cout << "Transform from/to:\n";
37  std::cout << from->location_number << " --> " << to->location_number << '\n';
38 #endif
39 
40  const goto_programt::instructiont &instruction = *from;
41  switch(instruction.type())
42  {
43  case DECL:
44  {
45  abstract_object_pointert top_object =
48  instruction.decl_symbol().type(), ns, true, false)
49  ->write_location_context(from);
50  abstract_state.assign(instruction.decl_symbol(), top_object, ns);
51  }
52  // We now store top.
53  break;
54 
55  case DEAD:
56  // Remove symbol from map, the only time this occurs now (keep TOP.)
57  // It should be the case that DEAD only provides symbols for deletion.
58  abstract_state.erase(instruction.dead_symbol());
59  break;
60 
61  case ASSIGN:
62  {
63  // TODO : check return values
65  abstract_state.eval(instruction.assign_rhs(), ns)
66  ->write_location_context(from);
67  abstract_state.assign(instruction.assign_lhs(), rhs, ns);
68  }
69  break;
70 
71  case GOTO:
72  {
74  {
75  // Get the next line
76  locationt next = from;
77  next++;
78  // Is this a GOTO to the next line (i.e. pointless)
79  if(next != from->get_target())
80  {
81  if(to == from->get_target())
82  {
83  // The AI is exploring the branch where the jump is taken
84  assume(instruction.condition(), ns);
85  }
86  else
87  {
88  // Exploring the path where the jump is not taken - therefore assume
89  // the condition is false
90  assume(not_exprt(instruction.condition()), ns);
91  }
92  }
93  // ignore jumps to the next line, we can assume nothing
94  }
95  }
96  break;
97 
98  case ASSUME:
99  assume(instruction.condition(), ns);
100  break;
101 
102  case FUNCTION_CALL:
103  {
104  transform_function_call(from, to, ai, ns);
105  break;
106  }
107 
108  case END_FUNCTION:
109  {
110  // erase parameters
111 
112  const irep_idt id = function_from;
113  const symbolt &symbol = ns.lookup(id);
114 
115  const code_typet &type = to_code_type(symbol.type);
116 
117  for(const auto &param : type.parameters())
118  {
119  // Top the arguments to the function
121  symbol_exprt(param.get_identifier(), param.type()),
122  abstract_state.abstract_object_factory(param.type(), ns, true, false),
123  ns);
124  }
125 
126  break;
127  }
128 
129  /***************************************************************/
130 
131  case ASSERT:
132  // Conditions on the program, do not alter the data or information
133  // flow and thus can be ignored.
134  // Checking of assertions can only be reasonably done once the fix-point
135  // has been computed, i.e. after all of the calls to transform.
136  break;
137 
138  case SKIP:
139  case LOCATION:
140  // Can ignore
141  break;
142 
143  case SET_RETURN_VALUE:
144  throw "the SET_RETURN_VALUE instructions should be removed first";
145 
146  case START_THREAD:
147  case END_THREAD:
148  case ATOMIC_BEGIN:
149  case ATOMIC_END:
150  throw "threading not supported";
151 
152  case THROW:
153  case CATCH:
154  throw "exceptions not handled";
155 
156  case OTHER:
157  // throw "other";
158  break;
159 
160  case NO_INSTRUCTION_TYPE:
161  break;
162  case INCOMPLETE_GOTO:
163  break;
164  default:
165  throw "unrecognised instruction type";
166  }
167 
168  DATA_INVARIANT(abstract_state.verify(), "Structural invariant");
169 }
170 
172  std::ostream &out,
173  const ai_baset &ai,
174  const namespacet &ns) const
175 {
176  abstract_state.output(out, ai, ns);
177 }
178 
180 {
181  return abstract_state.to_predicate();
182 }
183 
185  const exprt &expr,
186  const namespacet &ns) const
187 {
188  auto result = abstract_state.eval(expr, ns);
189  return result->to_predicate(expr);
190 }
191 
193  const exprt::operandst &exprs,
194  const namespacet &ns) const
195 {
196  if(exprs.empty())
197  return false_exprt();
198  if(exprs.size() == 1)
199  return to_predicate(exprs.front(), ns);
200 
201  auto predicates = std::vector<exprt>{};
203  exprs.cbegin(),
204  exprs.cend(),
205  std::back_inserter(predicates),
206  [this, &ns](const exprt &expr) { return to_predicate(expr, ns); });
207  return and_exprt(predicates);
208 }
209 
211 {
213  return;
214 }
215 
217 {
219 }
220 
223  trace_ptrt from,
224  trace_ptrt to)
225 {
226 #ifdef DEBUG
227  std::cout << "Merging from/to:\n "
228  << from->current_location()->location_number << " --> "
229  << to->current_location()->location_number << '\n';
230 #endif
231  auto widen_mode =
232  from->should_widen(*to) ? widen_modet::could_widen : widen_modet::no;
233  // Use the abstract_environment merge
234  bool any_changes =
235  abstract_state.merge(b.abstract_state, to->current_location(), widen_mode);
236 
237  DATA_INVARIANT(abstract_state.verify(), "Structural invariant");
238  return any_changes;
239 }
240 
242  exprt &condition,
243  const namespacet &ns) const
244 {
245  abstract_object_pointert res = abstract_state.eval(condition, ns);
246  exprt c = res->to_constant();
247 
248  if(c.id() == ID_nil)
249  {
250  bool no_simplification = true;
251 
252  // Try to simplify recursively any child operations
253  for(exprt &op : condition.operands())
254  {
255  no_simplification &= ai_simplify(op, ns);
256  }
257 
258  return no_simplification;
259  }
260  else
261  {
262  bool condition_changed = (condition != c);
263  condition = c;
264  return !condition_changed;
265  }
266 }
267 
269 {
270  return abstract_state.is_bottom();
271 }
272 
274 {
275  return abstract_state.is_top();
276 }
277 
279  locationt from,
280  locationt to,
281  ai_baset &ai,
282  const namespacet &ns)
283 {
284  PRECONDITION(from->type() == FUNCTION_CALL);
285 
286  const exprt &function = from->call_function();
287 
288  const locationt next = std::next(from);
289 
290  if(function.id() == ID_symbol)
291  {
292  // called function identifier
293  const symbol_exprt &symbol_expr = to_symbol_expr(function);
294  const irep_idt function_id = symbol_expr.get_identifier();
295 
296  const code_function_callt::argumentst &called_arguments =
297  from->call_arguments();
298 
299  if(to->location_number == next->location_number)
300  {
301  if(ignore_function_call_transform(function_id))
302  {
303  return;
304  }
305 
306  if(0) // Sound on opaque function calls
307  {
308  abstract_state.havoc("opaque function call");
309  }
310  else
311  {
312  // For any parameter that is a non-const pointer, top the value it is
313  // pointing at.
314  for(const exprt &called_arg : called_arguments)
315  {
316  if(
317  called_arg.type().id() == ID_pointer &&
318  !to_pointer_type(called_arg.type())
319  .base_type()
320  .get_bool(ID_C_constant))
321  {
322  abstract_object_pointert pointer_value =
323  abstract_state.eval(called_arg, ns);
324 
325  CHECK_RETURN(pointer_value);
326 
327  // Write top to the pointer
328  pointer_value->write(
330  ns,
331  std::stack<exprt>(),
332  nil_exprt(),
334  to_pointer_type(called_arg.type()).base_type(),
335  ns,
336  true,
337  false),
338  false);
339  }
340  }
341 
342  // Top any global values
343  for(const auto &symbol : ns.get_symbol_table().symbols)
344  {
345  if(symbol.second.is_static_lifetime)
346  {
348  symbol_exprt(symbol.first, symbol.second.type),
350  symbol.second.type, ns, true, false),
351  ns);
352  }
353  }
354  }
355  }
356  else
357  {
358  // we have an actual call
359  const symbolt &symbol = ns.lookup(function_id);
360  const code_typet &code_type = to_code_type(symbol.type);
361  const code_typet::parameterst &declaration_parameters =
362  code_type.parameters();
363 
364  code_typet::parameterst::const_iterator parameter_it =
365  declaration_parameters.begin();
366 
367  for(const exprt &called_arg : called_arguments)
368  {
369  if(parameter_it == declaration_parameters.end())
370  {
371  INVARIANT(
372  code_type.has_ellipsis(), "Only case for insufficient args");
373  break;
374  }
375 
376  // Evaluate the expression that is being
377  // passed into the function call (called_arg)
378  abstract_object_pointert param_val =
379  abstract_state.eval(called_arg, ns)->write_location_context(from);
380 
381  // Assign the evaluated value to the symbol associated with the
382  // parameter of the function
383  const symbol_exprt parameter_expr(
384  parameter_it->get_identifier(), called_arg.type());
385  abstract_state.assign(parameter_expr, param_val, ns);
386 
387  ++parameter_it;
388  }
389 
390  // Too few arguments so invalid code
392  parameter_it == declaration_parameters.end(),
393  "Number of arguments should match parameters");
394  }
395  }
396  else
397  {
398  PRECONDITION(to == next);
399  abstract_state.havoc("unknown opaque function call");
400  }
401 }
402 
404  const irep_idt &function_id) const
405 {
406  static const std::set<irep_idt> ignored_internal_function = {
407  CPROVER_PREFIX "set_must",
408  CPROVER_PREFIX "get_must",
409  CPROVER_PREFIX "set_may",
410  CPROVER_PREFIX "get_may",
411  CPROVER_PREFIX "cleanup",
412  CPROVER_PREFIX "clear_may",
413  CPROVER_PREFIX "clear_must"};
414 
415  return ignored_internal_function.find(function_id) !=
416  ignored_internal_function.cend();
417 }
418 
420  const ai_domain_baset &function_start,
421  const ai_domain_baset &function_end,
422  const namespacet &ns)
423 {
424  const variable_sensitivity_domaint &cast_function_start =
425  static_cast<const variable_sensitivity_domaint &>(function_start);
426 
427  const variable_sensitivity_domaint &cast_function_end =
428  static_cast<const variable_sensitivity_domaint &>(function_end);
429 
430  const std::vector<irep_idt> &modified_symbol_names =
432  cast_function_start.abstract_state, cast_function_end.abstract_state);
433 
434  std::vector<symbol_exprt> modified_symbols;
435  modified_symbols.reserve(modified_symbol_names.size());
437  modified_symbol_names.begin(),
438  modified_symbol_names.end(),
439  std::back_inserter(modified_symbols),
440  [&ns](const irep_idt &id) { return ns.lookup(id).symbol_expr(); });
441 
442  for(const auto &symbol : modified_symbols)
443  {
445  cast_function_end.abstract_state.eval(symbol, ns);
446  abstract_state.assign(symbol, value, ns);
447  }
448 
449  return;
450 }
451 
453 {
454  ai_simplify(expr, ns);
455  abstract_state.assume(expr, ns);
456 }
457 
458 #ifdef ENABLE_STATS
460 variable_sensitivity_domaint::gather_statistics(const namespacet &ns) const
461 {
463 }
464 #endif
sharing_ptrt< class abstract_objectt > abstract_object_pointert
static abstract_object_pointert transform(const exprt &expr, const std::vector< abstract_object_pointert > &operands, const abstract_environmentt &environment, const namespacet &ns)
virtual bool assume(const exprt &expr, const namespacet &ns)
Reduces the domain based on a condition.
void output(std::ostream &out, const class ai_baset &ai, const namespacet &ns) const
Print out all the values in the abstract object map.
bool is_bottom() const
Gets whether the domain is bottom.
exprt to_predicate() const
Gives a boolean condition that is true for all values represented by the environment.
void make_top()
Set the domain to top (i.e. everything)
virtual abstract_object_pointert eval(const exprt &expr, const namespacet &ns) const
These three are really the heart of the method.
void erase(const symbol_exprt &expr)
Delete a symbol from the map.
static std::vector< abstract_environmentt::map_keyt > modified_symbols(const abstract_environmentt &first, const abstract_environmentt &second)
For our implementation of variable sensitivity domains, we need to be able to efficiently find symbol...
bool verify() const
Check the structural invariants are maintained.
virtual void havoc(const std::string &havoc_string)
This should be used as a default case / everything else has failed The string is so that I can easily...
abstract_object_statisticst gather_statistics(const namespacet &ns) const
virtual bool merge(const abstract_environmentt &env, const goto_programt::const_targett &merge_location, widen_modet widen_mode)
Computes the join between "this" and "b".
void make_bottom()
Set the domain to top (i.e. no possible states / unreachable)
bool is_top() const
Gets whether the domain is top.
virtual abstract_object_pointert abstract_object_factory(const typet &type, const namespacet &ns, bool top, bool bottom) const
Look at the configuration for the sensitivity and create an appropriate abstract_object.
virtual bool assign(const exprt &expr, const abstract_object_pointert &value, const namespacet &ns)
Assign a value to an expression.
This is the basic interface of the abstract interpreter with default implementations of the core func...
Definition: ai.h:117
The interface offered by a domain, allows code to manipulate domains without knowing their exact type...
Definition: ai_domain.h:54
ai_history_baset::trace_ptrt trace_ptrt
Definition: ai_domain.h:73
goto_programt::const_targett locationt
Definition: ai_domain.h:72
Boolean AND.
Definition: std_expr.h:2125
exprt::operandst argumentst
Base type of functions.
Definition: std_types.h:583
std::vector< parametert > parameterst
Definition: std_types.h:585
bool has_ellipsis() const
Definition: std_types.h:655
const parameterst & parameters() const
Definition: std_types.h:699
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
typet & type()
Return the type of the expression.
Definition: expr.h:84
operandst & operands()
Definition: expr.h:94
The Boolean constant false.
Definition: std_expr.h:3082
This class represents an instruction in the GOTO intermediate representation.
Definition: goto_program.h:181
const exprt & condition() const
Get the condition of gotos, assume, assert.
Definition: goto_program.h:366
const exprt & assign_rhs() const
Get the rhs of the assignment for ASSIGN.
Definition: goto_program.h:214
const symbol_exprt & dead_symbol() const
Get the symbol for DEAD.
Definition: goto_program.h:244
const symbol_exprt & decl_symbol() const
Get the declared symbol for DECL.
Definition: goto_program.h:228
const exprt & assign_lhs() const
Get the lhs of the assignment for ASSIGN.
Definition: goto_program.h:200
goto_program_instruction_typet type() const
What kind of instruction?
Definition: goto_program.h:344
bool get_bool(const irep_idt &name) const
Definition: irep.cpp:57
const irep_idt & id() const
Definition: irep.h:388
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:94
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
Definition: namespace.cpp:148
const symbol_table_baset & get_symbol_table() const
Return first symbol table registered with the namespace.
Definition: namespace.h:126
The NIL expression.
Definition: std_expr.h:3091
Boolean negation.
Definition: std_expr.h:2337
const typet & base_type() const
The type of the data what we point to.
Definition: pointer_expr.h:35
Expression to hold a symbol (variable)
Definition: std_expr.h:131
const irep_idt & get_identifier() const
Definition: std_expr.h:160
const symbolst & symbols
Read-only field, used to look up symbols given their names.
Symbol table entry.
Definition: symbol.h:28
typet type
Type of symbol.
Definition: symbol.h:31
bool ai_simplify(exprt &condition, const namespacet &ns) const override
Use the information in the domain to simplify the expression with respect to the current location.
exprt to_predicate() const override
Gives a Boolean condition that is true for all values represented by the domain.
void make_bottom() override
Sets the domain to bottom (no states / unreachable).
void output(std::ostream &out, const ai_baset &ai, const namespacet &ns) const override
Basic text output of the abstract domain.
void make_top() override
Sets the domain to top (all states).
void transform(const irep_idt &function_from, trace_ptrt trace_from, const irep_idt &function_to, trace_ptrt trace_to, ai_baset &ai, const namespacet &ns) override
Compute the abstract transformer for a single instruction.
virtual void merge_three_way_function_return(const ai_domain_baset &function_start, const ai_domain_baset &function_end, const namespacet &ns)
Merges just the things that have changes between "function_start" and "function_end" into "this".
bool is_bottom() const override
Find out if the domain is currently unreachable.
void transform_function_call(locationt from, locationt to, ai_baset &ai, const namespacet &ns)
Used by variable_sensitivity_domaint::transform to handle FUNCTION_CALL transforms.
virtual bool merge(const variable_sensitivity_domaint &b, trace_ptrt from, trace_ptrt to)
Computes the join between "this" and "b".
void assume(exprt expr, namespacet ns)
bool is_top() const override
Is the domain completely top at this state.
bool ignore_function_call_transform(const irep_idt &function_id) const
Used to specify which CPROVER internal functions should be skipped over when doing function call tran...
#define CPROVER_PREFIX
@ FUNCTION_CALL
Definition: goto_program.h:49
@ ATOMIC_END
Definition: goto_program.h:44
@ DEAD
Definition: goto_program.h:48
@ END_FUNCTION
Definition: goto_program.h:42
@ ASSIGN
Definition: goto_program.h:46
@ ASSERT
Definition: goto_program.h:36
@ SET_RETURN_VALUE
Definition: goto_program.h:45
@ ATOMIC_BEGIN
Definition: goto_program.h:43
@ CATCH
Definition: goto_program.h:51
@ END_THREAD
Definition: goto_program.h:40
@ SKIP
Definition: goto_program.h:38
@ NO_INSTRUCTION_TYPE
Definition: goto_program.h:33
@ START_THREAD
Definition: goto_program.h:39
@ THROW
Definition: goto_program.h:50
@ DECL
Definition: goto_program.h:47
@ OTHER
Definition: goto_program.h:37
@ GOTO
Definition: goto_program.h:34
@ INCOMPLETE_GOTO
Definition: goto_program.h:52
API to expression classes for Pointers.
const pointer_typet & to_pointer_type(const typet &type)
Cast a typet to a pointer_typet.
Definition: pointer_expr.h:93
#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
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition: std_expr.h:272
const code_typet & to_code_type(const typet &type)
Cast a typet to a code_typet.
Definition: std_types.h:788
Author: Diffblue Ltd.
There are different ways of handling arrays, structures, unions and pointers.