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]: PR29335 evaluate transcendentals at compile-time using MPFR


Here's a proof-of-concept patch for PR middle-end/29335, evaluating
transcendental functions at compile-time using MPFR.

It'll depend on whether we include GMP/MPFR in the GCC repository as
discussed here.  We either need to do that or add configure goo to detect
if we have MPFR.  I'm in favor of including GMP/MPFR (but I need help!)
http://gcc.gnu.org/ml/gcc/2006-10/msg00136.html

The patch also needs testcases and more builtin functions to be converted.
So far it does sin, cos and tan.  But I made it very easy to add more, you
only need a couple of lines of code for each one.  The bulk of the work is
done in do_mpfr_arg1 which should satisfy all one-argument math builtins.
We can use it as a template for a do_mpfr_arg2 function for two-argument
builtins when we do pow, etc.

Thoughts?

		Thanks,
		--Kaveh


2006-10-07  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>

	PR middle-end/29335
	* Makefile.in (LIBS): Add $(GMPLIBS)
	* builtins.c (fold_builtin_sin, fold_builtin_cos,
	fold_builtin_tan): Fold all constant arguments.  Take a "type"
	argument as necessary.
	(do_mpfr_arg1): New.
	* real.c, real.h (real_from_mpfr, mpfr_from_real): New.

diff -rup orig/egcc-SVN20061001/gcc/Makefile.in egcc-SVN20061001/gcc/Makefile.in
--- orig/egcc-SVN20061001/gcc/Makefile.in	2006-09-28 20:01:48.000000000 -0400
+++ egcc-SVN20061001/gcc/Makefile.in	2006-10-02 21:20:26.759953919 -0400
@@ -845,7 +845,7 @@ BUILD_LIBDEPS= $(BUILD_LIBIBERTY)

 # How to link with both our special library facilities
 # and the system's installed libraries.
-LIBS = @LIBS@ $(CPPLIB) $(LIBINTL) $(LIBICONV) $(LIBIBERTY) $(LIBDECNUMBER)
+LIBS = @LIBS@ $(CPPLIB) $(LIBINTL) $(LIBICONV) $(LIBIBERTY) $(LIBDECNUMBER) $(GMPLIBS)

 # Any system libraries needed just for GNAT.
 SYSLIBS = @GNAT_LIBEXC@
diff -rup orig/egcc-SVN20061001/gcc/builtins.c egcc-SVN20061001/gcc/builtins.c
--- orig/egcc-SVN20061001/gcc/builtins.c	2006-09-29 20:02:07.000000000 -0400
+++ egcc-SVN20061001/gcc/builtins.c	2006-10-06 20:53:18.206478524 -0400
@@ -149,9 +149,9 @@ static tree fold_builtin_sqrt (tree, tre
 static tree fold_builtin_cbrt (tree, tree);
 static tree fold_builtin_pow (tree, tree, tree);
 static tree fold_builtin_powi (tree, tree, tree);
-static tree fold_builtin_sin (tree);
+static tree fold_builtin_sin (tree, tree);
 static tree fold_builtin_cos (tree, tree, tree);
-static tree fold_builtin_tan (tree);
+static tree fold_builtin_tan (tree, tree);
 static tree fold_builtin_atan (tree, tree);
 static tree fold_builtin_trunc (tree, tree);
 static tree fold_builtin_floor (tree, tree);
@@ -204,6 +204,7 @@ static unsigned HOST_WIDE_INT target_s;
 static char target_percent_c[3];
 static char target_percent_s[3];
 static char target_percent_s_newline[4];
+static tree do_mpfr_arg1 (tree, tree, int (*)(mpfr_ptr, mpfr_srcptr, mp_rnd_t));

 /* Return true if NODE should be considered for inline expansion regardless
    of the optimization level.  This means whenever a function is invoked with
@@ -7141,17 +7142,18 @@ fold_builtin_cbrt (tree arglist, tree ty
 /* Fold function call to builtin sin, sinf, or sinl.  Return
    NULL_TREE if no simplification can be made.  */
 static tree
-fold_builtin_sin (tree arglist)
+fold_builtin_sin (tree arglist, tree type)
 {
-  tree arg = TREE_VALUE (arglist);
+  tree arg = TREE_VALUE (arglist), res;

   if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
     return NULL_TREE;

-  /* Optimize sin (0.0) = 0.0.  */
-  if (real_zerop (arg))
-    return arg;
-
+  /* Calculate the result when the argument is a constant.  */
+  res = do_mpfr_arg1 (arg, type, mpfr_sin);
+  if (res)
+    return res;
+
   return NULL_TREE;
 }

@@ -7160,15 +7162,16 @@ fold_builtin_sin (tree arglist)
 static tree
 fold_builtin_cos (tree arglist, tree type, tree fndecl)
 {
-  tree arg = TREE_VALUE (arglist);
+  tree arg = TREE_VALUE (arglist), res;

   if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
     return NULL_TREE;

-  /* Optimize cos (0.0) = 1.0.  */
-  if (real_zerop (arg))
-    return build_real (type, dconst1);
-
+  /* Calculate the result when the argument is a constant.  */
+  res = do_mpfr_arg1 (arg, type, mpfr_cos);
+  if (res)
+    return res;
+
   /* Optimize cos(-x) into cos (x).  */
   if (TREE_CODE (arg) == NEGATE_EXPR)
     {
@@ -7183,18 +7186,19 @@ fold_builtin_cos (tree arglist, tree typ
 /* Fold function call to builtin tan, tanf, or tanl.  Return
    NULL_TREE if no simplification can be made.  */
 static tree
-fold_builtin_tan (tree arglist)
+fold_builtin_tan (tree arglist, tree type)
 {
   enum built_in_function fcode;
-  tree arg = TREE_VALUE (arglist);
+  tree arg = TREE_VALUE (arglist), res;

   if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
     return NULL_TREE;

-  /* Optimize tan(0.0) = 0.0.  */
-  if (real_zerop (arg))
-    return arg;
-
+  /* Calculate the result when the argument is a constant.  */
+  res = do_mpfr_arg1 (arg, type, mpfr_tan);
+  if (res)
+    return res;
+
   /* Optimize tan(atan(x)) = x.  */
   fcode = builtin_mathfn_code (arg);
   if (flag_unsafe_math_optimizations
@@ -8916,7 +8920,7 @@ fold_builtin_1 (tree fndecl, tree arglis
       return fold_builtin_cbrt (arglist, type);

     CASE_FLT_FN (BUILT_IN_SIN):
-      return fold_builtin_sin (arglist);
+      return fold_builtin_sin (arglist, type);

     CASE_FLT_FN (BUILT_IN_COS):
       return fold_builtin_cos (arglist, type, fndecl);
@@ -8941,7 +8945,7 @@ fold_builtin_1 (tree fndecl, tree arglis
       return fold_builtin_logarithm (fndecl, arglist, &dconst10);

     CASE_FLT_FN (BUILT_IN_TAN):
-      return fold_builtin_tan (arglist);
+      return fold_builtin_tan (arglist, type);

     CASE_FLT_FN (BUILT_IN_ATAN):
       return fold_builtin_atan (arglist, type);
@@ -11134,3 +11138,35 @@ init_target_chars (void)
     }
   return true;
 }
+
+/* If the supplied argument ARG is a REAL_CST, call the supplied mpfr
+   function FUNC on it and return the resulting value as a tree with
+   type TYPE.  */
+
+static tree
+do_mpfr_arg1 (tree arg, tree type, int (*func)(mpfr_ptr, mpfr_srcptr, mp_rnd_t))
+{
+  tree result = NULL_TREE;
+
+  STRIP_NOPS (arg);
+
+  if (TREE_CODE (arg) == REAL_CST)
+    {
+      REAL_VALUE_TYPE r = TREE_REAL_CST (arg);
+      mpfr_t m;
+
+      mpfr_init2 (m, 128);
+      mpfr_from_real (m, &r);
+      func (m, m, GMP_RNDN);
+
+      /* Only proceed if we don't have NaN or Inf.  */
+      if (! mpfr_inf_p (m) && ! mpfr_nan_p (m))
+        {
+	  real_from_mpfr (&r, m);
+	  result = build_real (type, r);
+	}
+      mpfr_clear (m);
+    }
+
+  return result;
+}
diff -rup orig/egcc-SVN20061001/gcc/real.c egcc-SVN20061001/gcc/real.c
--- orig/egcc-SVN20061001/gcc/real.c	2006-03-16 20:01:37.000000000 -0500
+++ egcc-SVN20061001/gcc/real.c	2006-10-06 20:46:22.360424959 -0400
@@ -4922,3 +4922,45 @@ real_copysign (REAL_VALUE_TYPE *r, const
   r->sign = x->sign;
 }

+/* Convert from REAL_VALUE_TYPE to MPFR.  The caller is responsible
+   for initializing and clearing the MPFR parmeter.  */
+
+void
+mpfr_from_real (mpfr_ptr m, const REAL_VALUE_TYPE *r)
+{
+  /* We use a string as an intermediate type.  */
+  char buf[256];
+
+  real_to_decimal (buf, r, sizeof (buf), 0, 1);
+  gcc_assert (mpfr_set_str (m, buf, 10, GMP_RNDN) == 0);
+}
+
+/* Convert from MPFR to REAL_VALUE_TYPE.  */
+
+void
+real_from_mpfr (REAL_VALUE_TYPE *r, mpfr_srcptr m)
+{
+  /* We use a string as an intermediate type.  */
+  char buf[256], *rstr;
+  mp_exp_t exp;
+
+  rstr = mpfr_get_str (NULL, &exp, 16, 0, m, GMP_RNDN);
+
+  /* The additional 12 chars add space for the sprintf below.  This
+     leaves 6 digits for the exponent which is supposedly enough.  */
+  gcc_assert (rstr != NULL && strlen (rstr) < sizeof (buf) - 12);
+
+  /* REAL_VALUE_ATOF expects the exponent for mantissa * 2**exp,
+     mpfr_get_str returns the exponent for mantissa * 16**exp, adjust
+     for that.  */
+  exp *= 4;
+
+  if (rstr[0] == '-')
+    sprintf (buf, "-0x.%sp%d", &rstr[1], (int) exp);
+  else
+    sprintf (buf, "0x.%sp%d", rstr, (int) exp);
+
+  free (rstr); /* FIXME: use mpfr_free_str() when we know it exists.  */
+
+  real_from_string (r, buf);
+}
diff -rup orig/egcc-SVN20061001/gcc/real.h egcc-SVN20061001/gcc/real.h
--- orig/egcc-SVN20061001/gcc/real.h	2006-01-23 00:24:02.000000000 -0500
+++ egcc-SVN20061001/gcc/real.h	2006-10-06 18:52:05.182696079 -0400
@@ -22,6 +22,8 @@
 #ifndef GCC_REAL_H
 #define GCC_REAL_H

+#include <gmp.h>
+#include <mpfr.h>
 #include "machmode.h"

 /* An expanded form of the represented number.  */
@@ -425,4 +427,10 @@ extern void real_round (REAL_VALUE_TYPE
 /* Set the sign of R to the sign of X.  */
 extern void real_copysign (REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *);

+/* Convert between MPFR and REAL_VALUE_TYPE.  The caller is
+   responsible for initializing and clearing the MPFR parameter.  */
+
+extern void real_from_mpfr (REAL_VALUE_TYPE *, mpfr_srcptr);
+extern void mpfr_from_real (mpfr_ptr, const REAL_VALUE_TYPE *);
+
 #endif /* ! GCC_REAL_H */


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