CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
field_sensitivity.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Field-sensitive SSA
4
5Author: Michael Tautschnig
6
7\*******************************************************************/
8
9#include "field_sensitivity.h"
10
11#include <util/arith_tools.h>
12#include <util/byte_operators.h>
13#include <util/c_types.h>
15#include <util/simplify_expr.h>
16
17#include "goto_symex_state.h"
18#include "symex_target.h"
19
20#define ENABLE_ARRAY_FIELD_SENSITIVITY
21
23 const namespacet &ns,
24 goto_symex_statet &state,
26 bool write) const
27{
28 if(write)
29 return std::move(ssa_expr);
30 else
31 return get_fields(ns, state, ssa_expr, true);
32}
33
35 const namespacet &ns,
36 goto_symex_statet &state,
37 exprt expr,
38 bool write) const
39{
40 if(expr.id() != ID_address_of)
41 {
42 Forall_operands(it, expr)
43 *it = apply(ns, state, std::move(*it), write);
44 }
45
46 if(!write && is_ssa_expr(expr))
47 {
48 return get_fields(ns, state, to_ssa_expr(expr), true);
49 }
50 else if(
51 !write && expr.id() == ID_member &&
52 to_member_expr(expr).struct_op().id() == ID_struct)
53 {
54 return simplify_opt(std::move(expr), ns);
55 }
56#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
57 else if(
58 !write && expr.id() == ID_index &&
59 to_index_expr(expr).array().id() == ID_array)
60 {
61 return simplify_opt(std::move(expr), ns);
62 }
63#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
64 else if(expr.id() == ID_member)
65 {
66 // turn a member-of-an-SSA-expression into a single SSA expression, thus
67 // encoding the member as an individual symbol rather than only the full
68 // struct
69 member_exprt &member = to_member_expr(expr);
70
71 if(
72 is_ssa_expr(member.struct_op()) &&
73 (member.struct_op().type().id() == ID_struct ||
74 member.struct_op().type().id() == ID_struct_tag ||
75 member.struct_op().type().id() == ID_union ||
76 member.struct_op().type().id() == ID_union_tag))
77 {
78 // place the entire member expression, not just the struct operand, in an
79 // SSA expression
81 bool was_l2 = !tmp.get_level_2().empty();
82
83 tmp.remove_level_2();
84 member.struct_op() = tmp.get_original_expr();
85 tmp.set_expression(member);
86 if(was_l2)
87 return apply(ns, state, state.rename(std::move(tmp), ns).get(), write);
88 else
89 return apply(ns, state, std::move(tmp), write);
90 }
91 }
92 else if(
93 !write && (expr.id() == ID_byte_extract_little_endian ||
95 {
97 if(
98 (be.op().type().id() == ID_union ||
99 be.op().type().id() == ID_union_tag) &&
100 is_ssa_expr(be.op()) && be.offset().is_constant())
101 {
102 const union_typet &type =
103 be.op().type().id() == ID_union_tag
104 ? ns.follow_tag(to_union_tag_type(be.op().type()))
105 : to_union_type(be.op().type());
106 for(const auto &comp : type.components())
107 {
108 ssa_exprt tmp = to_ssa_expr(be.op());
109 bool was_l2 = !tmp.get_level_2().empty();
110 tmp.remove_level_2();
111 const member_exprt member{tmp.get_original_expr(), comp};
112 auto recursive_member =
113 get_subexpression_at_offset(member, be.offset(), be.type(), ns);
114 if(
115 recursive_member.has_value() &&
116 (recursive_member->id() == ID_member ||
117 recursive_member->id() == ID_index))
118 {
119 tmp.type() = be.type();
120 tmp.set_expression(*recursive_member);
121 if(was_l2)
122 {
123 return apply(
124 ns, state, state.rename(std::move(tmp), ns).get(), write);
125 }
126 else
127 return apply(ns, state, std::move(tmp), write);
128 }
129 else if(
130 recursive_member.has_value() && recursive_member->id() == ID_typecast)
131 {
132 if(was_l2)
133 {
134 return apply(
135 ns,
136 state,
137 state.rename(std::move(*recursive_member), ns).get(),
138 write);
139 }
140 else
141 return apply(ns, state, std::move(*recursive_member), write);
142 }
143 }
144 }
145 }
146#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
147 else if(expr.id() == ID_index)
148 {
149 // turn a index-of-an-SSA-expression into a single SSA expression, thus
150 // encoding the index access into an array as an individual symbol rather
151 // than only the full array
152 index_exprt &index = to_index_expr(expr);
154 simplify(index.index(), ns);
155
156 if(
157 is_ssa_expr(index.array()) && index.array().type().id() == ID_array &&
158 index.index().is_constant())
159 {
160 // place the entire index expression, not just the array operand, in an
161 // SSA expression
162 ssa_exprt tmp = to_ssa_expr(index.array());
163 auto l2_index = state.rename(index.index(), ns);
165 l2_index.simplify(ns);
166 bool was_l2 = !tmp.get_level_2().empty();
167 exprt l2_size =
168 state.rename(to_array_type(index.array().type()).size(), ns).get();
169 if(l2_size.is_nil() && index.array().id() == ID_symbol)
170 {
171 // In case the array type was incomplete, attempt to retrieve it from
172 // the symbol table.
174 to_symbol_expr(index.array()).get_identifier());
175 if(array_from_symbol_table != nullptr)
177 }
178
179 if(
180 l2_size.is_constant() &&
183 {
184 if(l2_index.get().is_constant())
185 {
186 // place the entire index expression, not just the array operand,
187 // in an SSA expression
189 ssa_array.remove_level_2();
190 index.array() = ssa_array.get_original_expr();
191 index.index() = l2_index.get();
192 tmp.set_expression(index);
193 if(was_l2)
194 {
195 return apply(
196 ns, state, state.rename(std::move(tmp), ns).get(), write);
197 }
198 else
199 return apply(ns, state, std::move(tmp), write);
200 }
201 else if(!write)
202 {
203 // Expand the array and return `{array[0]; array[1]; ...}[index]`
205 get_fields(ns, state, to_ssa_expr(index.array()), true);
206 return index_exprt{std::move(expanded_array), index.index()};
207 }
208 }
209 }
210 }
211#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
212 return expr;
213}
214
216 const namespacet &ns,
217 goto_symex_statet &state,
218 const ssa_exprt &ssa_expr,
219 bool disjoined_fields_only) const
220{
221 if(
222 ssa_expr.type().id() == ID_struct ||
223 ssa_expr.type().id() == ID_struct_tag ||
224 (!disjoined_fields_only && (ssa_expr.type().id() == ID_union ||
225 ssa_expr.type().id() == ID_union_tag)))
226 {
227 const struct_union_typet::componentst &components =
228 (ssa_expr.type().id() == ID_struct_tag ||
229 ssa_expr.type().id() == ID_union_tag)
231 .components()
232 : to_struct_union_type(ssa_expr.type()).components();
233
234 exprt::operandst fields;
235 fields.reserve(components.size());
236
237 const exprt &compound_op = ssa_expr.get_original_expr();
238
239 for(const auto &comp : components)
240 {
242 bool was_l2 = !tmp.get_level_2().empty();
243 tmp.remove_level_2();
244 tmp.set_expression(
245 member_exprt{compound_op, comp.get_name(), comp.type()});
247 if(was_l2)
248 {
249 fields.emplace_back(state.rename(std::move(field), ns).get());
250 }
251 else
252 fields.emplace_back(std::move(field));
253 }
254
255 if(
256 disjoined_fields_only && (ssa_expr.type().id() == ID_struct ||
257 ssa_expr.type().id() == ID_struct_tag))
258 {
259 return struct_exprt{std::move(fields), ssa_expr.type()};
260 }
261 else
262 return field_sensitive_ssa_exprt{ssa_expr, std::move(fields)};
263 }
264#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
265 else if(
266 ssa_expr.type().id() == ID_array &&
267 to_array_type(ssa_expr.type()).size().is_constant())
268 {
270 to_constant_expr(to_array_type(ssa_expr.type()).size()));
272 return ssa_expr;
273
274 const array_typet &type = to_array_type(ssa_expr.type());
276
277 array_exprt::operandst elements;
278 elements.reserve(array_size);
279
280 const exprt &array = ssa_expr.get_original_expr();
281
282 for(std::size_t i = 0; i < array_size; ++i)
283 {
284 const index_exprt index(array, from_integer(i, type.index_type()));
286 bool was_l2 = !tmp.get_level_2().empty();
287 tmp.remove_level_2();
288 tmp.set_expression(index);
289 exprt element = get_fields(ns, state, tmp, disjoined_fields_only);
290 if(was_l2)
291 {
292 elements.emplace_back(state.rename(std::move(element), ns).get());
293 }
294 else
295 elements.emplace_back(std::move(element));
296 }
297
299 return array_exprt(std::move(elements), type);
300 else
301 return field_sensitive_ssa_exprt{ssa_expr, std::move(elements)};
302 }
303#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
304 else
305 return ssa_expr;
306}
307
309 const namespacet &ns,
310 goto_symex_statet &state,
311 const ssa_exprt &lhs,
312 const exprt &rhs,
313 symex_targett &target,
314 bool allow_pointer_unsoundness) const
315{
316 const exprt lhs_fs = get_fields(ns, state, lhs, false);
317
318 if(lhs != lhs_fs)
319 {
321 ns, state, lhs_fs, rhs, target, allow_pointer_unsoundness);
322 // Erase the composite symbol from our working state. Note that we need to
323 // have it in the propagation table and the value set while doing the field
324 // assignments, thus we cannot skip putting it in there above.
325 if(is_divisible(lhs, true))
326 {
327 state.propagation.erase_if_exists(lhs.get_identifier());
328 state.value_set.erase_symbol(lhs, ns);
329 }
330 }
331}
332
344 const namespacet &ns,
345 goto_symex_statet &state,
346 const exprt &lhs_fs,
347 const exprt &ssa_rhs,
348 symex_targett &target,
349 bool allow_pointer_unsoundness) const
350{
352 {
354 const ssa_exprt ssa_lhs =
355 state
356 .assignment(l1_lhs, ssa_rhs, ns, true, true, allow_pointer_unsoundness)
357 .get();
358
359 // do the assignment
360 target.assignment(
361 state.guard.as_expr(),
362 ssa_lhs,
363 ssa_lhs,
364 ssa_lhs.get_original_expr(),
365 ssa_rhs,
366 state.source,
368 // Erase the composite symbol from our working state. Note that we need to
369 // have it in the propagation table and the value set while doing the field
370 // assignments, thus we cannot skip putting it in there above.
371 if(is_divisible(l1_lhs, true))
372 {
373 state.propagation.erase_if_exists(l1_lhs.get_identifier());
374 state.value_set.erase_symbol(l1_lhs, ns);
375 }
376 }
377 else if(
378 ssa_rhs.type().id() == ID_struct || ssa_rhs.type().id() == ID_struct_tag)
379 {
380 const struct_typet &type =
381 ssa_rhs.type().id() == ID_struct_tag
382 ? ns.follow_tag(to_struct_tag_type(ssa_rhs.type()))
383 : to_struct_type(ssa_rhs.type());
384 const struct_union_typet::componentst &components = type.components();
385
387 components.empty() ||
388 (lhs_fs.has_operands() && lhs_fs.operands().size() == components.size()));
389
390 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
391 for(const auto &comp : components)
392 {
393 const exprt member_rhs = apply(
394 ns,
395 state,
396 simplify_opt(member_exprt{ssa_rhs, comp.get_name(), comp.type()}, ns),
397 false);
398
399 const exprt &member_lhs = *fs_it;
400 if(
401 auto fs_ssa =
403 {
405 ns,
406 state,
407 fs_ssa->get_object_ssa(),
409 target,
410 allow_pointer_unsoundness);
411 }
412
414 ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
415 ++fs_it;
416 }
417 }
418 else if(
419 ssa_rhs.type().id() == ID_union || ssa_rhs.type().id() == ID_union_tag)
420 {
421 const union_typet &type =
422 ssa_rhs.type().id() == ID_union_tag
423 ? ns.follow_tag(to_union_tag_type(ssa_rhs.type()))
424 : to_union_type(ssa_rhs.type());
425 const struct_union_typet::componentst &components = type.components();
426
428 components.empty() ||
429 (lhs_fs.has_operands() && lhs_fs.operands().size() == components.size()));
430
431 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
432 for(const auto &comp : components)
433 {
434 const exprt member_rhs = apply(
435 ns,
436 state,
439 ssa_rhs, from_integer(0, c_index_type()), comp.type()),
440 ns),
441 false);
442
443 const exprt &member_lhs = *fs_it;
444 if(
445 auto fs_ssa =
447 {
449 ns,
450 state,
451 fs_ssa->get_object_ssa(),
453 target,
454 allow_pointer_unsoundness);
455 }
456
458 ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
459 ++fs_it;
460 }
461 }
462#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
463 else if(const auto &type = type_try_dynamic_cast<array_typet>(ssa_rhs.type()))
464 {
465 const std::size_t array_size =
467 PRECONDITION(lhs_fs.operands().size() == array_size);
468
470 return;
471
472 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
473 for(std::size_t i = 0; i < array_size; ++i)
474 {
475 const exprt index_rhs = apply(
476 ns,
477 state,
479 index_exprt{ssa_rhs, from_integer(i, type->index_type())}, ns),
480 false);
481
482 const exprt &index_lhs = *fs_it;
483 if(
484 auto fs_ssa =
486 {
488 ns,
489 state,
490 fs_ssa->get_object_ssa(),
491 index_rhs,
492 target,
493 allow_pointer_unsoundness);
494 }
495
497 ns, state, index_lhs, index_rhs, target, allow_pointer_unsoundness);
498 ++fs_it;
499 }
500 }
501#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
502 else if(lhs_fs.has_operands())
503 {
505 ssa_rhs.has_operands() &&
506 lhs_fs.operands().size() == ssa_rhs.operands().size());
507
508 exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
509 for(const exprt &op : ssa_rhs.operands())
510 {
512 {
514 ns,
515 state,
516 fs_ssa->get_object_ssa(),
517 op,
518 target,
519 allow_pointer_unsoundness);
520 }
521
523 ns, state, *fs_it, op, target, allow_pointer_unsoundness);
524 ++fs_it;
525 }
526 }
527 else
528 {
530 }
531}
532
534 const ssa_exprt &expr,
535 bool disjoined_fields_only) const
536{
537 if(expr.type().id() == ID_struct || expr.type().id() == ID_struct_tag)
538 return true;
539
540#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
541 if(
542 expr.type().id() == ID_array &&
543 to_array_type(expr.type()).size().is_constant() &&
546 {
547 return true;
548 }
549#endif
550
551 if(
553 (expr.type().id() == ID_union || expr.type().id() == ID_union_tag))
554 {
555 return true;
556 }
557
558 return false;
559}
560
562{
563 if(!should_simplify)
564 return e;
565
566 return simplify_expr(std::move(e), ns);
567}
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
byte_extract_exprt make_byte_extract(const exprt &_op, const exprt &_offset, const typet &_type)
Construct a byte_extract_exprt with endianness and byte width matching the current configuration.
Expression classes for byte-level operators.
const byte_extract_exprt & to_byte_extract_expr(const exprt &expr)
bitvector_typet c_index_type()
Definition c_types.cpp:16
const union_typet & to_union_type(const typet &type)
Cast a typet to a union_typet.
Definition c_types.h:184
const union_tag_typet & to_union_tag_type(const typet &type)
Cast a typet to a union_tag_typet.
Definition c_types.h:224
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Array constructor from list of elements.
Definition std_expr.h:1621
Arrays with given size.
Definition std_types.h:807
typet index_type() const
The type of the index expressions into any instance of this type.
Definition std_types.cpp:34
Expression of type type extracted from some object op starting at position offset (given in number of...
Base class for all expressions.
Definition expr.h:56
std::vector< exprt > operandst
Definition expr.h:58
bool has_operands() const
Return true if there is at least one operand.
Definition expr.h:91
bool is_constant() const
Return whether the expression is a constant.
Definition expr.h:212
typet & type()
Return the type of the expression.
Definition expr.h:84
operandst & operands()
Definition expr.h:94
exprt simplify_opt(exprt e, const namespacet &ns) const
const std::size_t max_field_sensitivity_array_size
exprt get_fields(const namespacet &ns, goto_symex_statet &state, const ssa_exprt &ssa_expr, bool disjoined_fields_only) const
Compute an expression representing the individual components of a field-sensitive SSA representation ...
void field_assignments_rec(const namespacet &ns, goto_symex_statet &state, const exprt &lhs_fs, const exprt &ssa_rhs, symex_targett &target, bool allow_pointer_unsoundness) const
Assign to the individual fields lhs_fs of a non-expanded symbol lhs.
exprt apply(const namespacet &ns, goto_symex_statet &state, exprt expr, bool write) const
Turn an expression expr into a field-sensitive SSA expression.
void field_assignments(const namespacet &ns, goto_symex_statet &state, const ssa_exprt &lhs, const exprt &rhs, symex_targett &target, bool allow_pointer_unsoundness) const
Assign to the individual fields of a non-expanded symbol lhs.
bool is_divisible(const ssa_exprt &expr, bool disjoined_fields_only) const
Determine whether expr would translate to an atomic SSA expression (returns false) or a composite obj...
guardt guard
Definition goto_state.h:58
sharing_mapt< irep_idt, exprt > propagation
Definition goto_state.h:71
value_sett value_set
Uses level 1 names, and is used to do dereferencing.
Definition goto_state.h:51
Central data structure: state.
renamedt< ssa_exprt, L2 > assignment(ssa_exprt lhs, const exprt &rhs, const namespacet &ns, bool rhs_is_simplified, bool record_value, bool allow_pointer_unsoundness=false)
renamedt< exprt, level > rename(exprt expr, const namespacet &ns)
Rewrites symbol expressions in exprt, applying a suffix to each symbol reflecting its most recent ver...
symex_targett::sourcet source
exprt as_expr() const
Definition guard_expr.h:46
Array index operator.
Definition std_expr.h:1470
exprt & index()
Definition std_expr.h:1510
exprt & array()
Definition std_expr.h:1500
const irep_idt & id() const
Definition irep.h:388
Extract member of struct or union.
Definition std_expr.h:2971
const exprt & struct_op() const
Definition std_expr.h:3009
const union_typet & follow_tag(const union_tag_typet &) const
Follow type tag of union type.
Definition namespace.cpp:49
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
const symbol_table_baset & get_symbol_table() const
Return first symbol table registered with the namespace.
Definition namespace.h:123
Expression providing an SSA-renamed symbol of expressions.
Definition ssa_expr.h:17
const exprt & get_original_expr() const
Definition ssa_expr.h:33
Struct constructor from list of elements.
Definition std_expr.h:1877
Structure type, corresponds to C style structs.
Definition std_types.h:231
const componentst & components() const
Definition std_types.h:147
std::vector< componentt > componentst
Definition std_types.h:140
const irep_idt & get_identifier() const
Definition std_expr.h:160
Symbol table entry.
Definition symbol.h:28
The interface of the target container for symbolic execution to record its symbolic steps into.
virtual void assignment(const exprt &guard, const ssa_exprt &ssa_lhs, const exprt &ssa_full_lhs, const exprt &original_full_lhs, const exprt &ssa_rhs, const sourcet &source, assignment_typet assignment_type)=0
Write to a local variable.
The union type.
Definition c_types.h:147
void erase_symbol(const symbol_exprt &symbol_expr, const namespacet &ns)
#define Forall_operands(it, expr)
Definition expr.h:27
Symbolic Execution.
std::optional< exprt > get_subexpression_at_offset(const exprt &expr, const mp_integer &offset_bytes, const typet &target_type_raw, const namespacet &ns)
Pointer Logic.
bool simplify(exprt &expr, const namespacet &ns)
exprt simplify_expr(exprt src, const namespacet &ns)
#define UNREACHABLE
This should be used to mark dead code.
Definition invariant.h:525
#define PRECONDITION(CONDITION)
Definition invariant.h:463
bool is_ssa_expr(const exprt &expr)
Definition ssa_expr.h:125
const ssa_exprt & to_ssa_expr(const exprt &expr)
Cast a generic exprt to an ssa_exprt.
Definition ssa_expr.h:145
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition std_expr.h:1538
const member_exprt & to_member_expr(const exprt &expr)
Cast an exprt to a member_exprt.
Definition std_expr.h:3063
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
Definition std_expr.h:3172
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:272
const struct_typet & to_struct_type(const typet &type)
Cast a typet to a struct_typet.
Definition std_types.h:308
const struct_tag_typet & to_struct_tag_type(const typet &type)
Cast a typet to a struct_tag_typet.
Definition std_types.h:518
const struct_or_union_tag_typet & to_struct_or_union_tag_type(const typet &type)
Cast a typet to a struct_or_union_tag_typet.
Definition std_types.h:478
const array_typet & to_array_type(const typet &type)
Cast a typet to an array_typet.
Definition std_types.h:888
const struct_union_typet & to_struct_union_type(const typet &type)
Cast a typet to a struct_union_typet.
Definition std_types.h:214
Generate Equation using Symbolic Execution.
ssize_t write(int fildes, const void *buf, size_t nbyte)
Definition unistd.c:195