Skip to content

Commit 12c8f62

Browse files
authored
Merge pull request #7392 from peterschrammel/per-byte-initializer
Extend expr_initializer to support byte-wise initialization
2 parents 2c1a38e + e049de7 commit 12c8f62

File tree

3 files changed

+816
-132
lines changed

3 files changed

+816
-132
lines changed

src/util/expr_initializer.cpp

Lines changed: 135 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,41 @@ Author: Daniel Kroening, [email protected]
1212
#include "expr_initializer.h"
1313

1414
#include "arith_tools.h"
15+
#include "bitvector_expr.h"
1516
#include "c_types.h"
17+
#include "config.h"
1618
#include "magic.h"
1719
#include "namespace.h" // IWYU pragma: keep
1820
#include "std_code.h"
1921

20-
template <bool nondet>
2122
class expr_initializert
2223
{
2324
public:
2425
explicit expr_initializert(const namespacet &_ns) : ns(_ns)
2526
{
2627
}
2728

28-
optionalt<exprt>
29-
operator()(const typet &type, const source_locationt &source_location)
29+
optionalt<exprt> operator()(
30+
const typet &type,
31+
const source_locationt &source_location,
32+
const exprt &init_expr)
3033
{
31-
return expr_initializer_rec(type, source_location);
34+
return expr_initializer_rec(type, source_location, init_expr);
3235
}
3336

3437
protected:
3538
const namespacet &ns;
3639

3740
optionalt<exprt> expr_initializer_rec(
3841
const typet &type,
39-
const source_locationt &source_location);
42+
const source_locationt &source_location,
43+
const exprt &init_expr);
4044
};
4145

42-
template <bool nondet>
43-
optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
46+
optionalt<exprt> expr_initializert::expr_initializer_rec(
4447
const typet &type,
45-
const source_locationt &source_location)
48+
const source_locationt &source_location,
49+
const exprt &init_expr)
4650
{
4751
const irep_idt &type_id=type.id();
4852

@@ -57,10 +61,12 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
5761
type_id==ID_fixedbv)
5862
{
5963
exprt result;
60-
if(nondet)
64+
if(init_expr.id() == ID_nondet)
6165
result = side_effect_expr_nondett(type, source_location);
62-
else
66+
else if(init_expr.is_zero())
6367
result = from_integer(0, type);
68+
else
69+
result = duplicate_per_byte(init_expr, type);
6470

6571
result.add_source_location()=source_location;
6672
return result;
@@ -69,10 +75,12 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
6975
type_id==ID_real)
7076
{
7177
exprt result;
72-
if(nondet)
78+
if(init_expr.id() == ID_nondet)
7379
result = side_effect_expr_nondett(type, source_location);
74-
else
80+
else if(init_expr.is_zero())
7581
result = constant_exprt(ID_0, type);
82+
else
83+
result = duplicate_per_byte(init_expr, type);
7684

7785
result.add_source_location()=source_location;
7886
return result;
@@ -81,33 +89,37 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
8189
type_id==ID_verilog_unsignedbv)
8290
{
8391
exprt result;
84-
if(nondet)
92+
if(init_expr.id() == ID_nondet)
8593
result = side_effect_expr_nondett(type, source_location);
86-
else
94+
else if(init_expr.is_zero())
8795
{
8896
const std::size_t width = to_bitvector_type(type).get_width();
8997
std::string value(width, '0');
9098

9199
result = constant_exprt(value, type);
92100
}
101+
else
102+
result = duplicate_per_byte(init_expr, type);
93103

94104
result.add_source_location()=source_location;
95105
return result;
96106
}
97107
else if(type_id==ID_complex)
98108
{
99109
exprt result;
100-
if(nondet)
110+
if(init_expr.id() == ID_nondet)
101111
result = side_effect_expr_nondett(type, source_location);
102-
else
112+
else if(init_expr.is_zero())
103113
{
104-
auto sub_zero =
105-
expr_initializer_rec(to_complex_type(type).subtype(), source_location);
114+
auto sub_zero = expr_initializer_rec(
115+
to_complex_type(type).subtype(), source_location, init_expr);
106116
if(!sub_zero.has_value())
107117
return {};
108118

109119
result = complex_exprt(*sub_zero, *sub_zero, to_complex_type(type));
110120
}
121+
else
122+
result = duplicate_per_byte(init_expr, type);
111123

112124
result.add_source_location()=source_location;
113125
return result;
@@ -127,8 +139,8 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
127139
}
128140
else
129141
{
130-
auto tmpval =
131-
expr_initializer_rec(array_type.element_type(), source_location);
142+
auto tmpval = expr_initializer_rec(
143+
array_type.element_type(), source_location, init_expr);
132144
if(!tmpval.has_value())
133145
return {};
134146

@@ -137,7 +149,7 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
137149
array_type.size().id() == ID_infinity || !array_size.has_value() ||
138150
*array_size > MAX_FLATTENED_ARRAY_SIZE)
139151
{
140-
if(nondet)
152+
if(init_expr.id() == ID_nondet)
141153
return side_effect_expr_nondett(type, source_location);
142154

143155
array_of_exprt value(*tmpval, array_type);
@@ -159,8 +171,8 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
159171
{
160172
const vector_typet &vector_type=to_vector_type(type);
161173

162-
auto tmpval =
163-
expr_initializer_rec(vector_type.element_type(), source_location);
174+
auto tmpval = expr_initializer_rec(
175+
vector_type.element_type(), source_location, init_expr);
164176
if(!tmpval.has_value())
165177
return {};
166178

@@ -190,7 +202,8 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
190202
DATA_INVARIANT(
191203
c.type().id() != ID_code, "struct member must not be of code type");
192204

193-
const auto member = expr_initializer_rec(c.type(), source_location);
205+
const auto member =
206+
expr_initializer_rec(c.type(), source_location, init_expr);
194207
if(!member.has_value())
195208
return {};
196209

@@ -216,8 +229,8 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
216229
if(!widest_member.has_value())
217230
return {};
218231

219-
auto component_value =
220-
expr_initializer_rec(widest_member->first.type(), source_location);
232+
auto component_value = expr_initializer_rec(
233+
widest_member->first.type(), source_location, init_expr);
221234

222235
if(!component_value.has_value())
223236
return {};
@@ -230,7 +243,7 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
230243
else if(type_id==ID_c_enum_tag)
231244
{
232245
auto result = expr_initializer_rec(
233-
ns.follow_tag(to_c_enum_tag_type(type)), source_location);
246+
ns.follow_tag(to_c_enum_tag_type(type)), source_location, init_expr);
234247

235248
if(!result.has_value())
236249
return {};
@@ -243,7 +256,7 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
243256
else if(type_id==ID_struct_tag)
244257
{
245258
auto result = expr_initializer_rec(
246-
ns.follow_tag(to_struct_tag_type(type)), source_location);
259+
ns.follow_tag(to_struct_tag_type(type)), source_location, init_expr);
247260

248261
if(!result.has_value())
249262
return {};
@@ -256,7 +269,7 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
256269
else if(type_id==ID_union_tag)
257270
{
258271
auto result = expr_initializer_rec(
259-
ns.follow_tag(to_union_tag_type(type)), source_location);
272+
ns.follow_tag(to_union_tag_type(type)), source_location, init_expr);
260273

261274
if(!result.has_value())
262275
return {};
@@ -269,10 +282,12 @@ optionalt<exprt> expr_initializert<nondet>::expr_initializer_rec(
269282
else if(type_id==ID_string)
270283
{
271284
exprt result;
272-
if(nondet)
285+
if(init_expr.id() == ID_nondet)
273286
result = side_effect_expr_nondett(type, source_location);
274-
else
287+
else if(init_expr.is_zero())
275288
result = constant_exprt(irep_idt(), type);
289+
else
290+
result = duplicate_per_byte(init_expr, type);
276291

277292
result.add_source_location()=source_location;
278293
return result;
@@ -292,7 +307,8 @@ optionalt<exprt> zero_initializer(
292307
const source_locationt &source_location,
293308
const namespacet &ns)
294309
{
295-
return expr_initializert<false>(ns)(type, source_location);
310+
return expr_initializert(ns)(
311+
type, source_location, constant_exprt(ID_0, char_type()));
296312
}
297313

298314
/// Create a non-deterministic value for type `type`, with all subtypes
@@ -307,5 +323,91 @@ optionalt<exprt> nondet_initializer(
307323
const source_locationt &source_location,
308324
const namespacet &ns)
309325
{
310-
return expr_initializert<true>(ns)(type, source_location);
326+
return expr_initializert(ns)(type, source_location, exprt(ID_nondet));
327+
}
328+
329+
/// Create a value for type `type`, with all subtype bytes
330+
/// initialized to the given value.
331+
/// \param type: Type of the target expression.
332+
/// \param source_location: Location to record in all created sub-expressions.
333+
/// \param ns: Namespace to perform type symbol/tag lookups.
334+
/// \param init_byte_expr: Value to be used for initialization.
335+
/// \return An expression if a byte-initialized expression of the input type
336+
/// can be built.
337+
optionalt<exprt> expr_initializer(
338+
const typet &type,
339+
const source_locationt &source_location,
340+
const namespacet &ns,
341+
const exprt &init_byte_expr)
342+
{
343+
return expr_initializert(ns)(type, source_location, init_byte_expr);
344+
}
345+
346+
/// Builds an expression of the given output type with each of its bytes
347+
/// initialized to the given initialization expression.
348+
/// Integer bitvector types are currently supported.
349+
/// For unsupported `output_type` the initialization expression is casted to the
350+
/// output type.
351+
/// \param init_byte_expr The initialization expression
352+
/// \param output_type The output type
353+
/// \return The built expression
354+
/// \note `init_byte_expr` must be a boolean or a bitvector and must be of at
355+
/// most `size <= config.ansi_c.char_width`
356+
exprt duplicate_per_byte(const exprt &init_byte_expr, const typet &output_type)
357+
{
358+
const auto init_type_as_bitvector =
359+
type_try_dynamic_cast<bitvector_typet>(init_byte_expr.type());
360+
// Fail if `init_byte_expr` is not a bitvector of maximum 8 bits or a boolean.
361+
PRECONDITION(
362+
(init_type_as_bitvector &&
363+
init_type_as_bitvector->get_width() <= config.ansi_c.char_width) ||
364+
init_byte_expr.type().id() == ID_bool);
365+
if(const auto output_bv = type_try_dynamic_cast<bitvector_typet>(output_type))
366+
{
367+
const auto out_width = output_bv->get_width();
368+
// Replicate `init_byte_expr` enough times until it completely fills
369+
// `output_type`.
370+
371+
// We've got a constant. So, precompute the value of the constant.
372+
if(init_byte_expr.is_constant())
373+
{
374+
const auto init_size = init_type_as_bitvector->get_width();
375+
const irep_idt init_value = to_constant_expr(init_byte_expr).get_value();
376+
377+
// Create a new BV od `output_type` size with its representation being the
378+
// replication of the init_byte_expr (padded with 0) enough times to fill.
379+
const auto output_value =
380+
make_bvrep(out_width, [&init_size, &init_value](std::size_t index) {
381+
// Index modded by 8 to access the i-th bit of init_value
382+
const auto source_index = index % config.ansi_c.char_width;
383+
// If the modded i-th bit exists get it, otherwise add 0 padding.
384+
return source_index < init_size &&
385+
get_bvrep_bit(init_value, init_size, source_index);
386+
});
387+
388+
return constant_exprt{output_value, output_type};
389+
}
390+
391+
const size_t size =
392+
(out_width + config.ansi_c.char_width - 1) / config.ansi_c.char_width;
393+
394+
// We haven't got a constant. So, build the expression using shift-and-or.
395+
exprt::operandst values;
396+
// Let's cast init_byte_expr to output_type.
397+
const exprt casted_init_byte_expr =
398+
typecast_exprt::conditional_cast(init_byte_expr, output_type);
399+
values.push_back(casted_init_byte_expr);
400+
for(size_t i = 1; i < size; ++i)
401+
{
402+
values.push_back(shl_exprt(
403+
casted_init_byte_expr,
404+
from_integer(config.ansi_c.char_width * i, size_type())));
405+
}
406+
if(values.size() == 1)
407+
return values[0];
408+
return multi_ary_exprt(ID_bitor, values, output_type);
409+
}
410+
411+
// Anything else. We don't know what to do with it. So, just cast.
412+
return typecast_exprt::conditional_cast(init_byte_expr, output_type);
311413
}

src/util/expr_initializer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,12 @@ optionalt<exprt> nondet_initializer(
2727
const source_locationt &source_location,
2828
const namespacet &ns);
2929

30+
optionalt<exprt> expr_initializer(
31+
const typet &type,
32+
const source_locationt &source_location,
33+
const namespacet &ns,
34+
const exprt &init_byte_expr);
35+
36+
exprt duplicate_per_byte(const exprt &init_byte_expr, const typet &output_type);
37+
3038
#endif // CPROVER_UTIL_EXPR_INITIALIZER_H

0 commit comments

Comments
 (0)