Skip to content

Commit ed855b9

Browse files
committed
Byte update lowering: make array update tails conditional
When updating an array/vector at a non-constant byte offset the update value may affect multiple array/vector elements. The last such element needs to be updated with however many bytes have not yet been used from the update value. With a non-constant byte offset the size of remaining bytes may be zero. Therefore, make the tail update use symbolic offsets rather than constants. When fixing this, it also became apparent that we must not use byte extracts of non-constant extraction size to build update elements. Instead, the update offset should be non-constant, and will actually need to be negative on such occasions.
1 parent 93e7d5b commit ed855b9

File tree

2 files changed

+54
-62
lines changed

2 files changed

+54
-62
lines changed

regression/cbmc/Array_operations2/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE broken-smt-backend
1+
CORE broken-cprover-smt-backend
22
main.c
33

44
^\[main.assertion.12\] line 34 assertion arr\[1\].c\[2\] == 0: FAILURE$

src/solvers/lowering/byte_operators.cpp

Lines changed: 53 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,17 +1550,14 @@ static exprt lower_byte_update_array_vector_unbounded(
15501550
plus_exprt{last_index, from_integer(1, last_index.type())}}};
15511551

15521552
// The actual value of a partial update at the end.
1553-
exprt last_update_value = lower_byte_operators(
1553+
exprt last_update_value = lower_byte_update(
15541554
byte_update_exprt{
15551555
src.id(),
15561556
index_exprt{src.op(), last_index},
1557-
from_integer(0, src.offset().type()),
1558-
byte_extract_exprt{extract_opcode,
1559-
value_as_byte_array,
1560-
mult_exprt{typecast_exprt::conditional_cast(
1561-
last_index, subtype_size.type()),
1562-
subtype_size},
1563-
array_typet{bv_typet{8}, tail_size}}},
1557+
unary_minus_exprt{mult_exprt{
1558+
typecast_exprt::conditional_cast(last_index, subtype_size.type()),
1559+
subtype_size}},
1560+
value_as_byte_array},
15641561
ns);
15651562

15661563
if_exprt array_comprehension_body{
@@ -1601,10 +1598,6 @@ static exprt lower_byte_update_array_vector_non_const(
16011598
const optionalt<exprt> &non_const_update_bound,
16021599
const namespacet &ns)
16031600
{
1604-
const irep_idt extract_opcode = src.id() == ID_byte_update_little_endian
1605-
? ID_byte_extract_little_endian
1606-
: ID_byte_extract_big_endian;
1607-
16081601
// do all arithmetic below using index/offset types - the array theory
16091602
// back-end is really picky about indices having the same type
16101603
auto subtype_size_opt = size_of_expr(subtype, ns);
@@ -1625,6 +1618,8 @@ static exprt lower_byte_update_array_vector_non_const(
16251618

16261619
// compute the number of bytes (from the update value) that are going to be
16271620
// consumed for updating the first element
1621+
const exprt update_size =
1622+
from_integer(value_as_byte_array.operands().size(), subtype_size.type());
16281623
exprt initial_bytes = minus_exprt{subtype_size, update_offset};
16291624
exprt update_bound;
16301625
if(non_const_update_bound.has_value())
@@ -1637,26 +1632,23 @@ static exprt lower_byte_update_array_vector_non_const(
16371632
DATA_INVARIANT(
16381633
value_as_byte_array.id() == ID_array,
16391634
"value should be an array expression if the update bound is constant");
1640-
update_bound =
1641-
from_integer(value_as_byte_array.operands().size(), initial_bytes.type());
1635+
update_bound = update_size;
16421636
}
16431637
initial_bytes =
16441638
if_exprt{binary_predicate_exprt{initial_bytes, ID_lt, update_bound},
16451639
initial_bytes,
16461640
update_bound};
16471641
simplify(initial_bytes, ns);
16481642

1649-
// encode the first update: update the original element at first_index with
1650-
// initial_bytes-many bytes extracted from value_as_byte_array
1651-
exprt first_update_value = lower_byte_operators(
1643+
// encode the first update: update the original element at first_index (the
1644+
// actual update will only be initial_bytes-many bytes from
1645+
// value_as_byte_array)
1646+
exprt first_update_value = lower_byte_update(
16521647
byte_update_exprt{
16531648
src.id(),
16541649
index_exprt{src.op(), first_index},
16551650
update_offset,
1656-
byte_extract_exprt{extract_opcode,
1657-
value_as_byte_array,
1658-
from_integer(0, src.offset().type()),
1659-
array_typet{bv_typet{8}, initial_bytes}}},
1651+
value_as_byte_array},
16601652
ns);
16611653

16621654
if(value_as_byte_array.id() != ID_array)
@@ -1691,56 +1683,44 @@ static exprt lower_byte_update_array_vector_non_const(
16911683

16921684
with_exprt result{src.op(), first_index, first_update_value};
16931685

1694-
std::size_t i = 1;
1695-
for(; offset + step_size <= value_as_byte_array.operands().size();
1696-
offset += step_size, ++i)
1697-
{
1686+
auto lower_byte_update_array_vector_non_const_one_element =
1687+
[&src,
1688+
&first_index,
1689+
&initial_bytes,
1690+
&subtype_size,
1691+
&value_as_byte_array,
1692+
&ns,
1693+
&result](std::size_t i) -> void {
16981694
exprt where = simplify_expr(
16991695
plus_exprt{first_index, from_integer(i, first_index.type())}, ns);
17001696

1701-
exprt offset_expr = simplify_expr(
1702-
plus_exprt{
1697+
exprt neg_offset_expr = simplify_expr(
1698+
unary_minus_exprt{plus_exprt{
17031699
initial_bytes,
1704-
mult_exprt{subtype_size, from_integer(i - 1, subtype_size.type())}},
1700+
mult_exprt{subtype_size, from_integer(i - 1, subtype_size.type())}}},
17051701
ns);
17061702

1707-
exprt element = lower_byte_operators(
1703+
exprt element = lower_byte_update(
17081704
byte_update_exprt{
17091705
src.id(),
17101706
index_exprt{src.op(), where},
1711-
from_integer(0, src.offset().type()),
1712-
byte_extract_exprt{extract_opcode,
1713-
value_as_byte_array,
1714-
std::move(offset_expr),
1715-
array_typet{bv_typet{8}, subtype_size}}},
1707+
neg_offset_expr,
1708+
value_as_byte_array},
17161709
ns);
17171710

17181711
result.add_to_operands(std::move(where), std::move(element));
1712+
};
1713+
1714+
std::size_t i = 1;
1715+
for(; offset + step_size <= value_as_byte_array.operands().size();
1716+
offset += step_size, ++i)
1717+
{
1718+
lower_byte_update_array_vector_non_const_one_element(i);
17191719
}
17201720

17211721
// do the tail
17221722
if(offset < value_as_byte_array.operands().size())
1723-
{
1724-
const std::size_t tail_size =
1725-
value_as_byte_array.operands().size() - offset;
1726-
1727-
exprt where = simplify_expr(
1728-
plus_exprt{first_index, from_integer(i, first_index.type())}, ns);
1729-
1730-
exprt element = lower_byte_operators(
1731-
byte_update_exprt{
1732-
src.id(),
1733-
index_exprt{src.op(), where},
1734-
from_integer(0, src.offset().type()),
1735-
byte_extract_exprt{
1736-
extract_opcode,
1737-
value_as_byte_array,
1738-
from_integer(offset, src.offset().type()),
1739-
array_typet{bv_typet{8}, from_integer(tail_size, size_type())}}},
1740-
ns);
1741-
1742-
result.add_to_operands(std::move(where), std::move(element));
1743-
}
1723+
lower_byte_update_array_vector_non_const_one_element(i);
17441724

17451725
return simplify_expr(std::move(result), ns);
17461726
}
@@ -2218,11 +2198,17 @@ static exprt lower_byte_update(
22182198
const binary_predicate_exprt offset_ge_zero{
22192199
offset_times_eight, ID_ge, from_integer(0, offset_type)};
22202200

2221-
if_exprt mask_shifted{offset_ge_zero,
2222-
shl_exprt{mask, offset_times_eight},
2223-
lshr_exprt{mask, offset_times_eight}};
2201+
if_exprt mask_shifted{
2202+
offset_ge_zero,
2203+
shl_exprt{mask, offset_times_eight},
2204+
lshr_exprt{mask, unary_minus_exprt{offset_times_eight}}};
22242205
if(!is_little_endian)
2206+
{
22252207
mask_shifted.true_case().swap(mask_shifted.false_case());
2208+
to_shift_expr(mask_shifted.true_case())
2209+
.distance()
2210+
.swap(to_shift_expr(mask_shifted.false_case()).distance());
2211+
}
22262212

22272213
// original_bits &= ~mask
22282214
bitand_exprt bitand_expr{val_before, bitnot_exprt{mask_shifted}};
@@ -2249,11 +2235,17 @@ static exprt lower_byte_update(
22492235
zero_extended = value;
22502236

22512237
// shift the value
2252-
if_exprt value_shifted{offset_ge_zero,
2253-
shl_exprt{zero_extended, offset_times_eight},
2254-
lshr_exprt{zero_extended, offset_times_eight}};
2238+
if_exprt value_shifted{
2239+
offset_ge_zero,
2240+
shl_exprt{zero_extended, offset_times_eight},
2241+
lshr_exprt{zero_extended, unary_minus_exprt{offset_times_eight}}};
22552242
if(!is_little_endian)
2243+
{
22562244
value_shifted.true_case().swap(value_shifted.false_case());
2245+
to_shift_expr(value_shifted.true_case())
2246+
.distance()
2247+
.swap(to_shift_expr(value_shifted.false_case()).distance());
2248+
}
22572249

22582250
// original_bits |= newvalue
22592251
bitor_exprt bitor_expr{bitand_expr, value_shifted};

0 commit comments

Comments
 (0)