Move fold_trunc_transparent_mathfn to match.pd
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
This commit is contained in:
parent
735a559c68
commit
67dbe5829e
|
@ -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
|
||||
|
|
284
gcc/builtins.c
284
gcc/builtins.c
|
@ -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):
|
||||
|
|
|
@ -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))
|
||||
|
|
222
gcc/fold-const.c
222
gcc/fold-const.c
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
77
gcc/match.pd
77
gcc/match.pd
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue