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); +}
