CBMC
boolbv_index.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module:
4 
5 Author: Daniel Kroening, kroening@kroening.com
6 
7 \*******************************************************************/
8 
9 #include "boolbv.h"
10 
11 #include <algorithm>
12 
13 #include <util/arith_tools.h>
14 #include <util/byte_operators.h>
15 #include <util/cprover_prefix.h>
16 #include <util/pointer_expr.h>
18 #include <util/simplify_expr.h>
19 #include <util/std_expr.h>
20 
22 
24 {
25  const exprt &array=expr.array();
26  const exprt &index=expr.index();
27 
28  const typet &array_op_type = array.type();
29 
30  bvt bv;
31 
32  if(array_op_type.id()==ID_array)
33  {
34  const array_typet &array_type=
35  to_array_type(array_op_type);
36 
37  // see if the array size is constant
38 
39  if(is_unbounded_array(array_type))
40  {
41  // use array decision procedure
42 
43  if(has_byte_operator(expr))
44  {
45  const index_exprt final_expr =
47  CHECK_RETURN(final_expr != expr);
48  bv = convert_bv(final_expr);
49 
50  // record type if array is a symbol
51  const exprt &final_array = final_expr.array();
52  if(
53  final_array.id() == ID_symbol || final_array.id() == ID_nondet_symbol)
54  {
55  const auto &array_width_opt = bv_width.get_width_opt(array_type);
56  (void)map.get_literals(
57  final_array.get(ID_identifier),
58  array_type,
59  array_width_opt.value_or(0));
60  }
61 
62  // make sure we have the index in the cache
63  convert_bv(final_expr.index());
64  }
65  else
66  {
67  // free variables
68  bv = prop.new_variables(boolbv_width(expr.type()));
69 
70  record_array_index(expr);
71 
72  // record type if array is a symbol
73  if(array.id() == ID_symbol || array.id() == ID_nondet_symbol)
74  {
75  const auto &array_width_opt = bv_width.get_width_opt(array_type);
76  (void)map.get_literals(
77  array.get(ID_identifier), array_type, array_width_opt.value_or(0));
78  }
79 
80  // make sure we have the index in the cache
81  convert_bv(index);
82  }
83 
84  return bv;
85  }
86 
87  // Must have a finite size
88  mp_integer array_size =
89  numeric_cast_v<mp_integer>(to_constant_expr(array_type.size()));
90 
91  {
92  // see if the index address is constant
93  // many of these are compacted by simplify_expr
94  // but variable location writes will block this
95  auto maybe_index_value = numeric_cast<mp_integer>(index);
96  if(maybe_index_value.has_value())
97  {
98  return convert_index(array, maybe_index_value.value());
99  }
100  }
101 
102  // Special case : arrays of one thing (useful for constants)
103  // TODO : merge with ACTUAL_ARRAY_HACK so that ranges of the same
104  // value, bit-patterns with the same value, etc. are treated like
105  // this rather than as a series of individual options.
106  #define UNIFORM_ARRAY_HACK
107  #ifdef UNIFORM_ARRAY_HACK
108  bool is_uniform = array.id() == ID_array_of;
109 
110  if(array.id() == ID_constant || array.id() == ID_array)
111  {
112  is_uniform = array.operands().size() <= 1 ||
113  std::all_of(
114  ++array.operands().begin(),
115  array.operands().end(),
116  [&array](const exprt &expr) {
117  return expr == to_multi_ary_expr(array).op0();
118  });
119  }
120 
121  if(is_uniform && prop.has_set_to())
122  {
123  static int uniform_array_counter; // Temporary hack
124 
125  const std::string identifier = CPROVER_PREFIX "internal_uniform_array_" +
126  std::to_string(uniform_array_counter++);
127 
128  symbol_exprt result(identifier, expr.type());
129  bv = convert_bv(result);
130 
131  // return an unconstrained value in case of an empty array (the access is
132  // necessarily out-of-bounds)
133  if(!array.has_operands())
134  return bv;
135 
136  equal_exprt value_equality(result, to_multi_ary_expr(array).op0());
137 
138  binary_relation_exprt lower_bound(
139  from_integer(0, index.type()), ID_le, index);
140  binary_relation_exprt upper_bound(
141  index, ID_lt, from_integer(array_size, index.type()));
142 
143  and_exprt range_condition(std::move(lower_bound), std::move(upper_bound));
145  std::move(range_condition), std::move(value_equality));
146 
147  // Simplify may remove the lower bound if the type
148  // is correct.
150 
151  return bv;
152  }
153  #endif
154 
155  #define ACTUAL_ARRAY_HACK
156  #ifdef ACTUAL_ARRAY_HACK
157  // More useful when updates to concrete locations in
158  // actual arrays are compacted by simplify_expr
159  if((array.id()==ID_constant || array.id()==ID_array) &&
160  prop.has_set_to())
161  {
162  #ifdef CONSTANT_ARRAY_HACK
163  // TODO : Compile the whole array into a single relation
164  #endif
165 
166  // Symbol for output
167  static int actual_array_counter; // Temporary hack
168 
169  const std::string identifier = CPROVER_PREFIX "internal_actual_array_" +
170  std::to_string(actual_array_counter++);
171 
172  symbol_exprt result(identifier, expr.type());
173  bv = convert_bv(result);
174 
175  // add implications
176 
177 #ifdef COMPACT_EQUAL_CONST
178  bv_utils.equal_const_register(convert_bv(index)); // Definitely
179  bv_utils.equal_const_register(convert_bv(result)); // Maybe
180 #endif
181 
182  exprt::operandst::const_iterator it = array.operands().begin();
183 
184  for(mp_integer i=0; i<array_size; i=i+1)
185  {
186  INVARIANT(
187  it != array.operands().end(),
188  "this loop iterates over the array, so `it` shouldn't be increased "
189  "past the array's end");
190 
191  // Cache comparisons and equalities
193  equal_exprt(index, from_integer(i, index.type())),
194  equal_exprt(result, *it++))));
195  }
196 
197  return bv;
198  }
199 
200 #endif
201 
202 
203  // TODO : As with constant index, there is a trade-off
204  // of when it is best to flatten the whole array and
205  // when it is best to use the array theory and then use
206  // one or more of the above encoding strategies.
207 
208  // get literals for the whole array
209  const std::size_t width = boolbv_width(expr.type());
210 
211  const bvt &array_bv =
212  convert_bv(array, numeric_cast_v<std::size_t>(array_size * width));
213 
214  // TODO: maybe a shifter-like construction would be better
215  // Would be a lot more compact but propagate worse
216 
217  if(prop.has_set_to())
218  {
219  // free variables
220  bv = prop.new_variables(width);
221 
222  // add implications
223 
224 #ifdef COMPACT_EQUAL_CONST
225  bv_utils.equal_const_register(convert_bv(index)); // Definitely
226 #endif
227 
228  bvt equal_bv;
229  equal_bv.resize(width);
230 
231  for(mp_integer i=0; i<array_size; i=i+1)
232  {
233  mp_integer offset=i*width;
234 
235  for(std::size_t j=0; j<width; j++)
236  equal_bv[j] = prop.lequal(
237  bv[j], array_bv[numeric_cast_v<std::size_t>(offset + j)]);
238 
240  convert(equal_exprt(index, from_integer(i, index.type()))),
241  prop.land(equal_bv)));
242  }
243  }
244  else
245  {
246  bv.resize(width);
247 
248 #ifdef COMPACT_EQUAL_CONST
249  bv_utils.equal_const_register(convert_bv(index)); // Definitely
250 #endif
251 
252  typet constant_type=index.type(); // type of index operand
253 
255  array_size > 0,
256  "non-positive array sizes are forbidden in goto programs");
257 
258  for(mp_integer i=0; i<array_size; i=i+1)
259  {
260  literalt e =
261  convert(equal_exprt(index, from_integer(i, constant_type)));
262 
263  mp_integer offset=i*width;
264 
265  for(std::size_t j=0; j<width; j++)
266  {
267  literalt l = array_bv[numeric_cast_v<std::size_t>(offset + j)];
268 
269  if(i==0) // this initializes bv
270  bv[j]=l;
271  else
272  bv[j]=prop.lselect(e, l, bv[j]);
273  }
274  }
275  }
276  }
277  else
278  return conversion_failed(expr);
279 
280  return bv;
281 }
282 
285  const exprt &array,
286  const mp_integer &index)
287 {
288  const array_typet &array_type = to_array_type(array.type());
289 
290  std::size_t width = boolbv_width(array_type.element_type());
291 
292  // TODO: If the underlying array can use one of the
293  // improvements given above then it may be better to use
294  // the array theory for short chains of updates and then
295  // the improved array handling rather than full flattening.
296  // Note that the calculation is non-trivial as the cost of
297  // full flattening is amortised against all uses of
298  // the array (constant and variable indexes) and updated
299  // versions of it.
300 
301  const bvt &tmp=convert_bv(array); // recursive call
302 
303  mp_integer offset=index*width;
304 
305  if(offset>=0 &&
306  offset+width<=mp_integer(tmp.size()))
307  {
308  // in bounds
309 
310  // The assertion below is disabled as we want to be able
311  // to run CBMC without simplifier.
312  // Expression simplification should remove these cases
313  // assert(array.id()!=ID_array_of &&
314  // array.id()!=ID_array);
315  // If not there are large improvements possible as above
316 
317  std::size_t offset_int = numeric_cast_v<std::size_t>(offset);
318  return bvt(tmp.begin() + offset_int, tmp.begin() + offset_int + width);
319  }
320  else if(array.id() == ID_member || array.id() == ID_index)
321  {
322  // out of bounds for the component, but not necessarily outside the bounds
323  // of the underlying object
325  o.build(array, ns);
326  CHECK_RETURN(o.offset().id() != ID_unknown);
327 
328  const auto subtype_bytes_opt =
329  pointer_offset_size(array_type.element_type(), ns);
330  CHECK_RETURN(subtype_bytes_opt.has_value());
331 
332  exprt new_offset = simplify_expr(
333  plus_exprt(
334  o.offset(), from_integer(index * (*subtype_bytes_opt), o.offset().type())),
335  ns);
336 
337  byte_extract_exprt be =
338  make_byte_extract(o.root_object(), new_offset, array_type.element_type());
339 
340  return convert_bv(be);
341  }
342  else if(
343  array.id() == ID_byte_extract_big_endian ||
344  array.id() == ID_byte_extract_little_endian)
345  {
346  const byte_extract_exprt &byte_extract_expr = to_byte_extract_expr(array);
347 
348  const auto subtype_bytes_opt =
349  pointer_offset_size(array_type.element_type(), ns);
350  CHECK_RETURN(subtype_bytes_opt.has_value());
351 
352  // add offset to index
353  exprt new_offset = simplify_expr(
354  plus_exprt{
355  byte_extract_expr.offset(),
356  from_integer(
357  index * (*subtype_bytes_opt), byte_extract_expr.offset().type())},
358  ns);
359 
360  byte_extract_exprt be = byte_extract_expr;
361  be.offset() = new_offset;
362  be.type() = array_type.element_type();
363 
364  return convert_bv(be);
365  }
366  else
367  {
368  // out of bounds
369  return prop.new_variables(width);
370  }
371 }
pointer_offset_size.h
boolbvt::bv_width
boolbv_widtht bv_width
Definition: boolbv.h:116
make_byte_extract
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.
Definition: byte_operators.cpp:48
boolbvt::map
boolbv_mapt map
Definition: boolbv.h:123
mp_integer
BigInt mp_integer
Definition: smt_terms.h:17
arith_tools.h
object_descriptor_exprt::build
void build(const exprt &expr, const namespacet &ns)
Given an expression expr, attempt to find the underlying object it represents by skipping over type c...
Definition: pointer_expr.cpp:107
propt::new_variables
virtual bvt new_variables(std::size_t width)
generates a bitvector of given width with new variables
Definition: prop.cpp:20
boolbvt::is_unbounded_array
bool is_unbounded_array(const typet &type) const override
Definition: boolbv.cpp:518
CHECK_RETURN
#define CHECK_RETURN(CONDITION)
Definition: invariant.h:495
typet
The type of an expression, extends irept.
Definition: type.h:28
bvt
std::vector< literalt > bvt
Definition: literal.h:201
to_byte_extract_expr
const byte_extract_exprt & to_byte_extract_expr(const exprt &expr)
Definition: byte_operators.h:57
to_index_expr
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition: std_expr.h:1478
has_byte_operator
bool has_byte_operator(const exprt &src)
Definition: byte_operators.cpp:2377
boolbv_mapt::get_literals
const bvt & get_literals(const irep_idt &identifier, const typet &type, std::size_t width)
Definition: boolbv_map.cpp:41
object_descriptor_exprt
Split an expression into a base object and a (byte) offset.
Definition: pointer_expr.h:166
and_exprt
Boolean AND.
Definition: std_expr.h:2070
plus_exprt
The plus expression Associativity is not specified.
Definition: std_expr.h:946
boolbvt::convert_index
virtual bvt convert_index(const exprt &array, const mp_integer &index)
index operator with constant index
Definition: boolbv_index.cpp:284
exprt
Base class for all expressions.
Definition: expr.h:55
propt::l_set_to_true
void l_set_to_true(literalt a)
Definition: prop.h:50
to_string
std::string to_string(const string_not_contains_constraintt &expr)
Used for debug printing.
Definition: string_constraint.cpp:58
symbol_exprt
Expression to hold a symbol (variable)
Definition: std_expr.h:112
propt::land
virtual literalt land(literalt a, literalt b)=0
equal_exprt
Equality.
Definition: std_expr.h:1305
boolbv_widtht::get_width_opt
virtual optionalt< std::size_t > get_width_opt(const typet &type) const
Definition: boolbv_width.h:31
object_descriptor_exprt::root_object
static const exprt & root_object(const exprt &expr)
Definition: pointer_expr.cpp:168
irept::get
const irep_idt & get(const irep_idt &name) const
Definition: irep.cpp:45
expr_lowering.h
array_typet::size
const exprt & size() const
Definition: std_types.h:800
exprt::type
typet & type()
Return the type of the expression.
Definition: expr.h:84
byte_operators.h
Expression classes for byte-level operators.
DATA_INVARIANT
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:510
exprt::has_operands
bool has_operands() const
Return true if there is at least one operand.
Definition: expr.h:91
propt::limplies
virtual literalt limplies(literalt a, literalt b)=0
boolbvt::boolbv_width
virtual std::size_t boolbv_width(const typet &type) const
Definition: boolbv.h:102
boolbvt::convert_bv
virtual const bvt & convert_bv(const exprt &expr, const optionalt< std::size_t > expected_width={})
Convert expression to vector of literalts, using an internal cache to speed up conversion if availabl...
Definition: boolbv.cpp:39
implication
static exprt implication(exprt lhs, exprt rhs)
Definition: goto_check_c.cpp:408
arrayst::ns
const namespacet & ns
Definition: arrays.h:56
lower_byte_operators
exprt lower_byte_operators(const exprt &src, const namespacet &ns)
Rewrite an expression possibly containing byte-extract or -update expressions to more fundamental ope...
Definition: byte_operators.cpp:2392
pointer_expr.h
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
index_exprt::array
exprt & array()
Definition: std_expr.h:1440
irept::id
const irep_idt & id() const
Definition: irep.h:396
arrayst::record_array_index
void record_array_index(const index_exprt &expr)
Definition: arrays.cpp:44
cprover_prefix.h
prop_conv_solvert::convert
literalt convert(const exprt &expr) override
Convert a Boolean expression and return the corresponding literal.
Definition: prop_conv_solver.cpp:156
simplify_expr.h
byte_extract_exprt::offset
exprt & offset()
Definition: byte_operators.h:44
propt::lequal
virtual literalt lequal(literalt a, literalt b)=0
pointer_offset_size
optionalt< mp_integer > pointer_offset_size(const typet &type, const namespacet &ns)
Compute the size of a type in bytes, rounding up to full bytes.
Definition: pointer_offset_size.cpp:90
array_typet
Arrays with given size.
Definition: std_types.h:762
boolbvt::bv_utils
bv_utilst bv_utils
Definition: boolbv.h:117
from_integer
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
Definition: arith_tools.cpp:100
binary_relation_exprt
A base class for relations, i.e., binary predicates whose two operands have the same type.
Definition: std_expr.h:706
literalt
Definition: literal.h:25
CPROVER_PREFIX
#define CPROVER_PREFIX
Definition: cprover_prefix.h:14
byte_extract_exprt
Expression of type type extracted from some object op starting at position offset (given in number of...
Definition: byte_operators.h:28
to_array_type
const array_typet & to_array_type(const typet &type)
Cast a typet to an array_typet.
Definition: std_types.h:844
propt::has_set_to
virtual bool has_set_to() const
Definition: prop.h:79
boolbvt::conversion_failed
bvt conversion_failed(const exprt &expr)
Print that the expression of x has failed conversion, then return a vector of x's width.
Definition: boolbv.cpp:83
implies_exprt
Boolean implication.
Definition: std_expr.h:2133
boolbv.h
exprt::operands
operandst & operands()
Definition: expr.h:94
index_exprt
Array index operator.
Definition: std_expr.h:1409
to_multi_ary_expr
const multi_ary_exprt & to_multi_ary_expr(const exprt &expr)
Cast an exprt to a multi_ary_exprt.
Definition: std_expr.h:932
std_expr.h
propt::lselect
virtual literalt lselect(literalt a, literalt b, literalt c)=0
validation_modet::INVARIANT
@ INVARIANT
prop_conv_solvert::prop
propt & prop
Definition: prop_conv_solver.h:130
array_typet::element_type
const typet & element_type() const
The type of the elements of the array.
Definition: std_types.h:785
to_constant_expr
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
Definition: std_expr.h:2992
object_descriptor_exprt::offset
exprt & offset()
Definition: pointer_expr.h:209