]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
bitint: Fix up INTEGER_CST PHI handling [PR121413]
authorJakub Jelinek <jakub@redhat.com>
Wed, 6 Aug 2025 09:30:08 +0000 (11:30 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Wed, 6 Aug 2025 09:30:08 +0000 (11:30 +0200)
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  <jakub@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.

gcc/gimple-lower-bitint.cc
gcc/testsuite/gcc.dg/torture/bitint-85.c [new file with mode: 0644]

index ff5b12c6f5c4cbb52ce430d88430280f17fbcc86..40f3bfadaf05d44947c850f6ad0e57cc276ba6b0 100644 (file)
@@ -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 (file)
index 0000000..43eb6ff
--- /dev/null
@@ -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
+}