re PR middle-end/31796 (Evaluate remquo/remainder/drem at compile-time)
authorKaveh R. Ghazi <ghazi@caip.rutgers.edu>
Fri, 18 May 2007 01:31:20 +0000 (01:31 +0000)
committerKaveh Ghazi <ghazi@gcc.gnu.org>
Fri, 18 May 2007 01:31:20 +0000 (01:31 +0000)
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

gcc/ChangeLog
gcc/builtins.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/torture/builtin-math-2.c
gcc/testsuite/gcc.dg/torture/builtin-math-4.c

index 66c9acc..a7b9b0c 100644 (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.
index da6bbc6..96c1c03 100644 (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
index 5d13049..c0f4b67 100644 (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.
 
index f3cdaf6..81608b8 100644 (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" } } */
index b7b8e5e..e91f14c 100644 (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;
 }