CBMC
field_sensitivity.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Field-sensitive SSA
4 
5 Author: Michael Tautschnig
6 
7 \*******************************************************************/
8 
9 #include "field_sensitivity.h"
10 
11 #include <util/arith_tools.h>
12 #include <util/simplify_expr.h>
13 #include <util/std_expr.h>
14 
15 #include "goto_symex_state.h"
16 #include "symex_target.h"
17 
18 #define ENABLE_ARRAY_FIELD_SENSITIVITY
19 
21  const namespacet &ns,
22  goto_symex_statet &state,
23  ssa_exprt ssa_expr,
24  bool write) const
25 {
26  if(!run_apply || write)
27  return std::move(ssa_expr);
28  else
29  return get_fields(ns, state, ssa_expr);
30 }
31 
33  const namespacet &ns,
34  goto_symex_statet &state,
35  exprt expr,
36  bool write) const
37 {
38  if(!run_apply)
39  return expr;
40 
41  if(expr.id() != ID_address_of)
42  {
43  Forall_operands(it, expr)
44  *it = apply(ns, state, std::move(*it), write);
45  }
46 
47  if(!write && is_ssa_expr(expr))
48  {
49  return apply(ns, state, to_ssa_expr(expr), write);
50  }
51  else if(
52  !write && expr.id() == ID_member &&
53  to_member_expr(expr).struct_op().id() == ID_struct)
54  {
55  return simplify_opt(std::move(expr), ns);
56  }
57 #ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
58  else if(
59  !write && expr.id() == ID_index &&
60  to_index_expr(expr).array().id() == ID_array)
61  {
62  return simplify_opt(std::move(expr), ns);
63  }
64 #endif // ENABLE_ARRAY_FIELD_SENSITIVITY
65  else if(expr.id() == ID_member)
66  {
67  // turn a member-of-an-SSA-expression into a single SSA expression, thus
68  // encoding the member as an individual symbol rather than only the full
69  // struct
70  member_exprt &member = to_member_expr(expr);
71 
72  if(
73  is_ssa_expr(member.struct_op()) &&
74  (member.struct_op().type().id() == ID_struct ||
75  member.struct_op().type().id() == ID_struct_tag))
76  {
77  // place the entire member expression, not just the struct operand, in an
78  // SSA expression
79  ssa_exprt tmp = to_ssa_expr(member.struct_op());
80  bool was_l2 = !tmp.get_level_2().empty();
81 
82  tmp.remove_level_2();
83  member.struct_op() = tmp.get_original_expr();
84  tmp.set_expression(member);
85  if(was_l2)
86  return state.rename(std::move(tmp), ns).get();
87  else
88  return std::move(tmp);
89  }
90  }
91 #ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
92  else if(expr.id() == ID_index)
93  {
94  // turn a index-of-an-SSA-expression into a single SSA expression, thus
95  // encoding the index access into an array as an individual symbol rather
96  // than only the full array
97  index_exprt &index = to_index_expr(expr);
98  if(should_simplify)
99  simplify(index.index(), ns);
100 
101  if(
102  is_ssa_expr(index.array()) && index.array().type().id() == ID_array &&
103  index.index().id() == ID_constant)
104  {
105  // place the entire index expression, not just the array operand, in an
106  // SSA expression
107  ssa_exprt tmp = to_ssa_expr(index.array());
108  auto l2_index = state.rename(index.index(), ns);
109  if(should_simplify)
110  l2_index.simplify(ns);
111  bool was_l2 = !tmp.get_level_2().empty();
112  exprt l2_size =
113  state.rename(to_array_type(index.array().type()).size(), ns).get();
114  if(l2_size.is_nil() && index.array().id() == ID_symbol)
115  {
116  // In case the array type was incomplete, attempt to retrieve it from
117  // the symbol table.
118  const symbolt *array_from_symbol_table = ns.get_symbol_table().lookup(
119  to_symbol_expr(index.array()).get_identifier());
120  if(array_from_symbol_table != nullptr)
121  l2_size = to_array_type(array_from_symbol_table->type).size();
122  }
123 
124  if(
125  l2_size.id() == ID_constant &&
126  numeric_cast_v<mp_integer>(to_constant_expr(l2_size)) <=
128  {
129  if(l2_index.get().id() == ID_constant)
130  {
131  // place the entire index expression, not just the array operand,
132  // in an SSA expression
133  ssa_exprt ssa_array = to_ssa_expr(index.array());
134  ssa_array.remove_level_2();
135  index.array() = ssa_array.get_original_expr();
136  index.index() = l2_index.get();
137  tmp.set_expression(index);
138  if(was_l2)
139  return state.rename(std::move(tmp), ns).get();
140  else
141  return std::move(tmp);
142  }
143  else if(!write)
144  {
145  // Expand the array and return `{array[0]; array[1]; ...}[index]`
146  exprt expanded_array =
147  get_fields(ns, state, to_ssa_expr(index.array()));
148  return index_exprt{std::move(expanded_array), index.index()};
149  }
150  }
151  }
152  }
153 #endif // ENABLE_ARRAY_FIELD_SENSITIVITY
154  return expr;
155 }
156 
158  const namespacet &ns,
159  goto_symex_statet &state,
160  const ssa_exprt &ssa_expr) const
161 {
162  if(ssa_expr.type().id() == ID_struct || ssa_expr.type().id() == ID_struct_tag)
163  {
164  const struct_typet &type = to_struct_type(ns.follow(ssa_expr.type()));
165  const struct_union_typet::componentst &components = type.components();
166 
168  fields.reserve(components.size());
169 
170  const exprt &struct_op = ssa_expr.get_original_expr();
171 
172  for(const auto &comp : components)
173  {
174  ssa_exprt tmp = ssa_expr;
175  bool was_l2 = !tmp.get_level_2().empty();
176  tmp.remove_level_2();
177  tmp.set_expression(member_exprt{struct_op, comp.get_name(), comp.type()});
178  if(was_l2)
179  {
180  fields.push_back(state.rename(get_fields(ns, state, tmp), ns).get());
181  }
182  else
183  fields.push_back(get_fields(ns, state, tmp));
184  }
185 
186  return struct_exprt(std::move(fields), ssa_expr.type());
187  }
188 #ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
189  else if(
190  ssa_expr.type().id() == ID_array &&
191  to_array_type(ssa_expr.type()).size().id() == ID_constant)
192  {
193  const mp_integer mp_array_size = numeric_cast_v<mp_integer>(
194  to_constant_expr(to_array_type(ssa_expr.type()).size()));
195  if(mp_array_size < 0 || mp_array_size > max_field_sensitivity_array_size)
196  return ssa_expr;
197 
198  const array_typet &type = to_array_type(ssa_expr.type());
199  const std::size_t array_size = numeric_cast_v<std::size_t>(mp_array_size);
200 
201  array_exprt::operandst elements;
202  elements.reserve(array_size);
203 
204  const exprt &array = ssa_expr.get_original_expr();
205 
206  for(std::size_t i = 0; i < array_size; ++i)
207  {
208  const index_exprt index(array, from_integer(i, type.index_type()));
209  ssa_exprt tmp = ssa_expr;
210  bool was_l2 = !tmp.get_level_2().empty();
211  tmp.remove_level_2();
212  tmp.set_expression(index);
213  if(was_l2)
214  {
215  elements.push_back(state.rename(get_fields(ns, state, tmp), ns).get());
216  }
217  else
218  elements.push_back(get_fields(ns, state, tmp));
219  }
220 
221  return array_exprt(std::move(elements), type);
222  }
223 #endif // ENABLE_ARRAY_FIELD_SENSITIVITY
224  else
225  return ssa_expr;
226 }
227 
229  const namespacet &ns,
230  goto_symex_statet &state,
231  const ssa_exprt &lhs,
232  const exprt &rhs,
233  symex_targett &target,
234  bool allow_pointer_unsoundness)
235 {
236  const exprt lhs_fs = apply(ns, state, lhs, false);
237 
238  if(lhs != lhs_fs)
239  {
240  bool run_apply_bak = run_apply;
241  run_apply = false;
243  ns, state, lhs_fs, rhs, target, allow_pointer_unsoundness);
244  run_apply = run_apply_bak;
245  }
246 }
247 
259  const namespacet &ns,
260  goto_symex_statet &state,
261  const exprt &lhs_fs,
262  const exprt &ssa_rhs,
263  symex_targett &target,
264  bool allow_pointer_unsoundness)
265 {
266  if(is_ssa_expr(lhs_fs))
267  {
268  const ssa_exprt ssa_lhs = state
269  .assignment(
270  to_ssa_expr(lhs_fs),
271  ssa_rhs,
272  ns,
273  true,
274  true,
275  allow_pointer_unsoundness)
276  .get();
277 
278  // do the assignment
279  target.assignment(
280  state.guard.as_expr(),
281  ssa_lhs,
282  ssa_lhs,
283  ssa_lhs.get_original_expr(),
284  ssa_rhs,
285  state.source,
287  }
288  else if(
289  ssa_rhs.type().id() == ID_struct || ssa_rhs.type().id() == ID_struct_tag)
290  {
291  const struct_typet &type = to_struct_type(ns.follow(ssa_rhs.type()));
292  const struct_union_typet::componentst &components = type.components();
293 
294  PRECONDITION(
295  components.empty() ||
296  (lhs_fs.has_operands() && lhs_fs.operands().size() == components.size()));
297 
298  exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
299  for(const auto &comp : components)
300  {
301  const exprt member_rhs =
302  simplify_opt(member_exprt{ssa_rhs, comp.get_name(), comp.type()}, ns);
303  const exprt &member_lhs = *fs_it;
304 
306  ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
307  ++fs_it;
308  }
309  }
310 #ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
311  else if(const auto &type = type_try_dynamic_cast<array_typet>(ssa_rhs.type()))
312  {
313  const std::size_t array_size =
314  numeric_cast_v<std::size_t>(to_constant_expr(type->size()));
315  PRECONDITION(lhs_fs.operands().size() == array_size);
316 
317  if(array_size > max_field_sensitivity_array_size)
318  return;
319 
320  exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
321  for(std::size_t i = 0; i < array_size; ++i)
322  {
323  const exprt index_rhs = simplify_opt(
324  index_exprt{ssa_rhs, from_integer(i, type->index_type())}, ns);
325  const exprt &index_lhs = *fs_it;
326 
328  ns, state, index_lhs, index_rhs, target, allow_pointer_unsoundness);
329  ++fs_it;
330  }
331  }
332 #endif // ENABLE_ARRAY_FIELD_SENSITIVITY
333  else if(lhs_fs.has_operands())
334  {
335  PRECONDITION(
336  ssa_rhs.has_operands() &&
337  lhs_fs.operands().size() == ssa_rhs.operands().size());
338 
339  exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
340  for(const exprt &op : ssa_rhs.operands())
341  {
343  ns, state, *fs_it, op, target, allow_pointer_unsoundness);
344  ++fs_it;
345  }
346  }
347  else
348  {
349  UNREACHABLE;
350  }
351 }
352 
354 {
355  if(expr.type().id() == ID_struct || expr.type().id() == ID_struct_tag)
356  return true;
357 
358 #ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
359  if(
360  expr.type().id() == ID_array &&
361  to_array_type(expr.type()).size().id() == ID_constant &&
362  numeric_cast_v<mp_integer>(to_constant_expr(
364  {
365  return true;
366  }
367 #endif
368 
369  return false;
370 }
371 
373 {
374  if(!should_simplify)
375  return e;
376 
377  return simplify_expr(std::move(e), ns);
378 }
UNREACHABLE
#define UNREACHABLE
This should be used to mark dead code.
Definition: invariant.h:503
struct_union_typet::components
const componentst & components() const
Definition: std_types.h:147
mp_integer
BigInt mp_integer
Definition: smt_terms.h:17
Forall_operands
#define Forall_operands(it, expr)
Definition: expr.h:27
arith_tools.h
ssa_exprt::remove_level_2
void remove_level_2()
goto_symex_statet::assignment
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)
Definition: goto_symex_state.cpp:73
to_struct_type
const struct_typet & to_struct_type(const typet &type)
Cast a typet to a struct_typet.
Definition: std_types.h:308
to_index_expr
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition: std_expr.h:1478
symbolt::type
typet type
Type of symbol.
Definition: symbol.h:31
goto_symex_statet
Central data structure: state.
Definition: goto_symex_state.h:41
field_sensitivityt::should_simplify
const bool should_simplify
Definition: field_sensitivity.h:165
exprt
Base class for all expressions.
Definition: expr.h:55
struct_union_typet::componentst
std::vector< componentt > componentst
Definition: std_types.h:140
goto_symex_statet::source
symex_targett::sourcet source
Definition: goto_symex_state.h:61
field_sensitivityt::apply
exprt apply(const namespacet &ns, goto_symex_statet &state, exprt expr, bool write) const
Turn an expression expr into a field-sensitive SSA expression.
Definition: field_sensitivity.cpp:32
field_sensitivityt::max_field_sensitivity_array_size
const std::size_t max_field_sensitivity_array_size
Definition: field_sensitivity.h:163
ssa_exprt::get_original_expr
const exprt & get_original_expr() const
Definition: ssa_expr.h:33
field_sensitivityt::run_apply
bool run_apply
whether or not to invoke field_sensitivityt::apply
Definition: field_sensitivity.h:161
symex_targett::assignment_typet::STATE
@ STATE
irept::get
const irep_idt & get(const irep_idt &name) const
Definition: irep.cpp:45
struct_exprt
Struct constructor from list of elements.
Definition: std_expr.h:1818
ssa_exprt
Expression providing an SSA-renamed symbol of expressions.
Definition: ssa_expr.h:16
array_typet::size
const exprt & size() const
Definition: std_types.h:800
namespacet
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:90
exprt::type
typet & type()
Return the type of the expression.
Definition: expr.h:84
is_ssa_expr
bool is_ssa_expr(const exprt &expr)
Definition: ssa_expr.h:125
symex_targett::assignment
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.
exprt::has_operands
bool has_operands() const
Return true if there is at least one operand.
Definition: expr.h:91
to_ssa_expr
const ssa_exprt & to_ssa_expr(const exprt &expr)
Cast a generic exprt to an ssa_exprt.
Definition: ssa_expr.h:145
guard_exprt::as_expr
exprt as_expr() const
Definition: guard_expr.h:46
member_exprt::struct_op
const exprt & struct_op() const
Definition: std_expr.h:2832
PRECONDITION
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
symbol_exprt::get_identifier
const irep_idt & get_identifier() const
Definition: std_expr.h:142
goto_symex_statet::rename
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...
Definition: goto_symex_state.cpp:169
goto_statet::guard
guardt guard
Definition: goto_state.h:58
ssa_exprt::set_expression
void set_expression(exprt expr)
Replace the underlying, original expression by expr while maintaining SSA indices.
simplify
bool simplify(exprt &expr, const namespacet &ns)
Definition: simplify_expr.cpp:2926
simplify_expr
exprt simplify_expr(exprt src, const namespacet &ns)
Definition: simplify_expr.cpp:2931
index_exprt::index
exprt & index()
Definition: std_expr.h:1450
ssa_exprt::get_level_2
const irep_idt get_level_2() const
Definition: ssa_expr.h:73
index_exprt::array
exprt & array()
Definition: std_expr.h:1440
to_symbol_expr
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition: std_expr.h:222
irept::is_nil
bool is_nil() const
Definition: irep.h:376
irept::id
const irep_idt & id() const
Definition: irep.h:396
exprt::operandst
std::vector< exprt > operandst
Definition: expr.h:58
dstringt::empty
bool empty() const
Definition: dstring.h:88
simplify_expr.h
member_exprt
Extract member of struct or union.
Definition: std_expr.h:2793
namespacet::get_symbol_table
const symbol_table_baset & get_symbol_table() const
Return first symbol table registered with the namespace.
Definition: namespace.h:123
struct_typet
Structure type, corresponds to C style structs.
Definition: std_types.h:230
array_typet
Arrays with given size.
Definition: std_types.h:762
goto_symex_state.h
field_sensitivityt::is_divisible
bool is_divisible(const ssa_exprt &expr) const
Determine whether expr would translate to an atomic SSA expression (returns false) or a composite obj...
Definition: field_sensitivity.cpp:353
namespace_baset::follow
const typet & follow(const typet &) const
Resolve type symbol to the type it points to.
Definition: namespace.cpp:49
field_sensitivityt::field_assignments
void field_assignments(const namespacet &ns, goto_symex_statet &state, const ssa_exprt &lhs, const exprt &rhs, symex_targett &target, bool allow_pointer_unsoundness)
Assign to the individual fields of a non-expanded symbol lhs.
Definition: field_sensitivity.cpp:228
symbolt
Symbol table entry.
Definition: symbol.h:27
from_integer
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
Definition: arith_tools.cpp:100
to_array_type
const array_typet & to_array_type(const typet &type)
Cast a typet to an array_typet.
Definition: std_types.h:844
symbol_table_baset::lookup
const symbolt * lookup(const irep_idt &name) const
Find a symbol in the symbol table for read-only access.
Definition: symbol_table_base.h:95
to_member_expr
const member_exprt & to_member_expr(const exprt &expr)
Cast an exprt to a member_exprt.
Definition: std_expr.h:2886
field_sensitivityt::get_fields
exprt get_fields(const namespacet &ns, goto_symex_statet &state, const ssa_exprt &ssa_expr) const
Compute an expression representing the individual components of a field-sensitive SSA representation ...
Definition: field_sensitivity.cpp:157
exprt::operands
operandst & operands()
Definition: expr.h:94
field_sensitivity.h
index_exprt
Array index operator.
Definition: std_expr.h:1409
field_sensitivityt::simplify_opt
exprt simplify_opt(exprt e, const namespacet &ns) const
Definition: field_sensitivity.cpp:372
std_expr.h
field_sensitivityt::field_assignments_rec
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)
Assign to the individual fields lhs_fs of a non-expanded symbol lhs.
Definition: field_sensitivity.cpp:258
array_typet::index_type
typet index_type() const
The type of the index expressions into any instance of this type.
Definition: std_types.cpp:33
array_exprt
Array constructor from list of elements.
Definition: std_expr.h:1562
symex_target.h
symex_targett
The interface of the target container for symbolic execution to record its symbolic steps into.
Definition: symex_target.h:24
to_constant_expr
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
Definition: std_expr.h:2992