]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
bitintlower: Avoid coalescing lhs with operands for .MUL_OVERFLOW [PR126262]
authorJakub Jelinek <jakub@redhat.com>
Thu, 16 Jul 2026 07:55:37 +0000 (09:55 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 16 Jul 2026 07:55:37 +0000 (09:55 +0200)
We need to avoid overlap between the lhs and input operands of __mulbitint3
and __divmodbitint4.  This is done in build_bitint_stmt_ssa_conflicts, when
muldiv_p is set, we call use on all the SSA use operands (including operands
of stmts on worklist) first and def on the lhs at the end, while for
!muldiv_p, at least for stmts with a single lhs we call def first and then
all the use calls.  For MULT_EXPR etc. we already handle it:
                case MULT_EXPR:
                case TRUNC_DIV_EXPR:
                case EXACT_DIV_EXPR:
                case TRUNC_MOD_EXPR:
                  muldiv_p = true;
Now, for the IFN_*_OVERFLOW, we handle it for bitint_big_endian only
currently, on big endian there is a problem that if the sizes don't match
exactly, even in order updates of the limbs can clobber stuff.
But, for IFN_MUL_OVERFLOW and IFN_UBSAN_CHECK_MUL, we actually use
__mulbitint3 libgcc call and that function really can't be called with
overlapping destination and inputs, because it traverses the inputs multiple
times while writing destination one by one (and it intentionally doesn't
allocate memory for temporaries).

So, the following patch fixes it by making IFN_MUL_OVERFLOW and
IFN_UBSAN_CHECK_MUL calls be always handled as muldiv_p.

2026-07-16  Jakub Jelinek  <jakub@redhat.com>

PR tree-optimization/126262
* gimple-lower-bitint.cc (build_bitint_stmt_ssa_conflicts): Treat
IFN_MUL_OVERFLOW and IFN_UBSAN_CHECK_MUL like IFN_BSWAP, regardless
of bitint_big_endian.

* gcc.dg/torture/bitint-102.c: New test.

Reviewed-by: Richard Biener <rguenth@suse.de>
gcc/gimple-lower-bitint.cc
gcc/testsuite/gcc.dg/torture/bitint-102.c [new file with mode: 0644]

index 8dad0005d5734d5aee6c853a7714c67f2feb7afe..9767e9821f86fead8396893d02f2d207bd636636 100644 (file)
@@ -7028,8 +7028,6 @@ build_bitint_stmt_ssa_conflicts (gimple *stmt, live_track *live,
       case IFN_SUB_OVERFLOW:
       case IFN_UBSAN_CHECK_ADD:
       case IFN_UBSAN_CHECK_SUB:
-      case IFN_MUL_OVERFLOW:
-      case IFN_UBSAN_CHECK_MUL:
        if (bitint_big_endian)
          {
            lhs = gimple_call_lhs (stmt);
@@ -7037,6 +7035,8 @@ build_bitint_stmt_ssa_conflicts (gimple *stmt, live_track *live,
              muldiv_p = true;
          }
        break;
+      case IFN_MUL_OVERFLOW:
+      case IFN_UBSAN_CHECK_MUL:
       case IFN_BSWAP:
       case IFN_BITREVERSE:
        lhs = gimple_call_lhs (stmt);
diff --git a/gcc/testsuite/gcc.dg/torture/bitint-102.c b/gcc/testsuite/gcc.dg/torture/bitint-102.c
new file mode 100644 (file)
index 0000000..7541849
--- /dev/null
@@ -0,0 +1,33 @@
+/* PR tree-optimization/126262 */
+/* { dg-do run { target bitint } } */
+/* { dg-options "-std=gnu23" } */
+
+#if __BITINT_MAXWIDTH__ >= 1024
+typedef unsigned _BitInt (512) A;
+typedef _BitInt (1024) B;
+
+[[gnu::noipa]] int
+foo (signed char x, A y)
+{
+  B b = -(B) y;
+  A c;
+  if (__builtin_mul_overflow (1, b, &c))
+    c = 42;
+  B f;
+  if (__builtin_mul_overflow (x, 9, &f))
+    f = 42;
+  int i;
+  if (__builtin_mul_overflow (f, 1, &i))
+    i = 42;
+  return c + i;
+}
+#endif
+
+int
+main ()
+{
+#if __BITINT_MAXWIDTH__ >= 1024
+  if (foo (1, -2) != 51)
+    __builtin_abort ();
+#endif
+}