https://gcc.gnu.org/g:b0a84b6100389c5b24658743252a7ff259ba0816

commit r17-2274-gb0a84b6100389c5b24658743252a7ff259ba0816
Author: Zhou Xuhang <[email protected]>
Date:   Thu Jun 4 11:31:14 2026 +0800

    tree-optimization: Recognize widened unsigned multiply overflow checks
    
    For unsigned operands, a widened multiplication compared against the maximum
    value of the narrow type is an overflow check when the widened type is at
    least twice as precise as the operand type.
    
    Canonicalize the shifted high-half form to the widened comparison form, then
    fold the comparison form to IFN_MUL_OVERFLOW when the target has direct
    unsigned multiply-overflow support.
    
    gcc/
            * match.pd: Canonicalize shifted high-half unsigned multiply
            overflow checks to widened comparisons.  Simplify widened unsigned
            multiply comparison against the narrow type maximum to 
IFN_MUL_OVERFLOW.
    
    gcc/testsuite/
            * g++.target/i386/mul-overflow-widen-1.C: New test.
            * g++.target/i386/mul-overflow-widen-2.C: New test.
    
    Signed-off-by: Zhou Xuhang <[email protected]>

Diff:
---
 gcc/match.pd                                       | 31 +++++++++--
 .../g++.target/i386/mul-overflow-widen-1.C         | 64 ++++++++++++++++++++++
 .../g++.target/i386/mul-overflow-widen-2.C         | 36 ++++++++++++
 3 files changed, 127 insertions(+), 4 deletions(-)

diff --git a/gcc/match.pd b/gcc/match.pd
index 4520a23a026b..84fdb420af31 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -8659,10 +8659,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
    (with { tree t = TREE_TYPE (@0), cpx = build_complex_type (t); }
     (out (imagpart (IFN_MUL_OVERFLOW:cpx @0 @1)) { build_zero_cst (t); })))))
 
-/* Similarly, for unsigned operands, (((type) A * B) >> prec) != 0 where type
-   is at least twice as wide as type of A and B, simplify to
-   __builtin_mul_overflow (A, B, <unused>).  */
+/* Similarly, for unsigned operands, (((type) A * B) >> prec) != 0
+   (or == 0) where type is at least twice as wide as type of A and B,
+   canonicalize to the following ((utype) A * B) > max (or <= max) pattern.  */
 (for cmp (eq ne)
+     out (le gt)
  (simplify
   (cmp (rshift (mult:s (convert@3 @0) (convert @1)) INTEGER_CST@2)
        integer_zerop)
@@ -8674,11 +8675,33 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        && tree_fits_uhwi_p (@2)
        && tree_to_uhwi (@2) == TYPE_PRECISION (TREE_TYPE (@0))
        && types_match (@0, @1)
+       && TYPE_MAX_VALUE (TREE_TYPE (@0)))
+   (with { tree utype = unsigned_type_for (TREE_TYPE (@3)); }
+    (out (mult:utype (convert:utype @0) (convert:utype @1))
+        { fold_convert (utype, TYPE_MAX_VALUE (TREE_TYPE (@0))); })))))
+
+/* For unsigned operands, ((type) A * B) > max where type is at least twice as 
wide
+   as the type of unsigned A and B.
+   Simplify it to __builtin_mul_overflow (A, B, <unused>). */
+(for cmp (gt le)
+     out (ne eq)
+ (simplify
+  (cmp (mult:s (convert@3 @0) (convert @1)) INTEGER_CST@2)
+  (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
+       && INTEGRAL_TYPE_P (TREE_TYPE (@3))
+       && TYPE_UNSIGNED (TREE_TYPE (@0))
+       && TYPE_UNSIGNED (TREE_TYPE (@3))
+       && (TYPE_PRECISION (TREE_TYPE (@3))
+          >= 2 * TYPE_PRECISION (TREE_TYPE (@0)))
+       && TYPE_MAX_VALUE (TREE_TYPE (@0))
+       && (wi::to_widest (@2)
+          == wi::to_widest (TYPE_MAX_VALUE (TREE_TYPE (@0))))
+       && types_match (@0, @1)
        && type_has_mode_precision_p (TREE_TYPE (@0))
        && (optab_handler (umulv4_optab, TYPE_MODE (TREE_TYPE (@0)))
           != CODE_FOR_nothing))
    (with { tree t = TREE_TYPE (@0), cpx = build_complex_type (t); }
-    (cmp (imagpart (IFN_MUL_OVERFLOW:cpx @0 @1)) { build_zero_cst (t); })))))
+    (out (imagpart (IFN_MUL_OVERFLOW:cpx @0 @1)) { build_zero_cst (t); })))))
 
 /* Demote operands of IFN_{ADD,SUB,MUL}_OVERFLOW.  */
 (for ovf (IFN_ADD_OVERFLOW IFN_SUB_OVERFLOW IFN_MUL_OVERFLOW)
diff --git a/gcc/testsuite/g++.target/i386/mul-overflow-widen-1.C 
b/gcc/testsuite/g++.target/i386/mul-overflow-widen-1.C
new file mode 100644
index 000000000000..d1002717a13e
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/mul-overflow-widen-1.C
@@ -0,0 +1,64 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized -masm=att" } */
+/* { dg-final { scan-tree-dump-times " = \\.MUL_OVERFLOW " 8 "optimized" } } */
+/* { dg-final { scan-assembler-times "\tmulw\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tmull\t" 5 } } */
+/* { dg-final { scan-assembler-times "\tseto\t" 5 } } */
+/* { dg-final { scan-assembler-times "\tsetno\t" 3 } } */
+
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long uint64_t;
+
+#define UINT16_MAX 65535U
+#define UINT32_MAX 4294967295U
+
+bool
+mul16_gt (uint16_t x, uint16_t y)
+{
+  return uint32_t (x) * y > UINT16_MAX;
+}
+
+bool
+mul16_le (uint16_t x, uint16_t y)
+{
+  return uint32_t (x) * y <= UINT16_MAX;
+}
+
+bool
+mul16_builtin (uint16_t x, uint16_t y)
+{
+  uint16_t result;
+  return __builtin_mul_overflow (x, y, &result);
+}
+
+bool
+mul32_gt (uint32_t x, uint32_t y)
+{
+  return uint64_t (x) * y > UINT32_MAX;
+}
+
+bool
+mul32_le (uint32_t x, uint32_t y)
+{
+  return uint64_t (x) * y <= UINT32_MAX;
+}
+
+bool
+mul32_gt_rev (uint32_t x, uint32_t y)
+{
+  return UINT32_MAX < uint64_t (x) * y;
+}
+
+bool
+mul32_le_rev (uint32_t x, uint32_t y)
+{
+  return UINT32_MAX >= uint64_t (x) * y;
+}
+
+bool
+mul32_builtin (uint32_t x, uint32_t y)
+{
+  uint32_t result;
+  return __builtin_mul_overflow (x, y, &result);
+}
diff --git a/gcc/testsuite/g++.target/i386/mul-overflow-widen-2.C 
b/gcc/testsuite/g++.target/i386/mul-overflow-widen-2.C
new file mode 100644
index 000000000000..94f5eb9de30d
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/mul-overflow-widen-2.C
@@ -0,0 +1,36 @@
+/* { dg-do compile { target int128 } } */
+/* { dg-options "-O2 -fdump-tree-optimized -masm=att" } */
+/* { dg-final { scan-tree-dump-times " = \\.MUL_OVERFLOW " 4 "optimized" } } */
+/* { dg-final { scan-assembler-times "\tmulq\t" 4 } } */
+/* { dg-final { scan-assembler-times "\tseto\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tsetno\t" 1 } } */
+
+typedef unsigned long long uint64_t;
+typedef unsigned __int128 uint128_t;
+
+#define UINT64_MAX 18446744073709551615ULL
+
+bool
+mul64_gt (uint64_t x, uint64_t y)
+{
+  return uint128_t (x) * y > UINT64_MAX;
+}
+
+bool
+mul64_le (uint64_t x, uint64_t y)
+{
+  return uint128_t (x) * y <= UINT64_MAX;
+}
+
+bool
+mul64_high (uint64_t x, uint64_t y)
+{
+  return ((__int128) x * y >> 64) != 0;
+}
+
+bool
+mul64_builtin (uint64_t x, uint64_t y)
+{
+  uint64_t result;
+  return __builtin_mul_overflow (x, y, &result);
+}

Reply via email to