https://gcc.gnu.org/g:d5e7633c5a4e91f2c51cc12ca4995e51179ab96e

commit r17-5468-gd5e7633c5a4e91f2c51cc12ca4995e51179ab96e
Author: Richard Biener <[email protected]>
Date:   Wed Oct 21 16:48:33 2026 +0200

    tree-optimization/128052 - miscompilation of unaligned loads from covered 
stores
    
    When vn_reference_lookup_3 interprets a load fully covered by a single
    store of a constant, the bits needed start at bit
    (offseti - offset2i) % BITS_PER_UNIT of the first encoded byte and thus
    span that remainder plus maxsizei bits.  The code only checked that
    maxsizei bits were encoded, and with a 64 byte encoding limit an
    unaligned load of up to 512 bits (for example a 510-bit _BitInt
    bit-field at bit offset 3) could need a 65th byte that was not
    encoded, which then was zero-filled before the shift, dropping bits
    from the stored value.
    
    The following accounts for the bit remainder in the check, makes room
    for encoding one extra byte and caps the access size at
    bufsize * BITS_PER_UNIT to retain the previous limit.
    
            PR tree-optimization/128052
            * tree-ssa-sccvn.cc (vn_reference_lookup_3): Guard single-store
            lookup with maxsizei <= bufsize * BITS_PER_UNIT.  Increase
            buffer size to bufsize + 2.  Account for the bit remainder
            (offseti - offset2i) % BITS_PER_UNIT when verifying that
            sufficient bits were encoded.
    
            * gcc.dg/torture/bitint-115.c: New test.

Diff:
---
 gcc/testsuite/gcc.dg/torture/bitint-115.c | 95 +++++++++++++++++++++++++++++++
 gcc/tree-ssa-sccvn.cc                     | 12 +++-
 2 files changed, 104 insertions(+), 3 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/torture/bitint-115.c 
b/gcc/testsuite/gcc.dg/torture/bitint-115.c
new file mode 100644
index 000000000000..e675fbb96415
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/bitint-115.c
@@ -0,0 +1,95 @@
+/* { dg-do run { target { bitint && le } } } */
+
+#if __BITINT_MAXWIDTH__ >= 1024
+union U1 {
+  _BitInt(1024) big;
+  struct __attribute__((packed)) { unsigned a : 3; unsigned _BitInt(510) f : 
510; } s;
+};
+
+union U2 {
+  _BitInt(1024) big;
+  struct __attribute__((packed)) { unsigned a : 1; unsigned _BitInt(512) f : 
512; } s;
+};
+
+union U3 {
+  _BitInt(1024) big;
+  struct __attribute__((packed)) { unsigned a : 5; unsigned _BitInt(508) f : 
508; } s;
+};
+
+union U4 {
+  _BitInt(1024) big;
+  struct __attribute__((packed)) { unsigned a : 7; unsigned _BitInt(506) f : 
506; } s;
+};
+
+__attribute__((noinline)) void
+test1 (void)
+{
+  union U1 u;
+  u.big = (_BitInt(1024))1 << 512;
+  if (u.s.f >> 509 != 1)
+    __builtin_abort ();
+
+  u.big = ((_BitInt(1024))1 << 512) | ((_BitInt(1024))1 << 511) | 
((_BitInt(1024))1 << 3);
+  if ((u.s.f & 1) != 1)
+    __builtin_abort ();
+  if ((u.s.f >> 508) != 3)
+    __builtin_abort ();
+}
+
+__attribute__((noinline)) void
+test2 (void)
+{
+  union U2 u;
+  u.big = (_BitInt(1024))1 << 512;
+  if (u.s.f >> 511 != 1)
+    __builtin_abort ();
+
+  u.big = ((_BitInt(1024))1 << 512) | ((_BitInt(1024))1 << 511) | 
((_BitInt(1024))1 << 1);
+  if ((u.s.f & 1) != 1)
+    __builtin_abort ();
+  if ((u.s.f >> 510) != 3)
+    __builtin_abort ();
+}
+
+__attribute__((noinline)) void
+test3 (void)
+{
+  union U3 u;
+  u.big = (_BitInt(1024))1 << 512;
+  if (u.s.f >> 507 != 1)
+    __builtin_abort ();
+
+  u.big = ((_BitInt(1024))1 << 512) | ((_BitInt(1024))1 << 511) | 
((_BitInt(1024))1 << 5);
+  if ((u.s.f & 1) != 1)
+    __builtin_abort ();
+  if ((u.s.f >> 506) != 3)
+    __builtin_abort ();
+}
+
+__attribute__((noinline)) void
+test4 (void)
+{
+  union U4 u;
+  u.big = (_BitInt(1024))1 << 512;
+  if (u.s.f >> 505 != 1)
+    __builtin_abort ();
+
+  u.big = ((_BitInt(1024))1 << 512) | ((_BitInt(1024))1 << 511) | 
((_BitInt(1024))1 << 7);
+  if ((u.s.f & 1) != 1)
+    __builtin_abort ();
+  if ((u.s.f >> 504) != 3)
+    __builtin_abort ();
+}
+#endif
+
+int
+main (void)
+{
+#if __BITINT_MAXWIDTH__ >= 1024
+  test1 ();
+  test2 ();
+  test3 ();
+  test4 ();
+#endif
+  return 0;
+}
diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc
index ac9cb6e7e4c1..6d7f0076266b 100644
--- a/gcc/tree-ssa-sccvn.cc
+++ b/gcc/tree-ssa-sccvn.cc
@@ -3267,10 +3267,14 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void 
*data_,
          && size2.is_constant (&size2i))
        {
          if (data->partial_defs.is_empty ()
+             && (maxsizei
+                 <= (HOST_WIDE_INT) vn_walk_cb_data::bufsize * BITS_PER_UNIT)
              && known_subrange_p (offseti, maxsizei, offset2, size2))
            {
-             /* We support up to 512-bit values (for V8DFmode).  */
-             unsigned char buffer[65];
+             /* We support up to 512-bit values (for V8DFmode).  An
+                unaligned access can need one more byte from the
+                encoding, plus one for the shift carry-over.  */
+             unsigned char buffer[vn_walk_cb_data::bufsize + 2];
              int len;
 
              tree rhs = gimple_assign_rhs1 (def_stmt);
@@ -3279,7 +3283,9 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void 
*data_,
              len = native_encode_expr (rhs,
                                        buffer, sizeof (buffer) - 1,
                                        (offseti - offset2i) / BITS_PER_UNIT);
-             if (len > 0 && len * BITS_PER_UNIT >= maxsizei)
+             if (len > 0
+                 && (len * BITS_PER_UNIT
+                     >= ((offseti - offset2i) % BITS_PER_UNIT) + maxsizei))
                {
                  tree type = vr->type;
                  unsigned char *buf = buffer;

Reply via email to