real.c (real_trunc, [...]): New functions to implement trunc, floor and ceil respectively.

* real.c (real_trunc, real_floor, real_ceil): New functions
	to implement trunc, floor and ceil respectively.
	* real.h (real_trunc, real_floor, real_ceil): Prototype here.
	* builtins.c (integer_valued_real_p): New function to test if
	a floating point expression has an integer valued result.
	(fold_trunc_transparent_mathfn): Optimize foo(foo(x)) as
	foo(x) where foo is an integer rounding function.  Similarly,
	optimize foo(bar(x)) as bar(x), and foo((double)(int)x) as
	(double)(int)x when both foo and bar are integer rounding
	functions and we don't need to honor errno.
	(fold_builtin_trunc, fold_builtin_floor, fold_builtin_ceil):
	New functions to fold trunc, floor and ceil.
	(fold_builtin): Use fold_builtin_trunc to fold BUILT_IN_TRUNC*,
	fold_builtin_floor to fold BUILT_IN_FLOOR* and fold_builtin_ceil
	to fold BUILT_IN_CEIL*.
	* fold-const.c (tree_expr_nonnegative_p): Handle FLOAT_EXPR and
	the remaining integer rounding functions.

	* gcc.dg/builtins-25.c: New testcase.
	* gcc.dg/builtins-26.c: New testcase.

From-SVN: r68903
This commit is contained in:
Roger Sayle 2003-07-03 21:38:55 +00:00 committed by Roger Sayle
parent 7516d73631
commit 0a9530a9d7
8 changed files with 589 additions and 3 deletions

View File

@ -1,3 +1,23 @@
2003-07-03 Roger Sayle <roger@eyesopen.com>
* real.c (real_trunc, real_floor, real_ceil): New functions
to implement trunc, floor and ceil respectively.
* real.h (real_trunc, real_floor, real_ceil): Prototype here.
* builtins.c (integer_valued_real_p): New function to test if
a floating point expression has an integer valued result.
(fold_trunc_transparent_mathfn): Optimize foo(foo(x)) as
foo(x) where foo is an integer rounding function. Similarly,
optimize foo(bar(x)) as bar(x), and foo((double)(int)x) as
(double)(int)x when both foo and bar are integer rounding
functions and we don't need to honor errno.
(fold_builtin_trunc, fold_builtin_floor, fold_builtin_ceil):
New functions to fold trunc, floor and ceil.
(fold_builtin): Use fold_builtin_trunc to fold BUILT_IN_TRUNC*,
fold_builtin_floor to fold BUILT_IN_FLOOR* and fold_builtin_ceil
to fold BUILT_IN_CEIL*.
* fold-const.c (tree_expr_nonnegative_p): Handle FLOAT_EXPR and
the remaining integer rounding functions.
2003-07-03 Eric Botcazou <ebotcazou@libertysurf.fr>
* config/sparc/sparc.c (function_arg_partial_nregs): Use

View File

@ -149,12 +149,16 @@ static tree fold_builtin_classify_type (tree);
static tree fold_builtin_inf (tree, int);
static tree fold_builtin_nan (tree, tree, int);
static int validate_arglist (tree, ...);
static bool integer_valued_real_p (tree);
static tree fold_trunc_transparent_mathfn (tree);
static bool readonly_data_expr (tree);
static rtx expand_builtin_fabs (tree, rtx, rtx);
static rtx expand_builtin_cabs (tree, rtx);
static void init_builtin_dconsts (void);
static tree fold_builtin_cabs (tree, tree, tree);
static tree fold_builtin_trunc (tree);
static tree fold_builtin_floor (tree);
static tree fold_builtin_ceil (tree);
/* Initialize mathematical constants for constant folding builtins.
These constants need to be given to at least 160 bits precision. */
@ -5347,19 +5351,118 @@ fold_builtin_nan (tree arglist, tree type, int quiet)
return build_real (type, real);
}
/* EXP is assumed to me builtin call where truncation can be propagated
/* 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:
case NON_LVALUE_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:
if (! TREE_CONSTANT_OVERFLOW (t))
{
REAL_VALUE_TYPE c, cint;
c = TREE_REAL_CST (t);
real_trunc (&cint, TYPE_MODE (TREE_TYPE (t)), &c);
return real_identical (&c, &cint);
}
case NOP_EXPR:
{
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 BUILT_IN_CEIL:
case BUILT_IN_CEILF:
case BUILT_IN_CEILL:
case BUILT_IN_FLOOR:
case BUILT_IN_FLOORF:
case BUILT_IN_FLOORL:
case BUILT_IN_NEARBYINT:
case BUILT_IN_NEARBYINTF:
case BUILT_IN_NEARBYINTL:
case BUILT_IN_ROUND:
case BUILT_IN_ROUNDF:
case BUILT_IN_ROUNDL:
case BUILT_IN_TRUNC:
case BUILT_IN_TRUNCF:
case BUILT_IN_TRUNCL:
return true;
default:
break;
}
break;
default:
break;
}
return false;
}
/* EXP is assumed to be builtin call where truncation can be propagated
across (for instance floor((double)f) == (double)floorf (f).
Do the transformation. */
static tree
fold_trunc_transparent_mathfn (tree exp)
{
tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
tree arglist = TREE_OPERAND (exp, 1);
enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
tree arg;
if (optimize && validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
return 0;
arg = TREE_VALUE (arglist);
/* 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 (TREE_VALUE (arglist));
tree arg0 = strip_float_extensions (arg);
tree ftype = TREE_TYPE (exp);
tree newtype = TREE_TYPE (arg0);
tree decl;
@ -5461,6 +5564,97 @@ fold_builtin_cabs (tree fndecl, tree arglist, tree type)
return NULL_TREE;
}
/* Fold function call to builtin trunc, truncf or truncl. Return
NULL_TREE if no simplification can be made. */
static tree
fold_builtin_trunc (tree exp)
{
tree arglist = TREE_OPERAND (exp, 1);
tree arg;
if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
return 0;
/* Optimize trunc of constant value. */
arg = TREE_VALUE (arglist);
if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
{
REAL_VALUE_TYPE r, x;
tree type = TREE_TYPE (exp);
x = TREE_REAL_CST (arg);
real_trunc (&r, TYPE_MODE (type), &x);
return build_real (type, r);
}
return fold_trunc_transparent_mathfn (exp);
}
/* Fold function call to builtin floor, floorf or floorl. Return
NULL_TREE if no simplification can be made. */
static tree
fold_builtin_floor (tree exp)
{
tree arglist = TREE_OPERAND (exp, 1);
tree arg;
if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
return 0;
/* Optimize floor of constant value. */
arg = TREE_VALUE (arglist);
if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
{
REAL_VALUE_TYPE x;
x = TREE_REAL_CST (arg);
if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
{
tree type = TREE_TYPE (exp);
REAL_VALUE_TYPE r;
real_floor (&r, TYPE_MODE (type), &x);
return build_real (type, r);
}
}
return fold_trunc_transparent_mathfn (exp);
}
/* Fold function call to builtin ceil, ceilf or ceill. Return
NULL_TREE if no simplification can be made. */
static tree
fold_builtin_ceil (tree exp)
{
tree arglist = TREE_OPERAND (exp, 1);
tree arg;
if (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
return 0;
/* Optimize ceil of constant value. */
arg = TREE_VALUE (arglist);
if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
{
REAL_VALUE_TYPE x;
x = TREE_REAL_CST (arg);
if (! REAL_VALUE_ISNAN (x) || ! flag_errno_math)
{
tree type = TREE_TYPE (exp);
REAL_VALUE_TYPE r;
real_ceil (&r, TYPE_MODE (type), &x);
return build_real (type, r);
}
}
return fold_trunc_transparent_mathfn (exp);
}
/* Used by constant folding to eliminate some builtin calls early. EXP is
the CALL_EXPR of a call to a builtin function. */
@ -5918,12 +6112,18 @@ fold_builtin (tree exp)
case BUILT_IN_FLOOR:
case BUILT_IN_FLOORF:
case BUILT_IN_FLOORL:
return fold_builtin_floor (exp);
case BUILT_IN_CEIL:
case BUILT_IN_CEILF:
case BUILT_IN_CEILL:
return fold_builtin_ceil (exp);
case BUILT_IN_TRUNC:
case BUILT_IN_TRUNCF:
case BUILT_IN_TRUNCL:
return fold_builtin_trunc (exp);
case BUILT_IN_ROUND:
case BUILT_IN_ROUNDF:
case BUILT_IN_ROUNDL:

View File

@ -8105,6 +8105,8 @@ tree_expr_nonnegative_p (tree t)
return tree_expr_nonnegative_p (TREE_OPERAND (t, 0));
case NON_LVALUE_EXPR:
return tree_expr_nonnegative_p (TREE_OPERAND (t, 0));
case FLOAT_EXPR:
return tree_expr_nonnegative_p (TREE_OPERAND (t, 0));
case RTL_EXPR:
return rtl_expr_nonnegative_p (RTL_EXPR_RTL (t));
@ -8141,6 +8143,15 @@ tree_expr_nonnegative_p (tree t)
case BUILT_IN_FLOOR:
case BUILT_IN_FLOORF:
case BUILT_IN_FLOORL:
case BUILT_IN_NEARBYINT:
case BUILT_IN_NEARBYINTF:
case BUILT_IN_NEARBYINTL:
case BUILT_IN_ROUND:
case BUILT_IN_ROUNDF:
case BUILT_IN_ROUNDL:
case BUILT_IN_TRUNC:
case BUILT_IN_TRUNCF:
case BUILT_IN_TRUNCL:
return tree_expr_nonnegative_p (TREE_VALUE (arglist));
case BUILT_IN_POW:

View File

@ -4748,3 +4748,49 @@ real_powi (r, mode, x, n)
return inexact;
}
/* Round X to the nearest integer not larger in absolute value, i.e.
towards zero, placing the result in R in mode MODE. */
void
real_trunc (r, mode, x)
REAL_VALUE_TYPE *r;
enum machine_mode mode;
const REAL_VALUE_TYPE *x;
{
do_fix_trunc (r, x);
if (mode != VOIDmode)
real_convert (r, mode, r);
}
/* Round X to the largest integer not greater in value, i.e. round
down, placing the result in R in mode MODE. */
void
real_floor (r, mode, x)
REAL_VALUE_TYPE *r;
enum machine_mode mode;
const REAL_VALUE_TYPE *x;
{
do_fix_trunc (r, x);
if (! real_identical (r, x) && r->sign)
do_add (r, r, &dconstm1, 0);
if (mode != VOIDmode)
real_convert (r, mode, r);
}
/* Round X to the smallest integer not less then argument, i.e. round
up, placing the result in R in mode MODE. */
void
real_ceil (r, mode, x)
REAL_VALUE_TYPE *r;
enum machine_mode mode;
const REAL_VALUE_TYPE *x;
{
do_fix_trunc (r, x);
if (! real_identical (r, x) && ! r->sign)
do_add (r, r, &dconst1, 0);
if (mode != VOIDmode)
real_convert (r, mode, r);
}

View File

@ -375,4 +375,15 @@ extern bool real_powi PARAMS ((REAL_VALUE_TYPE *,
const REAL_VALUE_TYPE *,
HOST_WIDE_INT));
/* Standard round to integer value functions. */
extern void real_trunc PARAMS ((REAL_VALUE_TYPE *,
enum machine_mode,
const REAL_VALUE_TYPE *));
extern void real_floor PARAMS ((REAL_VALUE_TYPE *,
enum machine_mode,
const REAL_VALUE_TYPE *));
extern void real_ceil PARAMS ((REAL_VALUE_TYPE *,
enum machine_mode,
const REAL_VALUE_TYPE *));
#endif /* ! GCC_REAL_H */

View File

@ -1,3 +1,8 @@
2003-07-03 Roger Sayle <roger@eyesopen.com>
* gcc.dg/builtins-25.c: New testcase.
* gcc.dg/builtins-26.c: New testcase.
2003-07-03 Janis Johnson <janis187@us.ibm.com>
* gcc.dg/compat/vector-defs.h: New file.

View File

@ -0,0 +1,188 @@
/* Copyright (C) 2003 Free Software Foundation.
Check that constant folding of built-in math functions doesn't
break anything and produces the expected results.
Written by Roger Sayle, 28th June 2003. */
/* { dg-do link } */
/* { dg-options "-O2" } */
extern void link_error(void);
extern double trunc(double);
extern double floor(double);
extern double ceil(double);
extern float truncf(float);
extern float floorf(float);
extern float ceilf(float);
extern long double truncl(long double);
extern long double floorl(long double);
extern long double ceill(long double);
void test()
{
if (trunc (0.0) != 0.0)
link_error ();
if (floor (0.0) != 0.0)
link_error ();
if (ceil (0.0) != 0.0)
link_error ();
if (trunc (6.0) != 6.0)
link_error ();
if (floor (6.0) != 6.0)
link_error ();
if (ceil (6.0) != 6.0)
link_error ();
if (trunc (-8.0) != -8.0)
link_error ();
if (floor (-8.0) != -8.0)
link_error ();
if (ceil (-8.0) != -8.0)
link_error ();
if (trunc (3.2) != 3.0)
link_error ();
if (floor (3.2) != 3.0)
link_error ();
if (ceil (3.2) != 4.0)
link_error ();
if (trunc (-2.8) != -2.0)
link_error ();
if (floor (-2.8) != -3.0)
link_error ();
if (ceil (-2.8) != -2.0)
link_error ();
if (trunc (0.01) != 0.0)
link_error ();
if (floor (0.01) != 0.0)
link_error ();
if (ceil (0.01) != 1.0)
link_error ();
if (trunc (-0.7) != 0.0)
link_error ();
if (floor (-0.7) != -1.0)
link_error ();
if (ceil (-0.7) != 0.0)
link_error ();
}
void testf()
{
if (truncf (0.0f) != 0.0f)
link_error ();
if (floorf (0.0f) != 0.0f)
link_error ();
if (ceilf (0.0f) != 0.0f)
link_error ();
if (truncf (6.0f) != 6.0f)
link_error ();
if (floorf (6.0f) != 6.0f)
link_error ();
if (ceilf (6.0f) != 6.0f)
link_error ();
if (truncf (-8.0f) != -8.0f)
link_error ();
if (floorf (-8.0f) != -8.0f)
link_error ();
if (ceilf (-8.0f) != -8.0f)
link_error ();
if (truncf (3.2f) != 3.0f)
link_error ();
if (floorf (3.2f) != 3.0f)
link_error ();
if (ceilf (3.2f) != 4.0f)
link_error ();
if (truncf (-2.8f) != -2.0f)
link_error ();
if (floorf (-2.8f) != -3.0f)
link_error ();
if (ceilf (-2.8f) != -2.0f)
link_error ();
if (truncf (0.01f) != 0.0f)
link_error ();
if (floorf (0.01f) != 0.0f)
link_error ();
if (ceilf (0.01f) != 1.0f)
link_error ();
if (truncf (-0.7f) != 0.0f)
link_error ();
if (floorf (-0.7f) != -1.0f)
link_error ();
if (ceilf (-0.7f) != 0.0f)
link_error ();
}
void testl()
{
if (truncl (0.0l) != 0.0l)
link_error ();
if (floorl (0.0l) != 0.0l)
link_error ();
if (ceill (0.0l) != 0.0l)
link_error ();
if (truncl (6.0l) != 6.0l)
link_error ();
if (floorl (6.0l) != 6.0l)
link_error ();
if (ceill (6.0l) != 6.0l)
link_error ();
if (truncl (-8.0l) != -8.0l)
link_error ();
if (floorl (-8.0l) != -8.0l)
link_error ();
if (ceill (-8.0l) != -8.0l)
link_error ();
if (truncl (3.2l) != 3.0l)
link_error ();
if (floorl (3.2l) != 3.0l)
link_error ();
if (ceill (3.2l) != 4.0l)
link_error ();
if (truncl (-2.8l) != -2.0l)
link_error ();
if (floorl (-2.8l) != -3.0l)
link_error ();
if (ceill (-2.8l) != -2.0l)
link_error ();
if (truncl (0.01l) != 0.0l)
link_error ();
if (floorl (0.01l) != 0.0l)
link_error ();
if (ceill (0.01l) != 1.0l)
link_error ();
if (truncl (-0.7l) != 0.0l)
link_error ();
if (floorl (-0.7l) != -1.0l)
link_error ();
if (ceill (-0.7l) != 0.0l)
link_error ();
}
int main()
{
test ();
testf ();
testl ();
return 0;
}

View File

@ -0,0 +1,105 @@
/* Copyright (C) 2003 Free Software Foundation.
Check that constant folding of built-in math functions doesn't
break anything and produces the expected results.
Written by Roger Sayle, 28th June 2003. */
/* { dg-do link } */
/* { dg-options "-O2 -ffast-math" } */
extern void link_error(void);
extern double trunc(double);
extern double floor(double);
extern double ceil(double);
extern float truncf(float);
extern float floorf(float);
extern float ceilf(float);
extern long double truncl(long double);
extern long double floorl(long double);
extern long double ceill(long double);
void test(double x)
{
if (trunc (trunc (x)) != trunc (x))
link_error ();
if (trunc (floor (x)) != floor (x))
link_error ();
if (trunc (ceil (x)) != ceil (x))
link_error ();
if (floor (trunc (x)) != trunc (x))
link_error ();
if (floor (floor (x)) != floor (x))
link_error ();
if (floor (ceil (x)) != ceil (x))
link_error ();
if (ceil (trunc (x)) != trunc (x))
link_error ();
if (ceil (floor (x)) != floor (x))
link_error ();
if (ceil (ceil (x)) != ceil (x))
link_error ();
}
void testf(float x)
{
if (truncf (truncf (x)) != truncf (x))
link_error ();
if (truncf (floorf (x)) != floorf (x))
link_error ();
if (truncf (ceilf (x)) != ceilf (x))
link_error ();
if (floorf (truncf (x)) != truncf (x))
link_error ();
if (floorf (floorf (x)) != floorf (x))
link_error ();
if (floorf (ceilf (x)) != ceilf (x))
link_error ();
if (ceilf (truncf (x)) != truncf (x))
link_error ();
if (ceilf (floorf (x)) != floorf (x))
link_error ();
if (ceilf (ceilf (x)) != ceilf (x))
link_error ();
}
void testl(long double x)
{
if (truncl (truncl (x)) != truncl (x))
link_error ();
if (truncl (floorl (x)) != floorl (x))
link_error ();
if (truncl (ceill (x)) != ceill (x))
link_error ();
if (floorl (truncl (x)) != truncl (x))
link_error ();
if (floorl (floorl (x)) != floorl (x))
link_error ();
if (floorl (ceill (x)) != ceill (x))
link_error ();
if (ceill (truncl (x)) != truncl (x))
link_error ();
if (ceill (floorl (x)) != floorl (x))
link_error ();
if (ceill (ceill (x)) != ceill (x))
link_error ();
}
int main()
{
test (3.2);
testf (3.2f);
testl (3.2l);
return 0;
}