https://gcc.gnu.org/g:70aff5112ec25f2391d8048d8c7994160d3cb008

commit r16-3034-g70aff5112ec25f2391d8048d8c7994160d3cb008
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Wed Aug 6 11:30:08 2025 +0200

    bitint: Fix up INTEGER_CST PHI handling [PR121413]
    
    The following testcase is miscompiled on aarch64-linux.
    The problem is in the optimization to shorten large constants
    in PHI arguments.
    In a couple of places during bitint lowering we compute
    minimal precision of constant and if it is significantly
    smaller than the precision of the type, store smaller constant
    in memory and extend it at runtime (zero or all ones).
    Now, in most places that works fine, we handle the stored number
    of limbs by loading them from memory and then the rest is
    extended.  In the PHI INTEGER_CST argument handling we do
    it differently, we don't form there any loops (because we
    insert stmt sequences on the edges).
    The problem is that we copy the whole _BitInt variable from
    memory to the PHI VAR_DECL + initialize the rest to = {} or
    memset to -1.  It has
    min_prec = CEIL (min_prec, limb_prec) * limb_prec;
    precision, so e.g. on x86_64 there is no padding and it works
    just fine.  But on aarch64 which has abi_limb_mode TImode
    and limb_mode DImode it doesn't in some cases.
    In the testcase the constant has 408 bits min precision, rounded up
    to limb_prec (64) is 448, i.e. 7 limbs.  But aarch64 with TImode
    abi_limb_mode will actually allocate 8 limbs and the most significant
    limb is solely padding.  As we want to extend the constant with all
    ones, copying the padding (from memory, so 0s) will result in
    64 0 bits where 1 bits were needed.
    
    The following patch fixes it by detecting this case and setting
    min_prec to a multiple of abi limb precision so that it has
    no padding.
    
    2025-08-06  Jakub Jelinek  <ja...@redhat.com>
    
            PR tree-optimization/121413
            * gimple-lower-bitint.cc (abi_limb_prec): New variable
            (bitint_precision_kind): Initialize it.
            (gimple_lower_bitint): Clear it at the start.  For
            min_prec > limb_prec descreased precision vars for
            INTEGER_CST PHI arguments ensure min_prec is either
            prec or multiple of abi_limb_prec.
    
            * gcc.dg/torture/bitint-85.c: New test.

Diff:
---
 gcc/gimple-lower-bitint.cc               | 21 +++++++++++++++++---
 gcc/testsuite/gcc.dg/torture/bitint-85.c | 34 ++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/gcc/gimple-lower-bitint.cc b/gcc/gimple-lower-bitint.cc
index ff5b12c6f5c4..40f3bfadaf05 100644
--- a/gcc/gimple-lower-bitint.cc
+++ b/gcc/gimple-lower-bitint.cc
@@ -76,7 +76,7 @@ enum bitint_prec_kind {
 /* Caches to speed up bitint_precision_kind.  */
 
 static int small_max_prec, mid_min_prec, large_min_prec, huge_min_prec;
-static int limb_prec;
+static int limb_prec, abi_limb_prec;
 static bool bitint_big_endian, bitint_extended;
 
 /* Categorize _BitInt(PREC) as small, middle, large or huge.  */
@@ -109,6 +109,9 @@ bitint_precision_kind (int prec)
     large_min_prec = MAX_FIXED_MODE_SIZE + 1;
   if (!limb_prec)
     limb_prec = GET_MODE_PRECISION (limb_mode);
+  if (!abi_limb_prec)
+    abi_limb_prec
+      = GET_MODE_PRECISION (as_a <scalar_int_mode> (info.abi_limb_mode));
   if (!huge_min_prec)
     {
       if (4 * limb_prec >= MAX_FIXED_MODE_SIZE)
@@ -6678,7 +6681,7 @@ static unsigned int
 gimple_lower_bitint (void)
 {
   small_max_prec = mid_min_prec = large_min_prec = huge_min_prec = 0;
-  limb_prec = 0;
+  limb_prec = abi_limb_prec = 0;
   bitint_big_endian = false;
 
   unsigned int i;
@@ -7640,7 +7643,19 @@ gimple_lower_bitint (void)
                       from smaller number.  */
                    min_prec = prec;
                  else
-                   min_prec = CEIL (min_prec, limb_prec) * limb_prec;
+                   {
+                     min_prec = CEIL (min_prec, limb_prec) * limb_prec;
+                     if (min_prec > limb_prec && abi_limb_prec > limb_prec)
+                       {
+                         /* For targets with ABI limb precision higher than
+                            limb precision round to ABI limb precision,
+                            otherwise c can contain padding bits.  */
+                         min_prec
+                           = CEIL (min_prec, abi_limb_prec) * abi_limb_prec;
+                         if (min_prec > prec - rem - 2 * limb_prec)
+                           min_prec = prec;
+                       }
+                   }
                  if (min_prec == 0)
                    c = NULL_TREE;
                  else if (min_prec == prec)
diff --git a/gcc/testsuite/gcc.dg/torture/bitint-85.c 
b/gcc/testsuite/gcc.dg/torture/bitint-85.c
new file mode 100644
index 000000000000..43eb6ffc0b68
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/bitint-85.c
@@ -0,0 +1,34 @@
+/* { dg-do run { target bitint } } */
+/* { dg-options "-std=c23 -pedantic-errors" } */
+/* { dg-skip-if "" { ! run_expensive_tests }  { "*" } { "-O0" "-O2" } } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "-flto" } { "" } } */
+
+#if __BITINT_MAXWIDTH__ >= 1024
+constexpr _BitInt(1024) d = 
-541140097068598424394740839221562143161511518875518765552323978870598341733206554363735813878577506997168480201818027232521wb;
+int c;
+
+static inline void
+foo (_BitInt(1024) b, _BitInt(1024) *r)
+{
+  if (c)
+    b = 0;
+  *r = b;
+}
+
+[[gnu::noipa]] void
+bar (_BitInt(1024) y)
+{
+  if (y != d)
+    __builtin_abort ();
+}
+#endif
+
+int
+main ()
+{
+#if __BITINT_MAXWIDTH__ >= 1024
+  _BitInt(1024) x;
+  foo (d, &x);
+  bar (x);
+#endif
+}

Reply via email to