Hi!
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.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk and 14+?
2026-07-16 Jakub Jelinek <[email protected]>
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.
--- gcc/gimple-lower-bitint.cc.jj 2026-07-14 10:39:46.547982133 +0200
+++ gcc/gimple-lower-bitint.cc 2026-07-15 17:51:01.922732017 +0200
@@ -7028,8 +7028,6 @@ build_bitint_stmt_ssa_conflicts (gimple
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
muldiv_p = true;
}
break;
+ case IFN_MUL_OVERFLOW:
+ case IFN_UBSAN_CHECK_MUL:
case IFN_BSWAP:
case IFN_BITREVERSE:
lhs = gimple_call_lhs (stmt);
--- gcc/testsuite/gcc.dg/torture/bitint-102.c.jj 2026-07-15
18:43:06.967455017 +0200
+++ gcc/testsuite/gcc.dg/torture/bitint-102.c 2026-07-15 18:42:51.156653874
+0200
@@ -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
+}
Jakub