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/30250: Evaluate lgamma/gamma at compile-time using MPFR


This patch completes PR middle-end/30250.  It uses MPFR to evaluate the
non-reentrant (i.e. plain/regular) lgamma/gamma builtins at compile-time.

In addition to returning a value, the C99 lgamma function sets an extern
int variable named signgam.  Up until now, things to set besides the
return value were supplied to the math builtins as pointer parameters
(e.g. frexp, modf, remquo, etc) which made it easy to get a handle on it.
Setting a global variable required a new mechanism.

I found I needed to use identifier_global_value() to grab a handle on
signgam.  This function only appears in the C-family frontends.  So I
ended up deciding to use a langhook.  The langhook defaults to returning
NULL_TREE which stops the transformation in most languages.  From the
C-family, c_get_signgam() will return a usable tree for signgam if it's
been declared extern int at that point in the user's code.  (Simply
including math.h will declare it in conforming C99 libc implementations.)
I didn't bother to declare signgam on-the-fly if it wasn't found since I
wasn't sure that was safe.

The patch relies on five previously submitted 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
http://gcc.gnu.org/ml/gcc-patches/2007-05/msg00297.html
http://gcc.gnu.org/ml/gcc-patches/2007-05/msg00320.html
http://gcc.gnu.org/ml/gcc-patches/2007-05/msg00408.html

Patch below tested in conjunction with the above five patches on
sparc-sun-solaris2.10, no regressions and the new testcases all pass. In
addition, I ran it against the FP accuracy testsuite and all lgamma inputs
generated "perfect" results.  Finally, I wrote a one lgamma call test for
each of the C-family languages (C, C++, objc, objc++) to ensure the
langhook worked from all four, which it did.

With this patch, I've finished my recent work to take advantage of new
features in mpfr-2.3.0.  I believe that now all of the math builtins
(except for some _Complex ones) are evauated by GCC at compile-time.
Yay. :-)

Oh because of the langhook, the patch needs review from a C and C++
maintainer, in addition to the middle-end bits.

Okay for mainline?

		Thanks,
		--Kaveh

:ADDPATCH middle-end C C++:


2007-05-08  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>

	PR middle-end/30250
	* builtins.c (do_mpfr_lgamma): New.
	(fold_builtin_1): Handle builtin gamma/lgamma.
	* c-common.c (c_get_signgam): New.
	* c-common.h (c_get_signgam): Likewise.
	* c-objc-common.h (LANG_HOOKS_GET_SIGNGAM): Redefine.
	* langhooks-def.h (LANG_HOOKS_GET_SIGNGAM): Define and use.
	* langhooks.h (struct lang_hooks): Add get_signgam member.

cp:
	* cp-objcp-common.h (LANG_HOOKS_GET_SIGNGAM): Redefine.

testsuite:
	* gcc.dg/torture/builtin-math-2.c: Add lgamma/gamma tests.
	* gcc.dg/torture/builtin-math-4.c: Likewise.

diff -rup orig/egcc-SVN20070505/gcc/builtins.c egcc-SVN20070505/gcc/builtins.c
--- orig/egcc-SVN20070505/gcc/builtins.c	2007-05-07 12:21:52.970210257 -0400
+++ egcc-SVN20070505/gcc/builtins.c	2007-05-07 14:12:15.582690635 -0400
@@ -235,6 +235,7 @@ static tree do_mpfr_bessel_n (tree, tree
 			      const REAL_VALUE_TYPE *, bool);
 static tree do_mpfr_remquo (tree, tree, tree);
 static tree do_mpfr_lgamma_r (tree, tree, tree);
+static tree do_mpfr_lgamma (tree, tree);
 #endif

 /* Return true if NODE should be considered for inline expansion regardless
@@ -9731,6 +9732,12 @@ fold_builtin_1 (tree fndecl, tree arg0,
 	return do_mpfr_arg1 (arg0, type, mpfr_y1,
 			     &dconst0, NULL, false);
     break;
+
+    CASE_FLT_FN (BUILT_IN_GAMMA):
+    CASE_FLT_FN (BUILT_IN_LGAMMA):
+      if (validate_arg (arg0, REAL_TYPE))
+	return do_mpfr_lgamma (arg0, type);
+    break;
 #endif

     CASE_FLT_FN (BUILT_IN_NAN):
@@ -12686,4 +12693,22 @@ do_mpfr_lgamma_r (tree arg, tree arg_sg,

   return result;
 }
+
+/* If ARG is a REAL_CST and we can find extern int signgam, then call
+   mpfr_lgamma() to determine the result and return it as a tree with
+   type TYPE.  The value of extern int signgam is also set.  Return
+   NULL_TREE if no transformation was made.  */
+
+static tree
+do_mpfr_lgamma (tree arg, tree type)
+{
+  tree const sg = lang_hooks.get_signgam();
+
+  /* If we find extern int signgam, take the address and pass it to
+     do_mpfr_lgamma_r.  */
+  if (sg)
+    return do_mpfr_lgamma_r (arg, build_fold_addr_expr(sg), type);
+  else
+    return NULL_TREE;
+}
 #endif
diff -rup orig/egcc-SVN20070505/gcc/c-common.c egcc-SVN20070505/gcc/c-common.c
--- orig/egcc-SVN20070505/gcc/c-common.c	2007-05-02 20:02:45.000000000 -0400
+++ egcc-SVN20070505/gcc/c-common.c	2007-05-07 14:09:25.985696173 -0400
@@ -7066,4 +7066,25 @@ warn_for_div_by_zero (tree divisor)
     warning (OPT_Wdiv_by_zero, "division by zero");
 }

+/* If we find extern int signgam, return it.  Otherwise return
+   NULL_TREE.  Used from the middle-end for the lgamma builtin.  */
+
+tree
+c_get_signgam (void)
+{
+  tree sg = maybe_get_identifier ("signgam");
+
+  if (sg)
+    {
+      sg = identifier_global_value (sg);
+      if (sg
+	  && TREE_CODE (sg) == VAR_DECL
+	  && TREE_CODE (TREE_TYPE (sg)) == INTEGER_TYPE
+	  && TREE_TYPE (sg) == integer_type_node)
+	return sg;
+    }
+
+  return NULL_TREE;
+}
+
 #include "gt-c-common.h"
diff -rup orig/egcc-SVN20070505/gcc/c-common.h egcc-SVN20070505/gcc/c-common.h
--- orig/egcc-SVN20070505/gcc/c-common.h	2007-05-02 20:02:45.000000000 -0400
+++ egcc-SVN20070505/gcc/c-common.h	2007-05-07 12:58:17.913539157 -0400
@@ -1010,4 +1010,6 @@ extern GTY (()) tree static_dtors;
 extern void c_record_cdtor_fn (tree);
 extern void c_build_cdtor_fns (void);

+extern tree c_get_signgam (void);
+
 #endif /* ! GCC_C_COMMON_H */
diff -rup orig/egcc-SVN20070505/gcc/c-objc-common.h egcc-SVN20070505/gcc/c-objc-common.h
--- orig/egcc-SVN20070505/gcc/c-objc-common.h	2007-04-08 20:02:38.000000000 -0400
+++ egcc-SVN20070505/gcc/c-objc-common.h	2007-05-07 12:58:29.120180388 -0400
@@ -76,6 +76,8 @@ extern void c_initialize_diagnostics (di
 #define LANG_HOOKS_DUP_LANG_SPECIFIC_DECL c_dup_lang_specific_decl
 #undef  LANG_HOOKS_BUILTIN_FUNCTION
 #define LANG_HOOKS_BUILTIN_FUNCTION c_builtin_function
+#undef LANG_HOOKS_GET_SIGNGAM
+#define LANG_HOOKS_GET_SIGNGAM c_get_signgam

 /* Attribute hooks.  */
 #undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE
diff -rup orig/egcc-SVN20070505/gcc/cp/cp-objcp-common.h egcc-SVN20070505/gcc/cp/cp-objcp-common.h
--- orig/egcc-SVN20070505/gcc/cp/cp-objcp-common.h	2007-04-08 20:02:34.000000000 -0400
+++ egcc-SVN20070505/gcc/cp/cp-objcp-common.h	2007-05-07 14:15:40.981225485 -0400
@@ -89,6 +89,8 @@ extern tree objcp_tsubst_copy_and_build
 #define LANG_HOOKS_COMDAT_GROUP cxx_comdat_group
 #undef  LANG_HOOKS_BUILTIN_FUNCTION
 #define LANG_HOOKS_BUILTIN_FUNCTION cxx_builtin_function
+#undef LANG_HOOKS_GET_SIGNGAM
+#define LANG_HOOKS_GET_SIGNGAM c_get_signgam

 #undef LANG_HOOKS_FUNCTION_INIT
 #define LANG_HOOKS_FUNCTION_INIT cxx_push_function_context
diff -rup orig/egcc-SVN20070505/gcc/langhooks-def.h egcc-SVN20070505/gcc/langhooks-def.h
--- orig/egcc-SVN20070505/gcc/langhooks-def.h	2007-04-17 20:03:11.000000000 -0400
+++ egcc-SVN20070505/gcc/langhooks-def.h	2007-05-07 12:41:52.207493503 -0400
@@ -124,6 +124,7 @@ extern void lhd_omp_firstprivatize_type_
 #define LANG_HOOKS_TREE_SIZE		lhd_tree_size
 #define LANG_HOOKS_TYPES_COMPATIBLE_P	lhd_types_compatible_p
 #define LANG_HOOKS_BUILTIN_FUNCTION	lhd_builtin_function
+#define LANG_HOOKS_GET_SIGNGAM		lhd_return_null_tree_v
 #define LANG_HOOKS_SIGNED_OR_UNSIGNED_TYPE	lhd_signed_or_unsigned_type
 #define LANG_HOOKS_EXPR_TO_DECL		lhd_expr_to_decl
 #define LANG_HOOKS_TO_TARGET_CHARSET	lhd_to_target_charset
@@ -309,6 +310,7 @@ extern tree lhd_make_node (enum tree_cod
   LANG_HOOKS_GIMPLIFY_EXPR, \
   LANG_HOOKS_FOLD_OBJ_TYPE_REF, \
   LANG_HOOKS_BUILTIN_FUNCTION, \
+  LANG_HOOKS_GET_SIGNGAM, \
   LANG_HOOKS_INIT_TS,          \
   LANG_HOOKS_EXPR_TO_DECL, \
 }
diff -rup orig/egcc-SVN20070505/gcc/langhooks.h egcc-SVN20070505/gcc/langhooks.h
--- orig/egcc-SVN20070505/gcc/langhooks.h	2007-04-17 20:03:10.000000000 -0400
+++ egcc-SVN20070505/gcc/langhooks.h	2007-05-07 12:38:17.371949261 -0400
@@ -430,6 +430,11 @@ struct lang_hooks
   /* Do language specific processing in the builtin function DECL  */
   tree (*builtin_function) (tree decl);

+  /* Get the global identifier "signgam" for use with the C99 lgamma
+     builtin.  The default implementation returns NULL_TREE.  */
+
+  tree (*get_signgam) (void);
+
   /* Used to set up the tree_contains_structure array for a frontend. */
   void (*init_ts) (void);

diff -rup orig/egcc-SVN20070505/gcc/testsuite/gcc.dg/torture/builtin-math-2.c egcc-SVN20070505/gcc/testsuite/gcc.dg/torture/builtin-math-2.c
--- orig/egcc-SVN20070505/gcc/testsuite/gcc.dg/torture/builtin-math-2.c	2007-05-07 12:21:52.978515504 -0400
+++ egcc-SVN20070505/gcc/testsuite/gcc.dg/torture/builtin-math-2.c	2007-05-08 02:33:18.699686912 -0400
@@ -288,6 +288,29 @@ void bar()
   TESTIT_REENT (gamma, -1.0); /* gamma_r */
   TESTIT_REENT (gamma, -0.0); /* gamma_r */
   TESTIT_REENT (gamma, 0.0); /* gamma_r */
+
+  /* These should not tranform if signgam has not been declared.  */
+  TESTIT (lgamma, 1.0);
+  TESTIT (lgamma, 2.0);
+  TESTIT (gamma, 1.0);
+  TESTIT (gamma, 2.0);
+
+  /* These should not transform even if signgam has been declared.  */
+  extern int signgam;
+
+  TESTIT (lgamma, -4.0);
+  TESTIT (lgamma, -3.0);
+  TESTIT (lgamma, -2.0);
+  TESTIT (lgamma, -1.0);
+  TESTIT (lgamma, -0.0);
+  TESTIT (lgamma, 0.0);
+
+  TESTIT (gamma, -4.0);
+  TESTIT (gamma, -3.0);
+  TESTIT (gamma, -2.0);
+  TESTIT (gamma, -1.0);
+  TESTIT (gamma, -0.0);
+  TESTIT (gamma, 0.0);
 }

 /* { dg-final { scan-tree-dump-times "exp2 " 9 "original" } } */
@@ -368,4 +391,10 @@ void bar()
 /* { dg-final { scan-tree-dump-times "_gamma_r " 6 "original" } } */
 /* { dg-final { scan-tree-dump-times "_gammaf_r" 6 "original" } } */
 /* { dg-final { scan-tree-dump-times "_gammal_r" 6 "original" } } */
+/* { dg-final { scan-tree-dump-times "lgamma " 8 "original" } } */
+/* { dg-final { scan-tree-dump-times "lgammaf " 8 "original" } } */
+/* { dg-final { scan-tree-dump-times "lgammal " 8 "original" } } */
+/* { dg-final { scan-tree-dump-times "_gamma " 8 "original" } } */
+/* { dg-final { scan-tree-dump-times "_gammaf " 8 "original" } } */
+/* { dg-final { scan-tree-dump-times "_gammal " 8 "original" } } */
 /* { dg-final { cleanup-tree-dump "original" } } */
diff -rup orig/egcc-SVN20070505/gcc/testsuite/gcc.dg/torture/builtin-math-4.c egcc-SVN20070505/gcc/testsuite/gcc.dg/torture/builtin-math-4.c
--- orig/egcc-SVN20070505/gcc/testsuite/gcc.dg/torture/builtin-math-4.c	2007-05-07 12:21:52.979726642 -0400
+++ egcc-SVN20070505/gcc/testsuite/gcc.dg/torture/builtin-math-4.c	2007-05-08 02:34:58.114789836 -0400
@@ -139,6 +139,45 @@ extern void link_error(int);
     link_error(__LINE__); \
   } while (0)

+/* This replaces the signgam found in libm.  */
+int signgam;
+
+/* Test that FUNC(ARG) == (RES) && signgam == RES_SG.  */
+#define TESTIT_LGAMMA(FUNC,ARG,RES,RES_SG) do { \
+  signgam = 123; \
+  if (__builtin_##FUNC##f(ARG##F) != RES##F \
+      || signgam != RES_SG \
+      || CKSGN_F(__builtin_##FUNC##f(ARG##F),RES##F)) \
+    link_error(__LINE__); \
+  signgam = 123; \
+  if (__builtin_##FUNC(ARG) != RES \
+      || signgam != RES_SG \
+      || CKSGN(__builtin_##FUNC(ARG),RES)) \
+    link_error(__LINE__); \
+  signgam = 123; \
+  if (__builtin_##FUNC##l(ARG##L) != RES##L \
+      || signgam != RES_SG \
+      || CKSGN_L(__builtin_##FUNC##l(ARG##L),RES##L)) \
+    link_error(__LINE__); \
+  } while (0)
+
+/* Range test, check that (LOW) < FUNC(ARG) < (HI), and also test
+   that signgam == RES_SG.  */
+#define TESTIT_LGAMMA_R(FUNC,ARG,LOW,HI,RES_SG) do { \
+  signgam = 123; \
+  if (__builtin_##FUNC##f(ARG) <= (LOW) || __builtin_##FUNC##f(ARG) >= (HI) \
+      || signgam != RES_SG) \
+    link_error(__LINE__); \
+  signgam = 123; \
+  if (__builtin_##FUNC(ARG) <= (LOW) || __builtin_##FUNC(ARG) >= (HI) \
+      || signgam != RES_SG) \
+    link_error(__LINE__); \
+  signgam = 123; \
+  if (__builtin_##FUNC##l(ARG) <= (LOW) || __builtin_##FUNC##l(ARG) >= (HI) \
+      || signgam != RES_SG) \
+    link_error(__LINE__); \
+  } while (0)
+
 int main (void)
 {
 #ifdef __OPTIMIZE__
@@ -305,6 +344,26 @@ int main (void)
   TESTIT_LGAMMA_REENT_R (gamma, 1.5, -0.13, -0.12, 1); /* gamma_r(1.5) == -0.120... */
   TESTIT_LGAMMA_REENT (gamma, 2.0, 0.0, 1); /* gamma_r(2) == 0 */
   TESTIT_LGAMMA_REENT_R (gamma, 2.5, 0.28, 0.29, 1); /* gamma_r(2.5) == 0.284... */
+
+  /* These tests rely on propagating the signgam variable.  This
+     happens only when optimization is turned on.  */
+  TESTIT_LGAMMA_R (lgamma, -2.5, -0.06, -0.05, -1); /* lgamma(-2.5) == -0.056... */
+  TESTIT_LGAMMA_R (lgamma, -1.5, 0.86, 0.87, 1); /* lgamma(-1.5) == 0.860... */
+  TESTIT_LGAMMA_R (lgamma, -0.5, 1.26, 1.27, -1); /* lgamma(-0.5) == 1.265... */
+  TESTIT_LGAMMA_R (lgamma, 0.5, 0.57, 0.58, 1); /* lgamma(0.5) == 0.572... */
+  TESTIT_LGAMMA (lgamma, 1.0, 0.0, 1); /* lgamma(1) == 0 */
+  TESTIT_LGAMMA_R (lgamma, 1.5, -0.13, -0.12, 1); /* lgamma(1.5) == -0.120... */
+  TESTIT_LGAMMA (lgamma, 2.0, 0.0, 1); /* lgamma(2) == 0 */
+  TESTIT_LGAMMA_R (lgamma, 2.5, 0.28, 0.29, 1); /* lgamma(2.5) == 0.284... */
+
+  TESTIT_LGAMMA_R (gamma, -2.5, -0.06, -0.05, -1); /* gamma(-2.5) == -0.056... */
+  TESTIT_LGAMMA_R (gamma, -1.5, 0.86, 0.87, 1); /* gamma(-1.5) == 0.860... */
+  TESTIT_LGAMMA_R (gamma, -0.5, 1.26, 1.27, -1); /* gamma(-0.5) == 1.265... */
+  TESTIT_LGAMMA_R (gamma, 0.5, 0.57, 0.58, 1); /* gamma(0.5) == 0.572... */
+  TESTIT_LGAMMA (gamma, 1.0, 0.0, 1); /* gamma(1) == 0 */
+  TESTIT_LGAMMA_R (gamma, 1.5, -0.13, -0.12, 1); /* gamma(1.5) == -0.120... */
+  TESTIT_LGAMMA (gamma, 2.0, 0.0, 1); /* gamma(2) == 0 */
+  TESTIT_LGAMMA_R (gamma, 2.5, 0.28, 0.29, 1); /* gamma(2.5) == 0.284... */
 #endif

   return 0;


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