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)