@@ -1717,17 +1717,18 @@ simplify_exprt::simplify_byte_extract(const byte_extract_exprt &expr)
1717
1717
}
1718
1718
}
1719
1719
1720
- // the following require a constant offset
1721
1720
auto offset = numeric_cast<mp_integer>(expr.offset ());
1722
- if (! offset.has_value () || *offset < 0 )
1721
+ if (offset.has_value () && *offset < 0 )
1723
1722
return unchanged (expr);
1724
1723
1725
1724
// try to simplify byte_extract(byte_update(...))
1726
1725
auto const bu = expr_try_dynamic_cast<byte_update_exprt>(expr.op ());
1727
1726
std::optional<mp_integer> update_offset;
1728
1727
if (bu)
1729
1728
update_offset = numeric_cast<mp_integer>(bu->offset ());
1730
- if (bu && el_size.has_value () && update_offset.has_value ())
1729
+ if (
1730
+ offset.has_value () && bu && el_size.has_value () &&
1731
+ update_offset.has_value ())
1731
1732
{
1732
1733
// byte_extract(byte_update(root, offset_u, value), offset_e) so that the
1733
1734
// update does not affect what is being extracted simplifies to
@@ -1775,12 +1776,13 @@ simplify_exprt::simplify_byte_extract(const byte_extract_exprt &expr)
1775
1776
// don't do any of the following if endianness doesn't match, as
1776
1777
// bytes need to be swapped
1777
1778
if (
1778
- *offset == 0 && ((expr.id () == ID_byte_extract_little_endian &&
1779
- config.ansi_c .endianness ==
1780
- configt::ansi_ct::endiannesst::IS_LITTLE_ENDIAN) ||
1781
- (expr.id () == ID_byte_extract_big_endian &&
1782
- config.ansi_c .endianness ==
1783
- configt::ansi_ct::endiannesst::IS_BIG_ENDIAN)))
1779
+ offset.has_value () && *offset == 0 &&
1780
+ ((expr.id () == ID_byte_extract_little_endian &&
1781
+ config.ansi_c .endianness ==
1782
+ configt::ansi_ct::endiannesst::IS_LITTLE_ENDIAN) ||
1783
+ (expr.id () == ID_byte_extract_big_endian &&
1784
+ config.ansi_c .endianness ==
1785
+ configt::ansi_ct::endiannesst::IS_BIG_ENDIAN)))
1784
1786
{
1785
1787
// byte extract of full object is object
1786
1788
if (expr.type () == expr.op ().type ())
@@ -1817,7 +1819,7 @@ simplify_exprt::simplify_byte_extract(const byte_extract_exprt &expr)
1817
1819
return unchanged (expr);
1818
1820
1819
1821
if (
1820
- expr.op ().id () == ID_array_of &&
1822
+ offset. has_value () && expr.op ().id () == ID_array_of &&
1821
1823
to_array_of_expr (expr.op ()).op ().is_constant ())
1822
1824
{
1823
1825
const auto const_bits_opt = expr2bits (
@@ -1854,7 +1856,7 @@ simplify_exprt::simplify_byte_extract(const byte_extract_exprt &expr)
1854
1856
1855
1857
// in some cases we even handle non-const array_of
1856
1858
if (
1857
- expr.op ().id () == ID_array_of &&
1859
+ offset. has_value () && expr.op ().id () == ID_array_of &&
1858
1860
(*offset * expr.get_bits_per_byte ()) % (*el_size) == 0 &&
1859
1861
*el_size <=
1860
1862
pointer_offset_bits (to_array_of_expr (expr.op ()).what ().type (), ns))
@@ -1870,7 +1872,7 @@ simplify_exprt::simplify_byte_extract(const byte_extract_exprt &expr)
1870
1872
expr2bits (expr.op (), expr.id () == ID_byte_extract_little_endian, ns);
1871
1873
1872
1874
if (
1873
- bits.has_value () &&
1875
+ offset. has_value () && bits.has_value () &&
1874
1876
mp_integer (bits->size ()) >= *el_size + *offset * expr.get_bits_per_byte ())
1875
1877
{
1876
1878
// make sure we don't lose bits with structs containing flexible array
@@ -1986,7 +1988,9 @@ simplify_exprt::simplify_byte_extract(const byte_extract_exprt &expr)
1986
1988
const array_typet &array_type = to_array_type (expr.op ().type ());
1987
1989
const auto &element_bit_width =
1988
1990
pointer_offset_bits (array_type.element_type (), ns);
1989
- if (element_bit_width.has_value () && *element_bit_width > 0 )
1991
+ if (
1992
+ offset.has_value () && element_bit_width.has_value () &&
1993
+ *element_bit_width > 0 )
1990
1994
{
1991
1995
if (
1992
1996
*offset > 0 &&
@@ -2026,7 +2030,7 @@ simplify_exprt::simplify_byte_extract(const byte_extract_exprt &expr)
2026
2030
2027
2031
// try to refine it down to extracting from a member or an index in an array
2028
2032
auto subexpr =
2029
- get_subexpression_at_offset (expr.op (), * offset, expr.type (), ns);
2033
+ get_subexpression_at_offset (expr.op (), expr. offset () , expr.type (), ns);
2030
2034
if (subexpr.has_value () && subexpr.value () != expr)
2031
2035
return changed (simplify_rec (subexpr.value ())); // recursive call
2032
2036
@@ -2227,14 +2231,134 @@ simplify_exprt::simplify_byte_update(const byte_update_exprt &expr)
2227
2231
}
2228
2232
}
2229
2233
2230
- // the following require a constant offset
2231
- if (!offset_int.has_value () || *offset_int < 0 )
2232
- return unchanged (expr);
2233
-
2234
2234
// size must be known
2235
2235
if (!val_size.has_value () || *val_size == 0 )
2236
2236
return unchanged (expr);
2237
2237
2238
+ // byte_update(root, offset, value) is with(root, index, value) when root is
2239
+ // array-typed, the size of value matches the array-element width, and offset
2240
+ // is guaranteed to be a multiple of the array-element width
2241
+ if (auto array_type = type_try_dynamic_cast<array_typet>(root.type ()))
2242
+ {
2243
+ auto el_size = pointer_offset_bits (array_type->element_type (), ns);
2244
+
2245
+ if (el_size.has_value () && *el_size > 0 && *val_size % *el_size == 0 )
2246
+ {
2247
+ if (
2248
+ offset_int.has_value () &&
2249
+ (*offset_int * expr.get_bits_per_byte ()) % *el_size == 0 )
2250
+ {
2251
+ mp_integer base_offset =
2252
+ (*offset_int * expr.get_bits_per_byte ()) / *el_size;
2253
+ with_exprt result_expr{
2254
+ root,
2255
+ from_integer (base_offset, array_type->index_type ()),
2256
+ byte_extract_exprt{
2257
+ matching_byte_extract_id,
2258
+ value,
2259
+ from_integer (0 , offset.type ()),
2260
+ expr.get_bits_per_byte (),
2261
+ array_type->element_type ()}};
2262
+ mp_integer n_elements = *val_size / *el_size;
2263
+
2264
+ for (mp_integer i = 1 ; i < n_elements; ++i)
2265
+ {
2266
+ result_expr.add_to_operands (
2267
+ from_integer (base_offset + i, array_type->index_type ()),
2268
+ byte_extract_exprt{
2269
+ matching_byte_extract_id,
2270
+ value,
2271
+ from_integer (
2272
+ i * (*el_size / expr.get_bits_per_byte ()), offset.type ()),
2273
+ expr.get_bits_per_byte (),
2274
+ array_type->element_type ()});
2275
+ }
2276
+
2277
+ return changed (simplify_rec (result_expr));
2278
+ }
2279
+ // if we have an offset C + x (where C is a constant) we can try to
2280
+ // recurse by first looking at the member at offset C
2281
+ else if (
2282
+ offset.id () == ID_plus && offset.operands ().size () == 2 &&
2283
+ (to_multi_ary_expr (offset).op0 ().is_constant () ||
2284
+ to_multi_ary_expr (offset).op1 ().is_constant ()))
2285
+ {
2286
+ const plus_exprt &offset_plus = to_plus_expr (offset);
2287
+ const auto &const_factor = offset_plus.op0 ().is_constant ()
2288
+ ? offset_plus.op0 ()
2289
+ : offset_plus.op1 ();
2290
+ const exprt &other_factor = offset_plus.op0 ().is_constant ()
2291
+ ? offset_plus.op1 ()
2292
+ : offset_plus.op0 ();
2293
+
2294
+ auto tmp = expr;
2295
+ tmp.set_offset (const_factor);
2296
+ exprt expr_at_offset_C = simplify_byte_update (tmp);
2297
+
2298
+ if (
2299
+ expr_at_offset_C.id () == ID_with &&
2300
+ to_with_expr (expr_at_offset_C).where ().is_zero ())
2301
+ {
2302
+ tmp.set_op (to_with_expr (expr_at_offset_C).old ());
2303
+ tmp.set_offset (other_factor);
2304
+ return changed (simplify_byte_update (tmp));
2305
+ }
2306
+ }
2307
+ else if (
2308
+ offset.id () == ID_mult && offset.operands ().size () == 2 &&
2309
+ (to_multi_ary_expr (offset).op0 ().is_constant () ||
2310
+ to_multi_ary_expr (offset).op1 ().is_constant ()))
2311
+ {
2312
+ const mult_exprt &offset_mult = to_mult_expr (offset);
2313
+ const auto &const_factor = numeric_cast_v<mp_integer>(to_constant_expr (
2314
+ offset_mult.op0 ().is_constant () ? offset_mult.op0 ()
2315
+ : offset_mult.op1 ()));
2316
+ const exprt &other_factor = offset_mult.op0 ().is_constant ()
2317
+ ? offset_mult.op1 ()
2318
+ : offset_mult.op0 ();
2319
+
2320
+ if ((const_factor * expr.get_bits_per_byte ()) % *el_size == 0 )
2321
+ {
2322
+ exprt base_offset = mult_exprt{
2323
+ other_factor,
2324
+ from_integer (
2325
+ (const_factor * expr.get_bits_per_byte ()) / *el_size,
2326
+ other_factor.type ())};
2327
+ with_exprt result_expr{
2328
+ root,
2329
+ typecast_exprt::conditional_cast (
2330
+ base_offset, array_type->index_type ()),
2331
+ byte_extract_exprt{
2332
+ matching_byte_extract_id,
2333
+ value,
2334
+ from_integer (0 , offset.type ()),
2335
+ expr.get_bits_per_byte (),
2336
+ array_type->element_type ()}};
2337
+ mp_integer n_elements = *val_size / *el_size;
2338
+ for (mp_integer i = 1 ; i < n_elements; ++i)
2339
+ {
2340
+ result_expr.add_to_operands (
2341
+ typecast_exprt::conditional_cast (
2342
+ plus_exprt{base_offset, from_integer (i, base_offset.type ())},
2343
+ array_type->index_type ()),
2344
+ byte_extract_exprt{
2345
+ matching_byte_extract_id,
2346
+ value,
2347
+ from_integer (
2348
+ i * (*el_size / expr.get_bits_per_byte ()), offset.type ()),
2349
+ expr.get_bits_per_byte (),
2350
+ array_type->element_type ()});
2351
+ }
2352
+ return changed (simplify_rec (result_expr));
2353
+ }
2354
+ }
2355
+ }
2356
+ }
2357
+
2358
+ // the following require a constant offset
2359
+ if (!offset_int.has_value () || *offset_int < 0 )
2360
+ return unchanged (expr);
2361
+
2238
2362
// Are we updating (parts of) a struct? Do individual member updates
2239
2363
// instead, unless there are non-byte-sized bit fields
2240
2364
if (root.type ().id () == ID_struct || root.type ().id () == ID_struct_tag)
0 commit comments