Move fold_trunc_transparent_mathfn to match.pd
authorRichard Sandiford <richard.sandiford@arm.com>
Fri, 23 Oct 2015 10:01:47 +0000 (10:01 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Fri, 23 Oct 2015 10:01:47 +0000 (10:01 +0000)
This moves the fold rules for trunc, floor, ceil, round, nearbyint and
rint in one go, since they're tested as a group.  Most of the code is
supporting the f(x)->x fold when x is known to be integer-valued.
Like with the non-negative test, this is probably more elegantly handled
by tracking range information for reals, but until that happens, I think
we should handle it analogously to tree_expr_nonnegative_p.

I've incorporated the fix for PR68031 in the new version of
integer_valued_real_p.  However, it seemed confusing to test for an
SSA name at the head of the function rather than the case statement,
and not fall through to tree_simple_nonnegative_warnv_p (which
conceptually shouldn't care whether an update is in progress).
But tree_simple_nonnegative_warnv_p is a no-op for SSA names,
so I simply changed it to:

          return (!name_registered_for_update_p (t)
      && depth < PARAM_VALUE (PARAM_MAX_SSA_NAME_QUERY_DEPTH)
      && gimple_stmt_nonnegative_warnv_p (SSA_NAME_DEF_STMT (t),
  strict_overflow_p, depth));

and used that in the new code too.

Doing these folds later meant that IPA would start to use information
about the aborting sinf and floor in 20030125-1.c before the folds
kicked in.  I changed them from noinline to weak to stop that.

Tested on x86_64-linux-gnu, aarch64-linux-gnu and arm-linux-gnueabi.

gcc/
* builtins.c (integer_valued_real_p): Move to fold-const.c.
(fold_trunc_transparent_mathfn, fold_builtin_trunc, fold_builtin_floor)
(fold_builtin_ceil, fold_builtin_round): Delete.
(fold_builtin_1): Handle constant trunc, floor, ceil and round
arguments here.
* convert.c (convert_to_real): Remove narrowing of rounding
functions.
* fold-const.h (integer_valued_real_unary_p)
(integer_valued_real_binary_p, integer_valued_real_call_p)
(integer_valued_real_single_p, integer_valued_real_p): Declare.
* fold-const.c (tree_single_nonnegative_warnv_p): Move
name_registered_for_update_p check to SSA_NAME case statement.
Don't call tree_simple_nonnegative_warnv_p for SSA names.
(integer_valued_real_unary_p, integer_valued_real_binary_p)
(integer_valued_real_call_p, integer_valued_real_single_p)
(integer_valued_real_invalid_p): New functions.
(integer_valued_real_p): Move from fold-const.c and rework
to call the functions above.  Handle SSA names.
* gimple-fold.h (gimple_stmt_integer_valued_real_p): Declare.
* gimple-fold.c (gimple_assign_integer_valued_real_p)
(gimple_call_integer_valued_real_p, gimple_phi_integer_valued_real_p)
(gimple_stmt_integer_valued_real_p): New functions.
* match.pd: Fold f(f(x))->f(x) for fp->fp rounding functions f.
Fold f(x)->x for the same f if x is known to be integer-valued.
Fold f(extend(x))->extend(f'(x)) if doing so doesn't affect
the result.  Canonicalize floor(x) as trunc(x) if x is
nonnegative.

gcc/testsuite/
* gcc.c-torture/execute/20030125-1.c (floor, floorf, sin, sinf):
Make weak rather than noinline.
* gcc.dg/builtins-57.c: Compile with -O.
* gcc.dg/torture/builtin-integral-1.c: Skip for -O0.

From-SVN: r229221

12 files changed:
gcc/ChangeLog
gcc/builtins.c
gcc/convert.c
gcc/fold-const.c
gcc/fold-const.h
gcc/gimple-fold.c
gcc/gimple-fold.h
gcc/match.pd
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/execute/20030125-1.c
gcc/testsuite/gcc.dg/builtins-57.c
gcc/testsuite/gcc.dg/torture/builtin-integral-1.c

index 13aec6f4d1ca0c5c5c4ee8d708709576c6a5369b..c699fa66fa67a85c373d02481fc623bd334b5c98 100644 (file)
@@ -1,3 +1,33 @@
+2015-10-23  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * builtins.c (integer_valued_real_p): Move to fold-const.c.
+       (fold_trunc_transparent_mathfn, fold_builtin_trunc, fold_builtin_floor)
+       (fold_builtin_ceil, fold_builtin_round): Delete.
+       (fold_builtin_1): Handle constant trunc, floor, ceil and round
+       arguments here.
+       * convert.c (convert_to_real): Remove narrowing of rounding
+       functions.
+       * fold-const.h (integer_valued_real_unary_p)
+       (integer_valued_real_binary_p, integer_valued_real_call_p)
+       (integer_valued_real_single_p, integer_valued_real_p): Declare.
+       * fold-const.c (tree_single_nonnegative_warnv_p): Move
+       name_registered_for_update_p check to SSA_NAME case statement.
+       Don't call tree_simple_nonnegative_warnv_p for SSA names.
+       (integer_valued_real_unary_p, integer_valued_real_binary_p)
+       (integer_valued_real_call_p, integer_valued_real_single_p)
+       (integer_valued_real_invalid_p): New functions.
+       (integer_valued_real_p): Move from fold-const.c and rework
+       to call the functions above.  Handle SSA names.
+       * gimple-fold.h (gimple_stmt_integer_valued_real_p): Declare.
+       * gimple-fold.c (gimple_assign_integer_valued_real_p)
+       (gimple_call_integer_valued_real_p, gimple_phi_integer_valued_real_p)
+       (gimple_stmt_integer_valued_real_p): New functions.
+       * match.pd: Fold f(f(x))->f(x) for fp->fp rounding functions f.
+       Fold f(x)->x for the same f if x is known to be integer-valued.
+       Fold f(extend(x))->extend(f'(x)) if doing so doesn't affect
+       the result.  Canonicalize floor(x) as trunc(x) if x is
+       nonnegative.
+
 2015-10-23  Tom de Vries  <tom@codesourcery.com>
 
        * tree-ssa-structalias.c (intra_create_variable_infos): Use
index 02bf9f65a33907fdcfc77e5621f6acff22fa55cf..8b5e3f383a02f0ce6e1b168377866da7cfe4f629 100644 (file)
@@ -154,16 +154,10 @@ static tree fold_builtin_inf (location_t, tree, int);
 static tree fold_builtin_nan (tree, tree, int);
 static tree rewrite_call_expr (location_t, tree, int, tree, int, ...);
 static bool validate_arg (const_tree, enum tree_code code);
-static bool integer_valued_real_p (tree);
-static tree fold_trunc_transparent_mathfn (location_t, tree, tree);
 static rtx expand_builtin_fabs (tree, rtx, rtx);
 static rtx expand_builtin_signbit (tree, rtx);
 static tree fold_builtin_pow (location_t, tree, tree, tree, tree);
 static tree fold_builtin_powi (location_t, tree, tree, tree, tree);
-static tree fold_builtin_trunc (location_t, tree, tree);
-static tree fold_builtin_floor (location_t, tree, tree);
-static tree fold_builtin_ceil (location_t, tree, tree);
-static tree fold_builtin_round (location_t, tree, tree);
 static tree fold_builtin_int_roundingfn (location_t, tree, tree);
 static tree fold_builtin_bitop (tree, tree);
 static tree fold_builtin_strchr (location_t, tree, tree, tree);
@@ -7320,117 +7314,6 @@ fold_builtin_nan (tree arg, tree type, int quiet)
   return build_real (type, real);
 }
 
-/* Return true if the floating point expression T has an integer value.
-   We also allow +Inf, -Inf and NaN to be considered integer values.  */
-
-static bool
-integer_valued_real_p (tree t)
-{
-  switch (TREE_CODE (t))
-    {
-    case FLOAT_EXPR:
-      return true;
-
-    case ABS_EXPR:
-    case SAVE_EXPR:
-      return integer_valued_real_p (TREE_OPERAND (t, 0));
-
-    case COMPOUND_EXPR:
-    case MODIFY_EXPR:
-    case BIND_EXPR:
-      return integer_valued_real_p (TREE_OPERAND (t, 1));
-
-    case PLUS_EXPR:
-    case MINUS_EXPR:
-    case MULT_EXPR:
-    case MIN_EXPR:
-    case MAX_EXPR:
-      return integer_valued_real_p (TREE_OPERAND (t, 0))
-            && integer_valued_real_p (TREE_OPERAND (t, 1));
-
-    case COND_EXPR:
-      return integer_valued_real_p (TREE_OPERAND (t, 1))
-            && integer_valued_real_p (TREE_OPERAND (t, 2));
-
-    case REAL_CST:
-      return real_isinteger (TREE_REAL_CST_PTR (t), TYPE_MODE (TREE_TYPE (t)));
-
-    CASE_CONVERT:
-      {
-       tree type = TREE_TYPE (TREE_OPERAND (t, 0));
-       if (TREE_CODE (type) == INTEGER_TYPE)
-         return true;
-       if (TREE_CODE (type) == REAL_TYPE)
-         return integer_valued_real_p (TREE_OPERAND (t, 0));
-       break;
-      }
-
-    case CALL_EXPR:
-      switch (builtin_mathfn_code (t))
-       {
-       CASE_FLT_FN (BUILT_IN_CEIL):
-       CASE_FLT_FN (BUILT_IN_FLOOR):
-       CASE_FLT_FN (BUILT_IN_NEARBYINT):
-       CASE_FLT_FN (BUILT_IN_RINT):
-       CASE_FLT_FN (BUILT_IN_ROUND):
-       CASE_FLT_FN (BUILT_IN_TRUNC):
-         return true;
-
-       CASE_FLT_FN (BUILT_IN_FMIN):
-       CASE_FLT_FN (BUILT_IN_FMAX):
-         return integer_valued_real_p (CALL_EXPR_ARG (t, 0))
-           && integer_valued_real_p (CALL_EXPR_ARG (t, 1));
-
-       default:
-         break;
-       }
-      break;
-
-    default:
-      break;
-    }
-  return false;
-}
-
-/* FNDECL is assumed to be a builtin where truncation can be propagated
-   across (for instance floor((double)f) == (double)floorf (f).
-   Do the transformation for a call with argument ARG.  */
-
-static tree
-fold_trunc_transparent_mathfn (location_t loc, tree fndecl, tree arg)
-{
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Integer rounding functions are idempotent.  */
-  if (fcode == builtin_mathfn_code (arg))
-    return arg;
-
-  /* If argument is already integer valued, and we don't need to worry
-     about setting errno, there's no need to perform rounding.  */
-  if (! flag_errno_math && integer_valued_real_p (arg))
-    return arg;
-
-  if (optimize)
-    {
-      tree arg0 = strip_float_extensions (arg);
-      tree ftype = TREE_TYPE (TREE_TYPE (fndecl));
-      tree newtype = TREE_TYPE (arg0);
-      tree decl;
-
-      if (TYPE_PRECISION (newtype) < TYPE_PRECISION (ftype)
-         && (decl = mathfn_built_in (newtype, fcode)))
-       return fold_convert_loc (loc, ftype,
-                                build_call_expr_loc (loc, decl, 1,
-                                                 fold_convert_loc (loc,
-                                                                   newtype,
-                                                                   arg0)));
-    }
-  return NULL_TREE;
-}
-
 /* FNDECL is assumed to be builtin which can narrow the FP type of
    the argument, for instance lround((double)f) -> lroundf (f).
    Do the transformation for a call with argument ARG.  */
@@ -7645,121 +7528,6 @@ fold_builtin_cexp (location_t loc, tree arg0, tree type)
   return NULL_TREE;
 }
 
-/* Fold function call to builtin trunc, truncf or truncl with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_trunc (location_t loc, tree fndecl, tree arg)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Optimize trunc of constant value.  */
-  if (TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      REAL_VALUE_TYPE r, x;
-      tree type = TREE_TYPE (TREE_TYPE (fndecl));
-
-      x = TREE_REAL_CST (arg);
-      real_trunc (&r, TYPE_MODE (type), &x);
-      return build_real (type, r);
-    }
-
-  return fold_trunc_transparent_mathfn (loc, fndecl, arg);
-}
-
-/* Fold function call to builtin floor, floorf or floorl with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_floor (location_t loc, tree fndecl, tree arg)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Optimize floor of constant value.  */
-  if (TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      REAL_VALUE_TYPE x;
-
-      x = TREE_REAL_CST (arg);
-      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
-       {
-         tree type = TREE_TYPE (TREE_TYPE (fndecl));
-         REAL_VALUE_TYPE r;
-
-         real_floor (&r, TYPE_MODE (type), &x);
-         return build_real (type, r);
-       }
-    }
-
-  /* Fold floor (x) where x is nonnegative to trunc (x).  */
-  if (tree_expr_nonnegative_p (arg))
-    {
-      tree truncfn = mathfn_built_in (TREE_TYPE (arg), BUILT_IN_TRUNC);
-      if (truncfn)
-       return build_call_expr_loc (loc, truncfn, 1, arg);
-    }
-
-  return fold_trunc_transparent_mathfn (loc, fndecl, arg);
-}
-
-/* Fold function call to builtin ceil, ceilf or ceill with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_ceil (location_t loc, tree fndecl, tree arg)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Optimize ceil of constant value.  */
-  if (TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      REAL_VALUE_TYPE x;
-
-      x = TREE_REAL_CST (arg);
-      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
-       {
-         tree type = TREE_TYPE (TREE_TYPE (fndecl));
-         REAL_VALUE_TYPE r;
-
-         real_ceil (&r, TYPE_MODE (type), &x);
-         return build_real (type, r);
-       }
-    }
-
-  return fold_trunc_transparent_mathfn (loc, fndecl, arg);
-}
-
-/* Fold function call to builtin round, roundf or roundl with argument ARG.
-   Return NULL_TREE if no simplification can be made.  */
-
-static tree
-fold_builtin_round (location_t loc, tree fndecl, tree arg)
-{
-  if (!validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Optimize round of constant value.  */
-  if (TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      REAL_VALUE_TYPE x;
-
-      x = TREE_REAL_CST (arg);
-      if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
-       {
-         tree type = TREE_TYPE (TREE_TYPE (fndecl));
-         REAL_VALUE_TYPE r;
-
-         real_round (&r, TYPE_MODE (type), &x);
-         return build_real (type, r);
-       }
-    }
-
-  return fold_trunc_transparent_mathfn (loc, fndecl, arg);
-}
-
 /* Fold function call to builtin lround, lroundf or lroundl (or the
    corresponding long long versions) and other rounding functions.  ARG
    is the argument to the call.  Return NULL_TREE if no simplification
@@ -9696,20 +9464,56 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
       return fold_builtin_nan (arg0, type, false);
 
     CASE_FLT_FN (BUILT_IN_FLOOR):
-      return fold_builtin_floor (loc, fndecl, arg0);
+      if (TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0))
+       {
+         REAL_VALUE_TYPE x = TREE_REAL_CST (arg0);
+         if (!REAL_VALUE_ISNAN (x) || !flag_errno_math)
+           {
+             tree type = TREE_TYPE (TREE_TYPE (fndecl));
+             REAL_VALUE_TYPE r;
+             real_floor (&r, TYPE_MODE (type), &x);
+             return build_real (type, r);
+           }
+       }
+      break;
 
     CASE_FLT_FN (BUILT_IN_CEIL):
-      return fold_builtin_ceil (loc, fndecl, arg0);
+      if (TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0))
+       {
+         REAL_VALUE_TYPE x = TREE_REAL_CST (arg0);
+         if (!REAL_VALUE_ISNAN (x) || !flag_errno_math)
+           {
+             tree type = TREE_TYPE (TREE_TYPE (fndecl));
+             REAL_VALUE_TYPE r;
+             real_ceil (&r, TYPE_MODE (type), &x);
+             return build_real (type, r);
+           }
+       }
+      break;
 
     CASE_FLT_FN (BUILT_IN_TRUNC):
-      return fold_builtin_trunc (loc, fndecl, arg0);
+      if (TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0))
+       {
+         REAL_VALUE_TYPE x = TREE_REAL_CST (arg0);
+         REAL_VALUE_TYPE r;
+         real_trunc (&r, TYPE_MODE (type), &x);
+         return build_real (type, r);
+       }
+      break;
 
     CASE_FLT_FN (BUILT_IN_ROUND):
-      return fold_builtin_round (loc, fndecl, arg0);
-
-    CASE_FLT_FN (BUILT_IN_NEARBYINT):
-    CASE_FLT_FN (BUILT_IN_RINT):
-      return fold_trunc_transparent_mathfn (loc, fndecl, arg0);
+      if (TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0))
+       {
+         REAL_VALUE_TYPE x = TREE_REAL_CST (arg0);
+         if (!REAL_VALUE_ISNAN (x) || !flag_errno_math)
+           {
+             tree type = TREE_TYPE (TREE_TYPE (fndecl));
+             REAL_VALUE_TYPE r;
+             real_round (&r, TYPE_MODE (type), &x);
+             return build_real (type, r);
+           }
+       }
+      break;
 
     CASE_FLT_FN (BUILT_IN_ICEIL):
     CASE_FLT_FN (BUILT_IN_LCEIL):
index bff29784e1809e1272a9ace18dfaa6089db71e71..498d3a5c748c8d171382375cdd43809807f31269 100644 (file)
@@ -225,37 +225,6 @@ convert_to_real (tree type, tree expr)
          break;
        }
     }
-  if (optimize
-      && (((fcode == BUILT_IN_FLOORL
-          || fcode == BUILT_IN_CEILL
-          || fcode == BUILT_IN_ROUNDL
-          || fcode == BUILT_IN_RINTL
-          || fcode == BUILT_IN_TRUNCL
-          || fcode == BUILT_IN_NEARBYINTL)
-         && (TYPE_MODE (type) == TYPE_MODE (double_type_node)
-             || TYPE_MODE (type) == TYPE_MODE (float_type_node)))
-         || ((fcode == BUILT_IN_FLOOR
-              || fcode == BUILT_IN_CEIL
-              || fcode == BUILT_IN_ROUND
-              || fcode == BUILT_IN_RINT
-              || fcode == BUILT_IN_TRUNC
-              || fcode == BUILT_IN_NEARBYINT)
-             && (TYPE_MODE (type) == TYPE_MODE (float_type_node)))))
-    {
-      tree fn = mathfn_built_in (type, fcode);
-
-      if (fn)
-       {
-         tree arg = strip_float_extensions (CALL_EXPR_ARG (expr, 0));
-
-         /* Make sure (type)arg0 is an extension, otherwise we could end up
-            changing (float)floor(double d) into floorf((float)d), which is
-            incorrect because (float)d uses round-to-nearest and can round
-            up to the next integer.  */
-         if (TYPE_PRECISION (type) >= TYPE_PRECISION (TREE_TYPE (arg)))
-           return build_call_expr (fn, 1, fold (convert_to_real (type, arg)));
-       }
-    }
 
   /* Propagate the cast into the operation.  */
   if (itype != type && FLOAT_TYPE_P (type))
index c4be017c50c55864ce1b3ba212215fb8807969fb..6eed7b611c664be5b4147cca5c5752c70594f139 100644 (file)
@@ -12896,10 +12896,6 @@ tree_binary_nonnegative_warnv_p (enum tree_code code, tree type, tree op0,
 bool
 tree_single_nonnegative_warnv_p (tree t, bool *strict_overflow_p, int depth)
 {
-  if (TREE_CODE (t) == SSA_NAME
-      && name_registered_for_update_p (t))
-    return false;
-
   if (TYPE_UNSIGNED (TREE_TYPE (t)))
     return true;
 
@@ -12923,11 +12919,11 @@ tree_single_nonnegative_warnv_p (tree t, bool *strict_overflow_p, int depth)
         If this code misses important cases that unbounded recursion
         would not, passes that need this information could be revised
         to provide it through dataflow propagation.  */
-      if (depth < PARAM_VALUE (PARAM_MAX_SSA_NAME_QUERY_DEPTH))
-       return gimple_stmt_nonnegative_warnv_p (SSA_NAME_DEF_STMT (t),
-                                               strict_overflow_p, depth);
+      return (!name_registered_for_update_p (t)
+             && depth < PARAM_VALUE (PARAM_MAX_SSA_NAME_QUERY_DEPTH)
+             && gimple_stmt_nonnegative_warnv_p (SSA_NAME_DEF_STMT (t),
+                                                 strict_overflow_p, depth));
 
-      /* Fallthru.  */
     default:
       return tree_simple_nonnegative_warnv_p (TREE_CODE (t), TREE_TYPE (t));
     }
@@ -13440,6 +13436,216 @@ tree_single_nonzero_warnv_p (tree t, bool *strict_overflow_p)
   return false;
 }
 
+#define integer_valued_real_p(X) \
+  _Pragma ("GCC error \"Use RECURSE for recursive calls\"") 0
+
+#define RECURSE(X) \
+  ((integer_valued_real_p) (X, depth + 1))
+
+/* Return true if the floating point result of (CODE OP0) has an
+   integer value.  We also allow +Inf, -Inf and NaN to be considered
+   integer values.
+
+   DEPTH is the current nesting depth of the query.  */
+
+bool
+integer_valued_real_unary_p (tree_code code, tree op0, int depth)
+{
+  switch (code)
+    {
+    case FLOAT_EXPR:
+      return true;
+
+    case ABS_EXPR:
+      return RECURSE (op0);
+
+    CASE_CONVERT:
+      {
+       tree type = TREE_TYPE (op0);
+       if (TREE_CODE (type) == INTEGER_TYPE)
+         return true;
+       if (TREE_CODE (type) == REAL_TYPE)
+         return RECURSE (op0);
+       break;
+      }
+
+    default:
+      break;
+    }
+  return false;
+}
+
+/* Return true if the floating point result of (CODE OP0 OP1) has an
+   integer value.  We also allow +Inf, -Inf and NaN to be considered
+   integer values.
+
+   DEPTH is the current nesting depth of the query.  */
+
+bool
+integer_valued_real_binary_p (tree_code code, tree op0, tree op1, int depth)
+{
+  switch (code)
+    {
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case MULT_EXPR:
+    case MIN_EXPR:
+    case MAX_EXPR:
+      return RECURSE (op0) && RECURSE (op1);
+
+    default:
+      break;
+    }
+  return false;
+}
+
+/* Return true if the floating point result of calling FNDECL with arguments
+   ARG0 and ARG1 has an integer value.  We also allow +Inf, -Inf and NaN to be
+   considered integer values.  If FNDECL takes fewer than 2 arguments,
+   the remaining ARGn are null.
+
+   DEPTH is the current nesting depth of the query.  */
+
+bool
+integer_valued_real_call_p (tree fndecl, tree arg0, tree arg1, int depth)
+{
+  if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+    switch (DECL_FUNCTION_CODE (fndecl))
+      {
+      CASE_FLT_FN (BUILT_IN_CEIL):
+      CASE_FLT_FN (BUILT_IN_FLOOR):
+      CASE_FLT_FN (BUILT_IN_NEARBYINT):
+      CASE_FLT_FN (BUILT_IN_RINT):
+      CASE_FLT_FN (BUILT_IN_ROUND):
+      CASE_FLT_FN (BUILT_IN_TRUNC):
+       return true;
+
+      CASE_FLT_FN (BUILT_IN_FMIN):
+      CASE_FLT_FN (BUILT_IN_FMAX):
+       return RECURSE (arg0) && RECURSE (arg1);
+
+      default:
+       break;
+      }
+  return false;
+}
+
+/* Return true if the floating point expression T (a GIMPLE_SINGLE_RHS)
+   has an integer value.  We also allow +Inf, -Inf and NaN to be
+   considered integer values.
+
+   DEPTH is the current nesting depth of the query.  */
+
+bool
+integer_valued_real_single_p (tree t, int depth)
+{
+  switch (TREE_CODE (t))
+    {
+    case REAL_CST:
+      return real_isinteger (TREE_REAL_CST_PTR (t), TYPE_MODE (TREE_TYPE (t)));
+
+    case COND_EXPR:
+      return RECURSE (TREE_OPERAND (t, 1)) && RECURSE (TREE_OPERAND (t, 2));
+
+    case SSA_NAME:
+      /* Limit the depth of recursion to avoid quadratic behavior.
+        This is expected to catch almost all occurrences in practice.
+        If this code misses important cases that unbounded recursion
+        would not, passes that need this information could be revised
+        to provide it through dataflow propagation.  */
+      return (!name_registered_for_update_p (t)
+             && depth < PARAM_VALUE (PARAM_MAX_SSA_NAME_QUERY_DEPTH)
+             && gimple_stmt_integer_valued_real_p (SSA_NAME_DEF_STMT (t),
+                                                   depth));
+
+    default:
+      break;
+    }
+  return false;
+}
+
+/* Return true if the floating point expression T (a GIMPLE_INVALID_RHS)
+   has an integer value.  We also allow +Inf, -Inf and NaN to be
+   considered integer values.
+
+   DEPTH is the current nesting depth of the query.  */
+
+static bool
+integer_valued_real_invalid_p (tree t, int depth)
+{
+  switch (TREE_CODE (t))
+    {
+    case COMPOUND_EXPR:
+    case MODIFY_EXPR:
+    case BIND_EXPR:
+      return RECURSE (TREE_OPERAND (t, 1));
+
+    case SAVE_EXPR:
+      return RECURSE (TREE_OPERAND (t, 0));
+
+    default:
+      break;
+    }
+  return false;
+}
+
+#undef RECURSE
+#undef integer_valued_real_p
+
+/* Return true if the floating point expression T has an integer value.
+   We also allow +Inf, -Inf and NaN to be considered integer values.
+
+   DEPTH is the current nesting depth of the query.  */
+
+bool
+integer_valued_real_p (tree t, int depth)
+{
+  if (t == error_mark_node)
+    return false;
+
+  tree_code code = TREE_CODE (t);
+  switch (TREE_CODE_CLASS (code))
+    {
+    case tcc_binary:
+    case tcc_comparison:
+      return integer_valued_real_binary_p (code, TREE_OPERAND (t, 0),
+                                          TREE_OPERAND (t, 1), depth);
+
+    case tcc_unary:
+      return integer_valued_real_unary_p (code, TREE_OPERAND (t, 0), depth);
+
+    case tcc_constant:
+    case tcc_declaration:
+    case tcc_reference:
+      return integer_valued_real_single_p (t, depth);
+
+    default:
+      break;
+    }
+
+  switch (code)
+    {
+    case COND_EXPR:
+    case SSA_NAME:
+      return integer_valued_real_single_p (t, depth);
+
+    case CALL_EXPR:
+      {
+       tree arg0 = (call_expr_nargs (t) > 0
+                    ? CALL_EXPR_ARG (t, 0)
+                    : NULL_TREE);
+       tree arg1 = (call_expr_nargs (t) > 1
+                    ? CALL_EXPR_ARG (t, 1)
+                    : NULL_TREE);
+       return integer_valued_real_call_p (get_callee_fndecl (t),
+                                          arg0, arg1, depth);
+      }
+
+    default:
+      return integer_valued_real_invalid_p (t, depth);
+    }
+}
+
 /* Given the components of a binary expression CODE, TYPE, OP0 and OP1,
    attempt to fold the expression to a constant without modifying TYPE,
    OP0 or OP1.
index 1bb68e48e2ab7710375f7ac148c65f2abd8ddd46..8e49c98b9c5cb6162e342b6dfaf2957000536c41 100644 (file)
@@ -139,6 +139,12 @@ extern bool tree_single_nonnegative_warnv_p (tree, bool *, int);
 extern bool tree_call_nonnegative_warnv_p (tree, tree, tree, tree, bool *,
                                           int);
 
+extern bool integer_valued_real_unary_p (tree_code, tree, int);
+extern bool integer_valued_real_binary_p (tree_code, tree, tree, int);
+extern bool integer_valued_real_call_p (tree, tree, tree, int);
+extern bool integer_valued_real_single_p (tree, int);
+extern bool integer_valued_real_p (tree, int = 0);
+
 extern bool fold_real_zero_addition_p (const_tree, const_tree, int);
 extern tree combine_comparisons (location_t, enum tree_code, enum tree_code,
                                 enum tree_code, tree, tree, tree);
index 85ff0186964765f005db8e484c49997b1ad3dee2..1869c097d7fb73e39dbbe4e7a1b99938e906d691 100644 (file)
@@ -6266,3 +6266,91 @@ gimple_stmt_nonnegative_warnv_p (gimple *stmt, bool *strict_overflow_p,
       return false;
     }
 }
+
+/* Return true if the floating-point value computed by assignment STMT
+   is known to have an integer value.  We also allow +Inf, -Inf and NaN
+   to be considered integer values.
+
+   DEPTH is the current nesting depth of the query.  */
+
+static bool
+gimple_assign_integer_valued_real_p (gimple *stmt, int depth)
+{
+  enum tree_code code = gimple_assign_rhs_code (stmt);
+  switch (get_gimple_rhs_class (code))
+    {
+    case GIMPLE_UNARY_RHS:
+      return integer_valued_real_unary_p (gimple_assign_rhs_code (stmt),
+                                         gimple_assign_rhs1 (stmt), depth);
+    case GIMPLE_BINARY_RHS:
+      return integer_valued_real_binary_p (gimple_assign_rhs_code (stmt),
+                                          gimple_assign_rhs1 (stmt),
+                                          gimple_assign_rhs2 (stmt), depth);
+    case GIMPLE_TERNARY_RHS:
+      return false;
+    case GIMPLE_SINGLE_RHS:
+      return integer_valued_real_single_p (gimple_assign_rhs1 (stmt), depth);
+    case GIMPLE_INVALID_RHS:
+      break;
+    }
+  gcc_unreachable ();
+}
+
+/* Return true if the floating-point value computed by call STMT is known
+   to have an integer value.  We also allow +Inf, -Inf and NaN to be
+   considered integer values.
+
+   DEPTH is the current nesting depth of the query.  */
+
+static bool
+gimple_call_integer_valued_real_p (gimple *stmt, int depth)
+{
+  tree arg0 = (gimple_call_num_args (stmt) > 0
+              ? gimple_call_arg (stmt, 0)
+              : NULL_TREE);
+  tree arg1 = (gimple_call_num_args (stmt) > 1
+              ? gimple_call_arg (stmt, 1)
+              : NULL_TREE);
+  return integer_valued_real_call_p (gimple_call_fndecl (stmt),
+                                    arg0, arg1, depth);
+}
+
+/* Return true if the floating-point result of phi STMT is known to have
+   an integer value.  We also allow +Inf, -Inf and NaN to be considered
+   integer values.
+
+   DEPTH is the current nesting depth of the query.  */
+
+static bool
+gimple_phi_integer_valued_real_p (gimple *stmt, int depth)
+{
+  for (unsigned i = 0; i < gimple_phi_num_args (stmt); ++i)
+    {
+      tree arg = gimple_phi_arg_def (stmt, i);
+      if (!integer_valued_real_single_p (arg, depth + 1))
+       return false;
+    }
+  return true;
+}
+
+/* Return true if the floating-point value computed by STMT is known
+   to have an integer value.  We also allow +Inf, -Inf and NaN to be
+   considered integer values.
+
+   DEPTH is the current nesting depth of the query.  */
+
+bool
+gimple_stmt_integer_valued_real_p (gimple *stmt, int depth)
+{
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_ASSIGN:
+      return gimple_assign_integer_valued_real_p (stmt, depth);
+    case GIMPLE_CALL:
+      return gimple_call_integer_valued_real_p (stmt, depth);
+    case GIMPLE_PHI:
+      return gimple_phi_integer_valued_real_p (stmt, depth);
+    default:
+      return false;
+    }
+}
index 61edd6960415c191a7e3282786ad671884b28469..9c24f77d48ae4d2ba10c68c8a383eda5897aefaf 100644 (file)
@@ -120,6 +120,7 @@ gimple_convert_to_ptrofftype (gimple_seq *seq, tree op)
 }
 
 extern bool gimple_stmt_nonnegative_warnv_p (gimple *, bool *, int = 0);
+extern bool gimple_stmt_integer_valued_real_p (gimple *, int = 0);
 
 /* In gimple-match.c.  */
 extern tree gimple_simplify (enum tree_code, tree, tree,
index b399786bac2e85b15a84b80b9131a85f8cc5193b..060363656f3620c5eb48ed7a353ce6e19ac55c42 100644 (file)
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
    zerop
    CONSTANT_CLASS_P
    tree_expr_nonnegative_p
+   integer_valued_real_p
    integer_pow2p
    HONOR_NANS)
 
@@ -70,6 +71,14 @@ along with GCC; see the file COPYING3.  If not see
                               BUILT_IN_COPYSIGN
                               BUILT_IN_COPYSIGNL)
 (define_operator_list CABS BUILT_IN_CABSF BUILT_IN_CABS BUILT_IN_CABSL)
+(define_operator_list TRUNC BUILT_IN_TRUNCF BUILT_IN_TRUNC BUILT_IN_TRUNCL)
+(define_operator_list FLOOR BUILT_IN_FLOORF BUILT_IN_FLOOR BUILT_IN_FLOORL)
+(define_operator_list CEIL BUILT_IN_CEILF BUILT_IN_CEIL BUILT_IN_CEILL)
+(define_operator_list ROUND BUILT_IN_ROUNDF BUILT_IN_ROUND BUILT_IN_ROUNDL)
+(define_operator_list NEARBYINT BUILT_IN_NEARBYINTF
+                               BUILT_IN_NEARBYINT
+                               BUILT_IN_NEARBYINTL)
+(define_operator_list RINT BUILT_IN_RINTF BUILT_IN_RINT BUILT_IN_RINTL)
 
 /* Simplifications of operations with one constant operand and
    simplifications to constants or single values.  */
@@ -2439,6 +2448,23 @@ along with GCC; see the file COPYING3.  If not see
  (CABS (complex:c @0 real_zerop@1))
  (abs @0))
 
+/* trunc(trunc(x)) -> trunc(x), etc.  */
+(for fns (TRUNC FLOOR CEIL ROUND NEARBYINT RINT)
+ (simplify
+  (fns (fns @0))
+  (fns @0)))
+/* f(x) -> x if x is integer valued and f does nothing for such values.  */
+(for fns (TRUNC FLOOR CEIL ROUND NEARBYINT)
+ (simplify
+  (fns integer_valued_real_p@0)
+  @0))
+/* Same for rint.  We have to check flag_errno_math because
+   integer_valued_real_p accepts +Inf, -Inf and NaNs as integers.  */
+(if (!flag_errno_math)
+ (simplify
+  (RINT integer_valued_real_p@0)
+  @0))
+
 /* Canonicalization of sequences of math builtins.  These rules represent
    IL simplifications but are not necessarily optimizations.
 
@@ -2537,6 +2563,57 @@ along with GCC; see the file COPYING3.  If not see
   (CABS (complex @0 @0))
   (mult (abs @0) { build_real_truncate (type, dconst_sqrt2 ()); })))
 
+(if (canonicalize_math_p ())
+ /* floor(x) -> trunc(x) if x is nonnegative.  */
+ (for floors (FLOOR)
+      truncs (TRUNC)
+  (simplify
+   (floors tree_expr_nonnegative_p@0)
+   (truncs @0))))
+
+(match double_value_p
+ @0
+ (if (TYPE_MAIN_VARIANT (TREE_TYPE (@0)) == double_type_node)))
+(for froms (BUILT_IN_TRUNCL
+           BUILT_IN_FLOORL
+           BUILT_IN_CEILL
+           BUILT_IN_ROUNDL
+           BUILT_IN_NEARBYINTL
+           BUILT_IN_RINTL)
+     tos (BUILT_IN_TRUNC
+         BUILT_IN_FLOOR
+         BUILT_IN_CEIL
+         BUILT_IN_ROUND
+         BUILT_IN_NEARBYINT
+         BUILT_IN_RINT)
+ /* truncl(extend(x)) -> extend(trunc(x)), etc., if x is a double.  */
+ (if (optimize && canonicalize_math_p ())
+  (simplify
+   (froms (convert double_value_p@0))
+   (convert (tos @0)))))
+
+(match float_value_p
+ @0
+ (if (TYPE_MAIN_VARIANT (TREE_TYPE (@0)) == float_type_node)))
+(for froms (BUILT_IN_TRUNCL BUILT_IN_TRUNC
+           BUILT_IN_FLOORL BUILT_IN_FLOOR
+           BUILT_IN_CEILL BUILT_IN_CEIL
+           BUILT_IN_ROUNDL BUILT_IN_ROUND
+           BUILT_IN_NEARBYINTL BUILT_IN_NEARBYINT
+           BUILT_IN_RINTL BUILT_IN_RINT)
+     tos (BUILT_IN_TRUNCF BUILT_IN_TRUNCF
+         BUILT_IN_FLOORF BUILT_IN_FLOORF
+         BUILT_IN_CEILF BUILT_IN_CEILF
+         BUILT_IN_ROUNDF BUILT_IN_ROUNDF
+         BUILT_IN_NEARBYINTF BUILT_IN_NEARBYINTF
+         BUILT_IN_RINTF BUILT_IN_RINTF)
+ /* truncl(extend(x)) and trunc(extend(x)) -> extend(truncf(x)), etc.,
+    if x is a float.  */
+ (if (optimize && canonicalize_math_p ())
+  (simplify
+   (froms (convert float_value_p@0))
+   (convert (tos @0)))))
+
 /* cproj(x) -> x if we're ignoring infinities.  */
 (simplify
  (CPROJ @0)
index 14a3709855b3c8571849f98860f58175861847af..98ad4faabb03abefaaf63622fb96bfc4ef6ec922 100644 (file)
@@ -1,3 +1,10 @@
+2015-10-23  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * gcc.c-torture/execute/20030125-1.c (floor, floorf, sin, sinf):
+       Make weak rather than noinline.
+       * gcc.dg/builtins-57.c: Compile with -O.
+       * gcc.dg/torture/builtin-integral-1.c: Skip for -O0.
+
 2015-10-23  Tom de Vries  <tom@codesourcery.com>
 
        * gcc.dg/tree-ssa/restrict-4.c: Add -fno-ipa-icf to dg-options.
index 60ede34f5da70d42fdd342de8a935f02a2c9a652..960552c3c3a5b5b4cb8f6a946262446430a6d151 100644 (file)
@@ -1,5 +1,6 @@
 /* Verify whether math functions are simplified.  */
 /* { dg-require-effective-target c99_runtime } */
+/* { dg-require-weak } */
 double sin(double);
 double floor(double);
 float 
@@ -29,25 +30,25 @@ main()
 #endif
        return 0;
 }
-__attribute__ ((noinline))
+__attribute__ ((weak))
 double
 floor(double a)
 {
        abort ();
 }
-__attribute__ ((noinline))
+__attribute__ ((weak))
 float
 floorf(float a)
 {
        return a;
 }
-__attribute__ ((noinline))
+__attribute__ ((weak))
 double
 sin(double a)
 {
        return a;
 }
-__attribute__ ((noinline))
+__attribute__ ((weak))
 float
 sinf(float a)
 {
index 361826cb00a5b82853659f190bf3624b01e322e6..18d40e8f7ffca93008a08c4d3da89a2cb2b21a74 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do link } */
-/* { dg-options "-std=c99 -ffinite-math-only" } */
+/* { dg-options "-std=c99 -ffinite-math-only -O" } */
 
 #include "builtins-config.h"
 
index 522646dd5cbec815cda558e7263cdba9644c83da..f3c3338342e84e797ebebc341ab7300f09f693cd 100644 (file)
@@ -10,6 +10,7 @@
    that various math functions are marked const/pure and can be
    folded.  */
 /* { dg-options "-ffinite-math-only -fno-math-errno" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */
 
 extern int link_failure (int);