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
This commit is contained in:
Kaveh R. Ghazi 2007-05-18 01:31:20 +00:00 committed by Kaveh Ghazi
parent fd2ef596b5
commit ea91f95764
5 changed files with 240 additions and 0 deletions

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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" } } */

View File

@ -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;
}