Skip to content

Commit d33a903

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 a4812d6 commit d33a903

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

src/solvers/lowering/byte_operators.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,54 @@ static struct_exprt bv_to_struct_expr(
146146
return struct_exprt{std::move(operands), struct_type};
147147
}
148148

149+
/// Convert a bitvector-typed expression \p bitvector_expr to a union-typed
150+
/// expression. See \ref bv_to_expr for an overview.
151+
static union_exprt bv_to_union_expr(
152+
const exprt &bitvector_expr,
153+
const union_typet &union_type,
154+
const endianness_mapt &endianness_map,
155+
const namespacet &ns)
156+
{
157+
const union_typet::componentst &components = union_type.components();
158+
159+
// empty union, handled the same way as done in expr_initializert
160+
if(components.empty())
161+
return union_exprt{irep_idt{}, nil_exprt{}, union_type};
162+
163+
const auto max_member_expr =
164+
find_maximum_union_component(exprt{}, union_type, ns);
165+
166+
std::size_t component_bits;
167+
if(max_member_expr.has_value())
168+
component_bits = numeric_cast_v<std::size_t>(max_member_expr->second);
169+
else
170+
component_bits = to_bitvector_type(bitvector_expr.type()).get_width();
171+
172+
if(component_bits == 0)
173+
{
174+
return union_exprt{components.front().get_name(),
175+
constant_exprt{irep_idt{}, components.front().type()},
176+
union_type};
177+
}
178+
179+
const auto bounds = map_bounds(endianness_map, 0, component_bits - 1);
180+
bitvector_typet type{bitvector_expr.type().id(), component_bits};
181+
const irep_idt &component_name =
182+
max_member_expr.has_value() ? max_member_expr->first.get_component_name()
183+
: components.front().get_name();
184+
const typet &component_type = max_member_expr.has_value()
185+
? max_member_expr->first.type()
186+
: components.front().type();
187+
return union_exprt{
188+
component_name,
189+
bv_to_expr(
190+
extractbits_exprt{bitvector_expr, bounds.ub, bounds.lb, std::move(type)},
191+
component_type,
192+
endianness_map,
193+
ns),
194+
union_type};
195+
}
196+
149197
/// Convert a bitvector-typed expression \p bitvector_expr to an array-typed
150198
/// expression. See \ref bv_to_expr for an overview.
151199
static array_exprt bv_to_array_expr(
@@ -334,6 +382,21 @@ static exprt bv_to_expr(
334382
result.type() = target_type;
335383
return std::move(result);
336384
}
385+
else if(target_type.id() == ID_union)
386+
{
387+
return bv_to_union_expr(
388+
bitvector_expr, to_union_type(target_type), endianness_map, ns);
389+
}
390+
else if(target_type.id() == ID_union_tag)
391+
{
392+
union_exprt result = bv_to_union_expr(
393+
bitvector_expr,
394+
ns.follow_tag(to_union_tag_type(target_type)),
395+
endianness_map,
396+
ns);
397+
result.type() = target_type;
398+
return std::move(result);
399+
}
337400
else if(target_type.id() == ID_array)
338401
{
339402
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)