@@ -1751,6 +1751,10 @@ static exprt lower_byte_update_struct(
1751
1751
const optionalt<exprt> &non_const_update_bound,
1752
1752
const namespacet &ns)
1753
1753
{
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
+
1754
1758
exprt::operandst elements;
1755
1759
elements.reserve (struct_type.components ().size ());
1756
1760
@@ -1789,10 +1793,6 @@ static exprt lower_byte_update_struct(
1789
1793
// indiviual struct members and instead turn the operand-to-be-updated
1790
1794
// into a byte array, which we know how to update even if the offset is
1791
1795
// 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
-
1796
1796
auto src_size_opt = size_of_expr (src.type (), ns);
1797
1797
CHECK_RETURN (src_size_opt.has_value ());
1798
1798
@@ -1816,13 +1816,18 @@ static exprt lower_byte_update_struct(
1816
1816
ns);
1817
1817
}
1818
1818
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.
1819
1823
mp_integer update_elements = (*element_width + 7 ) / 8 ;
1820
1824
std::size_t first = 0 ;
1821
1825
if (*offset_bytes < 0 )
1822
1826
{
1823
1827
offset = from_integer (0 , src.offset ().type ());
1824
1828
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,
1826
1831
" members past the update should be handled above" );
1827
1832
first = numeric_cast_v<std::size_t >(-*offset_bytes);
1828
1833
}
@@ -1834,13 +1839,50 @@ static exprt lower_byte_update_struct(
1834
1839
" members before the update should be handled above" );
1835
1840
}
1836
1841
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).
1837
1845
std::size_t end = first + numeric_cast_v<std::size_t >(update_elements);
1838
1846
if (value_as_byte_array.id () == ID_array)
1839
1847
end = std::min (end, value_as_byte_array.operands ().size ());
1840
1848
exprt::operandst update_values =
1841
1849
instantiate_byte_array (value_as_byte_array, first, end, ns);
1842
1850
const std::size_t update_size = update_values.size ();
1843
1851
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.
1844
1886
const std::size_t shift =
1845
1887
numeric_cast_v<std::size_t >(member_offset_bits % 8 );
1846
1888
const std::size_t element_bits_int =
@@ -1857,20 +1899,17 @@ static exprt lower_byte_update_struct(
1857
1899
member.op0 ().swap (member.op1 ());
1858
1900
}
1859
1901
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);
1867
1906
1868
1907
if (shift == 0 )
1869
- elements.push_back (lower_byte_operators (bu, ns) );
1908
+ elements.push_back (updated_element );
1870
1909
else
1871
1910
{
1872
1911
elements.push_back (typecast_exprt::conditional_cast (
1873
- extractbits_exprt{lower_byte_operators (bu, ns) ,
1912
+ extractbits_exprt{updated_element ,
1874
1913
element_bits_int - 1 + (little_endian ? shift : 0 ),
1875
1914
(little_endian ? shift : 0 ),
1876
1915
bv_typet{element_bits_int}},
@@ -2021,6 +2060,28 @@ static exprt lower_byte_update(
2021
2060
instantiate_byte_array (value_as_byte_array, 0 , (type_bits + 7 ) / 8 , ns);
2022
2061
}
2023
2062
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
+
2024
2085
const std::size_t update_size = update_bytes.size ();
2025
2086
const std::size_t width = std::max (type_bits, update_size * 8 );
2026
2087
0 commit comments