Skip to content

Commit c6c2eb5

Browse files
committed
WIP: Field sensitivity for unions
Field-sensitive tracking of unions permits propagating constants even when those do not affect the full width of the union (implying that some bits remain non-constant).
1 parent 05f2905 commit c6c2eb5

File tree

6 files changed

+157
-39
lines changed

6 files changed

+157
-39
lines changed

regression/cbmc/union18/main.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <assert.h>
2+
3+
union u_type
4+
{
5+
int i;
6+
char ch;
7+
};
8+
9+
int main()
10+
{
11+
union u_type u;
12+
13+
u.ch = 2;
14+
assert(u.ch == 2);
15+
}

regression/cbmc/union18/test.desc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
4+
^Generated 1 VCC\(s\), 0 remaining after simplification$
5+
^VERIFICATION SUCCESSFUL$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
^warning: ignoring
10+
--
11+
This should be fully solved by constant propagation when union field-sensitivity
12+
is in place.

src/goto-symex/field_sensitivity.cpp

Lines changed: 118 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Author: Michael Tautschnig
99
#include "field_sensitivity.h"
1010

1111
#include <util/arith_tools.h>
12+
#include <util/byte_operators.h>
13+
#include <util/c_types.h>
1214
#include <util/simplify_expr.h>
1315
#include <util/std_expr.h>
1416

@@ -17,6 +19,27 @@ Author: Michael Tautschnig
1719

1820
#define ENABLE_ARRAY_FIELD_SENSITIVITY
1921

22+
/// Produce a `struct_exprt` or an `array_exprt` when multiple fields are
23+
/// provided; else just return the original expression.
24+
static exprt fields_to_expr(std::vector<exprt> &&operands, const ssa_exprt &original_ssa_expr)
25+
{
26+
const typet &type = original_ssa_expr.type();
27+
if(type.id() == ID_struct || type.id() == ID_struct_tag)
28+
{
29+
return struct_exprt{std::move(operands), type};
30+
}
31+
else if(type.id() == ID_array)
32+
{
33+
return array_exprt{std::move(operands), type};
34+
}
35+
else
36+
{
37+
DATA_INVARIANT(operands.size() == 1, "only arrays or structs yield multiple fields");
38+
DATA_INVARIANT(operands.front() == original_ssa_expr, "expression should be unmodified");
39+
return std::move(operands.front());
40+
}
41+
}
42+
2043
exprt field_sensitivityt::apply(
2144
const namespacet &ns,
2245
goto_symex_statet &state,
@@ -26,7 +49,7 @@ exprt field_sensitivityt::apply(
2649
if(!run_apply || write)
2750
return std::move(ssa_expr);
2851
else
29-
return get_fields(ns, state, ssa_expr);
52+
return fields_to_expr(get_fields(ns, state, ssa_expr), ssa_expr);
3053
}
3154

3255
exprt field_sensitivityt::apply(
@@ -72,7 +95,9 @@ exprt field_sensitivityt::apply(
7295
if(
7396
is_ssa_expr(member.struct_op()) &&
7497
(member.struct_op().type().id() == ID_struct ||
75-
member.struct_op().type().id() == ID_struct_tag))
98+
member.struct_op().type().id() == ID_struct_tag ||
99+
member.struct_op().type().id() == ID_union ||
100+
member.struct_op().type().id() == ID_union_tag))
76101
{
77102
// place the entire member expression, not just the struct operand, in an
78103
// SSA expression
@@ -88,6 +113,35 @@ exprt field_sensitivityt::apply(
88113
return std::move(tmp);
89114
}
90115
}
116+
else if(
117+
expr.id() == ID_byte_extract_little_endian ||
118+
expr.id() == ID_byte_extract_big_endian)
119+
{
120+
const byte_extract_exprt &be = to_byte_extract_expr(expr);
121+
if(
122+
(be.op().type().id() == ID_union ||
123+
be.op().type().id() == ID_union_tag) &&
124+
be.offset().is_zero())
125+
{
126+
const union_typet &type = to_union_type(ns.follow(be.op().type()));
127+
for(const auto &comp : type.components())
128+
{
129+
if(comp.type() == be.type())
130+
{
131+
ssa_exprt tmp = to_ssa_expr(be.op());
132+
bool was_l2 = !tmp.get_level_2().empty();
133+
tmp.remove_level_2();
134+
member_exprt member{tmp.get_original_expr(), comp};
135+
tmp.type() = be.type();
136+
tmp.set_expression(member);
137+
if(was_l2)
138+
return state.rename(std::move(tmp), ns).get();
139+
else
140+
return std::move(tmp);
141+
}
142+
}
143+
}
144+
}
91145
#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
92146
else if(expr.id() == ID_index)
93147
{
@@ -143,8 +197,9 @@ exprt field_sensitivityt::apply(
143197
else if(!write)
144198
{
145199
// Expand the array and return `{array[0]; array[1]; ...}[index]`
200+
const ssa_exprt &array = to_ssa_expr(index.array());
146201
exprt expanded_array =
147-
get_fields(ns, state, to_ssa_expr(index.array()));
202+
fields_to_expr(get_fields(ns, state, array), array);
148203
return index_exprt{std::move(expanded_array), index.index()};
149204
}
150205
}
@@ -154,36 +209,39 @@ exprt field_sensitivityt::apply(
154209
return expr;
155210
}
156211

157-
exprt field_sensitivityt::get_fields(
212+
exprt::operandst field_sensitivityt::get_fields(
158213
const namespacet &ns,
159214
goto_symex_statet &state,
160215
const ssa_exprt &ssa_expr) const
161216
{
162-
if(ssa_expr.type().id() == ID_struct || ssa_expr.type().id() == ID_struct_tag)
217+
if(ssa_expr.type().id() == ID_struct || ssa_expr.type().id() == ID_struct_tag ||
218+
(do_union &&
219+
(ssa_expr.type().id() == ID_union || ssa_expr.type().id() == ID_union_tag)))
163220
{
164-
const struct_typet &type = to_struct_type(ns.follow(ssa_expr.type()));
221+
const struct_union_typet &type = to_struct_union_type(ns.follow(ssa_expr.type()));
165222
const struct_union_typet::componentst &components = type.components();
166223

167-
struct_exprt::operandst fields;
224+
exprt::operandst fields;
168225
fields.reserve(components.size());
169226

170-
const exprt &struct_op = ssa_expr.get_original_expr();
227+
const exprt &compound_op = ssa_expr.get_original_expr();
171228

172229
for(const auto &comp : components)
173230
{
174231
ssa_exprt tmp = ssa_expr;
175232
bool was_l2 = !tmp.get_level_2().empty();
176233
tmp.remove_level_2();
177-
tmp.set_expression(member_exprt{struct_op, comp.get_name(), comp.type()});
234+
tmp.set_expression(member_exprt{compound_op, comp.get_name(), comp.type()});
235+
exprt field = fields_to_expr(get_fields(ns, state, tmp), tmp);
178236
if(was_l2)
179237
{
180-
fields.push_back(state.rename(get_fields(ns, state, tmp), ns).get());
238+
fields.emplace_back(state.rename(std::move(field), ns).get());
181239
}
182240
else
183-
fields.push_back(get_fields(ns, state, tmp));
241+
fields.emplace_back(std::move(field));
184242
}
185243

186-
return struct_exprt(std::move(fields), ssa_expr.type());
244+
return fields;
187245
}
188246
#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
189247
else if(
@@ -210,19 +268,20 @@ exprt field_sensitivityt::get_fields(
210268
bool was_l2 = !tmp.get_level_2().empty();
211269
tmp.remove_level_2();
212270
tmp.set_expression(index);
271+
exprt element = fields_to_expr(get_fields(ns, state, tmp), tmp);
213272
if(was_l2)
214273
{
215-
elements.push_back(state.rename(get_fields(ns, state, tmp), ns).get());
274+
elements.emplace_back(state.rename(std::move(element), ns).get());
216275
}
217276
else
218-
elements.push_back(get_fields(ns, state, tmp));
277+
elements.emplace_back(std::move(element));
219278
}
220279

221-
return array_exprt(std::move(elements), type);
280+
return elements;
222281
}
223282
#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
224283
else
225-
return ssa_expr;
284+
return {ssa_expr};
226285
}
227286

228287
void field_sensitivityt::field_assignments(
@@ -233,9 +292,13 @@ void field_sensitivityt::field_assignments(
233292
symex_targett &target,
234293
bool allow_pointer_unsoundness)
235294
{
236-
const exprt lhs_fs = apply(ns, state, lhs, false);
295+
assert(run_apply);
296+
bool do_union_bak = do_union;
297+
do_union = true;
298+
const exprt::operandst lhs_fs = get_fields(ns, state, lhs);
299+
do_union = do_union_bak;
237300

238-
if(lhs != lhs_fs)
301+
if(lhs_fs.empty() || lhs != lhs_fs.front())
239302
{
240303
bool run_apply_bak = run_apply;
241304
run_apply = false;
@@ -258,16 +321,16 @@ void field_sensitivityt::field_assignments(
258321
void field_sensitivityt::field_assignments_rec(
259322
const namespacet &ns,
260323
goto_symex_statet &state,
261-
const exprt &lhs_fs,
324+
const exprt::operandst &lhs_fs,
262325
const exprt &ssa_rhs,
263326
symex_targett &target,
264327
bool allow_pointer_unsoundness)
265328
{
266-
if(is_ssa_expr(lhs_fs))
329+
if(lhs_fs.size() == 1 && is_ssa_expr(lhs_fs.front()))
267330
{
268331
const ssa_exprt ssa_lhs = state
269332
.assignment(
270-
to_ssa_expr(lhs_fs),
333+
to_ssa_expr(lhs_fs.front()),
271334
ssa_rhs,
272335
ns,
273336
true,
@@ -293,15 +356,39 @@ void field_sensitivityt::field_assignments_rec(
293356

294357
PRECONDITION(
295358
components.empty() ||
296-
(lhs_fs.has_operands() && lhs_fs.operands().size() == components.size()));
359+
lhs_fs.size() == components.size());
297360

298-
exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
361+
exprt::operandst::const_iterator fs_it = lhs_fs.begin();
299362
for(const auto &comp : components)
300363
{
301364
const exprt member_rhs =
302365
simplify_opt(member_exprt{ssa_rhs, comp.get_name(), comp.type()}, ns);
303366
const exprt &member_lhs = *fs_it;
304367

368+
field_assignments_rec(
369+
ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
370+
++fs_it;
371+
}
372+
}
373+
else if(
374+
ssa_rhs.type().id() == ID_union || ssa_rhs.type().id() == ID_union_tag)
375+
{
376+
const union_typet &type = to_union_type(ns.follow(ssa_rhs.type()));
377+
const struct_union_typet::componentst &components = type.components();
378+
379+
PRECONDITION(
380+
components.empty() ||
381+
lhs_fs.size() == components.size());
382+
383+
exprt::operandst::const_iterator fs_it = lhs_fs.begin();
384+
for(const auto &comp : components)
385+
{
386+
const exprt member_rhs = simplify_opt(
387+
make_byte_extract(
388+
ssa_rhs, from_integer(0, c_index_type()), comp.type()),
389+
ns);
390+
const exprt &member_lhs = *fs_it;
391+
305392
field_assignments_rec(
306393
ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
307394
++fs_it;
@@ -312,12 +399,12 @@ void field_sensitivityt::field_assignments_rec(
312399
{
313400
const std::size_t array_size =
314401
numeric_cast_v<std::size_t>(to_constant_expr(type->size()));
315-
PRECONDITION(lhs_fs.operands().size() == array_size);
402+
PRECONDITION(lhs_fs.size() == array_size);
316403

317404
if(array_size > max_field_sensitivity_array_size)
318405
return;
319406

320-
exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
407+
exprt::operandst::const_iterator fs_it = lhs_fs.begin();
321408
for(std::size_t i = 0; i < array_size; ++i)
322409
{
323410
const exprt index_rhs = simplify_opt(
@@ -330,24 +417,20 @@ void field_sensitivityt::field_assignments_rec(
330417
}
331418
}
332419
#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
333-
else if(lhs_fs.has_operands())
420+
else
334421
{
335422
PRECONDITION(
336423
ssa_rhs.has_operands() &&
337-
lhs_fs.operands().size() == ssa_rhs.operands().size());
424+
lhs_fs.size() == ssa_rhs.operands().size());
338425

339-
exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
426+
exprt::operandst::const_iterator fs_it = lhs_fs.begin();
340427
for(const exprt &op : ssa_rhs.operands())
341428
{
342429
field_assignments_rec(
343430
ns, state, *fs_it, op, target, allow_pointer_unsoundness);
344431
++fs_it;
345432
}
346433
}
347-
else
348-
{
349-
UNREACHABLE;
350-
}
351434
}
352435

353436
bool field_sensitivityt::is_divisible(const ssa_exprt &expr) const
@@ -366,6 +449,9 @@ bool field_sensitivityt::is_divisible(const ssa_exprt &expr) const
366449
}
367450
#endif
368451

452+
if(expr.type().id() == ID_union || expr.type().id() == ID_union_tag)
453+
return true;
454+
369455
return false;
370456
}
371457

src/goto-symex/field_sensitivity.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,15 @@ class field_sensitivityt
134134
ssa_exprt expr,
135135
bool write) const;
136136

137-
/// Compute an expression representing the individual components of a
138-
/// field-sensitive SSA representation of \p ssa_expr.
137+
/// Compute a sequence holding the individual components of a field-sensitive
138+
/// SSA representation of \p ssa_expr.
139139
/// \param ns: a namespace to resolve type symbols/tag types
140140
/// \param [in,out] state: symbolic execution state
141141
/// \param ssa_expr: the expression to evaluate
142-
/// \return Expanded expression; for example, for a \p ssa_expr of some struct
143-
/// type, a `struct_exprt` with each component now being an SSA expression
144-
/// is built.
145-
exprt get_fields(
142+
/// \return Expanded fields or vector containing just \p ssa_expr. For
143+
/// example, for a \p ssa_expr of some struct type, all operands (as SSA
144+
/// expressions) of what could then be turned into a `struct_exprt`.
145+
exprt::operandst get_fields(
146146
const namespacet &ns,
147147
goto_symex_statet &state,
148148
const ssa_exprt &ssa_expr) const;
@@ -159,6 +159,8 @@ class field_sensitivityt
159159
private:
160160
/// whether or not to invoke \ref field_sensitivityt::apply
161161
bool run_apply = true;
162+
/// whether or not to expand unions in \ref field_sensitivityt::get_fields
163+
bool do_union = false;
162164

163165
const std::size_t max_field_sensitivity_array_size;
164166

src/goto-symex/goto_symex_state.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,7 @@ ssa_exprt goto_symex_statet::declare(ssa_exprt ssa, const namespacet &ns)
836836
}
837837

838838
// L2 renaming
839+
// fix get_fields to obtain union support
839840
const exprt fields = field_sensitivity.get_fields(ns, *this, ssa);
840841
fields.visit_pre([this](const exprt &e) {
841842
if(auto l1_symbol = expr_try_dynamic_cast<symbol_exprt>(e))

src/goto-symex/symex_dead.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ static void remove_l1_object_rec(
4848
// identifier is still live.
4949
state.drop_l1_name(l1_identifier);
5050
}
51-
else if(l1_expr.id() == ID_array || l1_expr.id() == ID_struct)
51+
else if(
52+
l1_expr.id() == ID_array || l1_expr.id() == ID_struct ||
53+
l1_expr.id() == ID_union)
5254
{
5355
for(const auto &op : l1_expr.operands())
5456
remove_l1_object_rec(state, op, ns);

0 commit comments

Comments
 (0)