https://gcc.gnu.org/g:22448c7f647d03d8b2727a3e1601aa4078ad87b6
commit r16-4583-g22448c7f647d03d8b2727a3e1601aa4078ad87b6 Author: Andrew Pinski <[email protected]> Date: Mon Oct 20 22:45:47 2025 -0700 match: Add support for `((signed)a </>= 0) ? min/max (a, c) : b` [PR101024] This is the last patch that is needed to support to remove minmax_replacement. This fixes pr101024-1.c which is failing when minmax_replacement is removed. This next patch will remove it. Changes since v1: * v2: Add new version of minmax_from_comparison that takes widest_int. Constraint the pattern to constant integers in some cases. Use mask to create the SIGNED_MAX and use GT/LE instead. Use wi::le_p/wi::ge_p instead of fold_build to do the comparison. gcc/ChangeLog: PR tree-optimization/101024 * fold-const.cc (minmax_from_comparison): New version that takes widest_int instead of tree. (minmax_from_comparison): Call minmax_from_comparison for integer cst case. * fold-const.h (minmax_from_comparison): New declaration. * match.pd (`((signed)a </>= 0) ? min/max (a, c) : b`): New pattern. Signed-off-by: Andrew Pinski <[email protected]> Diff: --- gcc/fold-const.cc | 123 ++++++++++++++++++++++++++++++------------------------ gcc/fold-const.h | 3 ++ gcc/match.pd | 29 +++++++++++++ 3 files changed, 101 insertions(+), 54 deletions(-) diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc index e8cfee81553e..1311c6ed7de1 100644 --- a/gcc/fold-const.cc +++ b/gcc/fold-const.cc @@ -150,75 +150,90 @@ static tree fold_convert_const (enum tree_code, tree, tree); static tree fold_view_convert_expr (tree, tree); static tree fold_negate_expr (location_t, tree); +/* This is a helper function to detect min/max for some operands of COND_EXPR. + The form is "(exp0 CMP cst1) ? exp0 : cst2". */ +tree_code +minmax_from_comparison (tree_code cmp, tree exp0, + const widest_int cst1, + const widest_int cst2) +{ + if (cst1 == cst2) + { + if (cmp == LE_EXPR || cmp == LT_EXPR) + return MIN_EXPR; + if (cmp == GT_EXPR || cmp == GE_EXPR) + return MAX_EXPR; + } + if (cst1 == cst2 - 1) + { + /* X <= Y - 1 equals to X < Y. */ + if (cmp == LE_EXPR) + return MIN_EXPR; + /* X > Y - 1 equals to X >= Y. */ + if (cmp == GT_EXPR) + return MAX_EXPR; + /* a != MIN_RANGE<a> ? a : MIN_RANGE<a>+1 -> MAX_EXPR<MIN_RANGE<a>+1, a> */ + if (cmp == NE_EXPR && TREE_CODE (exp0) == SSA_NAME) + { + int_range_max r; + get_range_query (cfun)->range_of_expr (r, exp0); + if (r.undefined_p ()) + r.set_varying (TREE_TYPE (exp0)); + + widest_int min = widest_int::from (r.lower_bound (), + TYPE_SIGN (TREE_TYPE (exp0))); + if (min == cst1) + return MAX_EXPR; + } + } + if (cst1 == cst2 + 1) + { + /* X < Y + 1 equals to X <= Y. */ + if (cmp == LT_EXPR) + return MIN_EXPR; + /* X >= Y + 1 equals to X > Y. */ + if (cmp == GE_EXPR) + return MAX_EXPR; + /* a != MAX_RANGE<a> ? a : MAX_RANGE<a>-1 -> MIN_EXPR<MIN_RANGE<a>-1, a> */ + if (cmp == NE_EXPR && TREE_CODE (exp0) == SSA_NAME) + { + int_range_max r; + get_range_query (cfun)->range_of_expr (r, exp0); + if (r.undefined_p ()) + r.set_varying (TREE_TYPE (exp0)); + + widest_int max = widest_int::from (r.upper_bound (), + TYPE_SIGN (TREE_TYPE (exp0))); + if (max == cst1) + return MIN_EXPR; + } + } + return ERROR_MARK; +} + + /* This is a helper function to detect min/max for some operands of COND_EXPR. The form is "(EXP0 CMP EXP1) ? EXP2 : EXP3". */ tree_code minmax_from_comparison (tree_code cmp, tree exp0, tree exp1, tree exp2, tree exp3) { - enum tree_code code = ERROR_MARK; - if (HONOR_NANS (exp0) || HONOR_SIGNED_ZEROS (exp0)) return ERROR_MARK; if (!operand_equal_p (exp0, exp2)) return ERROR_MARK; - if (TREE_CODE (exp3) == INTEGER_CST && TREE_CODE (exp1) == INTEGER_CST) - { - if (wi::to_widest (exp1) == (wi::to_widest (exp3) - 1)) - { - /* X <= Y - 1 equals to X < Y. */ - if (cmp == LE_EXPR) - code = LT_EXPR; - /* X > Y - 1 equals to X >= Y. */ - if (cmp == GT_EXPR) - code = GE_EXPR; - /* a != MIN_RANGE<a> ? a : MIN_RANGE<a>+1 -> MAX_EXPR<MIN_RANGE<a>+1, a> */ - if (cmp == NE_EXPR && TREE_CODE (exp0) == SSA_NAME) - { - int_range_max r; - get_range_query (cfun)->range_of_expr (r, exp0); - if (r.undefined_p ()) - r.set_varying (TREE_TYPE (exp0)); - - widest_int min = widest_int::from (r.lower_bound (), - TYPE_SIGN (TREE_TYPE (exp0))); - if (min == wi::to_widest (exp1)) - code = MAX_EXPR; - } - } - if (wi::to_widest (exp1) == (wi::to_widest (exp3) + 1)) - { - /* X < Y + 1 equals to X <= Y. */ - if (cmp == LT_EXPR) - code = LE_EXPR; - /* X >= Y + 1 equals to X > Y. */ - if (cmp == GE_EXPR) - code = GT_EXPR; - /* a != MAX_RANGE<a> ? a : MAX_RANGE<a>-1 -> MIN_EXPR<MIN_RANGE<a>-1, a> */ - if (cmp == NE_EXPR && TREE_CODE (exp0) == SSA_NAME) - { - int_range_max r; - get_range_query (cfun)->range_of_expr (r, exp0); - if (r.undefined_p ()) - r.set_varying (TREE_TYPE (exp0)); - - widest_int max = widest_int::from (r.upper_bound (), - TYPE_SIGN (TREE_TYPE (exp0))); - if (max == wi::to_widest (exp1)) - code = MIN_EXPR; - } - } - } - if (code != ERROR_MARK - || operand_equal_p (exp1, exp3)) + if (operand_equal_p (exp1, exp3)) { if (cmp == LT_EXPR || cmp == LE_EXPR) - code = MIN_EXPR; + return MIN_EXPR; if (cmp == GT_EXPR || cmp == GE_EXPR) - code = MAX_EXPR; + return MAX_EXPR; } - return code; + if (TREE_CODE (exp3) == INTEGER_CST + && TREE_CODE (exp1) == INTEGER_CST) + return minmax_from_comparison (cmp, exp0, wi::to_widest (exp1), wi::to_widest (exp3)); + return ERROR_MARK; } /* Return EXPR_LOCATION of T if it is not UNKNOWN_LOCATION. diff --git a/gcc/fold-const.h b/gcc/fold-const.h index e95cf48c1766..00975dcddd61 100644 --- a/gcc/fold-const.h +++ b/gcc/fold-const.h @@ -254,6 +254,9 @@ extern tree fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE #define fold_build_pointer_plus_hwi(p,o) \ fold_build_pointer_plus_hwi_loc (UNKNOWN_LOCATION, p, o) +extern tree_code minmax_from_comparison (tree_code, tree, + const widest_int, + const widest_int); extern tree_code minmax_from_comparison (tree_code, tree, tree, tree, tree); diff --git a/gcc/match.pd b/gcc/match.pd index f7e074eaa564..d00b925416ae 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -6932,6 +6932,35 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) && integer_nonzerop (fold_build2 (GE_EXPR, boolean_type_node, @3, @4))) (max @2 @4)))))) +/* Optimize (((signed)a CMP 0) ? max<a,CST2> : CST3 */ +(for cmp (lt ge) + minmax (min max) + (simplify + (cond (cmp:c (nop_convert @0) integer_zerop@1) (minmax@2 @0 INTEGER_CST@3) INTEGER_CST@4) + (if (!TYPE_UNSIGNED (TREE_TYPE (@1)) + && TYPE_UNSIGNED (TREE_TYPE (@0))) + (with + { + tree_code code; + /* ((signed)a) < 0 -> a > SIGNED_MAX */ + /* ((signed)a) >= 0 -> a <= SIGNED_MAX */ + widest_int c1 = wi::mask<widest_int>(TYPE_PRECISION (type) - 1, 0); + tree_code ncmp = cmp == GE_EXPR ? LE_EXPR : GT_EXPR; + code = minmax_from_comparison (ncmp, @0, c1, wi::to_widest (@4)); + } + (if (ncmp == LE_EXPR + && code == MIN_EXPR + && wi::le_p (wi::to_wide (@3), + wi::to_wide (@4), + TYPE_SIGN (type))) + (min @2 @4) + (if (ncmp == GT_EXPR + && code == MAX_EXPR + && wi::ge_p (wi::to_wide (@3), + wi::to_wide (@4), + TYPE_SIGN (type))) + (max @2 @4))))))) + #if GIMPLE /* These patterns should be after min/max detection as simplifications of `(type)(zero_one ==/!= 0)` to `(type)(zero_one)`
