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)

Reply via email to