Skip to content

Commit 051911e

Browse files
authored
Merge pull request #5252 from tautschnig/byte-extract-unbounded-union
Support byte-extracting from unions of non-fixed size
2 parents 62760f9 + 645f4e8 commit 051911e

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-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: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,59 @@ SCENARIO("byte_extract_lowering", "[core][solvers][lowering][byte_extract]")
9191
}
9292
}
9393

94+
GIVEN("A an unbounded union byte_extract over a bounded operand")
95+
{
96+
const exprt deadbeef = from_integer(0xdeadbeef, unsignedbv_typet(32));
97+
const byte_extract_exprt be1(
98+
ID_byte_extract_little_endian,
99+
deadbeef,
100+
from_integer(1, index_type()),
101+
union_typet(
102+
{{"unbounded_array",
103+
array_typet(
104+
unsignedbv_typet(16), exprt(ID_infinity, size_type()))}}));
105+
106+
THEN("byte_extract lowering does not raise an exception")
107+
{
108+
const exprt lower_be1 = lower_byte_extract(be1, ns);
109+
110+
REQUIRE(!has_subexpr(lower_be1, ID_byte_extract_little_endian));
111+
REQUIRE(lower_be1.type() == be1.type());
112+
113+
byte_extract_exprt be2 = be1;
114+
be2.id(ID_byte_extract_big_endian);
115+
const exprt lower_be2 = lower_byte_extract(be2, ns);
116+
117+
REQUIRE(!has_subexpr(lower_be2, ID_byte_extract_big_endian));
118+
REQUIRE(lower_be2.type() == be2.type());
119+
}
120+
}
121+
122+
GIVEN("A an empty union byte_extract over a bounded operand")
123+
{
124+
const exprt deadbeef = from_integer(0xdeadbeef, unsignedbv_typet(32));
125+
const byte_extract_exprt be1(
126+
ID_byte_extract_little_endian,
127+
deadbeef,
128+
from_integer(1, index_type()),
129+
union_typet{});
130+
131+
THEN("byte_extract lowering does not raise an exception")
132+
{
133+
const exprt lower_be1 = lower_byte_extract(be1, ns);
134+
135+
REQUIRE(!has_subexpr(lower_be1, ID_byte_extract_little_endian));
136+
REQUIRE(lower_be1.type() == be1.type());
137+
138+
byte_extract_exprt be2 = be1;
139+
be2.id(ID_byte_extract_big_endian);
140+
const exprt lower_be2 = lower_byte_extract(be2, ns);
141+
142+
REQUIRE(!has_subexpr(lower_be2, ID_byte_extract_big_endian));
143+
REQUIRE(lower_be2.type() == be2.type());
144+
}
145+
}
146+
94147
GIVEN("A a byte_extract from a string constant")
95148
{
96149
string_constantt s("ABCD");

0 commit comments

Comments
 (0)