diff options
author | Kaveh R. Ghazi <ghazi@caip.rutgers.edu> | 2007-05-18 01:31:20 +0000 |
---|---|---|
committer | Kaveh Ghazi <ghazi@gcc.gnu.org> | 2007-05-18 01:31:20 +0000 |
commit | ea91f95764aaa7a1ab8ceb78e81414dc28fb6b42 (patch) | |
tree | d397abaa47309791779548be5eedc00901d236bb | |
parent | fd2ef596b5fe6bfdf10bed1c8a4b4100d8a0772a (diff) | |
download | gcc-ea91f95764aaa7a1ab8ceb78e81414dc28fb6b42.zip gcc-ea91f95764aaa7a1ab8ceb78e81414dc28fb6b42.tar.gz gcc-ea91f95764aaa7a1ab8ceb78e81414dc28fb6b42.tar.bz2 |
re PR middle-end/31796 (Evaluate remquo/remainder/drem at compile-time)
PR middle-end/31796
* builtins.c (do_mpfr_remquo): New.
(fold_builtin_2): Handle BUILT_IN_DREM/BUILT_IN_REMAINDER.
(fold_builtin_3): Handle BUILT_IN_REMQUO.
testsuite:
* gcc.dg/torture/builtin-math-2.c: Add tests for remquo, remainder
and drem.
* gcc.dg/torture/builtin-math-4.c: Likewise.
From-SVN: r124820
-rw-r--r-- | gcc/ChangeLog | 5 | ||||
-rw-r--r-- | gcc/builtins.c | 89 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/torture/builtin-math-2.c | 23 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/torture/builtin-math-4.c | 119 |
5 files changed, 240 insertions, 0 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 66c9acc..a7b9b0c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,10 @@ 2007-05-17 Kaveh R. Ghazi <ghazi@caip.rutgers.edu> + PR middle-end/31796 + * builtins.c (do_mpfr_remquo): New. + (fold_builtin_2): Handle BUILT_IN_DREM/BUILT_IN_REMAINDER. + (fold_builtin_3): Handle BUILT_IN_REMQUO. + PR middle-end/30251 * builtins.c (fold_builtin_1): Handle y0, y1. (fold_builtin_2): Handle yn. diff --git a/gcc/builtins.c b/gcc/builtins.c index da6bbc6..96c1c03 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -235,6 +235,7 @@ static tree do_mpfr_sincos (tree, tree, tree); static tree do_mpfr_bessel_n (tree, tree, tree, int (*)(mpfr_ptr, long, mpfr_srcptr, mp_rnd_t), const REAL_VALUE_TYPE *, bool); +static tree do_mpfr_remquo (tree, tree, tree); #endif /* Return true if NODE should be considered for inline expansion regardless @@ -9920,6 +9921,13 @@ fold_builtin_2 (tree fndecl, tree arg0, tree arg1, bool ignore) return do_mpfr_bessel_n (arg0, arg1, type, mpfr_yn, &dconst0, false); break; + + CASE_FLT_FN (BUILT_IN_DREM): + CASE_FLT_FN (BUILT_IN_REMAINDER): + if (validate_arg (arg0, REAL_TYPE) + && validate_arg(arg1, REAL_TYPE)) + return do_mpfr_arg2 (arg0, arg1, type, mpfr_remainder); + break; #endif CASE_FLT_FN (BUILT_IN_ATAN2): @@ -10077,6 +10085,15 @@ fold_builtin_3 (tree fndecl, tree arg0, tree arg1, tree arg2, bool ignore) return do_mpfr_arg3 (arg0, arg1, arg2, type, mpfr_fma); break; +#if MPFR_VERSION >= MPFR_VERSION_NUM(2,3,0) + CASE_FLT_FN (BUILT_IN_REMQUO): + if (validate_arg (arg0, REAL_TYPE) + && validate_arg(arg1, REAL_TYPE) + && validate_arg(arg2, POINTER_TYPE)) + return do_mpfr_remquo (arg0, arg1, arg2); + break; +#endif + case BUILT_IN_MEMSET: return fold_builtin_memset (arg0, arg1, arg2, type, ignore); @@ -12596,4 +12613,76 @@ do_mpfr_bessel_n (tree arg1, tree arg2, tree type, return result; } + +/* If arguments ARG0 and ARG1 are REAL_CSTs, call mpfr_remquo() to set + the pointer *(ARG_QUO) and return the result. The type is taken + from the type of ARG0 and is used for setting the precision of the + calculation and results. */ + +static tree +do_mpfr_remquo (tree arg0, tree arg1, tree arg_quo) +{ + tree const type = TREE_TYPE (arg0); + tree result = NULL_TREE; + + STRIP_NOPS (arg0); + STRIP_NOPS (arg1); + + /* To proceed, MPFR must exactly represent the target floating point + format, which only happens when the target base equals two. */ + if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2 + && TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0) + && TREE_CODE (arg1) == REAL_CST && !TREE_OVERFLOW (arg1)) + { + const REAL_VALUE_TYPE *const ra0 = TREE_REAL_CST_PTR (arg0); + const REAL_VALUE_TYPE *const ra1 = TREE_REAL_CST_PTR (arg1); + + if (!real_isnan (ra0) && !real_isinf (ra0) + && !real_isnan (ra1) && !real_isinf (ra1)) + { + const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p; + tree result_rem; + long integer_quo; + mpfr_t m0, m1; + + mpfr_inits2 (prec, m0, m1, NULL); + mpfr_from_real (m0, ra0, GMP_RNDN); + mpfr_from_real (m1, ra1, GMP_RNDN); + mpfr_clear_flags (); + mpfr_remquo (m0, &integer_quo, m0, m1, GMP_RNDN); + /* Remquo is independent of the rounding mode, so pass + inexact=0 to do_mpfr_ckconv(). */ + result_rem = do_mpfr_ckconv (m0, type, /*inexact=*/ 0); + mpfr_clears (m0, m1, NULL); + if (result_rem) + { + /* MPFR calculates quo in the host's long so it may + return more bits in quo than the target int can hold + if sizeof(host long) > sizeof(target int). This can + happen even for native compilers in LP64 mode. In + these cases, modulo the quo value with the largest + number that the target int can hold while leaving one + bit for the sign. */ + if (sizeof (integer_quo) * CHAR_BIT > INT_TYPE_SIZE) + integer_quo %= (long)(1UL << (INT_TYPE_SIZE - 1)); + + /* Dereference the quo pointer argument. */ + arg_quo = build_fold_indirect_ref (arg_quo); + /* Proceed iff a valid pointer type was passed in. */ + if (TYPE_MAIN_VARIANT (TREE_TYPE (arg_quo)) == integer_type_node) + { + /* Set the value. */ + tree result_quo = fold_build2 (MODIFY_EXPR, + TREE_TYPE (arg_quo), arg_quo, + build_int_cst (NULL, integer_quo)); + TREE_SIDE_EFFECTS (result_quo) = 1; + /* Combine the quo assignment with the rem. */ + result = non_lvalue (fold_build2 (COMPOUND_EXPR, type, + result_quo, result_rem)); + } + } + } + } + return result; +} #endif diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 5d13049..c0f4b67 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,9 @@ 2007-05-17 Kaveh R. Ghazi <ghazi@caip.rutgers.edu> + * gcc.dg/torture/builtin-math-2.c: Add tests for remquo, remainder + and drem. + * gcc.dg/torture/builtin-math-4.c: Likewise. + * gcc.dg/torture/builtin-math-2.c: Test y0, y1, yn. * gcc.dg/torture/builtin-math-4.c: Likewise. diff --git a/gcc/testsuite/gcc.dg/torture/builtin-math-2.c b/gcc/testsuite/gcc.dg/torture/builtin-math-2.c index f3cdaf6..81608b8 100644 --- a/gcc/testsuite/gcc.dg/torture/builtin-math-2.c +++ b/gcc/testsuite/gcc.dg/torture/builtin-math-2.c @@ -42,6 +42,13 @@ extern void fool (long double); fool (__builtin_##FUNC##l (ARG1##L, ARG2)); \ } while (0) +#define TESTIT_REMQUO(ARG1, ARG2) do { \ + int quo; \ + foof (__builtin_remquof (ARG1##F, ARG2##F, &quo)); \ + foo (__builtin_remquo (ARG1, ARG2, &quo)); \ + fool (__builtin_remquol (ARG1##L, ARG2##L, &quo)); \ +} while (0) + void bar() { /* An argument of NaN is not evaluated at compile-time. */ @@ -252,6 +259,13 @@ void bar() TESTIT2_I1 (yn, -3, 0.0); TESTIT2_I1 (yn, -3, -0.0); + /* The second argument of remquo/remainder/drem must not be 0. */ + TESTIT_REMQUO (1.0, 0.0); + TESTIT_REMQUO (1.0, -0.0); + TESTIT2 (remainder, 1.0, 0.0); + TESTIT2 (remainder, 1.0, -0.0); + TESTIT2 (drem, 1.0, 0.0); + TESTIT2 (drem, 1.0, -0.0); } /* { dg-final { scan-tree-dump-times "exp2 " 9 "original" } } */ @@ -317,4 +331,13 @@ void bar() /* { dg-final { scan-tree-dump-times "yn " 6 "original" } } */ /* { dg-final { scan-tree-dump-times "ynf" 6 "original" } } */ /* { dg-final { scan-tree-dump-times "ynl" 6 "original" } } */ +/* { dg-final { scan-tree-dump-times "remquo " 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "remquof" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "remquol" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "remainder " 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "remainderf" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "remainderl" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "drem " 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "dremf" 2 "original" } } */ +/* { dg-final { scan-tree-dump-times "dreml" 2 "original" } } */ /* { dg-final { cleanup-tree-dump "original" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/builtin-math-4.c b/gcc/testsuite/gcc.dg/torture/builtin-math-4.c index b7b8e5e..e91f14c 100644 --- a/gcc/testsuite/gcc.dg/torture/builtin-math-4.c +++ b/gcc/testsuite/gcc.dg/torture/builtin-math-4.c @@ -71,8 +71,44 @@ extern void link_error(int); link_error(__LINE__); \ } while (0) +/* Test that remquo(ARG0, ARG1, &ARG_Q) == RES and ARG_Q == RES_Q. + Also test remainder/drem (ARG0,ARG1) == RES. */ +#define TESTIT2_REMQUO(ARG0,ARG1,ARG_Q,RES,RES_Q) do { \ + ARG_Q = 12345; \ + if (__builtin_remquof(ARG0##F, ARG1##F, &ARG_Q) != RES##F \ + || CKSGN_F(__builtin_remquof(ARG0##F, ARG1##F, &ARG_Q),RES##F) \ + || ARG_Q != RES_Q \ + || __builtin_remainderf(ARG0##F, ARG1##F) != RES##F \ + || CKSGN_F(__builtin_remainderf(ARG0##F, ARG1##F),RES##F) \ + || __builtin_dremf(ARG0##F, ARG1##F) != RES##F \ + || CKSGN_F(__builtin_dremf(ARG0##F, ARG1##F),RES##F)) \ + link_error(__LINE__); \ + ARG_Q = 12345; \ + if (__builtin_remquo(ARG0, ARG1, &ARG_Q) != RES \ + || CKSGN(__builtin_remquo(ARG0, ARG1, &ARG_Q),RES) \ + || ARG_Q != RES_Q \ + || __builtin_remainder(ARG0, ARG1) != RES \ + || CKSGN(__builtin_remainder(ARG0, ARG1),RES) \ + || __builtin_drem(ARG0, ARG1) != RES \ + || CKSGN(__builtin_drem(ARG0, ARG1),RES)) \ + link_error(__LINE__); \ + ARG_Q = 12345; \ + if (__builtin_remquol(ARG0##L, ARG1##L, &ARG_Q) != RES##L \ + || CKSGN_L(__builtin_remquol(ARG0##L, ARG1##L, &ARG_Q),RES##L) \ + || ARG_Q != RES_Q \ + || __builtin_remainderl(ARG0##L, ARG1##L) != RES##L \ + || CKSGN_L(__builtin_remainderl(ARG0##L, ARG1##L),RES##L) \ + || __builtin_dreml(ARG0##L, ARG1##L) != RES##L \ + || CKSGN_L(__builtin_dreml(ARG0##L, ARG1##L),RES##L)) \ + link_error(__LINE__); \ + } while (0) + int main (void) { +#ifdef __OPTIMIZE__ + int q; +#endif + TESTIT (j0, 0.0, 1.0); /* j0(0) == 1 */ TESTIT (j0, -0.0, 1.0); /* j0(-0) == 1 */ TESTIT_R (j0, 1.0, 0.765, 0.766); /* j0(1) == 0.7651... */ @@ -131,6 +167,89 @@ int main (void) TESTIT2_R (yn, 3, 0.89, -8.03, -8.02); /* yn(3,0.89) == -8.020... */ TESTIT2_R (yn, -3, 8.0, -0.03, -0.02); /* yn(-3,8) == -0.026... */ TESTIT2_R (yn, -3, 0.99, 5.98, 5.99); /* yn(-3,0.99) == 5.982... */ + +#ifdef __OPTIMIZE__ + /* These tests rely on propagating the variable q, which happens + only when optimization is turned on. This macro also tests + remainder/drem. */ + TESTIT2_REMQUO (0.0, 1.0, q, 0.0, 0); /* remquo(0,1,&q)==0, q==0 */ + TESTIT2_REMQUO (1.0, 1.0, q, 0.0, 1); /* remquo(1,1,&q)==0, q==1 */ + TESTIT2_REMQUO (2.0, 1.0, q, 0.0, 2); /* remquo(2,1,&q)==0, q==2 */ + TESTIT2_REMQUO (-0.0, 1.0, q, -0.0, 0); /* remquo(-0,1,&q)==-0, q==0 */ + TESTIT2_REMQUO (-1.0, 1.0, q, -0.0, -1); /* remquo(-1,1,&q)==-0, q==-1 */ + TESTIT2_REMQUO (-2.0, 1.0, q, -0.0, -2); /* remquo(-2,1,&q)==-0, q==-2 */ + + TESTIT2_REMQUO (0.0, -1.0, q, 0.0, 0); /* remquo(0,-1,&q)==0, q==0 */ + TESTIT2_REMQUO (1.0, -1.0, q, 0.0, -1); /* remquo(1,-1,&q)==0, q==-1 */ + TESTIT2_REMQUO (2.0, -1.0, q, 0.0, -2); /* remquo(2,-1,&q)==0, q==-2 */ + TESTIT2_REMQUO (-0.0, -1.0, q, -0.0, 0); /* remquo(-0,-1,&q)==-0, q==0 */ + TESTIT2_REMQUO (-1.0, -1.0, q, -0.0, 1); /* remquo(-1,-1,&q)==-0, q==1 */ + TESTIT2_REMQUO (-2.0, -1.0, q, -0.0, 2); /* remquo(-2,-1,&q)==-0, q==2 */ + + TESTIT2_REMQUO (1.0, 2.0, q, 1.0, 0); /* remquo(1,2,&q)==1, q==0 */ + TESTIT2_REMQUO (3.0, 2.0, q, -1.0, 2); /* remquo(3,2,&q)==-1, q==2 */ + TESTIT2_REMQUO (5.0, 2.0, q, 1.0, 2); /* remquo(5,2,&q)==1, q==2 */ + TESTIT2_REMQUO (-1.0, 2.0, q, -1.0, 0); /* remquo(-1,2,&q)==-1, q==0 */ + TESTIT2_REMQUO (-3.0, 2.0, q, 1.0, -2); /* remquo(-3,2,&q)==1, q==-2 */ + TESTIT2_REMQUO (-5.0, 2.0, q, -1.0, -2); /* remquo(-5,2,&q)==-1, q==-2 */ + + TESTIT2_REMQUO (1.0, -2.0, q, 1.0, 0); /* remquo(1,-2,&q)==1, q==0 */ + TESTIT2_REMQUO (3.0, -2.0, q, -1.0, -2); /* remquo(3,-2,&q)==-1, q==-2 */ + TESTIT2_REMQUO (5.0, -2.0, q, 1.0, -2); /* remquo(5,-2,&q)==1, q==-2 */ + TESTIT2_REMQUO (-1.0, -2.0, q, -1.0, 0); /* remquo(-1,-2,&q)==-1, q==0 */ + TESTIT2_REMQUO (-3.0, -2.0, q, 1.0, 2); /* remquo(-3,-2,&q)==1, q==2 */ + TESTIT2_REMQUO (-5.0, -2.0, q, -1.0, 2); /* remquo(-5,-2,&q)==-1, q==2 */ + + /* Test that the maximum possible value can be generated into the + int quotient, and check for wrap around (modulo) when that value + is exceeded. We can only check for this when the mantissa has + enough bits to hold an INT_MAX value with complete precision. */ + +#define MAXIT(FUNC,X,R) do { \ + q = 12345; \ + if (__builtin_##FUNC((X), 1, &q) != 0 || q != (R)) \ + link_error (__LINE__); \ +} while (0) + if (sizeof(int)*__CHAR_BIT__ <= __FLT_MANT_DIG__) + { + MAXIT(remquof, __INT_MAX__-1.0F, __INT_MAX__-1); + MAXIT(remquof, __INT_MAX__+0.0F, __INT_MAX__); + MAXIT(remquof, __INT_MAX__+1.0F, 0); + MAXIT(remquof, __INT_MAX__+2.0F, 1); + + MAXIT(remquof, -(__INT_MAX__-1.0F), -(__INT_MAX__-1)); + MAXIT(remquof, -(__INT_MAX__+0.0F), -__INT_MAX__); + MAXIT(remquof, -(__INT_MAX__+1.0F), 0); + MAXIT(remquof, -(__INT_MAX__+2.0F), -1); + } + + if (sizeof(int)*__CHAR_BIT__ <= __DBL_MANT_DIG__) + { + MAXIT(remquo, __INT_MAX__-1.0, __INT_MAX__-1); + MAXIT(remquo, __INT_MAX__+0.0, __INT_MAX__); + MAXIT(remquo, __INT_MAX__+1.0, 0); + MAXIT(remquo, __INT_MAX__+2.0, 1); + + MAXIT(remquo, -(__INT_MAX__-1.0), -(__INT_MAX__-1)); + MAXIT(remquo, -(__INT_MAX__+0.0), -__INT_MAX__); + MAXIT(remquo, -(__INT_MAX__+1.0), 0); + MAXIT(remquo, -(__INT_MAX__+2.0), -1); + } + + if (sizeof(int)*__CHAR_BIT__ <= __LDBL_MANT_DIG__) + { + MAXIT(remquo, __INT_MAX__-1.0L, __INT_MAX__-1); + MAXIT(remquo, __INT_MAX__+0.0L, __INT_MAX__); + MAXIT(remquo, __INT_MAX__+1.0L, 0); + MAXIT(remquo, __INT_MAX__+2.0L, 1); + + MAXIT(remquol, -(__INT_MAX__-1.0L), -(__INT_MAX__-1)); + MAXIT(remquol, -(__INT_MAX__+0.0L), -__INT_MAX__); + MAXIT(remquol, -(__INT_MAX__+1.0L), 0); + MAXIT(remquol, -(__INT_MAX__+2.0L), -1); + } +#endif + return 0; } |