This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH]: PR middle-end/31796 use MPFR to evaluate remquo/remainder/drem at compile-time


This patch address PR middle-end/31796.  It uses MPFR to evaluate remquo,
remainder and drem at compile-time when they are supplied with constant
arguments.

This patch relies on the follow two previous (and as yet unreviewed)
patches to apply cleanly:

http://gcc.gnu.org/ml/gcc-patches/2007-04/msg01624.html
http://gcc.gnu.org/ml/gcc-patches/2007-04/msg01663.html

Tested on sparc-sun-solaris2.10, no regressions and the new tests all
pass.

Okay for mainline?

		Thanks,
		--Kaveh

:ADDPATCH middle-end:


2007-05-05  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.

testsuite:
	* gcc.dg/torture/builtin-math-2.c: Add tests for remquo, remainder
	and drem.
	* gcc.dg/torture/builtin-math-4.c: Likewise.

diff -rup orig/egcc-SVN20070503/gcc/builtins.c egcc-SVN20070503/gcc/builtins.c
--- orig/egcc-SVN20070503/gcc/builtins.c	2007-05-03 11:53:35.558265159 -0400
+++ egcc-SVN20070503/gcc/builtins.c	2007-05-04 13:55:41.150413906 -0400
@@ -233,6 +233,7 @@ static tree do_mpfr_sincos (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
@@ -9847,6 +9848,13 @@ fold_builtin_2 (tree fndecl, tree arg0,
 	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):
@@ -10004,6 +10012,15 @@ fold_builtin_3 (tree fndecl, tree arg0,
 	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);

@@ -12520,4 +12537,76 @@ do_mpfr_bessel_n (tree arg1, tree arg2,

   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 -rup orig/egcc-SVN20070503/gcc/testsuite/gcc.dg/torture/builtin-math-2.c egcc-SVN20070503/gcc/testsuite/gcc.dg/torture/builtin-math-2.c
--- orig/egcc-SVN20070503/gcc/testsuite/gcc.dg/torture/builtin-math-2.c	2007-05-03 11:53:35.560597114 -0400
+++ egcc-SVN20070503/gcc/testsuite/gcc.dg/torture/builtin-math-2.c	2007-05-05 02:21:52.035004051 -0400
@@ -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 -rup orig/egcc-SVN20070503/gcc/testsuite/gcc.dg/torture/builtin-math-4.c egcc-SVN20070503/gcc/testsuite/gcc.dg/torture/builtin-math-4.c
--- orig/egcc-SVN20070503/gcc/testsuite/gcc.dg/torture/builtin-math-4.c	2007-05-03 11:53:35.561372886 -0400
+++ egcc-SVN20070503/gcc/testsuite/gcc.dg/torture/builtin-math-4.c	2007-05-05 02:10:00.098824960 -0400
@@ -69,8 +69,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... */
@@ -129,6 +165,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;
 }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]