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:
Richard Sandiford 2015-10-23 10:01:47 +00:00 committed by Richard Sandiford
parent 735a559c68
commit 67dbe5829e
12 changed files with 474 additions and 284 deletions

View 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

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

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

View 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.

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

View 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;
}
}

View 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,

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

View 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.

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

View 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"

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