Skip to content

Commit e2265db

Browse files
committed
byte_extract lowering: also use a lower bound
We already limited extraction at an uppper bound, but when we know the offset we can also start as late as possible. For structs, for example, we now do not expand components that will be irrelevant to the final result.
1 parent 5c38606 commit e2265db

File tree

1 file changed

+75
-7
lines changed

1 file changed

+75
-7
lines changed

src/solvers/lowering/byte_operators.cpp

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Author: Daniel Kroening, kroening@kroening.com
2121
static exprt unpack_rec(
2222
const exprt &src,
2323
bool little_endian,
24+
const exprt &offset_bytes,
2425
const exprt &max_bytes,
2526
const namespacet &ns,
2627
bool unpack_byte_array = false);
@@ -31,6 +32,8 @@ static exprt unpack_rec(
3132
/// a constant value, otherwise we fail with an exception
3233
/// \param element_width: bit width of array/vector elements
3334
/// \param little_endian: true, iff assumed endianness is little-endian
35+
/// \param offset_bytes: if not nil, bytes prior to this offset will be filled
36+
/// with nil values
3437
/// \param max_bytes: if not nil, use as upper bound of the number of bytes to
3538
/// unpack
3639
/// \param ns: namespace for type lookups
@@ -40,6 +43,7 @@ static array_exprt unpack_array_vector(
4043
const exprt &src_size,
4144
const mp_integer &element_width,
4245
bool little_endian,
46+
const exprt &offset_bytes,
4347
const exprt &max_bytes,
4448
const namespacet &ns)
4549
{
@@ -51,6 +55,7 @@ static array_exprt unpack_array_vector(
5155
throw non_const_array_sizet(src.type(), max_bytes);
5256
}
5357

58+
exprt::operandst byte_operands;
5459
mp_integer first_element = 0;
5560

5661
// refine the number of elements to extract in case the element width is known
@@ -65,6 +70,17 @@ static array_exprt unpack_array_vector(
6570
// turn bytes into elements, rounding up
6671
max_elements = (*max_bytes_int + el_bytes - 1) / el_bytes;
6772
}
73+
74+
if(auto offset_bytes_int = numeric_cast<mp_integer>(offset_bytes))
75+
{
76+
// compute offset as number of elements
77+
first_element = *offset_bytes_int / el_bytes;
78+
// insert offset_bytes-many nil bytes into the output array
79+
*offset_bytes_int -= *offset_bytes_int % el_bytes;
80+
byte_operands.resize(
81+
numeric_cast_v<std::size_t>(*offset_bytes_int),
82+
from_integer(0, unsignedbv_typet(8)));
83+
}
6884
}
6985

7086
// the maximum number of bytes is an upper bound in case the size of the
@@ -75,7 +91,6 @@ static array_exprt unpack_array_vector(
7591

7692
const exprt src_simp = simplify_expr(src, ns);
7793

78-
exprt::operandst byte_operands;
7994
for(mp_integer i = first_element; i < *num_elements; ++i)
8095
{
8196
exprt element;
@@ -94,7 +109,8 @@ static array_exprt unpack_array_vector(
94109

95110
// recursively unpack each element until so that we eventually just have an
96111
// array of bytes left
97-
exprt sub = unpack_rec(element, little_endian, max_bytes, ns, true);
112+
exprt sub =
113+
unpack_rec(element, little_endian, nil_exprt(), max_bytes, ns, true);
98114
byte_operands.insert(
99115
byte_operands.end(), sub.operands().begin(), sub.operands().end());
100116
}
@@ -108,6 +124,8 @@ static array_exprt unpack_array_vector(
108124
/// Rewrite an object into its individual bytes.
109125
/// \param src: object to unpack
110126
/// \param little_endian: true, iff assumed endianness is little-endian
127+
/// \param offset_bytes: if not nil, bytes prior to this offset will be filled
128+
/// with nil values
111129
/// \param max_bytes: if not nil, use as upper bound of the number of bytes to
112130
/// unpack
113131
/// \param ns: namespace for type lookups
@@ -120,6 +138,7 @@ static array_exprt unpack_array_vector(
120138
static exprt unpack_rec(
121139
const exprt &src,
122140
bool little_endian,
141+
const exprt &offset_bytes,
123142
const exprt &max_bytes,
124143
const namespacet &ns,
125144
bool unpack_byte_array)
@@ -136,7 +155,13 @@ static exprt unpack_rec(
136155
return src;
137156

138157
return unpack_array_vector(
139-
src, array_type.size(), *element_width, little_endian, max_bytes, ns);
158+
src,
159+
array_type.size(),
160+
*element_width,
161+
little_endian,
162+
offset_bytes,
163+
max_bytes,
164+
ns);
140165
}
141166
else if(src.type().id() == ID_vector)
142167
{
@@ -150,13 +175,21 @@ static exprt unpack_rec(
150175
return src;
151176

152177
return unpack_array_vector(
153-
src, vector_type.size(), *element_width, little_endian, max_bytes, ns);
178+
src,
179+
vector_type.size(),
180+
*element_width,
181+
little_endian,
182+
offset_bytes,
183+
max_bytes,
184+
ns);
154185
}
155186
else if(ns.follow(src.type()).id()==ID_struct)
156187
{
157188
const struct_typet &struct_type=to_struct_type(ns.follow(src.type()));
158189
const struct_typet::componentst &components=struct_type.components();
159190

191+
mp_integer member_offset_bits = 0;
192+
160193
exprt::operandst byte_operands;
161194
for(const auto &comp : components)
162195
{
@@ -170,11 +203,43 @@ static exprt unpack_rec(
170203
throw non_byte_alignedt(struct_type, comp, *element_width);
171204
}
172205

206+
exprt offset_in_member = nil_exprt();
207+
auto offset_in_member_int = numeric_cast<mp_integer>(offset_bytes);
208+
exprt max_bytes_left = nil_exprt();
209+
auto max_bytes_left_int = numeric_cast<mp_integer>(max_bytes);
210+
211+
if(offset_in_member_int.has_value())
212+
{
213+
*offset_in_member_int -= member_offset_bits / 8;
214+
// if the offset is negative, offset_in_member remains nil, which has
215+
// the same effect as setting it to zero
216+
if(*offset_in_member_int >= 0)
217+
{
218+
offset_in_member =
219+
from_integer(*offset_in_member_int, offset_bytes.type());
220+
}
221+
}
222+
223+
if(max_bytes_left_int.has_value())
224+
{
225+
*max_bytes_left_int -= member_offset_bits / 8;
226+
if(*max_bytes_left_int >= 0)
227+
max_bytes_left = from_integer(*max_bytes_left_int, max_bytes.type());
228+
else
229+
break;
230+
}
231+
173232
member_exprt member(src, comp.get_name(), comp.type());
174-
exprt sub=unpack_rec(member, little_endian, max_bytes, ns, true);
233+
if(src.id() == ID_struct)
234+
simplify(member, ns);
235+
236+
exprt sub = unpack_rec(
237+
member, little_endian, offset_in_member, max_bytes_left, ns, true);
175238

176239
byte_operands.insert(
177240
byte_operands.end(), sub.operands().begin(), sub.operands().end());
241+
242+
member_offset_bits += *element_width;
178243
}
179244

180245
const std::size_t size = byte_operands.size();
@@ -294,9 +359,12 @@ exprt lower_byte_extract(const byte_extract_exprt &src, const namespacet &ns)
294359
typecast_exprt(src.offset(), upper_bound.type())),
295360
ns);
296361

362+
exprt lb = src.offset();
363+
if(!lb.is_constant())
364+
lb.make_nil();
365+
297366
byte_extract_exprt unpacked(src);
298-
unpacked.op()=
299-
unpack_rec(src.op(), little_endian, upper_bound, ns);
367+
unpacked.op() = unpack_rec(src.op(), little_endian, lb, upper_bound, ns);
300368

301369
if(src.type().id()==ID_array)
302370
{

0 commit comments

Comments
 (0)