From 0a9530a9d7cf389493916027f9980fcbf028b602 Mon Sep 17 00:00:00 2001 From: Roger Sayle Date: Thu, 3 Jul 2003 21:38:55 +0000 Subject: [PATCH] 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 --- gcc/ChangeLog | 20 +++ gcc/builtins.c | 206 ++++++++++++++++++++++++++++- gcc/fold-const.c | 11 ++ gcc/real.c | 46 +++++++ gcc/real.h | 11 ++ gcc/testsuite/ChangeLog | 5 + gcc/testsuite/gcc.dg/builtins-25.c | 188 ++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/builtins-26.c | 105 +++++++++++++++ 8 files changed, 589 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/builtins-25.c create mode 100644 gcc/testsuite/gcc.dg/builtins-26.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7e5ccb44da8..2d9c8294307 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2003-07-03 Roger Sayle + + * 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 * config/sparc/sparc.c (function_arg_partial_nregs): Use diff --git a/gcc/builtins.c b/gcc/builtins.c index e8ab4d5740d..94712150ad1 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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: diff --git a/gcc/fold-const.c b/gcc/fold-const.c index c4826c784af..0524e1dc16e 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -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: diff --git a/gcc/real.c b/gcc/real.c index b491d885a9f..6bc4d52fabc 100644 --- a/gcc/real.c +++ b/gcc/real.c @@ -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); +} + diff --git a/gcc/real.h b/gcc/real.h index 71e3cc4b6b9..fcd7ae7f155 100644 --- a/gcc/real.h +++ b/gcc/real.h @@ -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 */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7e4c0e659d4..b2db902de72 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2003-07-03 Roger Sayle + + * gcc.dg/builtins-25.c: New testcase. + * gcc.dg/builtins-26.c: New testcase. + 2003-07-03 Janis Johnson * gcc.dg/compat/vector-defs.h: New file. diff --git a/gcc/testsuite/gcc.dg/builtins-25.c b/gcc/testsuite/gcc.dg/builtins-25.c new file mode 100644 index 00000000000..495056615ca --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtins-25.c @@ -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; +} + diff --git a/gcc/testsuite/gcc.dg/builtins-26.c b/gcc/testsuite/gcc.dg/builtins-26.c new file mode 100644 index 00000000000..c4d03cd7da6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtins-26.c @@ -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; +} +