[gcc r17-3052] match.pd: drop a clamp whose bounds cross

Kyrylo Tkachov via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:60379a459b0eab9fee4ad4681cb3a1e7f3092307

commit r17-3052-g60379a459b0eab9fee4ad4681cb3a1e7f3092307
Author: Kyrylo Tkachov <[email protected]>
Date:   Fri Jul 31 05:13:13 2026 +0200

    match.pd: drop a clamp whose bounds cross
    
    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.
    
    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.
            * gcc.dg/tree-ssa/minmax-offset-absorb-2.c: Likewise.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>

Diff:
---
 gcc/match.pd                                       | 14 ++++++++++++++
 .../gcc.dg/tree-ssa/minmax-offset-absorb-1.c       | 22 ++++++++++++++++++++++
 .../gcc.dg/tree-ssa/minmax-offset-absorb-2.c       | 20 ++++++++++++++++++++
 3 files changed, 56 insertions(+)

diff --git a/gcc/match.pd b/gcc/match.pd
index 7e644bc84d84..94801f19fc61 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -4637,6 +4637,20 @@ 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.  */
+(for outer (min max)
+     inner (max min)
+ (simplify
+  (outer:c (inner:c @0 (plus @1 INTEGER_CST@2)) @1)
+  (if (TYPE_OVERFLOW_UNDEFINED (type)
+       && !TYPE_OVERFLOW_SANITIZED (type)
+       && ((outer == MIN_EXPR && tree_int_cst_sgn (@2) > 0)
+	   || (outer == MAX_EXPR && 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 000000000000..d53d61248c07
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-offset-absorb-1.c
@@ -0,0 +1,22 @@
+/* { 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;
+}
+
+/* f1 and f2 collapse to a bare return of Y.  */
+/* { dg-final { scan-tree-dump-times "return y_" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "MAX_EXPR" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "MIN_EXPR" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-offset-absorb-2.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-offset-absorb-2.c
new file mode 100644
index 000000000000..4773c59d9835
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-offset-absorb-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* With a wrapping type Y + CST can land below Y.  */
+unsigned int keep1 (unsigned int x, unsigned int y)
+{
+  unsigned int a = x > y + 7 ? x : y + 7;
+  return a < y ? a : y;
+}
+
+/* This clamp has the opposite offset sign.  Use >= so phiopt forms both
+   the inner MAX_EXPR and the outer MIN_EXPR.  */
+int keep2 (int x, int y)
+{
+  int a = x >= y - 7 ? x : y - 7;
+  return a < y ? a : y;
+}
+
+/* { dg-final { scan-tree-dump-times "MAX_EXPR" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MIN_EXPR" 2 "optimized" } } */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.