https://gcc.gnu.org/g:ed9f5bf1c1288168b14edd3e35f4d32330bef66d

commit r17-4500-ged9f5bf1c1288168b14edd3e35f4d32330bef66d
Author: Samarth Tandale <[email protected]>
Date:   Sun Sep 20 17:18:25 2026 -0600

    [PATCH v2] match: Simplify value-range guarded min/max expressions 
[PR125700]
    
    For MIN_EXPR, if x is TYPE_MAX_VALUE, then min (x, y) is y.
    So:
            x == TYPE_MAX_VALUE ? y : min (x, y)
    can be simplified to:
            min (x, y)
    
    Similarly for MAX_EXPR, if x is TYPE_MIN_VALUE, then max (x, y)
    is y.
    So:
            x == TYPE_MIN_VALUE ? y : max (x, y)
    can be simplified to:
            max (x, y)
    
    This adds a combined match.pd pattern for MIN_EXPR and MAX_EXPR.
    
    Changes since v1:
    - Extended the optimization beyond fixed type limits. It now handles 
constants when Ranger proves their relationship with y's value range and 
conservatively falls back when no useful range is available.
    - Added positive and negative test cases for Ranger-derived ranges.
    
            PR tree-optimization/125700
    
    gcc/ChangeLog:
    
            * match.pd: Fold type-bound guarded min/max expressions.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/tree-ssa/pr125700.c: New test.
            * gcc.dg/tree-ssa/pr125700-int-width.c: New test.
            * gcc.dg/tree-ssa/pr125700-negative.c: New test.
            * gcc.dg/tree-ssa/pr125700-range.c: New test.
    
    Signed-off-by: Samarth Tandale <[email protected]>

Diff:
---
 gcc/match.pd                                       |  25 ++++
 gcc/testsuite/gcc.dg/tree-ssa/pr125700-int-width.c |  51 ++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr125700-negative.c  |  70 ++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr125700-range.c     | 144 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr125700.c           | 117 +++++++++++++++++
 5 files changed, 407 insertions(+)

diff --git a/gcc/match.pd b/gcc/match.pd
index 341962a3faac..e5ffd6b0d991 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -13589,3 +13589,28 @@ and,
  (if (INTEGRAL_TYPE_P (type))
    (with { tree itype = TREE_TYPE (@2); }
     (convert (minus @2 (convert:itype @1))))))
+
+#if GIMPLE
+/* Replace (x == C) ? y : MIN (x, y) -> MIN (x, y) when C >= upper_bound (y),
+   and (x == C) ? y : MAX (x, y) -> MAX (x, y) when C <= lower_bound (y),
+   because in both cases the guarded arm equals the min/max expression.  */
+(for minmax (min max)
+ (simplify
+  (cond (eq @0 INTEGER_CST@1) @2 (minmax:c@3 @0 @2))
+  (if (INTEGRAL_TYPE_P (type))
+   (with {
+     int_range_max r;
+     if (!gimple_match_range_of_expr (r, @2, @2))
+       r.set_varying (TREE_TYPE (@2));
+     wide_int bound;
+     if (minmax == MIN_EXPR)
+       bound = r.upper_bound ();
+     else
+       bound = r.lower_bound ();
+    }
+   (if ((minmax == MIN_EXPR
+        && wi::ge_p (wi::to_wide (@1), bound, TYPE_SIGN (type)))
+       || (minmax == MAX_EXPR
+           && wi::le_p (wi::to_wide (@1), bound, TYPE_SIGN (type))))
+    @3)))))
+#endif
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125700-int-width.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr125700-int-width.c
new file mode 100644
index 000000000000..a203fdb01189
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125700-int-width.c
@@ -0,0 +1,51 @@
+/* PR tree-optimization/125700  */
+/* Integer width variants to cover non-int widths and both signed/unsigned.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-optimized" } */
+
+#include <limits.h>
+
+#define GEN_FUNCS(TAG, UTYPE, STYPE, UMAX, SMAX, SMIN) \
+UTYPE \
+fumin_##TAG (UTYPE x, UTYPE y) \
+{ \
+  if (x == (UMAX)) \
+    return y; \
+  return x < y ? x : y; \
+} \
+\
+STYPE \
+fsmin_##TAG (STYPE x, STYPE y) \
+{ \
+  if (x == (SMAX)) \
+    return y; \
+  return x < y ? x : y; \
+} \
+\
+UTYPE \
+fumax_##TAG (UTYPE x, UTYPE y) \
+{ \
+  if (x == 0) \
+    return y; \
+  return x > y ? x : y; \
+} \
+\
+STYPE \
+fsmax_##TAG (STYPE x, STYPE y) \
+{ \
+  if (x == (SMIN)) \
+    return y; \
+  return x > y ? x : y; \
+}
+
+GEN_FUNCS (char, unsigned char, signed char, UCHAR_MAX, SCHAR_MAX, SCHAR_MIN)
+GEN_FUNCS (short, unsigned short, short, USHRT_MAX, SHRT_MAX, SHRT_MIN)
+GEN_FUNCS (long, unsigned long, long, ULONG_MAX, LONG_MAX, LONG_MIN)
+GEN_FUNCS (long_long, unsigned long long, long long, ULLONG_MAX, LLONG_MAX,
+    LLONG_MIN)
+
+/* { dg-final { scan-tree-dump-not {if \(} "optimized" } } */
+/* { dg-final { scan-tree-dump-not " == " "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MIN_EXPR" 8 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MAX_EXPR" 8 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125700-negative.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr125700-negative.c
new file mode 100644
index 000000000000..2cfa280a138b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125700-negative.c
@@ -0,0 +1,70 @@
+/* PR tree-optimization/125700  */
+/* Negative test cases, should not transform.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-optimized" } */
+
+unsigned
+fumin_negative (unsigned x, unsigned y)
+{
+  if (x == 0)
+    return y;
+  return x < y ? x : y;
+}
+
+unsigned
+fumax_negative (unsigned x, unsigned y)
+{
+  if (x == -1u)
+    return y;
+  return x > y ? x : y;
+}
+
+signed
+fsmin_negative (signed x, signed y)
+{
+  if (x == -__INT_MAX__ - 1)
+    return y;
+  return x < y ? x : y;
+}
+
+signed
+fsmax_negative (signed x, signed y)
+{
+  if (x == __INT_MAX__)
+    return y;
+  return x > y ? x : y;
+}
+
+/* Unsigned char widened to unsigned int.  */
+unsigned
+fumin_range_negative (unsigned char a, unsigned int b)
+{
+  unsigned aa = a;
+  if (aa == 255)
+    return b;
+  return aa < b ? aa : b;
+}
+
+/* Signed char widened to int.  */
+int
+fsmin_range_negative (signed char a, signed int b)
+{
+  int aa = a;
+  if (aa == 127)
+    return b;
+  return aa < b ? aa : b;
+}
+
+int
+fsmax_range_negative (signed char a, signed int b)
+{
+  int aa = a;
+  if (aa == -128)
+    return b;
+  return aa > b ? aa : b;
+}
+
+/* None of the if and '==' comparisons should be eliminated.  */
+/* { dg-final { scan-tree-dump-times {if \(} 7 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " == " 7 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125700-range.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr125700-range.c
new file mode 100644
index 000000000000..d474162d5ceb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125700-range.c
@@ -0,0 +1,144 @@
+/* PR tree-optimization/125700  */
+/* Test that the optimization fires using Ranger-derived value ranges,
+   including ranges from widened types and range-limiting operations.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-optimized" } */
+
+/* Unsigned char widened to unsigned int.  */
+unsigned
+fumin_char_range (unsigned char a, unsigned char b)
+{
+  unsigned aa = a;
+  unsigned bb = b;
+  if (aa == 255)
+    return bb;
+  return aa < bb ? aa : bb;
+}
+
+unsigned
+fumax_char_range (unsigned char a, unsigned char b)
+{
+  unsigned aa = a;
+  unsigned bb = b;
+  if (aa == 0)
+    return bb;
+  return aa > bb ? aa : bb;
+}
+
+/* Signed char widened to int.  */
+int
+fsmin_char_range (signed char a, signed char b)
+{
+  int aa = a;
+  int bb = b;
+  if (aa == 127)
+    return bb;
+  return aa < bb ? aa : bb;
+}
+
+int
+fsmax_char_range (signed char a, signed char b)
+{
+  int aa = a;
+  int bb = b;
+  if (aa == -128)
+    return bb;
+  return aa > bb ? aa : bb;
+}
+
+/* C is strictly above the upper bound of y's value range for MIN,
+   and strictly below the lower bound for MAX.  */
+unsigned
+fumin_range_above_bound (unsigned x, unsigned y)
+{
+  if (y > 100)
+    y = 100;
+  if (x == 120)
+    return y;
+  return x < y ? x : y;
+}
+
+unsigned
+fumax_range_below_bound (unsigned x, unsigned y)
+{
+  y &= 127;
+  if (y < 27)
+    y = 27;
+  if (x == 10)
+    return y;
+  return x > y ? x : y;
+}
+
+int
+fsmin_range_above_bound (int x, int y)
+{
+  y &= 127;
+  if (y > 100)
+    y = 100;
+  if (x == 120)
+    return y;
+  return x < y ? x : y;
+}
+
+int
+fsmax_range_below_bound (int x, int y)
+{
+  y &= 127;
+  if (y < 27)
+    y = 27;
+  if (x == 10)
+    return y;
+  return x > y ? x : y;
+}
+
+/* C is equal to the upper bound of y's value range for MIN,
+   and equal to the lower bound for MAX.  */
+unsigned
+fumin_range_at_bound (unsigned x, unsigned y)
+{
+  y &= 127;
+  if (y > 100)
+    y = 100;
+  if (x == 100)
+    return y;
+  return x < y ? x : y;
+}
+
+unsigned
+fumax_range_at_bound (unsigned x, unsigned y)
+{
+  y &= 127;
+  if (y < 27)
+    y = 27;
+  if (x == 27)
+    return y;
+  return x > y ? x : y;
+}
+
+int
+fsmin_range_at_bound (int x, int y)
+{
+  y &= 127;
+  if (y > 100)
+    y = 100;
+  if (x == 100)
+    return y;
+  return x < y ? x : y;
+}
+
+int
+fsmax_range_at_bound (int x, int y)
+{
+  y &= 127;
+  if (y < 27)
+    y = 27;
+  if (x == 27)
+    return y;
+  return x > y ? x : y;
+}
+
+/* { dg-final { scan-tree-dump-not {if \(} "optimized" } } */
+/* { dg-final { scan-tree-dump-not " == " "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MIN_EXPR" 10 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MAX_EXPR" 10 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125700.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr125700.c
new file mode 100644
index 000000000000..397c72544355
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125700.c
@@ -0,0 +1,117 @@
+/* PR tree-optimization/125700  */
+/* Test the original cases, alternative condition forms and reversed
+   minimum/maximum operand orders.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-optimized" } */
+
+/* Base test cases.  */
+unsigned
+fumin (unsigned x, unsigned y)
+{
+  if (x == -1u)
+    return y;
+  return x < y ? x : y;
+}
+
+signed
+fsmin (signed x, signed y)
+{
+  if (x == __INT_MAX__)
+    return y;
+  return x < y ? x : y;
+}
+
+unsigned
+fumax (unsigned x, unsigned y)
+{
+  if (x == 0)
+    return y;
+  return x > y ? x : y;
+}
+
+signed
+fsmax (signed x, signed y)
+{
+  if (x == -__INT_MAX__ - 1)
+    return y;
+  return x > y ? x : y;
+}
+
+/* Conditional operator variants.  */
+unsigned
+fumin_ne (unsigned x, unsigned y)
+{
+  if (x != -1u)
+    return x < y ? x : y;
+  return y;
+}
+
+unsigned
+fumax_ne (unsigned x, unsigned y)
+{
+  if (x != 0)
+    return x > y ? x : y;
+  return y;
+}
+
+unsigned
+fumin_ge (unsigned x, unsigned y)
+{
+  if (x >= -1u)
+    return y;
+  return x < y ? x : y;
+}
+
+unsigned
+fumax_le (unsigned x, unsigned y)
+{
+  if (x <= 0)
+    return y;
+  return x > y ? x : y;
+}
+
+unsigned
+fumin_lt (unsigned x, unsigned y)
+{
+  if (!(x < -1u))
+    return y;
+  return x < y ? x : y;
+}
+
+unsigned
+fumax_gt (unsigned x, unsigned y)
+{
+  if (!(x > 0))
+    return y;
+  return x > y ? x : y;
+}
+
+/* Commutativity tests for min/max operator.  */
+unsigned
+fumin_flip (unsigned x, unsigned y)
+{
+  if (x == -1u)
+    return y;
+  return y > x ? x : y;
+}
+
+unsigned
+fumax_flip (unsigned x, unsigned y)
+{
+  if (x == 0)
+    return y;
+  return y < x ? x : y;
+}
+
+/* The if and all conditional operators should be removed
+   from the optimized dump output.  */
+/* { dg-final { scan-tree-dump-not {if \(} "optimized" } } */
+/* { dg-final { scan-tree-dump-not " == " "optimized" } } */
+/* { dg-final { scan-tree-dump-not " != " "optimized" } } */
+/* { dg-final { scan-tree-dump-not " <= " "optimized" } } */
+/* { dg-final { scan-tree-dump-not " >= " "optimized" } } */
+/* { dg-final { scan-tree-dump-not " < " "optimized" } } */
+/* { dg-final { scan-tree-dump-not " > " "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MIN_EXPR" 6 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MAX_EXPR" 6 "optimized" } } */

Reply via email to