Skip to content

Commit 3a2aa7d

Browse files
authored
Merge pull request #4713 from tautschnig/array-comprehension-follow-up
Byte-update lowering: fixup invariant for array-comprehension
2 parents 1631ec4 + 8cc423c commit 3a2aa7d

File tree

3 files changed

+103
-14
lines changed

3 files changed

+103
-14
lines changed

regression/cbmc/byte_update12/main.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <string.h>
2+
3+
struct S
4+
{
5+
int i;
6+
int j;
7+
};
8+
9+
int main()
10+
{
11+
unsigned x;
12+
char A[x];
13+
__CPROVER_assume(x == sizeof(int));
14+
A[0] = 42;
15+
struct S s;
16+
s.j = 1;
17+
memcpy(&s, A, x);
18+
__CPROVER_assert((s.i & 0xFF) == 42, "lowest byte is 42");
19+
__CPROVER_assert(s.j == 1, "s.j is unaffected by upate");
20+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE broken-smt-backend
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

src/solvers/lowering/byte_operators.cpp

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,6 +1751,10 @@ static exprt lower_byte_update_struct(
17511751
const optionalt<exprt> &non_const_update_bound,
17521752
const namespacet &ns)
17531753
{
1754+
const irep_idt extract_opcode = src.id() == ID_byte_update_little_endian
1755+
? ID_byte_extract_little_endian
1756+
: ID_byte_extract_big_endian;
1757+
17541758
exprt::operandst elements;
17551759
elements.reserve(struct_type.components().size());
17561760

@@ -1789,10 +1793,6 @@ static exprt lower_byte_update_struct(
17891793
// indiviual struct members and instead turn the operand-to-be-updated
17901794
// into a byte array, which we know how to update even if the offset is
17911795
// non-constant.
1792-
const irep_idt extract_opcode = src.id() == ID_byte_update_little_endian
1793-
? ID_byte_extract_little_endian
1794-
: ID_byte_extract_big_endian;
1795-
17961796
auto src_size_opt = size_of_expr(src.type(), ns);
17971797
CHECK_RETURN(src_size_opt.has_value());
17981798

@@ -1816,13 +1816,18 @@ static exprt lower_byte_update_struct(
18161816
ns);
18171817
}
18181818

1819+
// We now need to figure out how many bytes to consume from the update
1820+
// value. If the size of the update is unknown, then we need to leave some
1821+
// of this work to a back-end solver via the non_const_update_bound branch
1822+
// below.
18191823
mp_integer update_elements = (*element_width + 7) / 8;
18201824
std::size_t first = 0;
18211825
if(*offset_bytes < 0)
18221826
{
18231827
offset = from_integer(0, src.offset().type());
18241828
INVARIANT(
1825-
value_as_byte_array.operands().size() > -*offset_bytes,
1829+
value_as_byte_array.id() != ID_array ||
1830+
value_as_byte_array.operands().size() > -*offset_bytes,
18261831
"members past the update should be handled above");
18271832
first = numeric_cast_v<std::size_t>(-*offset_bytes);
18281833
}
@@ -1834,13 +1839,50 @@ static exprt lower_byte_update_struct(
18341839
"members before the update should be handled above");
18351840
}
18361841

1842+
// Now that we have an idea on how many bytes we need from the update value,
1843+
// determine the exact range [first, end) in the update value, and create
1844+
// that sequence of bytes (via instantiate_byte_array).
18371845
std::size_t end = first + numeric_cast_v<std::size_t>(update_elements);
18381846
if(value_as_byte_array.id() == ID_array)
18391847
end = std::min(end, value_as_byte_array.operands().size());
18401848
exprt::operandst update_values =
18411849
instantiate_byte_array(value_as_byte_array, first, end, ns);
18421850
const std::size_t update_size = update_values.size();
18431851

1852+
// With an upper bound on the size of the update established, construct the
1853+
// actual update expression. If the exact size of the update is unknown,
1854+
// make the size expression conditional.
1855+
exprt update_size_expr = from_integer(update_size, size_type());
1856+
array_exprt update_expr{std::move(update_values),
1857+
array_typet{bv_typet{8}, update_size_expr}};
1858+
optionalt<exprt> member_update_bound;
1859+
if(non_const_update_bound.has_value())
1860+
{
1861+
// The size of the update is not constant, and thus is to be symbolically
1862+
// bound; first see how many bytes we have left in the update:
1863+
// non_const_update_bound > first ? non_const_update_bound - first: 0
1864+
const exprt remaining_update_bytes = typecast_exprt::conditional_cast(
1865+
if_exprt{
1866+
binary_predicate_exprt{
1867+
*non_const_update_bound,
1868+
ID_gt,
1869+
from_integer(first, non_const_update_bound->type())},
1870+
minus_exprt{*non_const_update_bound,
1871+
from_integer(first, non_const_update_bound->type())},
1872+
from_integer(0, non_const_update_bound->type())},
1873+
size_type());
1874+
// Now take the minimum of update-bytes-left and the previously computed
1875+
// size of the member to be updated:
1876+
update_size_expr = if_exprt{
1877+
binary_predicate_exprt{update_size_expr, ID_lt, remaining_update_bytes},
1878+
update_size_expr,
1879+
remaining_update_bytes};
1880+
simplify(update_size_expr, ns);
1881+
member_update_bound = std::move(update_size_expr);
1882+
}
1883+
1884+
// We have established the bytes to use for the update, but now need to
1885+
// account for sub-byte members.
18441886
const std::size_t shift =
18451887
numeric_cast_v<std::size_t>(member_offset_bits % 8);
18461888
const std::size_t element_bits_int =
@@ -1857,20 +1899,17 @@ static exprt lower_byte_update_struct(
18571899
member.op0().swap(member.op1());
18581900
}
18591901

1860-
byte_update_exprt bu{
1861-
src.id(),
1862-
std::move(member),
1863-
offset,
1864-
array_exprt{
1865-
std::move(update_values),
1866-
array_typet{bv_typet{8}, from_integer(update_size, size_type())}}};
1902+
// Finally construct the updated member.
1903+
byte_update_exprt bu{src.id(), std::move(member), offset, update_expr};
1904+
exprt updated_element =
1905+
lower_byte_update(bu, update_expr, member_update_bound, ns);
18671906

18681907
if(shift == 0)
1869-
elements.push_back(lower_byte_operators(bu, ns));
1908+
elements.push_back(updated_element);
18701909
else
18711910
{
18721911
elements.push_back(typecast_exprt::conditional_cast(
1873-
extractbits_exprt{lower_byte_operators(bu, ns),
1912+
extractbits_exprt{updated_element,
18741913
element_bits_int - 1 + (little_endian ? shift : 0),
18751914
(little_endian ? shift : 0),
18761915
bv_typet{element_bits_int}},
@@ -2021,6 +2060,28 @@ static exprt lower_byte_update(
20212060
instantiate_byte_array(value_as_byte_array, 0, (type_bits + 7) / 8, ns);
20222061
}
20232062

2063+
if(non_const_update_bound.has_value())
2064+
{
2065+
const exprt src_as_bytes = unpack_rec(
2066+
src.op(),
2067+
src.id() == ID_byte_update_little_endian,
2068+
mp_integer{0},
2069+
mp_integer{update_bytes.size()},
2070+
ns);
2071+
CHECK_RETURN(src_as_bytes.id() == ID_array);
2072+
CHECK_RETURN(src_as_bytes.operands().size() == update_bytes.size());
2073+
for(std::size_t i = 0; i < update_bytes.size(); ++i)
2074+
{
2075+
update_bytes[i] =
2076+
if_exprt{binary_predicate_exprt{
2077+
from_integer(i, non_const_update_bound->type()),
2078+
ID_lt,
2079+
*non_const_update_bound},
2080+
update_bytes[i],
2081+
src_as_bytes.operands()[i]};
2082+
}
2083+
}
2084+
20242085
const std::size_t update_size = update_bytes.size();
20252086
const std::size_t width = std::max(type_bits, update_size * 8);
20262087

0 commit comments

Comments
 (0)