Skip to content

Commit 7ac30d5

Browse files
committed
Support byte-extracting from unions of non-fixed size
Various other types (including structs) were handled already, just unions were missing. Treat them much like a single-member struct.
1 parent b44360c commit 7ac30d5

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

src/solvers/lowering/byte_operators.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,53 @@ static struct_exprt bv_to_struct_expr(
141141
return struct_exprt{std::move(operands), struct_type};
142142
}
143143

144+
/// Convert a bitvector-typed expression \p bitvector_expr to a union-typed
145+
/// expression. See \ref bv_to_expr for an overview.
146+
static union_exprt bv_to_union_expr(
147+
const exprt &bitvector_expr,
148+
const union_typet &union_type,
149+
const endianness_mapt &endianness_map,
150+
const namespacet &ns)
151+
{
152+
const union_typet::componentst &components = union_type.components();
153+
154+
// empty union, handled the same way as done in expr_initializert
155+
if(components.empty())
156+
return union_exprt{irep_idt{}, nil_exprt{}, union_type};
157+
158+
const auto widest_member = find_widest_union_component(union_type, ns);
159+
160+
std::size_t component_bits;
161+
if(widest_member.has_value())
162+
component_bits = numeric_cast_v<std::size_t>(widest_member->second);
163+
else
164+
component_bits = to_bitvector_type(bitvector_expr.type()).get_width();
165+
166+
if(component_bits == 0)
167+
{
168+
return union_exprt{components.front().get_name(),
169+
constant_exprt{irep_idt{}, components.front().type()},
170+
union_type};
171+
}
172+
173+
const auto bounds = map_bounds(endianness_map, 0, component_bits - 1);
174+
bitvector_typet type{bitvector_expr.type().id(), component_bits};
175+
const irep_idt &component_name = widest_member.has_value()
176+
? widest_member->first.get_name()
177+
: components.front().get_name();
178+
const typet &component_type = widest_member.has_value()
179+
? widest_member->first.type()
180+
: components.front().type();
181+
return union_exprt{
182+
component_name,
183+
bv_to_expr(
184+
extractbits_exprt{bitvector_expr, bounds.ub, bounds.lb, std::move(type)},
185+
component_type,
186+
endianness_map,
187+
ns),
188+
union_type};
189+
}
190+
144191
/// Convert a bitvector-typed expression \p bitvector_expr to an array-typed
145192
/// expression. See \ref bv_to_expr for an overview.
146193
static array_exprt bv_to_array_expr(
@@ -329,6 +376,21 @@ static exprt bv_to_expr(
329376
result.type() = target_type;
330377
return std::move(result);
331378
}
379+
else if(target_type.id() == ID_union)
380+
{
381+
return bv_to_union_expr(
382+
bitvector_expr, to_union_type(target_type), endianness_map, ns);
383+
}
384+
else if(target_type.id() == ID_union_tag)
385+
{
386+
union_exprt result = bv_to_union_expr(
387+
bitvector_expr,
388+
ns.follow_tag(to_union_tag_type(target_type)),
389+
endianness_map,
390+
ns);
391+
result.type() = target_type;
392+
return std::move(result);
393+
}
332394
else if(target_type.id() == ID_array)
333395
{
334396
return bv_to_array_expr(

unit/solvers/lowering/byte_operators.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,34 @@ SCENARIO("byte_extract_lowering", "[core][solvers][lowering][byte_extract]")
6363
}
6464
}
6565

66+
GIVEN("A an unbounded byte_extract over a bounded operand")
67+
{
68+
const exprt deadbeef = from_integer(0xdeadbeef, unsignedbv_typet(32));
69+
const byte_extract_exprt be1(
70+
ID_byte_extract_little_endian,
71+
deadbeef,
72+
from_integer(1, index_type()),
73+
union_typet(
74+
{{"unbounded_array",
75+
array_typet(
76+
unsignedbv_typet(16), exprt(ID_infinity, size_type()))}}));
77+
78+
THEN("byte_extract lowering does not raise an exception")
79+
{
80+
const exprt lower_be1 = lower_byte_extract(be1, ns);
81+
82+
REQUIRE(!has_subexpr(lower_be1, ID_byte_extract_little_endian));
83+
REQUIRE(lower_be1.type() == be1.type());
84+
85+
byte_extract_exprt be2 = be1;
86+
be2.id(ID_byte_extract_big_endian);
87+
const exprt lower_be2 = lower_byte_extract(be2, ns);
88+
89+
REQUIRE(!has_subexpr(lower_be2, ID_byte_extract_big_endian));
90+
REQUIRE(lower_be2.type() == be2.type());
91+
}
92+
}
93+
6694
GIVEN("A an unbounded byte_extract over a bounded operand")
6795
{
6896
const exprt deadbeef = from_integer(0xdeadbeef, unsignedbv_typet(32));

0 commit comments

Comments
 (0)