From: ghazi Date: Mon, 19 Feb 2007 00:14:14 +0000 (+0000) Subject: * builtins.c (fold_builtin_load_exponent): New. X-Git-Tag: upstream/4.9.2~50303 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7587301b1e0d0a689d5831856f7e84e205bf7996;p=platform%2Fupstream%2Flinaro-gcc.git * builtins.c (fold_builtin_load_exponent): New. (fold_builtin_2): Use it. testsuite: * gcc.dg/torture/builtin-ldexp-1.c: New. * gcc.dg/torture/builtin-math-2.c: Add ldexp/scalbn/scalbln cases. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@122110 138bc75d-0d04-0410-961f-82ee72b054a4 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f2aedd4..ff24358 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2007-02-18 Kaveh R. Ghazi + + * builtins.c (fold_builtin_load_exponent): New. + (fold_builtin_2): Use it. + 2007-02-18 Steven Bosscher PR rtl-optimization/30773 diff --git a/gcc/builtins.c b/gcc/builtins.c index 79d62d9..67d9bf6 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -9000,6 +9000,67 @@ fold_builtin_carg (tree arg, tree type) return NULL_TREE; } +/* Fold a call to builtin ldexp or scalbn/scalbln. If LDEXP is true + then we can assume the base is two. If it's false, then we have to + check the mode of the TYPE parameter in certain cases. */ + +static tree +fold_builtin_load_exponent (tree arg0, tree arg1, tree type, bool ldexp) +{ + if (validate_arg (arg0, REAL_TYPE) && validate_arg (arg1, INTEGER_TYPE)) + { + STRIP_NOPS (arg0); + STRIP_NOPS (arg1); + + /* If arg0 is 0, Inf or NaN, or if arg1 is 0, then return arg0. */ + if (real_zerop (arg0) || integer_zerop (arg1) + || (TREE_CODE (arg0) == REAL_CST + && (real_isnan (&TREE_REAL_CST (arg0)) + || real_isinf (&TREE_REAL_CST (arg0))))) + return omit_one_operand (type, arg0, arg1); + + /* If both arguments are constant, then try to evaluate it. */ + if ((ldexp || REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2) + && TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0) + && host_integerp (arg1, 0)) + { + /* Bound the maximum adjustment to twice the range of the + mode's valid exponents. Use abs to ensure the range is + positive as a sanity check. */ + const long max_exp_adj = 2 * + labs (REAL_MODE_FORMAT (TYPE_MODE (type))->emax + - REAL_MODE_FORMAT (TYPE_MODE (type))->emin); + + /* Get the user-requested adjustment. */ + const HOST_WIDE_INT req_exp_adj = tree_low_cst (arg1, 0); + + /* The requested adjustment must be inside this range. This + is a preliminary cap to avoid things like overflow, we + may still fail to compute the result for other reasons. */ + if (-max_exp_adj < req_exp_adj && req_exp_adj < max_exp_adj) + { + REAL_VALUE_TYPE initial_result; + + real_ldexp (&initial_result, &TREE_REAL_CST (arg0), req_exp_adj); + + /* Ensure we didn't overflow. */ + if (! real_isinf (&initial_result)) + { + const REAL_VALUE_TYPE trunc_result + = real_value_truncate (TYPE_MODE (type), initial_result); + + /* Only proceed if the target mode can hold the + resulting value. */ + if (REAL_VALUES_EQUAL (initial_result, trunc_result)) + return build_real (type, trunc_result); + } + } + } + } + + return NULL_TREE; +} + /* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite. ARG is the argument for the call. */ @@ -9460,6 +9521,12 @@ fold_builtin_2 (tree fndecl, tree arg0, tree arg1, bool ignore) CASE_FLT_FN (BUILT_IN_HYPOT): return fold_builtin_hypot (fndecl, arg0, arg1, type); + CASE_FLT_FN (BUILT_IN_LDEXP): + return fold_builtin_load_exponent (arg0, arg1, type, /*ldexp=*/true); + CASE_FLT_FN (BUILT_IN_SCALBN): + CASE_FLT_FN (BUILT_IN_SCALBLN): + return fold_builtin_load_exponent (arg0, arg1, type, /*ldexp=*/false); + case BUILT_IN_BZERO: return fold_builtin_bzero (arg0, arg1, ignore); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 3151a7d..06f560e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2007-02-18 Kaveh R. Ghazi + + * gcc.dg/torture/builtin-ldexp-1.c: New. + * gcc.dg/torture/builtin-math-2.c: Add ldexp/scalbn/scalbln cases. + 2007-02-18 Roger Sayle * gfortran.dg/array_constructor_15.f90: New test case. diff --git a/gcc/testsuite/gcc.dg/torture/builtin-ldexp-1.c b/gcc/testsuite/gcc.dg/torture/builtin-ldexp-1.c new file mode 100644 index 0000000..aa9906f --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/builtin-ldexp-1.c @@ -0,0 +1,203 @@ +/* Copyright (C) 2007 Free Software Foundation. + + Verify that built-in folding of ldexp et al. is correctly performed + by the compiler. + + Origin: Kaveh R. Ghazi, February 17, 2007. */ + +/* { dg-do link } */ + +extern void link_error(int); + +/* Return TRUE if the sign of X != sign of Y. This is important when + comparing signed zeros. */ +#define CKSGN_F(X,Y) \ + (__builtin_copysignf(1.0F,(X)) != __builtin_copysignf(1.0F,(Y))) +#define CKSGN(X,Y) \ + (__builtin_copysign(1.0,(X)) != __builtin_copysign(1.0,(Y))) +#define CKSGN_L(X,Y) \ + (__builtin_copysignl(1.0L,(X)) != __builtin_copysignl(1.0L,(Y))) + +/* Test that FUNC(ARG1,ARG2) == RES. Check the sign for -0.0. */ +#define TESTIT(FUNC,ARG1,ARG2,RES) do { \ + if (__builtin_##FUNC##f(ARG1##f,ARG2) != RES##f \ + || CKSGN(__builtin_##FUNC##f(ARG1##f,ARG2),RES##f)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC(ARG1,ARG2) != RES \ + || CKSGN(__builtin_##FUNC(ARG1,ARG2),RES)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC##l(ARG1##l,ARG2) != RES##l \ + || CKSGN(__builtin_##FUNC##l(ARG1##l,ARG2),RES##l)) \ + link_error(__LINE__); \ + } while (0) + +/* Test that (long)FUNC(ARG1,ARG2) == (long)RES. The cast is + necessary when RES is not a constant. */ +#define TESTIT2(FUNC,ARG1,ARG2,RES) do { \ + if ((long)__builtin_##FUNC##f(ARG1##f,ARG2) != (long)RES##f) \ + link_error(__LINE__); \ + if ((long)__builtin_##FUNC(ARG1,ARG2) != (long)RES) \ + link_error(__LINE__); \ + if ((long)__builtin_##FUNC##l(ARG1##l,ARG2) != (long)RES##l) \ + link_error(__LINE__); \ + } while (0) + +/* Test that FUNCRES(FUNC(NEG FUNCARG(ARGARG),ARG2)) is false. Check + the sign as well. */ +#define TESTIT3(FUNC,NEG,FUNCARG,ARGARG,ARG2,FUNCRES) do { \ + if (!__builtin_##FUNCRES##f(__builtin_##FUNC##f(NEG __builtin_##FUNCARG##f(ARGARG),ARG2)) \ + || CKSGN(__builtin_##FUNC##f(NEG __builtin_##FUNCARG##f(ARGARG),ARG2), NEG __builtin_##FUNCARG##f(ARGARG))) \ + link_error(__LINE__); \ + if (!__builtin_##FUNCRES(__builtin_##FUNC(NEG __builtin_##FUNCARG(ARGARG),ARG2)) \ + || CKSGN(__builtin_##FUNC(NEG __builtin_##FUNCARG(ARGARG),ARG2), NEG __builtin_##FUNCARG(ARGARG))) \ + link_error(__LINE__); \ + if (!__builtin_##FUNCRES##l(__builtin_##FUNC##l(NEG __builtin_##FUNCARG##l(ARGARG),ARG2)) \ + || CKSGN(__builtin_##FUNC##l(NEG __builtin_##FUNCARG##l(ARGARG),ARG2), NEG __builtin_##FUNCARG##l(ARGARG))) \ + link_error(__LINE__); \ + } while (0) + +/* Using foo==MIN/MAX float values, test that FUNC(foo,EXP) == foo*exp2(EXP), + and also that FUNC(foo,-EXP) == foo*exp2(-EXP). */ +#define TESTIT4(FUNC,EXP) do { \ + if (__builtin_##FUNC##f(__FLT_MIN__,EXP) != __FLT_MIN__*__builtin_exp2f(EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC##f(-__FLT_MIN__,EXP) != -__FLT_MIN__*__builtin_exp2f(EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC(__DBL_MIN__,EXP) != __DBL_MIN__*__builtin_exp2(EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC(-__DBL_MIN__,EXP) != -__DBL_MIN__*__builtin_exp2(EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC##l(__LDBL_MIN__,EXP) != __LDBL_MIN__*__builtin_exp2l(EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC##l(-__LDBL_MIN__,EXP) != -__LDBL_MIN__*__builtin_exp2l(EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC##f(__FLT_MAX__,-EXP) != __FLT_MAX__*__builtin_exp2f(-EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC##f(-__FLT_MAX__,-EXP) != -__FLT_MAX__*__builtin_exp2f(-EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC(__DBL_MAX__,-EXP) != __DBL_MAX__*__builtin_exp2(-EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC(-__DBL_MAX__,-EXP) != -__DBL_MAX__*__builtin_exp2(-EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC##l(__LDBL_MAX__,-EXP) != __LDBL_MAX__*__builtin_exp2l(-EXP)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC##l(-__LDBL_MAX__,-EXP) != -__LDBL_MAX__*__builtin_exp2l(-EXP)) \ + link_error(__LINE__); \ + } while (0) + +void __attribute__ ((__noinline__)) +foo(float xf, double x, long double xl, int i, long l) +{ + /* f(0.0, i) -> 0.0 and f(-0.0, i) -> -0.0. */ + TESTIT (ldexp, 0.0, i, 0.0); + TESTIT (ldexp, -0.0, i, -0.0); + TESTIT (scalbn, 0.0, i, 0.0); + TESTIT (scalbn, -0.0, i, -0.0); + TESTIT (scalbln, 0.0, l, 0.0); + TESTIT (scalbln, -0.0, l, -0.0); + + /* f(x,0) -> x. */ + TESTIT2 (ldexp, x, 0, x); + TESTIT2 (scalbn, x, 0, x); + TESTIT2 (scalbln, x, 0, x); + + /* f(Inf,i) -> Inf and f(NaN,i) -> NaN. */ + TESTIT3 (ldexp, , inf, , i, isinf); + TESTIT3 (ldexp, -, inf, , i, isinf); + TESTIT3 (ldexp, , nan, "", i, isnan); + TESTIT3 (ldexp, -, nan, "", i, isnan); + + TESTIT3 (scalbn, , inf, , i, isinf); + TESTIT3 (scalbn, -, inf, , i, isinf); + TESTIT3 (scalbn, , nan, "", i, isnan); + TESTIT3 (scalbn, -, nan, "", i, isnan); + + TESTIT3 (scalbln, , inf, , i, isinf); + TESTIT3 (scalbln, -, inf, , i, isinf); + TESTIT3 (scalbln, , nan, "", i, isnan); + TESTIT3 (scalbln, -, nan, "", i, isnan); + + /* Evaluate when both arguments are constant. */ + TESTIT (ldexp, 5.0, 3, 40.0); + TESTIT (ldexp, -5.0, 3, -40.0); + TESTIT (ldexp, 5.0, -3, 0.625); + TESTIT (ldexp, -5.0, -3, -0.625); + + TESTIT (ldexp, 1000.0, 5, 32000.0); + TESTIT (ldexp, -1000.0, 5, -32000.0); + TESTIT (ldexp, 1000.0, -5, 31.25); + TESTIT (ldexp, -1000.0, -5, -31.25); + + /* f(x,N) -> x*exp2(N), using MIN/MAX constants for x and constant N. */ + TESTIT4 (ldexp, 1); + TESTIT4 (ldexp, 2); + TESTIT4 (ldexp, 3); + TESTIT4 (ldexp, 5); + TESTIT4 (ldexp, 9); + TESTIT4 (ldexp, 10); + TESTIT4 (ldexp, 12); + TESTIT4 (ldexp, 18); + TESTIT4 (ldexp, 25); + TESTIT4 (ldexp, 50); + TESTIT4 (ldexp, 75); + TESTIT4 (ldexp, 100); + TESTIT4 (ldexp, 123); + + /* These are folded when float radix is two. */ +#if __FLT_RADIX__ == 2 + TESTIT (scalbn, 5.0, 3, 40.0); + TESTIT (scalbn, -5.0, 3, -40.0); + TESTIT (scalbn, 5.0, -3, 0.625); + TESTIT (scalbn, -5.0, -3, -0.625); + + TESTIT (scalbn, 1000.0, 5, 32000.0); + TESTIT (scalbn, -1000.0, 5, -32000.0); + TESTIT (scalbn, 1000.0, -5, 31.25); + TESTIT (scalbn, -1000.0, -5, -31.25); + + TESTIT4 (scalbn, 1); + TESTIT4 (scalbn, 2); + TESTIT4 (scalbn, 3); + TESTIT4 (scalbn, 5); + TESTIT4 (scalbn, 9); + TESTIT4 (scalbn, 10); + TESTIT4 (scalbn, 12); + TESTIT4 (scalbn, 18); + TESTIT4 (scalbn, 25); + TESTIT4 (scalbn, 50); + TESTIT4 (scalbn, 75); + TESTIT4 (scalbn, 100); + TESTIT4 (scalbn, 123); + + TESTIT (scalbln, 5.0, 3, 40.0); + TESTIT (scalbln, -5.0, 3, -40.0); + TESTIT (scalbln, 5.0, -3, 0.625); + TESTIT (scalbln, -5.0, -3, -0.625); + + TESTIT (scalbln, 1000.0, 5, 32000.0); + TESTIT (scalbln, -1000.0, 5, -32000.0); + TESTIT (scalbln, 1000.0, -5, 31.25); + TESTIT (scalbln, -1000.0, -5, -31.25); + + TESTIT4 (scalbln, 1); + TESTIT4 (scalbln, 2); + TESTIT4 (scalbln, 3); + TESTIT4 (scalbln, 5); + TESTIT4 (scalbln, 9); + TESTIT4 (scalbln, 10); + TESTIT4 (scalbln, 12); + TESTIT4 (scalbln, 18); + TESTIT4 (scalbln, 25); + TESTIT4 (scalbln, 50); + TESTIT4 (scalbln, 75); + TESTIT4 (scalbln, 100); + TESTIT4 (scalbln, 123); +#endif +} + +int main() +{ + foo (0, 0, 0, 0, 0); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/builtin-math-2.c b/gcc/testsuite/gcc.dg/torture/builtin-math-2.c index 2374992..7c6fad0 100644 --- a/gcc/testsuite/gcc.dg/torture/builtin-math-2.c +++ b/gcc/testsuite/gcc.dg/torture/builtin-math-2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2006 Free Software Foundation. +/* Copyright (C) 2006, 2007 Free Software Foundation. Test things that should block GCC from optimizing compile-time constants passed to a builtin transcendental function. @@ -24,6 +24,18 @@ extern void fool (long double); fool (__builtin_##FUNC##l (ARG1##L, ARG2##L)); \ } while (0) +#define TESTIT2_I2ALL(FUNC, ARGF, MAXF, ARGD, MAXD, ARGLD, MAXLD) do { \ + foof (__builtin_##FUNC##f (ARGF, MAXF)); \ + foo (__builtin_##FUNC (ARGD, MAXD)); \ + fool (__builtin_##FUNC##l (ARGLD, MAXLD)); \ +} while (0) + +#define TESTIT2_I2(FUNC, ARG1, ARG2) do { \ + foof (__builtin_##FUNC##f (ARG1##F, ARG2)); \ + foo (__builtin_##FUNC (ARG1, ARG2)); \ + fool (__builtin_##FUNC##l (ARG1##L, ARG2)); \ +} while (0) + void bar() { /* An argument of NaN is not evaluated at compile-time. */ @@ -150,6 +162,52 @@ void bar() TESTIT (sqrt, -0.5); TESTIT (sqrt, -0.0); TESTIT (sqrt, 0.0); + + /* Check for overflow/underflow. */ + + /* These adjustments are too big. */ +#define FLT_EXP_ADJ (2*(__FLT_MAX_EXP__-__FLT_MIN_EXP__)+1) +#define DBL_EXP_ADJ (2*(__DBL_MAX_EXP__-__DBL_MIN_EXP__)+1) +#define LDBL_EXP_ADJ (2*(__LDBL_MAX_EXP__-__LDBL_MIN_EXP__)+1) + + TESTIT2_I2 (ldexp, 1.0, __INT_MAX__); + TESTIT2_I2 (ldexp, 1.0, -__INT_MAX__-1); + TESTIT2_I2 (ldexp, -1.0, __INT_MAX__); + TESTIT2_I2 (ldexp, -1.0, -__INT_MAX__-1); + TESTIT2_I2ALL (ldexp, __FLT_MIN__, FLT_EXP_ADJ, __DBL_MIN__, + DBL_EXP_ADJ, __LDBL_MIN__, LDBL_EXP_ADJ); + TESTIT2_I2ALL (ldexp, __FLT_MAX__, -FLT_EXP_ADJ, __DBL_MAX__, + -DBL_EXP_ADJ, __LDBL_MAX__, -LDBL_EXP_ADJ); + TESTIT2_I2ALL (ldexp, __FLT_MIN__, __FLT_MIN_EXP__, __DBL_MIN__, + __DBL_MIN_EXP__, __LDBL_MIN__, __LDBL_MIN_EXP__); + TESTIT2_I2ALL (ldexp, __FLT_MAX__, __FLT_MAX_EXP__, __DBL_MAX__, + __DBL_MAX_EXP__, __LDBL_MAX__, __LDBL_MAX_EXP__); + + TESTIT2_I2 (scalbn, 1.0, __INT_MAX__); + TESTIT2_I2 (scalbn, 1.0, -__INT_MAX__-1); + TESTIT2_I2 (scalbn, -1.0, __INT_MAX__); + TESTIT2_I2 (scalbn, -1.0, -__INT_MAX__-1); + TESTIT2_I2ALL (scalbn, __FLT_MIN__, FLT_EXP_ADJ, __DBL_MIN__, + DBL_EXP_ADJ, __LDBL_MIN__, LDBL_EXP_ADJ); + TESTIT2_I2ALL (scalbn, __FLT_MAX__, -FLT_EXP_ADJ, __DBL_MAX__, + -DBL_EXP_ADJ, __LDBL_MAX__, -LDBL_EXP_ADJ); + TESTIT2_I2ALL (scalbn, __FLT_MIN__, __FLT_MIN_EXP__, __DBL_MIN__, + __DBL_MIN_EXP__, __LDBL_MIN__, __LDBL_MIN_EXP__); + TESTIT2_I2ALL (scalbn, __FLT_MAX__, __FLT_MAX_EXP__, __DBL_MAX__, + __DBL_MAX_EXP__, __LDBL_MAX__, __LDBL_MAX_EXP__); + + TESTIT2_I2 (scalbln, 1.0, __LONG_MAX__); + TESTIT2_I2 (scalbln, 1.0, -__LONG_MAX__-1); + TESTIT2_I2 (scalbln, -1.0, __LONG_MAX__); + TESTIT2_I2 (scalbln, -1.0, -__LONG_MAX__-1); + TESTIT2_I2ALL (scalbln, __FLT_MIN__, FLT_EXP_ADJ, __DBL_MIN__, + DBL_EXP_ADJ, __LDBL_MIN__, LDBL_EXP_ADJ); + TESTIT2_I2ALL (scalbln, __FLT_MAX__, -FLT_EXP_ADJ, __DBL_MAX__, + -DBL_EXP_ADJ, __LDBL_MAX__, -LDBL_EXP_ADJ); + TESTIT2_I2ALL (scalbln, __FLT_MIN__, __FLT_MIN_EXP__, __DBL_MIN__, + __DBL_MIN_EXP__, __LDBL_MIN__, __LDBL_MIN_EXP__); + TESTIT2_I2ALL (scalbln, __FLT_MAX__, __FLT_MAX_EXP__, __DBL_MAX__, + __DBL_MAX_EXP__, __LDBL_MAX__, __LDBL_MAX_EXP__); } /* { dg-final { scan-tree-dump-times "exp2 " 9 "original" } } */ @@ -191,4 +249,13 @@ void bar() /* { dg-final { scan-tree-dump-times "sqrt " 1 "original" } } */ /* { dg-final { scan-tree-dump-times "sqrtf" 1 "original" } } */ /* { dg-final { scan-tree-dump-times "sqrtl" 1 "original" } } */ +/* { dg-final { scan-tree-dump-times "ldexp " 8 "original" } } */ +/* { dg-final { scan-tree-dump-times "ldexpf" 8 "original" } } */ +/* { dg-final { scan-tree-dump-times "ldexpl" 8 "original" } } */ +/* { dg-final { scan-tree-dump-times "scalbn " 8 "original" } } */ +/* { dg-final { scan-tree-dump-times "scalbnf" 8 "original" } } */ +/* { dg-final { scan-tree-dump-times "scalbnl" 8 "original" } } */ +/* { dg-final { scan-tree-dump-times "scalbln " 8 "original" } } */ +/* { dg-final { scan-tree-dump-times "scalblnf" 8 "original" } } */ +/* { dg-final { scan-tree-dump-times "scalblnl" 8 "original" } } */ /* { dg-final { cleanup-tree-dump "original" } } */