aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog20
-rw-r--r--gcc/builtins.c206
-rw-r--r--gcc/fold-const.c11
-rw-r--r--gcc/real.c46
-rw-r--r--gcc/real.h11
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/gcc.dg/builtins-25.c188
-rw-r--r--gcc/testsuite/gcc.dg/builtins-26.c105
8 files changed, 589 insertions, 3 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7e5ccb4..2d9c829 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/builtins.c b/gcc/builtins.c
index e8ab4d574..9471215 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 (! validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ return 0;
- if (optimize && validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ 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 c4826c7..0524e1d 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 b491d88..6bc4d52 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 71e3cc4..fcd7ae7 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 7e4c0e6..b2db902 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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.
diff --git a/gcc/testsuite/gcc.dg/builtins-25.c b/gcc/testsuite/gcc.dg/builtins-25.c
new file mode 100644
index 0000000..4950566
--- /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 0000000..c4d03cd
--- /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;
+}
+