mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-02-24 16:25:56 +08:00
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:
parent
fd2ef596b5
commit
ea91f95764
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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" } } */
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user