From e61e5ddcceb34c5a5dc09febaedb44059d5bc96d Mon Sep 17 00:00:00 2001 From: "Kaveh R. Ghazi" Date: Fri, 10 Nov 2006 04:26:31 +0000 Subject: builtins.c (do_mpfr_arg3): New. * builtins.c (do_mpfr_arg3): New. (fold_builtin_1): Handle builtins fma, fmin and fmax. testsuite: * gcc.dg/torture/builtin-math-2.c: Test builtin fma. * gcc.dg/torture/builtin-math-3.c (CKSGN_F, CKSGN, CKSGN_L): New macros. Use them in exact tests. (TESTIT3): New macro. Add tests for fmin, fmax and fma. From-SVN: r118648 --- gcc/ChangeLog | 5 ++ gcc/builtins.c | 70 +++++++++++++++++++ gcc/testsuite/ChangeLog | 8 +++ gcc/testsuite/gcc.dg/torture/builtin-math-2.c | 15 ++++ gcc/testsuite/gcc.dg/torture/builtin-math-3.c | 99 +++++++++++++++++++++++++-- 5 files changed, 191 insertions(+), 6 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c9c7df8..9c2b589 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2006-11-09 Kaveh R. Ghazi + + * builtins.c (do_mpfr_arg3): New. + (fold_builtin_1): Handle builtins fma, fmin and fmax. + 2006-11-09 Eric Christopher PR bootstrap/26892 diff --git a/gcc/builtins.c b/gcc/builtins.c index 17355f7..9d27243 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -207,6 +207,8 @@ static tree do_mpfr_arg1 (tree, tree, int (*)(mpfr_ptr, mpfr_srcptr, mp_rnd_t), const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *, bool); static tree do_mpfr_arg2 (tree, tree, tree, int (*)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t)); +static tree do_mpfr_arg3 (tree, tree, tree, tree, + int (*)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t)); static tree do_mpfr_sincos (tree, tree, tree); /* Return true if NODE should be considered for inline expansion regardless @@ -9256,6 +9258,28 @@ fold_builtin_1 (tree fndecl, tree arglist, bool ignore) type, mpfr_atan2); break; + CASE_FLT_FN (BUILT_IN_FMA): + if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + return do_mpfr_arg3 (TREE_VALUE (arglist), + TREE_VALUE (TREE_CHAIN (arglist)), + TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist))), + type, mpfr_fma); + break; + + CASE_FLT_FN (BUILT_IN_FMIN): + if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + return do_mpfr_arg2 (TREE_VALUE (arglist), + TREE_VALUE (TREE_CHAIN (arglist)), + type, mpfr_min); + break; + + CASE_FLT_FN (BUILT_IN_FMAX): + if (validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) + return do_mpfr_arg2 (TREE_VALUE (arglist), + TREE_VALUE (TREE_CHAIN (arglist)), + type, mpfr_max); + break; + CASE_FLT_FN (BUILT_IN_HYPOT): return fold_builtin_hypot (fndecl, arglist, type); @@ -11593,6 +11617,52 @@ do_mpfr_arg2 (tree arg1, tree arg2, tree type, return result; } +/* If argument ARG is a REAL_CST, call the three-argument mpfr function + FUNC on it and return the resulting value as a tree with type TYPE. + The mpfr precision is set to the precision of TYPE. We assume that + function FUNC returns zero if the result could be calculated + exactly within the requested precision. */ + +static tree +do_mpfr_arg3 (tree arg1, tree arg2, tree arg3, tree type, + int (*func)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t)) +{ + tree result = NULL_TREE; + + STRIP_NOPS (arg1); + STRIP_NOPS (arg2); + STRIP_NOPS (arg3); + + if (TREE_CODE (arg1) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg1) + && TREE_CODE (arg2) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg2) + && TREE_CODE (arg3) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg3)) + { + const REAL_VALUE_TYPE *const ra1 = &TREE_REAL_CST (arg1); + const REAL_VALUE_TYPE *const ra2 = &TREE_REAL_CST (arg2); + const REAL_VALUE_TYPE *const ra3 = &TREE_REAL_CST (arg3); + + if (!real_isnan (ra1) && !real_isinf (ra1) + && !real_isnan (ra2) && !real_isinf (ra2) + && !real_isnan (ra3) && !real_isinf (ra3)) + { + const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p; + int inexact; + mpfr_t m1, m2, m3; + + mpfr_inits2 (prec, m1, m2, m3, NULL); + mpfr_from_real (m1, ra1); + mpfr_from_real (m2, ra2); + mpfr_from_real (m3, ra3); + mpfr_clear_flags(); + inexact = func (m1, m1, m2, m3, GMP_RNDN); + result = do_mpfr_ckconv (m1, type, inexact); + mpfr_clears (m1, m2, m3, NULL); + } + } + + return result; +} + /* If argument ARG is a REAL_CST, call mpfr_sin_cos() on it and set the pointers *(ARG_SINP) and *(ARG_COSP) to the resulting values. The type is taken from the type of ARG and is used for setting the diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 298fd30..659aa50 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2006-11-09 Kaveh R. Ghazi + + * gcc.dg/torture/builtin-math-2.c: Test builtin fma. + * gcc.dg/torture/builtin-math-3.c (CKSGN_F, CKSGN, CKSGN_L): + New macros. Use them in exact tests. + (TESTIT3): New macro. + Add tests for fmin, fmax and fma. + 2006-11-09 Paul Thomas PR fortran/29431 diff --git a/gcc/testsuite/gcc.dg/torture/builtin-math-2.c b/gcc/testsuite/gcc.dg/torture/builtin-math-2.c index 5b5d55a..f0dbf2e 100644 --- a/gcc/testsuite/gcc.dg/torture/builtin-math-2.c +++ b/gcc/testsuite/gcc.dg/torture/builtin-math-2.c @@ -133,6 +133,18 @@ void bar() foo (__builtin_pow (__DBL_MAX__, -3.5)); fool (__builtin_powl (__LDBL_MAX__, -3.5L)); TESTIT2 (pow, 2.0, -0x1p50); + + foof (__builtin_fmaf (__FLT_MAX__, __FLT_MAX__, 0.0F)); + foof (__builtin_fmaf (__FLT_MAX__, 1.0F, __FLT_MAX__)); + foof (__builtin_fmaf (__FLT_MIN__, __FLT_MIN__, 0.0F)); + + foo (__builtin_fma (__DBL_MAX__, __DBL_MAX__, 0.0)); + foo (__builtin_fma (__DBL_MAX__, 1.0, __DBL_MAX__)); + foo (__builtin_fma (__DBL_MIN__, __DBL_MIN__, 0.0)); + + fool (__builtin_fmal (__LDBL_MAX__, __LDBL_MAX__, 0.0L)); + fool (__builtin_fmal (__LDBL_MAX__, 1.0L, __LDBL_MAX__)); + fool (__builtin_fmal (__LDBL_MIN__, __LDBL_MIN__, 0.0L)); } /* { dg-final { scan-tree-dump-times "exp2 " 9 "original" } } */ @@ -168,4 +180,7 @@ void bar() /* { dg-final { scan-tree-dump-times "pow " 13 "original" } } */ /* { dg-final { scan-tree-dump-times "powf" 13 "original" } } */ /* { dg-final { scan-tree-dump-times "powl" 13 "original" } } */ +/* { dg-final { scan-tree-dump-times "fma " 3 "original" } } */ +/* { dg-final { scan-tree-dump-times "fmaf" 3 "original" } } */ +/* { dg-final { scan-tree-dump-times "fmal" 3 "original" } } */ /* { dg-final { cleanup-tree-dump "original" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/builtin-math-3.c b/gcc/testsuite/gcc.dg/torture/builtin-math-3.c index c8bef53..67304f8 100644 --- a/gcc/testsuite/gcc.dg/torture/builtin-math-3.c +++ b/gcc/testsuite/gcc.dg/torture/builtin-math-3.c @@ -13,13 +13,25 @@ /* All references to link_error should go away at compile-time. */ 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(ARG) == (RES). */ #define TESTIT(FUNC,ARG,RES) do { \ - if (__builtin_##FUNC##f(ARG##F) != RES##F) \ + if (__builtin_##FUNC##f(ARG##F) != RES##F \ + || CKSGN_F(__builtin_##FUNC##f(ARG##F),RES##F)) \ link_error(__LINE__); \ - if (__builtin_##FUNC(ARG) != RES) \ + if (__builtin_##FUNC(ARG) != RES \ + || CKSGN(__builtin_##FUNC(ARG),RES)) \ link_error(__LINE__); \ - if (__builtin_##FUNC##l(ARG##L) != RES##L) \ + if (__builtin_##FUNC##l(ARG##L) != RES##L \ + || CKSGN_L(__builtin_##FUNC##l(ARG##L),RES##L)) \ link_error(__LINE__); \ } while (0) @@ -35,11 +47,14 @@ extern void link_error(int); /* Test that FUNC(ARG1, ARG2) == (RES). */ #define TESTIT2(FUNC,ARG1,ARG2,RES) do { \ - if (__builtin_##FUNC##f(ARG1##F, ARG2##F) != RES##F) \ + if (__builtin_##FUNC##f(ARG1##F, ARG2##F) != RES##F \ + || CKSGN_F(__builtin_##FUNC##f(ARG1##F,ARG2##F),RES##F)) \ link_error(__LINE__); \ - if (__builtin_##FUNC(ARG1, ARG2) != RES) \ + if (__builtin_##FUNC(ARG1, ARG2) != RES \ + || CKSGN(__builtin_##FUNC(ARG1,ARG2),RES)) \ link_error(__LINE__); \ - if (__builtin_##FUNC##l(ARG1##L, ARG2##L) != RES##L) \ + if (__builtin_##FUNC##l(ARG1##L, ARG2##L) != RES##L \ + || CKSGN_L(__builtin_##FUNC##l(ARG1##L,ARG2##L),RES##L)) \ link_error(__LINE__); \ } while (0) @@ -56,6 +71,19 @@ extern void link_error(int); link_error(__LINE__); \ } while (0) +/* Test that FUNC(ARG1, ARG2, ARG3) == (RES). */ +#define TESTIT3(FUNC,ARG1,ARG2,ARG3,RES) do { \ + if (__builtin_##FUNC##f(ARG1##F, ARG2##F, ARG3##F) != RES##F \ + || CKSGN_F(__builtin_##FUNC##f(ARG1##F,ARG2##F,ARG3##F),RES##F)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC(ARG1, ARG2, ARG3) != RES \ + || CKSGN(__builtin_##FUNC(ARG1,ARG2,ARG3),RES)) \ + link_error(__LINE__); \ + if (__builtin_##FUNC##l(ARG1##L, ARG2##L, ARG3##L) != RES##L \ + || CKSGN_L(__builtin_##FUNC##l(ARG1##L,ARG2##L,ARG3##L),RES##L)) \ + link_error(__LINE__); \ + } while (0) + /* Test that for FUNC(ARG, &ARG_S, &ARG_C); assert (ARG_S == RES_S && ARG_C == RES_C);. */ #define TESTIT_2P(FUNC,ARG,ARG_S,ARG_C,RES_S,RES_C) do { \ @@ -266,5 +294,64 @@ int main (void) TESTIT2_R (atan2, -1.0, 0.0, -1.58, -1.57); /* atan2(-1,0) == -pi/2 */ TESTIT2_R (atan2, 1.0, 0.0, 1.57, 1.58); /* atan2(1,0) == pi/2 */ + TESTIT2 (fmin, 5.0, 6.0, 5.0); /* fmin(5,6) == 5 */ + TESTIT2 (fmin, 6.0, 5.0, 5.0); /* fmin(6,5) == 5 */ + TESTIT2 (fmin, -5.0, -6.0, -6.0); /* fmin(-5,-6) == -6 */ + TESTIT2 (fmin, -6.0, -5.0, -6.0); /* fmin(-6,-5) == -6 */ + TESTIT2 (fmin, -0.0, 0.0, -0.0); /* fmin(-0,0) == -0 */ + TESTIT2 (fmin, 0.0, -0.0, -0.0); /* fmin(-0,0) == -0 */ + + TESTIT2 (fmax, 5.0, 6.0, 6.0); /* fmax(5,6) == 6 */ + TESTIT2 (fmax, 6.0, 5.0, 6.0); /* fmax(6,5) == 6 */ + TESTIT2 (fmax, -5.0, -6.0, -5.0); /* fmax(-5,-6) == -5 */ + TESTIT2 (fmax, -6.0, -5.0, -5.0); /* fmax(-6,-5) == -5 */ + TESTIT2 (fmax, -0.0, 0.0, 0.0); /* fmax(-0,0) == 0 */ + TESTIT2 (fmax, 0.0, -0.0, 0.0); /* fmax(-0,0) == 0 */ + + TESTIT3 (fma, 2.0, 3.0, 4.0, 10.0); /* fma(2,3,4) == 10 */ + TESTIT3 (fma, 2.0, -3.0, 4.0, -2.0); /* fma(2,-3,4) == -2 */ + TESTIT3 (fma, 2.0, 3.0, -4.0, 2.0); /* fma(2,3,-4) == 2 */ + TESTIT3 (fma, 2.0, -3.0, -4.0, -10.0); /* fma(2,-3,-4) == -10 */ + TESTIT3 (fma, -2.0, -3.0, -4.0, 2.0); /* fma(-2,-3,-4) == 2 */ + TESTIT3 (fma, 6.0, -0.0, 0.0, 0.0); /* fma(6,-0,0) == 0 */ + TESTIT3 (fma, -0.0, 6.0, 0.0, 0.0); /* fma(-0,6,0) == 0 */ + TESTIT3 (fma, 6.0, -0.0, -0.0, -0.0); /* fma(6,-0,-0) == -0 */ + TESTIT3 (fma, -0.0, 6.0, -0.0, -0.0); /* fma(-0,6,-0) == -0 */ + TESTIT3 (fma, 0.0, 0.0, 0.0, 0.0); /* fma(0,0,0) == 0 */ + TESTIT3 (fma, -0.0, 0.0, 0.0, 0.0); /* fma(-0,0,0) == 0 */ + TESTIT3 (fma, 0.0, -0.0, 0.0, 0.0); /* fma(0,-0,0) == 0 */ + TESTIT3 (fma, -0.0, -0.0, 0.0, 0.0); /* fma(-0,-0,0) == 0 */ + TESTIT3 (fma, 0.0, 0.0, -0.0, 0.0); /* fma(0,0,-0) == 0 */ + TESTIT3 (fma, -0.0, 0.0, -0.0, -0.0); /* fma(-0,0,-0) == -0 */ + TESTIT3 (fma, 0.0, -0.0, -0.0, -0.0); /* fma(0,-0,-0) == -0 */ + TESTIT3 (fma, -0.0, -0.0, -0.0, 0.0); /* fma(-0,-0,-0) == 0 */ + + if (__builtin_fmaf(__FLT_MAX__, 2.0F, -__FLT_MAX__) != __FLT_MAX__) + link_error (__LINE__); + if (__builtin_fmaf(2.0F,__FLT_MAX__, -__FLT_MAX__) != __FLT_MAX__) + link_error (__LINE__); + if (__builtin_fmaf(__FLT_MIN__, 0.5F, __FLT_MIN__) != __FLT_MIN__*1.5F) + link_error (__LINE__); + if (__builtin_fmaf(0.5F,__FLT_MIN__, __FLT_MIN__) != __FLT_MIN__*1.5F) + link_error (__LINE__); + + if (__builtin_fma(__DBL_MAX__, 2.0, -__DBL_MAX__) != __DBL_MAX__) + link_error (__LINE__); + if (__builtin_fma(2.0,__DBL_MAX__, -__DBL_MAX__) != __DBL_MAX__) + link_error (__LINE__); + if (__builtin_fma(__DBL_MIN__, 0.5, __DBL_MIN__) != __DBL_MIN__*1.5) + link_error (__LINE__); + if (__builtin_fma(0.5,__DBL_MIN__, __DBL_MIN__) != __DBL_MIN__*1.5) + link_error (__LINE__); + + if (__builtin_fmal(__LDBL_MAX__, 2.0L, -__LDBL_MAX__) != __LDBL_MAX__) + link_error (__LINE__); + if (__builtin_fmal(2.0L,__LDBL_MAX__, -__LDBL_MAX__) != __LDBL_MAX__) + link_error (__LINE__); + if (__builtin_fmal(__LDBL_MIN__, 0.5L, __LDBL_MIN__) != __LDBL_MIN__*1.5L) + link_error (__LINE__); + if (__builtin_fmal(0.5L,__LDBL_MIN__, __LDBL_MIN__) != __LDBL_MIN__*1.5L) + link_error (__LINE__); + return 0; } -- cgit v1.1