[gcc r17-3016] match: fold a remainder compared with its dividend
Kyrylo Tkachov via Gcc-cvs <[email protected]> Thu, 6 Aug 2026 09:21:47 +0000 (GMT)
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:7ebe4eb29d8fc0167f98705b53b4fddb28f0cffb commit r17-3016-g7ebe4eb29d8fc0167f98705b53b4fddb28f0cffb Author: Kyrylo Tkachov <[email protected]> Date: Wed Jul 29 21:26:46 2026 +0200 match: fold a remainder compared with its dividend A remainder of non-negative operands equals its dividend exactly when the dividend is smaller than the divisor. Comparing the two therefore does not need the division at all. This sits next to the (X / Y) == 0 rule, which has the same shape and the same non-negativity requirement. int f (unsigned x, unsigned y) { return x % y == x; } aarch64 -O2: before after udiv w2, w0, w1 cmp w0, w1 msub w2, w2, w1, w0 cset w0, cc cmp w2, w0 cset w0, eq A zero divisor can raise a non-call exception. Keep the division or remainder when the divisor might be zero and non-call exceptions are enabled. Also keep an explicit zero divisor for diagnostics. Bootstrapped and tested on aarch64-none-linux-gnu. gcc/ChangeLog: * match.pd ((X / Y) ==/!= 0): Preserve a possible zero-divisor exception. ((X % Y) ==/!= X): New simplification. Preserve a possible zero-divisor exception. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/modcmp-1.c: New test. * gcc.dg/tree-ssa/modcmp-noncall-1.c: Likewise. Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/match.pd | 16 ++++++++++++-- gcc/testsuite/gcc.dg/tree-ssa/modcmp-1.c | 22 +++++++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/modcmp-noncall-1.c | 27 ++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/gcc/match.pd b/gcc/match.pd index e99a35046bc8..66cf628fc613 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -3113,13 +3113,25 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) /* Transform: (X / Y) == 0 -> X < Y if X, Y are non-negative. - (X / Y) != 0 -> X >= Y, if X, Y are non-negative. */ + (X / Y) != 0 -> X >= Y, if X, Y are non-negative. + (X % Y) == X -> X < Y if X, Y are non-negative. + (X % Y) != X -> X >= Y, if X, Y are non-negative. */ (for cmp (eq ne) ocmp (lt ge) (simplify (cmp (trunc_div tree_expr_nonnegative_p@0 tree_expr_nonnegative_p@1) integer_zerop) /* Complex ==/!= is allowed, but not </>=. */ - (if (TREE_CODE (TREE_TYPE (@0)) != COMPLEX_TYPE + (if (!integer_zerop (@1) + && (!flag_non_call_exceptions || tree_expr_nonzero_p (@1)) + && TREE_CODE (TREE_TYPE (@0)) != COMPLEX_TYPE + && (VECTOR_TYPE_P (type) || !VECTOR_TYPE_P (TREE_TYPE (@0)))) + (ocmp @0 @1))) + (simplify + (cmp:c (trunc_mod tree_expr_nonnegative_p@0 tree_expr_nonnegative_p@1) @0) + /* A complex modulo cannot exist, so unlike the division above this + needs no complex exclusion. */ + (if (!integer_zerop (@1) + && (!flag_non_call_exceptions || tree_expr_nonzero_p (@1)) && (VECTOR_TYPE_P (type) || !VECTOR_TYPE_P (TREE_TYPE (@0)))) (ocmp @0 @1)))) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modcmp-1.c b/gcc/testsuite/gcc.dg/tree-ssa/modcmp-1.c new file mode 100644 index 000000000000..a59882e66a3a --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/modcmp-1.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +/* A remainder of non-negative operands equals its dividend exactly when the + dividend is smaller than the divisor, so the division can go away. */ + +int f1 (unsigned int x, unsigned int y) { return x % y == x; } +int f2 (unsigned int x, unsigned int y) { return x % y != x; } +int f3 (unsigned long x, unsigned long y) { return x == x % y; } +int f4 (int x, int y) +{ + x &= __INT_MAX__; + y &= __INT_MAX__; + return x % y == x; +} + +/* Signed operands that may be negative do not have that property. */ +int f5 (int x, int y) { return x % y == x; } + +/* { dg-final { scan-tree-dump-times " % " 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " < " 3 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " >= " 1 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modcmp-noncall-1.c b/gcc/testsuite/gcc.dg/tree-ssa/modcmp-noncall-1.c new file mode 100644 index 000000000000..aa5074b45173 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/modcmp-noncall-1.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fnon-call-exceptions -fdump-tree-optimized" } */ + +/* A possibly zero divisor can trap when non-call exceptions are enabled, + so the comparison must keep the division or remainder. */ + +int f1 (unsigned int x, unsigned int y) { return x / y == 0; } +int f2 (unsigned int x, unsigned int y) { return x / y != 0; } +int f3 (unsigned int x, unsigned int y) { return x % y == x; } +int f4 (unsigned int x, unsigned int y) { return x % y != x; } + +/* A known nonzero divisor can still fold. */ +int f5 (unsigned int x) { return x / 3 == 0; } +int f6 (unsigned int x) { return x % 3 == x; } + +/* An explicit zero must retain the front-end diagnostic and trap. */ +int f7 (unsigned int x) +{ + return x / 0 == 0; /* { dg-warning "division by zero" } */ +} +int f8 (unsigned int x) +{ + return x % 0 == x; /* { dg-warning "division by zero" } */ +} + +/* { dg-final { scan-tree-dump-times " / " 3 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " % " 3 "optimized" } } */