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" } } */
