On Mon, Oct 5, 2015 at 5:02 PM, Richard Sandiford
<richard.sandif...@arm.com> wrote:
> 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?

Hmm.  I don't like having essentially two copies of the same machinery.
Can you instead fold gimple_val_nonnegative_real_p into a
tree_ssa_name_nonnegative_warnv_p used by tree_expr_nonnegative_warnv_p?
For integers it's also possible to look at SSA name range info.
You'd still limit recursion appropriately (by passing down a depth arg
everywhere,
defaulted to 0 I guess).

Note that the comment in gimple_val_nonnegative_real_p is correct in that
we really shouldn't recurse (but maybe handle fixed patterns - like you do here)
as the appropriate way is to have a "nonnegative" lattice.  SSA name range info
may already provide enough info here (well, not for reals - time to add basic
real range support to VRP!).

Thanks,
Richard.

> 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)
>

Reply via email to