CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
variable_sensitivity_domain.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Abstract Interpretation
4
5Author: Martin Brain
6
7Date: April 2016
8
9\*******************************************************************/
10
12
13#include <util/cprover_prefix.h>
14#include <util/namespace.h>
15#include <util/pointer_expr.h>
17
18#include <algorithm>
19
20#ifdef DEBUG
21# include <iostream>
22#endif
23
27 const irep_idt &function_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 {
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 {
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
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
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>{};
202 std::transform(
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
215
220
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 =
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{
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 {
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
272
274{
275 return abstract_state.is_top();
276}
277
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
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 &&
319 .base_type()
320 .get_bool(ID_C_constant))
321 {
324
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);
362 code_type.parameters();
363
364 code_typet::parameterst::const_iterator parameter_it =
366
367 for(const exprt &called_arg : called_arguments)
368 {
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)
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
384 parameter_it->get_identifier(), called_arg.type());
386
387 ++parameter_it;
388 }
389
390 // Too few arguments so invalid code
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) !=
417}
418
422 const namespacet &ns)
423{
425 static_cast<const variable_sensitivity_domaint &>(function_start);
426
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());
436 std::transform(
437 modified_symbol_names.begin(),
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
460variable_sensitivity_domaint::gather_statistics(const namespacet &ns) const
461{
463}
464#endif
sharing_ptrt< class abstract_objectt > abstract_object_pointert
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
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Boolean AND.
Definition std_expr.h:2125
Base type of functions.
Definition std_types.h:583
std::vector< parametert > parameterst
Definition std_types.h:586
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
operandst & operands()
Definition expr.h:94
The Boolean constant false.
Definition std_expr.h:3199
This class represents an instruction in the GOTO intermediate representation.
const symbol_exprt & dead_symbol() const
Get the symbol for DEAD.
const symbol_exprt & decl_symbol() const
Get the declared symbol for DECL.
const exprt & assign_lhs() const
Get the lhs of the assignment for ASSIGN.
const exprt & assign_rhs() const
Get the rhs of the assignment for ASSIGN.
const exprt & condition() const
Get the condition of gotos, assume, assert.
goto_program_instruction_typet type() const
What kind of instruction?
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:91
bool lookup(const irep_idt &name, const symbolt *&symbol) const override
See documentation for namespace_baset::lookup().
const symbol_table_baset & get_symbol_table() const
Return first symbol table registered with the namespace.
Definition namespace.h:123
The NIL expression.
Definition std_expr.h:3208
Boolean negation.
Definition std_expr.h:2454
Expression to hold a symbol (variable)
Definition std_expr.h:131
const irep_idt & get_identifier() const
Definition std_expr.h:160
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
@ ATOMIC_END
@ DEAD
@ LOCATION
@ END_FUNCTION
@ ASSIGN
@ ASSERT
@ SET_RETURN_VALUE
@ ATOMIC_BEGIN
@ CATCH
@ END_THREAD
@ SKIP
@ NO_INSTRUCTION_TYPE
@ START_THREAD
@ THROW
@ DECL
@ OTHER
@ GOTO
@ INCOMPLETE_GOTO
@ ASSUME
API to expression classes for Pointers.
const pointer_typet & to_pointer_type(const typet &type)
Cast a typet to a pointer_typet.
#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
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
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.