-
Notifications
You must be signed in to change notification settings - Fork 273
/
Copy pathexpr_initializer.cpp
458 lines (393 loc) · 13.9 KB
/
expr_initializer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
/*******************************************************************\
Module: Expression Initialization
Author: Daniel Kroening, [email protected]
\*******************************************************************/
/// \file
/// Expression Initialization
#include "expr_initializer.h"
#include "arith_tools.h"
#include "bitvector_expr.h"
#include "byte_operators.h"
#include "c_types.h"
#include "config.h"
#include "magic.h"
#include "namespace.h" // IWYU pragma: keep
#include "simplify_expr.h"
#include "std_code.h"
#include "symbol_table.h"
class expr_initializert
{
public:
explicit expr_initializert(const namespacet &_ns) : ns(_ns)
{
}
std::optional<exprt> operator()(
const typet &type,
const source_locationt &source_location,
const exprt &init_expr)
{
return expr_initializer_rec(type, source_location, init_expr);
}
protected:
const namespacet &ns;
std::optional<exprt> expr_initializer_rec(
const typet &type,
const source_locationt &source_location,
const exprt &init_expr);
};
std::optional<exprt> expr_initializert::expr_initializer_rec(
const typet &type,
const source_locationt &source_location,
const exprt &init_expr)
{
const irep_idt &type_id=type.id();
if(type_id==ID_unsignedbv ||
type_id==ID_signedbv ||
type_id==ID_pointer ||
type_id==ID_c_enum ||
type_id==ID_c_bit_field ||
type_id==ID_bool ||
type_id==ID_c_bool ||
type_id==ID_floatbv ||
type_id==ID_fixedbv)
{
exprt result;
if(init_expr.id() == ID_nondet)
result = side_effect_expr_nondett(type, source_location);
else if(init_expr.is_zero())
result = from_integer(0, type);
else
result = duplicate_per_byte(init_expr, type, ns);
result.add_source_location()=source_location;
return result;
}
else if(type_id==ID_rational ||
type_id==ID_real)
{
exprt result;
if(init_expr.id() == ID_nondet)
result = side_effect_expr_nondett(type, source_location);
else if(init_expr.is_zero())
result = constant_exprt(ID_0, type);
else
result = duplicate_per_byte(init_expr, type, ns);
result.add_source_location()=source_location;
return result;
}
else if(type_id==ID_verilog_signedbv ||
type_id==ID_verilog_unsignedbv)
{
exprt result;
if(init_expr.id() == ID_nondet)
result = side_effect_expr_nondett(type, source_location);
else if(init_expr.is_zero())
{
const std::size_t width = to_bitvector_type(type).get_width();
std::string value(width, '0');
result = constant_exprt(value, type);
}
else
result = duplicate_per_byte(init_expr, type, ns);
result.add_source_location()=source_location;
return result;
}
else if(type_id==ID_complex)
{
exprt result;
if(init_expr.id() == ID_nondet)
result = side_effect_expr_nondett(type, source_location);
else if(init_expr.is_zero())
{
auto sub_zero = expr_initializer_rec(
to_complex_type(type).subtype(), source_location, init_expr);
if(!sub_zero.has_value())
return {};
result = complex_exprt(*sub_zero, *sub_zero, to_complex_type(type));
}
else
result = duplicate_per_byte(init_expr, type, ns);
result.add_source_location()=source_location;
return result;
}
else if(type_id==ID_array)
{
const array_typet &array_type=to_array_type(type);
if(array_type.size().is_nil())
{
// we initialize this with an empty array
array_exprt value({}, array_type);
value.type().size() = from_integer(0, size_type());
value.add_source_location()=source_location;
return std::move(value);
}
else
{
auto tmpval = expr_initializer_rec(
array_type.element_type(), source_location, init_expr);
if(!tmpval.has_value())
return {};
const auto array_size = numeric_cast<mp_integer>(array_type.size());
if(
array_type.size().id() == ID_infinity || !array_size.has_value() ||
*array_size > MAX_FLATTENED_ARRAY_SIZE)
{
if(init_expr.id() == ID_nondet)
return side_effect_expr_nondett(type, source_location);
array_of_exprt value(*tmpval, array_type);
value.add_source_location()=source_location;
return std::move(value);
}
if(*array_size < 0)
return {};
array_exprt value({}, array_type);
value.operands().resize(
numeric_cast_v<std::size_t>(*array_size), *tmpval);
value.add_source_location()=source_location;
return std::move(value);
}
}
else if(type_id==ID_vector)
{
const vector_typet &vector_type=to_vector_type(type);
auto tmpval = expr_initializer_rec(
vector_type.element_type(), source_location, init_expr);
if(!tmpval.has_value())
return {};
const mp_integer vector_size =
numeric_cast_v<mp_integer>(vector_type.size());
if(vector_size < 0)
return {};
vector_exprt value({}, vector_type);
value.operands().resize(numeric_cast_v<std::size_t>(vector_size), *tmpval);
value.add_source_location()=source_location;
return std::move(value);
}
else if(type_id==ID_struct)
{
const struct_typet::componentst &components=
to_struct_type(type).components();
struct_exprt value({}, type);
value.operands().reserve(components.size());
for(const auto &c : components)
{
DATA_INVARIANT(
c.type().id() != ID_code, "struct member must not be of code type");
const auto member =
expr_initializer_rec(c.type(), source_location, init_expr);
if(!member.has_value())
return {};
value.add_to_operands(std::move(*member));
}
value.add_source_location()=source_location;
return std::move(value);
}
else if(type_id==ID_union)
{
const union_typet &union_type = to_union_type(type);
if(union_type.components().empty())
{
empty_union_exprt value{type};
value.add_source_location() = source_location;
return std::move(value);
}
const auto &widest_member = union_type.find_widest_union_component(ns);
if(!widest_member.has_value())
return {};
auto component_value = expr_initializer_rec(
widest_member->first.type(), source_location, init_expr);
if(!component_value.has_value())
return {};
union_exprt value{widest_member->first.get_name(), *component_value, type};
value.add_source_location() = source_location;
return std::move(value);
}
else if(type_id==ID_c_enum_tag)
{
auto result = expr_initializer_rec(
ns.follow_tag(to_c_enum_tag_type(type)), source_location, init_expr);
if(!result.has_value())
return {};
// use the tag type
result->type() = type;
return *result;
}
else if(type_id==ID_struct_tag)
{
auto result = expr_initializer_rec(
ns.follow_tag(to_struct_tag_type(type)), source_location, init_expr);
if(!result.has_value())
return {};
// use the tag type
result->type() = type;
return *result;
}
else if(type_id==ID_union_tag)
{
auto result = expr_initializer_rec(
ns.follow_tag(to_union_tag_type(type)), source_location, init_expr);
if(!result.has_value())
return {};
// use the tag type
result->type() = type;
return *result;
}
else if(type_id==ID_string)
{
exprt result;
if(init_expr.id() == ID_nondet)
result = side_effect_expr_nondett(type, source_location);
else if(init_expr.is_zero())
result = constant_exprt(irep_idt(), type);
else
result = duplicate_per_byte(init_expr, type, ns);
result.add_source_location()=source_location;
return result;
}
else
return {};
}
/// Create the equivalent of zero for type `type`.
/// \param type: Type of the target expression.
/// \param source_location: Location to record in all created sub-expressions.
/// \param ns: Namespace to perform type symbol/tag lookups.
/// \return An expression if a constant expression of the input type can be
/// built.
std::optional<exprt> zero_initializer(
const typet &type,
const source_locationt &source_location,
const namespacet &ns)
{
return expr_initializert(ns)(
type, source_location, constant_exprt(ID_0, char_type()));
}
/// Create a non-deterministic value for type `type`, with all subtypes
/// independently expanded as non-deterministic values.
/// \param type: Type of the target expression.
/// \param source_location: Location to record in all created sub-expressions.
/// \param ns: Namespace to perform type symbol/tag lookups.
/// \return An expression if a non-deterministic expression of the input type
/// can be built.
std::optional<exprt> nondet_initializer(
const typet &type,
const source_locationt &source_location,
const namespacet &ns)
{
return expr_initializert(ns)(type, source_location, exprt(ID_nondet));
}
/// Create a value for type `type`, with all subtype bytes
/// initialized to the given value.
/// \param type: Type of the target expression.
/// \param source_location: Location to record in all created sub-expressions.
/// \param ns: Namespace to perform type symbol/tag lookups.
/// \param init_byte_expr: Value to be used for initialization.
/// \return An expression if a byte-initialized expression of the input type
/// can be built.
std::optional<exprt> expr_initializer(
const typet &type,
const source_locationt &source_location,
const namespacet &ns,
const exprt &init_byte_expr)
{
return expr_initializert(ns)(type, source_location, init_byte_expr);
}
/// Typecast the input to the output if this is a signed/unsigned bv.
/// Perform a reinterpret cast using byte_extract otherwise.
/// @param expr the expression to be casted if necessary.
/// @param out_type the type to cast the expression to.
/// @return the casted or reinterpreted expression.
static exprt cast_or_reinterpret(const exprt &expr, const typet &out_type)
{
// Same type --> no cast
if(expr.type() == out_type)
{
return expr;
}
if(
can_cast_type<signedbv_typet>(out_type) ||
can_cast_type<unsignedbv_typet>(out_type))
{
return typecast_exprt::conditional_cast(expr, out_type);
}
return make_byte_extract(expr, from_integer(0, char_type()), out_type);
}
/// Builds an expression of the given output type with each of its bytes
/// initialized to the given initialization expression.
/// Integer bitvector types are currently supported.
/// For unsupported `output_type` the initialization expression is casted to the
/// output type.
/// \param init_byte_expr The initialization expression
/// \param output_type The output type
/// \param ns Namespace to perform type symbol/tag lookups
/// \return The built expression
/// \note `init_byte_expr` must be a boolean or a bitvector and must be of at
/// most `size <= config.ansi_c.char_width`
exprt duplicate_per_byte(
const exprt &init_byte_expr,
const typet &output_type,
const namespacet &ns)
{
const auto init_type_as_bitvector =
type_try_dynamic_cast<bitvector_typet>(init_byte_expr.type());
// Fail if `init_byte_expr` is not a bitvector of maximum 8 bits or a boolean.
PRECONDITION(
(init_type_as_bitvector &&
init_type_as_bitvector->get_width() <= config.ansi_c.char_width) ||
init_byte_expr.type().id() == ID_bool);
// Simplify init_expr to enable faster duplication when simplifiable to a
// constant expr
const auto simplified_init_expr = simplify_expr(init_byte_expr, ns);
if(const auto output_bv = type_try_dynamic_cast<bitvector_typet>(output_type))
{
const auto out_width = output_bv->get_width();
// Replicate `simplified_init_expr` enough times until it completely fills
// `output_type`.
// We've got a constant. So, precompute the value of the constant.
if(simplified_init_expr.is_constant())
{
const auto init_size = init_type_as_bitvector->get_width();
const irep_idt init_value =
to_constant_expr(simplified_init_expr).get_value();
// Create a new BV of `output_type` size with its representation being the
// replication of the simplified_init_expr (padded with 0) enough times to
// fill.
const auto output_value =
make_bvrep(out_width, [&init_size, &init_value](std::size_t index) {
// Index modded by 8 to access the i-th bit of init_value
const auto source_index = index % config.ansi_c.char_width;
// If the modded i-th bit exists get it, otherwise add 0 padding.
return source_index < init_size &&
get_bvrep_bit(init_value, init_size, source_index);
});
return constant_exprt{output_value, output_type};
}
const size_t size =
(out_width + config.ansi_c.char_width - 1) / config.ansi_c.char_width;
// We haven't got a constant. So, build the expression using shift-and-or.
exprt::operandst values;
// When doing the replication we extend the init_expr to the output size to
// compute the bitwise or. To avoid that the sign is extended too we change
// the type of the output to an unsigned bitvector with the same size as the
// output type.
typet operation_type = unsignedbv_typet{output_bv->get_width()};
// To avoid sign-extension during cast we first cast to an unsigned version
// of the same bv type, then we extend it to the output type (adding 0
// padding).
const exprt casted_init_byte_expr = typecast_exprt::conditional_cast(
typecast_exprt::conditional_cast(
init_byte_expr, unsignedbv_typet{init_type_as_bitvector->get_width()}),
operation_type);
values.push_back(casted_init_byte_expr);
for(size_t i = 1; i < size; ++i)
{
values.push_back(shl_exprt(
casted_init_byte_expr,
from_integer(config.ansi_c.char_width * i, size_type())));
}
return cast_or_reinterpret(
values.size() == 1 ? values[0]
: multi_ary_exprt(ID_bitor, values, operation_type),
output_type);
}
// Anything else. We don't know what to do with it. So, just cast.
return typecast_exprt::conditional_cast(init_byte_expr, output_type);
}