The integer_minus_onep case is really meant for multiplication by
-1, not by (unsigned char)0xff or (unsigned short)0xffff or 0xffffffffU etc.
We've already tested that the first operand is signed (otherwise the
earlier case is used) and also that int_fits_type_p (@1, TREE_TYPE (@0)),
but if @0 is signed and @1 is unsigned all ones with smaller precision
than that, it fits into @0's type, integer_minus_onep will be true
and it still should be handled through the ranges, not as @0 == min.
2026-01-30 Jakub Jelinek <jakub@redhat.com>
PR tree-optimization/123864
* match.pd (__builtin_mul_overflow_p (x, cst, (stype) 0) ->
x > stype_max / cst || x < stype_min / cst): Only check
integer_minus_onep for signed types.
* gcc.c-torture/execute/pr123864.c: New test.
* gcc.dg/torture/pr123864.c: New test.
(if (TYPE_UNSIGNED (TREE_TYPE (@0)))
(convert (gt @0 (trunc_div! { TYPE_MAX_VALUE (TREE_TYPE (@0)); } @1)))
(if (TYPE_MIN_VALUE (TREE_TYPE (@0)))
- (if (integer_minus_onep (@1))
+ (if (!TYPE_UNSIGNED (TREE_TYPE (@1)) && integer_minus_onep (@1))
(convert (eq @0 { TYPE_MIN_VALUE (TREE_TYPE (@0)); }))
(with
{
--- /dev/null
+/* PR tree-optimization/123864 */
+
+[[gnu::noipa]] static int
+foo (long long x)
+{
+ return __builtin_mul_overflow_p (x, ~0U, x);
+}
+
+int
+main ()
+{
+ if (foo (0))
+ __builtin_abort ();
+#if __SIZEOF_INT__ == 4 && __SIZEOF_LONG_LONG__ == 8 && __CHAR_BIT__ == 8
+ if (foo (__INT_MAX__ + 1LL))
+ __builtin_abort ();
+ if (!foo (__INT_MAX__ + 2LL))
+ __builtin_abort ();
+ if (foo (-__INT_MAX__ - 1LL))
+ __builtin_abort ();
+ if (!foo (-__INT_MAX__ - 2LL))
+ __builtin_abort ();
+#endif
+}
--- /dev/null
+/* PR tree-optimization/123864 */
+/* { dg-do run { target int128 } } */
+
+int u, v, x;
+
+[[gnu::noipa]] static void
+foo ()
+{
+ __int128 c = (__int128) 0xa5ee4bc88ULL << 64;
+ long long b = 0x207b8a7f7LL;
+ int a = 4;
+ long long y;
+ __builtin_add_overflow (a, v, &y);
+ c *= y;
+
+ unsigned z;
+ if (__builtin_add_overflow (b, u, &z))
+ z = 0xffffffffU;
+ x = __builtin_mul_overflow_p (z, c, c);
+}
+
+int
+main ()
+{
+ foo ();
+ if (!x)
+ __builtin_abort ();
+}