The upcoming patch to move sqrt and cbrt simplifications to match.pd caused a regression because the (abs @0)->@0 simplification didn't trigger for:
(abs (convert (abs X))) The simplification is based on tree_expr_nonnegative_p, which is pretty weak for gimple (it gives up if it sees an SSA_NAME). We have the stronger gimple_val_nonnegative_real_p, but (a) as its name implies, it's specific to reals and (b) in its current form it doesn't handle converts. This patch: - generalises the routine all types - reuses tree_{unary,binary,call}_nonnegative_warnv_p for the leaf cases - makes the routine handle CONVERT_EXPR - allows a nesting depth of 1 for CONVERT_EXPR - uses the routine instead of tree_expr_nonnegative_p for gimple. Limiting the depth to 1 is a little arbitrary but adding a param seemed over the top. Bootstrapped & regression-tested on x86_64-linux-gnu. I didn't write a specific test because this is already covered by the testsuite if the follow-on patch is also applied. OK to install? Thanks, Richard gcc/ * gimple-fold.h (gimple_val_nonnegative_real_p): Replace with... (gimple_val_nonnegative_p): ...this new function. * gimple-fold.c (gimple_val_nonnegative_real_p): Replace with... (gimple_val_nonnegative_p): ...this new function. Add a nesting depth. Handle conversions and allow them to be nested to a depth of 1. Generalize to non-reals. Use tree_binary_nonnegative_warnv_p, tree_unary_nonnegative_warnv_p and tree_call_nonnegative_warnv_p. * tree-ssa-math-opts.c (gimple_expand_builtin_pow): Update accordingly. * match.pd (nonnegative_p): New predicate. Use it instead of tree_expr_nonnegative_p to detect redundant abs expressions. Index: a/gcc/gimple-fold.c =================================================================== *** a/gcc/gimple-fold.c --- b/gcc/gimple-fold.c *************** gimple_get_virt_method_for_binfo (HOST_WIDE_INT token, tree known_binfo, *** 5773,5787 **** } /* Return true iff VAL is a gimple expression that is known to be ! non-negative. Restricted to floating-point inputs. */ bool ! gimple_val_nonnegative_real_p (tree val) { gimple *def_stmt; - gcc_assert (val && SCALAR_FLOAT_TYPE_P (TREE_TYPE (val))); - /* Use existing logic for non-gimple trees. */ if (tree_expr_nonnegative_p (val)) return true; --- 5773,5785 ---- } /* Return true iff VAL is a gimple expression that is known to be ! non-negative. DEPTH is the nesting depth. */ bool ! gimple_val_nonnegative_p (tree val, unsigned int depth) { gimple *def_stmt; /* Use existing logic for non-gimple trees. */ if (tree_expr_nonnegative_p (val)) return true; *************** gimple_val_nonnegative_real_p (tree val) *** 5789,5906 **** if (TREE_CODE (val) != SSA_NAME) return false; ! /* Currently we look only at the immediately defining statement ! to make this determination, since recursion on defining ! statements of operands can lead to quadratic behavior in the ! worst case. This is expected to catch almost all occurrences ! in practice. It would be possible to implement limited-depth ! recursion if important cases are lost. Alternatively, passes ! that need this information (such as the pow/powi lowering code ! in the cse_sincos pass) could be revised to provide it through dataflow propagation. */ def_stmt = SSA_NAME_DEF_STMT (val); if (is_gimple_assign (def_stmt)) { ! tree op0, op1; ! ! /* See fold-const.c:tree_expr_nonnegative_p for additional ! cases that could be handled with recursion. */ ! ! switch (gimple_assign_rhs_code (def_stmt)) { ! case ABS_EXPR: ! /* Always true for floating-point operands. */ ! return true; ! ! case MULT_EXPR: ! /* True if the two operands are identical (since we are ! restricted to floating-point inputs). */ ! op0 = gimple_assign_rhs1 (def_stmt); ! op1 = gimple_assign_rhs2 (def_stmt); ! ! if (op0 == op1 ! || operand_equal_p (op0, op1, 0)) ! return true; default: ! return false; } } else if (is_gimple_call (def_stmt)) { tree fndecl = gimple_call_fndecl (def_stmt); ! if (fndecl ! && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) { ! tree arg1; ! ! switch (DECL_FUNCTION_CODE (fndecl)) ! { ! CASE_FLT_FN (BUILT_IN_ACOS): ! CASE_FLT_FN (BUILT_IN_ACOSH): ! CASE_FLT_FN (BUILT_IN_CABS): ! CASE_FLT_FN (BUILT_IN_COSH): ! CASE_FLT_FN (BUILT_IN_ERFC): ! CASE_FLT_FN (BUILT_IN_EXP): ! CASE_FLT_FN (BUILT_IN_EXP10): ! CASE_FLT_FN (BUILT_IN_EXP2): ! CASE_FLT_FN (BUILT_IN_FABS): ! CASE_FLT_FN (BUILT_IN_FDIM): ! CASE_FLT_FN (BUILT_IN_HYPOT): ! CASE_FLT_FN (BUILT_IN_POW10): ! return true; ! ! CASE_FLT_FN (BUILT_IN_SQRT): ! /* sqrt(-0.0) is -0.0, and sqrt is not defined over other ! nonnegative inputs. */ ! if (!HONOR_SIGNED_ZEROS (val)) ! return true; ! ! break; ! ! CASE_FLT_FN (BUILT_IN_POWI): ! /* True if the second argument is an even integer. */ ! arg1 = gimple_call_arg (def_stmt, 1); ! ! if (TREE_CODE (arg1) == INTEGER_CST ! && (TREE_INT_CST_LOW (arg1) & 1) == 0) ! return true; ! ! break; ! ! CASE_FLT_FN (BUILT_IN_POW): ! /* True if the second argument is an even integer-valued ! real. */ ! arg1 = gimple_call_arg (def_stmt, 1); ! ! if (TREE_CODE (arg1) == REAL_CST) ! { ! REAL_VALUE_TYPE c; ! HOST_WIDE_INT n; ! ! c = TREE_REAL_CST (arg1); ! n = real_to_integer (&c); ! ! if ((n & 1) == 0) ! { ! REAL_VALUE_TYPE cint; ! real_from_integer (&cint, VOIDmode, n, SIGNED); ! if (real_identical (&c, &cint)) ! return true; ! } ! } ! ! break; ! ! default: ! return false; ! } } } return false; } /* Given a pointer value OP0, return a simplified version of an --- 5787,5871 ---- if (TREE_CODE (val) != SSA_NAME) return false; ! /* Currently we allow one level of recursion for conversions ! but look only at the immediately defining statement otherwise, ! since recursion on defining statements of operands can lead to ! quadratic behavior in the worst case. This is expected to catch ! almost all occurrences in practice. It would be possible to ! implement more recursion if important cases are lost. Alternatively, ! passes that need this information (such as the pow/powi lowering ! code in the cse_sincos pass) could be revised to provide it through dataflow propagation. */ + #define RECURSE(X) (depth == 0 && gimple_val_nonnegative_p (X, depth + 1)) def_stmt = SSA_NAME_DEF_STMT (val); if (is_gimple_assign (def_stmt)) { ! enum tree_code code = gimple_assign_rhs_code (def_stmt); ! tree lhs_type = TREE_TYPE (gimple_assign_lhs (def_stmt)); ! if (CONVERT_EXPR_CODE_P (code)) { ! tree rhs = gimple_assign_rhs1 (def_stmt); ! tree rhs_type = TREE_TYPE (rhs); ! if (TREE_CODE (lhs_type) == REAL_TYPE) ! { ! if (TREE_CODE (rhs_type) == REAL_TYPE) ! return RECURSE (rhs); ! if (INTEGRAL_TYPE_P (rhs_type)) ! { ! if (TYPE_UNSIGNED (rhs_type)) ! return true; ! return RECURSE (rhs); ! } ! } ! else if (INTEGRAL_TYPE_P (lhs_type)) ! { ! if (TREE_CODE (rhs_type) == REAL_TYPE) ! return RECURSE (rhs); ! if (INTEGRAL_TYPE_P (rhs_type)) ! return (TYPE_PRECISION (rhs_type) < TYPE_PRECISION (lhs_type) ! && TYPE_UNSIGNED (rhs_type)); ! } ! } ! bool strict_overflow_p = false; ! switch (TREE_CODE_CLASS (code)) ! { ! case tcc_binary: ! case tcc_comparison: ! return tree_binary_nonnegative_warnv_p ! (code, lhs_type, ! gimple_assign_rhs1 (def_stmt), ! gimple_assign_rhs2 (def_stmt), ! &strict_overflow_p); ! ! case tcc_unary: ! return tree_unary_nonnegative_warnv_p ! (code, lhs_type, ! gimple_assign_rhs1 (def_stmt), ! &strict_overflow_p); default: ! break; } } else if (is_gimple_call (def_stmt)) { tree fndecl = gimple_call_fndecl (def_stmt); ! if (fndecl) { ! bool strict_overflow_p = false; ! unsigned int nargs = gimple_call_num_args (def_stmt); ! tree type = TREE_TYPE (gimple_call_lhs (def_stmt)); ! tree arg0 = nargs > 0 ? gimple_call_arg (def_stmt, 0) : NULL_TREE; ! tree arg1 = nargs > 1 ? gimple_call_arg (def_stmt, 1) : NULL_TREE; ! return tree_call_nonnegative_warnv_p (type, fndecl, arg0, arg1, ! &strict_overflow_p); } } return false; + #undef RECURSE } /* Given a pointer value OP0, return a simplified version of an Index: a/gcc/gimple-fold.h =================================================================== *** a/gcc/gimple-fold.h --- b/gcc/gimple-fold.h *************** extern tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree, *** 48,54 **** extern tree gimple_get_virt_method_for_vtable (HOST_WIDE_INT, tree, unsigned HOST_WIDE_INT, bool *can_refer = NULL); ! extern bool gimple_val_nonnegative_real_p (tree); extern tree gimple_fold_indirect_ref (tree); extern bool arith_code_with_undefined_signed_overflow (tree_code); extern gimple_seq rewrite_to_defined_overflow (gimple *); --- 48,54 ---- extern tree gimple_get_virt_method_for_vtable (HOST_WIDE_INT, tree, unsigned HOST_WIDE_INT, bool *can_refer = NULL); ! extern bool gimple_val_nonnegative_p (tree, unsigned int = 0); extern tree gimple_fold_indirect_ref (tree); extern bool arith_code_with_undefined_signed_overflow (tree_code); extern gimple_seq rewrite_to_defined_overflow (gimple *); Index: a/gcc/match.pd =================================================================== *** a/gcc/match.pd --- b/gcc/match.pd *************** along with GCC; see the file COPYING3. If not see *** 34,39 **** --- 34,45 ---- integer_pow2p HONOR_NANS) + (match nonnegative_p + @0 + (if (GIMPLE + ? gimple_val_nonnegative_p (@0) + : tree_expr_nonnegative_p (@0)))) + /* Operator lists. */ (define_operator_list tcc_comparison lt le eq ne ge gt unordered ordered unlt unle ungt unge uneq ltgt) *************** along with GCC; see the file COPYING3. If not see *** 512,518 **** (abs (negate @0)) (abs @0)) (simplify ! (abs tree_expr_nonnegative_p@0) @0) /* A few cases of fold-const.c negate_expr_p predicate. */ --- 518,524 ---- (abs (negate @0)) (abs @0)) (simplify ! (abs nonnegative_p@0) @0) /* A few cases of fold-const.c negate_expr_p predicate. */ Index: a/gcc/tree-ssa-math-opts.c =================================================================== *** a/gcc/tree-ssa-math-opts.c --- b/gcc/tree-ssa-math-opts.c *************** gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc, *** 1522,1528 **** if (flag_unsafe_math_optimizations && cbrtfn ! && (gimple_val_nonnegative_real_p (arg0) || !HONOR_NANS (mode)) && real_equal (&c, &dconst1_3)) return build_and_insert_call (gsi, loc, cbrtfn, arg0); --- 1522,1528 ---- if (flag_unsafe_math_optimizations && cbrtfn ! && (gimple_val_nonnegative_p (arg0) || !HONOR_NANS (mode)) && real_equal (&c, &dconst1_3)) return build_and_insert_call (gsi, loc, cbrtfn, arg0); *************** gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc, *** 1534,1540 **** if (flag_unsafe_math_optimizations && sqrtfn && cbrtfn ! && (gimple_val_nonnegative_real_p (arg0) || !HONOR_NANS (mode)) && speed_p && hw_sqrt_exists && real_equal (&c, &dconst1_6)) --- 1534,1540 ---- if (flag_unsafe_math_optimizations && sqrtfn && cbrtfn ! && (gimple_val_nonnegative_p (arg0) || !HONOR_NANS (mode)) && speed_p && hw_sqrt_exists && real_equal (&c, &dconst1_6)) *************** gimple_expand_builtin_pow (gimple_stmt_iterator *gsi, location_t loc, *** 1589,1595 **** if (flag_unsafe_math_optimizations && cbrtfn ! && (gimple_val_nonnegative_real_p (arg0) || !HONOR_NANS (mode)) && real_identical (&c2, &c) && !c2_is_int && optimize_function_for_speed_p (cfun) --- 1589,1595 ---- if (flag_unsafe_math_optimizations && cbrtfn ! && (gimple_val_nonnegative_p (arg0) || !HONOR_NANS (mode)) && real_identical (&c2, &c) && !c2_is_int && optimize_function_for_speed_p (cfun)