[PATCH] match.pd: drop a clamp whose bounds cross
<[email protected]> Tue, 4 Aug 2026 11:54:04 +0200
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
From: Kyrylo Tkachov <[email protected]> MIN (MAX (X, Y + CST), Y) is Y for a positive CST: the inner select is at least Y + CST, which is strictly above Y where the type cannot wrap, so the outer select always takes Y. The dual holds for MAX over MIN with a negative offset. Both leave the whole nest dead. int f (int x, int y) { int a = x > y + 7 ? x : y + 7; return a < y ? a : y; } aarch64 -O2 before: add w2, w1, 7 cmp w2, w0 csel w0, w2, w0, ge cmp w0, w1 csel w0, w0, w1, le after: mov w0, w1 The shape arises after inlining, when a clamp helper is instantiated with a lower bound that the caller has already pushed above the upper one. The existing min/max-with-offset rules only cover the case where the two selects name the same operand, so the nest survived. Bootstrapped and tested on aarch64-none-linux-gnu. Ok for trunk? Thanks, Kyrill gcc/ChangeLog: * match.pd (MIN (MAX (X, Y + CST), Y)): New simplification. (MAX (MIN (X, Y + CST), Y)): Likewise. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/minmax-offset-absorb-1.c: New test. Signed-off-by: Kyrylo Tkachov <[email protected]> --- gcc/match.pd | 15 ++++++++ .../gcc.dg/tree-ssa/minmax-offset-absorb-1.c | 36 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/minmax-offset-absorb-1.c diff --git a/gcc/match.pd b/gcc/match.pd index 30a77da929a..71de58f2fd8 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -4680,6 +4680,21 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) @0 @2))) +/* MIN (MAX (X, Y + CST), Y) -> Y for a positive CST, and the dual + MAX (MIN (X, Y + CST), Y) -> Y for a negative one. The inner select is + at least Y + CST, which the absence of wrapping puts strictly beyond Y, + so the outer select always takes Y and the whole nest is dead. */ +(simplify + (min:c (max:c @0 (plus @1 INTEGER_CST@2)) @1) + (if (TYPE_OVERFLOW_UNDEFINED (type) && !TYPE_OVERFLOW_SANITIZED (type) + && tree_int_cst_sgn (@2) > 0) + @1)) +(simplify + (max:c (min:c @0 (plus @1 INTEGER_CST@2)) @1) + (if (TYPE_OVERFLOW_UNDEFINED (type) && !TYPE_OVERFLOW_SANITIZED (type) + && tree_int_cst_sgn (@2) < 0) + @1)) + /* min (a, b) op max (a, b) -> a op b */ (for op (plus mult bit_and bit_xor bit_ior eq ne min max) (simplify diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-offset-absorb-1.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-offset-absorb-1.c new file mode 100644 index 00000000000..ae41d6d801e --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-offset-absorb-1.c @@ -0,0 +1,36 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +/* MIN (MAX (X, Y + CST), Y) is Y for a positive CST, and the dual + MAX (MIN (X, Y + CST), Y) is Y for a negative one. */ + +int f1 (int x, int y) +{ + int a = x > y + 7 ? x : y + 7; + return a < y ? a : y; +} + +long f2 (long x, long y) +{ + long a = x < y - 9 ? x : y - 9; + return a > y ? a : y; +} + +/* Must not fold: with a wrapping type Y + CST can land below Y. */ +unsigned keep1 (unsigned x, unsigned y) +{ + unsigned a = x > y + 7 ? x : y + 7; + return a < y ? a : y; +} + +/* Must not fold: the offset has the wrong sign for the outer select. */ +int keep2 (int x, int y) +{ + int a = x > y - 7 ? x : y - 7; + return a < y ? a : y; +} + +/* f1 and f2 collapse to a bare return of Y. */ +/* { dg-final { scan-tree-dump-times "return y_" 2 "optimized" } } */ +/* keep1 holds on to its clamp. */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "optimized" } } */ -- 2.50.1 (Apple Git-155)