Skip to content

Commit e5d94c8

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 344566b commit e5d94c8

File tree

2 files changed

+48
-68
lines changed

2 files changed

+48
-68
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: 47 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,19 +1621,14 @@ static exprt lower_byte_update_array_vector_unbounded(
16211621
plus_exprt{last_index, from_integer(1, last_index.type())}}};
16221622

16231623
// The actual value of a partial update at the end.
1624-
exprt last_update_value = lower_byte_operators(
1624+
exprt last_update_value = lower_byte_update(
16251625
byte_update_exprt{
16261626
src.id(),
16271627
index_exprt{src.op(), last_index},
1628-
from_integer(0, src.offset().type()),
1629-
byte_extract_exprt{
1630-
extract_opcode,
1631-
value_as_byte_array,
1632-
mult_exprt{
1633-
typecast_exprt::conditional_cast(last_index, subtype_size.type()),
1634-
subtype_size},
1635-
src.get_bits_per_byte(),
1636-
array_typet{bv_typet{src.get_bits_per_byte()}, tail_size}},
1628+
unary_minus_exprt{mult_exprt{
1629+
typecast_exprt::conditional_cast(last_index, subtype_size.type()),
1630+
subtype_size}},
1631+
value_as_byte_array,
16371632
src.get_bits_per_byte()},
16381633
ns);
16391634

@@ -1675,10 +1670,6 @@ static exprt lower_byte_update_array_vector_non_const(
16751670
const optionalt<exprt> &non_const_update_bound,
16761671
const namespacet &ns)
16771672
{
1678-
const irep_idt extract_opcode = src.id() == ID_byte_update_little_endian
1679-
? ID_byte_extract_little_endian
1680-
: ID_byte_extract_big_endian;
1681-
16821673
// do all arithmetic below using index/offset types - the array theory
16831674
// back-end is really picky about indices having the same type
16841675
auto subtype_size_opt = size_of_expr(subtype, ns);
@@ -1699,6 +1690,8 @@ static exprt lower_byte_update_array_vector_non_const(
16991690

17001691
// compute the number of bytes (from the update value) that are going to be
17011692
// consumed for updating the first element
1693+
const exprt update_size =
1694+
from_integer(value_as_byte_array.operands().size(), subtype_size.type());
17021695
exprt initial_bytes = minus_exprt{subtype_size, update_offset};
17031696
exprt update_bound;
17041697
if(non_const_update_bound.has_value())
@@ -1711,28 +1704,23 @@ static exprt lower_byte_update_array_vector_non_const(
17111704
DATA_INVARIANT(
17121705
value_as_byte_array.id() == ID_array,
17131706
"value should be an array expression if the update bound is constant");
1714-
update_bound =
1715-
from_integer(value_as_byte_array.operands().size(), initial_bytes.type());
1707+
update_bound = update_size;
17161708
}
17171709
initial_bytes =
17181710
if_exprt{binary_predicate_exprt{initial_bytes, ID_lt, update_bound},
17191711
initial_bytes,
17201712
update_bound};
17211713
simplify(initial_bytes, ns);
17221714

1723-
// encode the first update: update the original element at first_index with
1724-
// initial_bytes-many bytes extracted from value_as_byte_array
1725-
exprt first_update_value = lower_byte_operators(
1715+
// encode the first update: update the original element at first_index (the
1716+
// actual update will only be initial_bytes-many bytes from
1717+
// value_as_byte_array)
1718+
exprt first_update_value = lower_byte_update(
17261719
byte_update_exprt{
17271720
src.id(),
17281721
index_exprt{src.op(), first_index},
17291722
update_offset,
1730-
byte_extract_exprt{
1731-
extract_opcode,
1732-
value_as_byte_array,
1733-
from_integer(0, src.offset().type()),
1734-
src.get_bits_per_byte(),
1735-
array_typet{bv_typet{src.get_bits_per_byte()}, initial_bytes}},
1723+
value_as_byte_array,
17361724
src.get_bits_per_byte()},
17371725
ns);
17381726

@@ -1768,63 +1756,45 @@ static exprt lower_byte_update_array_vector_non_const(
17681756

17691757
with_exprt result{src.op(), first_index, first_update_value};
17701758

1771-
std::size_t i = 1;
1772-
for(; offset + step_size <= value_as_byte_array.operands().size();
1773-
offset += step_size, ++i)
1774-
{
1759+
auto lower_byte_update_array_vector_non_const_one_element =
1760+
[&src,
1761+
&first_index,
1762+
&initial_bytes,
1763+
&subtype_size,
1764+
&value_as_byte_array,
1765+
&ns,
1766+
&result](std::size_t i) -> void {
17751767
exprt where = simplify_expr(
17761768
plus_exprt{first_index, from_integer(i, first_index.type())}, ns);
17771769

1778-
exprt offset_expr = simplify_expr(
1779-
plus_exprt{
1770+
exprt neg_offset_expr = simplify_expr(
1771+
unary_minus_exprt{plus_exprt{
17801772
initial_bytes,
1781-
mult_exprt{subtype_size, from_integer(i - 1, subtype_size.type())}},
1773+
mult_exprt{subtype_size, from_integer(i - 1, subtype_size.type())}}},
17821774
ns);
17831775

1784-
exprt element = lower_byte_operators(
1776+
exprt element = lower_byte_update(
17851777
byte_update_exprt{
17861778
src.id(),
17871779
index_exprt{src.op(), where},
1788-
from_integer(0, src.offset().type()),
1789-
byte_extract_exprt{
1790-
extract_opcode,
1791-
value_as_byte_array,
1792-
std::move(offset_expr),
1793-
src.get_bits_per_byte(),
1794-
array_typet{bv_typet{src.get_bits_per_byte()}, subtype_size}},
1780+
neg_offset_expr,
1781+
value_as_byte_array,
17951782
src.get_bits_per_byte()},
17961783
ns);
17971784

17981785
result.add_to_operands(std::move(where), std::move(element));
1786+
};
1787+
1788+
std::size_t i = 1;
1789+
for(; offset + step_size <= value_as_byte_array.operands().size();
1790+
offset += step_size, ++i)
1791+
{
1792+
lower_byte_update_array_vector_non_const_one_element(i);
17991793
}
18001794

18011795
// do the tail
18021796
if(offset < value_as_byte_array.operands().size())
1803-
{
1804-
const std::size_t tail_size =
1805-
value_as_byte_array.operands().size() - offset;
1806-
1807-
exprt where = simplify_expr(
1808-
plus_exprt{first_index, from_integer(i, first_index.type())}, ns);
1809-
1810-
exprt element = lower_byte_operators(
1811-
byte_update_exprt{
1812-
src.id(),
1813-
index_exprt{src.op(), where},
1814-
from_integer(0, src.offset().type()),
1815-
byte_extract_exprt{
1816-
extract_opcode,
1817-
value_as_byte_array,
1818-
from_integer(offset, src.offset().type()),
1819-
src.get_bits_per_byte(),
1820-
array_typet{
1821-
bv_typet{src.get_bits_per_byte()},
1822-
from_integer(tail_size, size_type())}},
1823-
src.get_bits_per_byte()},
1824-
ns);
1825-
1826-
result.add_to_operands(std::move(where), std::move(element));
1827-
}
1797+
lower_byte_update_array_vector_non_const_one_element(i);
18281798

18291799
return simplify_expr(std::move(result), ns);
18301800
}
@@ -2333,9 +2303,14 @@ static exprt lower_byte_update(
23332303
if_exprt mask_shifted{
23342304
offset_ge_zero,
23352305
shl_exprt{mask, offset_in_bits},
2336-
lshr_exprt{mask, offset_in_bits}};
2306+
lshr_exprt{mask, unary_minus_exprt{offset_in_bits}}};
23372307
if(!is_little_endian)
2308+
{
23382309
mask_shifted.true_case().swap(mask_shifted.false_case());
2310+
to_shift_expr(mask_shifted.true_case())
2311+
.distance()
2312+
.swap(to_shift_expr(mask_shifted.false_case()).distance());
2313+
}
23392314

23402315
// original_bits &= ~mask
23412316
bitand_exprt bitand_expr{val_before, bitnot_exprt{mask_shifted}};
@@ -2365,9 +2340,14 @@ static exprt lower_byte_update(
23652340
if_exprt value_shifted{
23662341
offset_ge_zero,
23672342
shl_exprt{zero_extended, offset_in_bits},
2368-
lshr_exprt{zero_extended, offset_in_bits}};
2343+
lshr_exprt{zero_extended, unary_minus_exprt{offset_in_bits}}};
23692344
if(!is_little_endian)
2345+
{
23702346
value_shifted.true_case().swap(value_shifted.false_case());
2347+
to_shift_expr(value_shifted.true_case())
2348+
.distance()
2349+
.swap(to_shift_expr(value_shifted.false_case()).distance());
2350+
}
23712351

23722352
// original_bits |= newvalue
23732353
bitor_exprt bitor_expr{bitand_expr, value_shifted};

0 commit comments

Comments
 (0)