Skip to content

Commit 96b0484

Browse files
committed
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 c446ecf commit 96b0484

File tree

14 files changed

+279
-45
lines changed

14 files changed

+279
-45
lines changed

regression/cbmc/Float22/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE broken-smt-backend
1+
CORE
22
main.c
33
--floatbv
44
^EXIT=0$

regression/cbmc/Pointer_Arithmetic19/test.desc

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
CORE new-smt-backend
22
main.c
33
--program-only
4-
ASSERT\(byte_extract_little_endian\(\{ 42, 43 \}, POINTER_OFFSET\(p!0@1#2\), signed int\) == 43\)$
54
^EXIT=0$
65
^SIGNAL=0$
76
--

regression/cbmc/array-cell-sensitivity15/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ access.c
33
--program-only
44
^EXIT=0$
55
^SIGNAL=0$
6-
s!0@1#1\.\.n == \{ s!0@1#1\.\.n\[\[0\]\], s!0@1#1\.\.n\[\[1\]\], s!0@1#1\.\.n\[\[2\]\], s!0@1#1\.\.n\[\[3\]\] } WITH \[\(.*\)i!0@1#2:=k!0@1#1\]
6+
s!0@1#2\.\.n == \{ s!0@1#1\.\.n\[\[0\]\], s!0@1#1\.\.n\[\[1\]\], s!0@1#1\.\.n\[\[2\]\], s!0@1#1\.\.n\[\[3\]\] } WITH \[\(.*\)i!0@1#2:=k!0@1#1\]
77
--
88
byte_update
99
--

regression/cbmc/array-cell-sensitivity8/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ main::1::array!0@1#2\[\[6\]\] =
1010
main::1::array!0@1#2\[\[7\]\] =
1111
main::1::array!0@1#2\[\[8\]\] =
1212
main::1::array!0@1#2\[\[9\]\] =
13-
main::1::array!0@1#2 =.*byte_extract_little_endian
13+
main::1::array!0@1#3 =.*byte_extract_little_endian
1414
main::1::array!0@1#3\[\[0\]\] =
1515
main::1::array!0@1#3\[\[1\]\] =
1616
main::1::array!0@1#3\[\[2\]\] =

regression/cbmc/field-sensitivity9/test.desc

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
CORE
22
test.c
33
--show-vcc
4-
main::1::a1!0@1#1 =
5-
main::1::a2!0@1#1 =
4+
main::1::a1!0@1#2 =
5+
main::1::a2!0@1#2 =
66
main::1::a1!0@1#2\.\.x =
77
main::1::a1!0@1#2\.\.y =
88
main::1::a1!0@1#2\.\.z =

regression/cbmc/union18/main.c

+15
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

+12
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

+174-23
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ 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>
14+
#include <util/pointer_offset_size.h>
1215
#include <util/simplify_expr.h>
13-
#include <util/std_expr.h>
1416

1517
#include "goto_symex_state.h"
1618
#include "symex_target.h"
@@ -26,7 +28,7 @@ exprt field_sensitivityt::apply(
2628
if(write)
2729
return std::move(ssa_expr);
2830
else
29-
return get_fields(ns, state, ssa_expr);
31+
return get_fields(ns, state, ssa_expr, true);
3032
}
3133

3234
exprt field_sensitivityt::apply(
@@ -43,7 +45,7 @@ exprt field_sensitivityt::apply(
4345

4446
if(!write && is_ssa_expr(expr))
4547
{
46-
return get_fields(ns, state, to_ssa_expr(expr));
48+
return get_fields(ns, state, to_ssa_expr(expr), true);
4749
}
4850
else if(
4951
!write && expr.id() == ID_member &&
@@ -69,7 +71,9 @@ exprt field_sensitivityt::apply(
6971
if(
7072
is_ssa_expr(member.struct_op()) &&
7173
(member.struct_op().type().id() == ID_struct ||
72-
member.struct_op().type().id() == ID_struct_tag))
74+
member.struct_op().type().id() == ID_struct_tag ||
75+
member.struct_op().type().id() == ID_union ||
76+
member.struct_op().type().id() == ID_union_tag))
7377
{
7478
// place the entire member expression, not just the struct operand, in an
7579
// SSA expression
@@ -85,6 +89,40 @@ exprt field_sensitivityt::apply(
8589
return std::move(tmp);
8690
}
8791
}
92+
else if(
93+
!write && (expr.id() == ID_byte_extract_little_endian ||
94+
expr.id() == ID_byte_extract_big_endian))
95+
{
96+
const byte_extract_exprt &be = to_byte_extract_expr(expr);
97+
if(
98+
(be.op().type().id() == ID_union ||
99+
be.op().type().id() == ID_union_tag) &&
100+
is_ssa_expr(be.op()) && be.offset().is_constant())
101+
{
102+
const union_typet &type = to_union_type(ns.follow(be.op().type()));
103+
for(const auto &comp : type.components())
104+
{
105+
ssa_exprt tmp = to_ssa_expr(be.op());
106+
bool was_l2 = !tmp.get_level_2().empty();
107+
tmp.remove_level_2();
108+
const member_exprt member{tmp.get_original_expr(), comp};
109+
auto recursive_member =
110+
get_subexpression_at_offset(member, be.offset(), be.type(), ns);
111+
if(
112+
recursive_member.has_value() &&
113+
(recursive_member->id() == ID_member ||
114+
recursive_member->id() == ID_index))
115+
{
116+
tmp.type() = be.type();
117+
tmp.set_expression(*recursive_member);
118+
if(was_l2)
119+
return state.rename(std::move(tmp), ns).get();
120+
else
121+
return std::move(tmp);
122+
}
123+
}
124+
}
125+
}
88126
#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
89127
else if(expr.id() == ID_index)
90128
{
@@ -141,7 +179,7 @@ exprt field_sensitivityt::apply(
141179
{
142180
// Expand the array and return `{array[0]; array[1]; ...}[index]`
143181
exprt expanded_array =
144-
get_fields(ns, state, to_ssa_expr(index.array()));
182+
get_fields(ns, state, to_ssa_expr(index.array()), true);
145183
return index_exprt{std::move(expanded_array), index.index()};
146184
}
147185
}
@@ -154,33 +192,48 @@ exprt field_sensitivityt::apply(
154192
exprt field_sensitivityt::get_fields(
155193
const namespacet &ns,
156194
goto_symex_statet &state,
157-
const ssa_exprt &ssa_expr) const
195+
const ssa_exprt &ssa_expr,
196+
bool disjoined_fields_only) const
158197
{
159-
if(ssa_expr.type().id() == ID_struct || ssa_expr.type().id() == ID_struct_tag)
198+
if(
199+
ssa_expr.type().id() == ID_struct ||
200+
ssa_expr.type().id() == ID_struct_tag ||
201+
(!disjoined_fields_only && (ssa_expr.type().id() == ID_union ||
202+
ssa_expr.type().id() == ID_union_tag)))
160203
{
161-
const struct_typet &type = to_struct_type(ns.follow(ssa_expr.type()));
204+
const struct_union_typet &type =
205+
to_struct_union_type(ns.follow(ssa_expr.type()));
162206
const struct_union_typet::componentst &components = type.components();
163207

164-
struct_exprt::operandst fields;
208+
exprt::operandst fields;
165209
fields.reserve(components.size());
166210

167-
const exprt &struct_op = ssa_expr.get_original_expr();
211+
const exprt &compound_op = ssa_expr.get_original_expr();
168212

169213
for(const auto &comp : components)
170214
{
171215
ssa_exprt tmp = ssa_expr;
172216
bool was_l2 = !tmp.get_level_2().empty();
173217
tmp.remove_level_2();
174-
tmp.set_expression(member_exprt{struct_op, comp.get_name(), comp.type()});
218+
tmp.set_expression(
219+
member_exprt{compound_op, comp.get_name(), comp.type()});
220+
exprt field = get_fields(ns, state, tmp, disjoined_fields_only);
175221
if(was_l2)
176222
{
177-
fields.push_back(state.rename(get_fields(ns, state, tmp), ns).get());
223+
fields.emplace_back(state.rename(std::move(field), ns).get());
178224
}
179225
else
180-
fields.push_back(get_fields(ns, state, tmp));
226+
fields.emplace_back(std::move(field));
181227
}
182228

183-
return struct_exprt(std::move(fields), ssa_expr.type());
229+
if(
230+
disjoined_fields_only && (ssa_expr.type().id() == ID_struct ||
231+
ssa_expr.type().id() == ID_struct_tag))
232+
{
233+
return struct_exprt{std::move(fields), ssa_expr.type()};
234+
}
235+
else
236+
return field_sensitive_ssa_exprt{ssa_expr, std::move(fields)};
184237
}
185238
#ifdef ENABLE_ARRAY_FIELD_SENSITIVITY
186239
else if(
@@ -207,15 +260,19 @@ exprt field_sensitivityt::get_fields(
207260
bool was_l2 = !tmp.get_level_2().empty();
208261
tmp.remove_level_2();
209262
tmp.set_expression(index);
263+
exprt element = get_fields(ns, state, tmp, disjoined_fields_only);
210264
if(was_l2)
211265
{
212-
elements.push_back(state.rename(get_fields(ns, state, tmp), ns).get());
266+
elements.emplace_back(state.rename(std::move(element), ns).get());
213267
}
214268
else
215-
elements.push_back(get_fields(ns, state, tmp));
269+
elements.emplace_back(std::move(element));
216270
}
217271

218-
return array_exprt(std::move(elements), type);
272+
if(disjoined_fields_only)
273+
return array_exprt(std::move(elements), type);
274+
else
275+
return field_sensitive_ssa_exprt{ssa_expr, std::move(elements)};
219276
}
220277
#endif // ENABLE_ARRAY_FIELD_SENSITIVITY
221278
else
@@ -230,7 +287,7 @@ void field_sensitivityt::field_assignments(
230287
symex_targett &target,
231288
bool allow_pointer_unsoundness) const
232289
{
233-
const exprt lhs_fs = get_fields(ns, state, lhs);
290+
const exprt lhs_fs = get_fields(ns, state, lhs, false);
234291

235292
if(lhs != lhs_fs)
236293
{
@@ -292,10 +349,67 @@ void field_sensitivityt::field_assignments_rec(
292349
exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
293350
for(const auto &comp : components)
294351
{
295-
const exprt member_rhs =
296-
simplify_opt(member_exprt{ssa_rhs, comp.get_name(), comp.type()}, ns);
352+
const exprt member_rhs = apply(
353+
ns,
354+
state,
355+
simplify_opt(member_exprt{ssa_rhs, comp.get_name(), comp.type()}, ns),
356+
false);
357+
const exprt &member_lhs = *fs_it;
358+
359+
if(
360+
auto fs_ssa =
361+
expr_try_dynamic_cast<field_sensitive_ssa_exprt>(member_lhs))
362+
{
363+
field_assignments_rec(
364+
ns,
365+
state,
366+
fs_ssa->get_object_ssa(),
367+
member_rhs,
368+
target,
369+
allow_pointer_unsoundness);
370+
}
371+
372+
field_assignments_rec(
373+
ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
374+
++fs_it;
375+
}
376+
}
377+
else if(
378+
ssa_rhs.type().id() == ID_union || ssa_rhs.type().id() == ID_union_tag)
379+
{
380+
const union_typet &type = to_union_type(ns.follow(ssa_rhs.type()));
381+
const struct_union_typet::componentst &components = type.components();
382+
383+
PRECONDITION(
384+
components.empty() ||
385+
(lhs_fs.has_operands() && lhs_fs.operands().size() == components.size()));
386+
387+
exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
388+
for(const auto &comp : components)
389+
{
390+
const exprt member_rhs = apply(
391+
ns,
392+
state,
393+
simplify_opt(
394+
make_byte_extract(
395+
ssa_rhs, from_integer(0, c_index_type()), comp.type()),
396+
ns),
397+
false);
297398
const exprt &member_lhs = *fs_it;
298399

400+
if(
401+
auto fs_ssa =
402+
expr_try_dynamic_cast<field_sensitive_ssa_exprt>(member_lhs))
403+
{
404+
field_assignments_rec(
405+
ns,
406+
state,
407+
fs_ssa->get_object_ssa(),
408+
member_rhs,
409+
target,
410+
allow_pointer_unsoundness);
411+
}
412+
299413
field_assignments_rec(
300414
ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
301415
++fs_it;
@@ -314,10 +428,27 @@ void field_sensitivityt::field_assignments_rec(
314428
exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
315429
for(std::size_t i = 0; i < array_size; ++i)
316430
{
317-
const exprt index_rhs = simplify_opt(
318-
index_exprt{ssa_rhs, from_integer(i, type->index_type())}, ns);
431+
const exprt index_rhs = apply(
432+
ns,
433+
state,
434+
simplify_opt(
435+
index_exprt{ssa_rhs, from_integer(i, type->index_type())}, ns),
436+
false);
319437
const exprt &index_lhs = *fs_it;
320438

439+
if(
440+
auto fs_ssa =
441+
expr_try_dynamic_cast<field_sensitive_ssa_exprt>(index_lhs))
442+
{
443+
field_assignments_rec(
444+
ns,
445+
state,
446+
fs_ssa->get_object_ssa(),
447+
index_rhs,
448+
target,
449+
allow_pointer_unsoundness);
450+
}
451+
321452
field_assignments_rec(
322453
ns, state, index_lhs, index_rhs, target, allow_pointer_unsoundness);
323454
++fs_it;
@@ -333,6 +464,17 @@ void field_sensitivityt::field_assignments_rec(
333464
exprt::operandst::const_iterator fs_it = lhs_fs.operands().begin();
334465
for(const exprt &op : ssa_rhs.operands())
335466
{
467+
if(auto fs_ssa = expr_try_dynamic_cast<field_sensitive_ssa_exprt>(*fs_it))
468+
{
469+
field_assignments_rec(
470+
ns,
471+
state,
472+
fs_ssa->get_object_ssa(),
473+
op,
474+
target,
475+
allow_pointer_unsoundness);
476+
}
477+
336478
field_assignments_rec(
337479
ns, state, *fs_it, op, target, allow_pointer_unsoundness);
338480
++fs_it;
@@ -344,7 +486,9 @@ void field_sensitivityt::field_assignments_rec(
344486
}
345487
}
346488

347-
bool field_sensitivityt::is_divisible(const ssa_exprt &expr) const
489+
bool field_sensitivityt::is_divisible(
490+
const ssa_exprt &expr,
491+
bool disjoined_fields_only) const
348492
{
349493
if(expr.type().id() == ID_struct || expr.type().id() == ID_struct_tag)
350494
return true;
@@ -360,6 +504,13 @@ bool field_sensitivityt::is_divisible(const ssa_exprt &expr) const
360504
}
361505
#endif
362506

507+
if(
508+
!disjoined_fields_only &&
509+
(expr.type().id() == ID_union || expr.type().id() == ID_union_tag))
510+
{
511+
return true;
512+
}
513+
363514
return false;
364515
}
365516

0 commit comments

Comments
 (0)