re PR middle-end/30250 (Evaluate lgamma/gamma at compile-time)
authorKaveh R. Ghazi <ghazi@caip.rutgers.edu>
Sat, 19 May 2007 04:18:05 +0000 (04:18 +0000)
committerKaveh Ghazi <ghazi@gcc.gnu.org>
Sat, 19 May 2007 04:18:05 +0000 (04:18 +0000)
PR middle-end/30250
* builtins.c (do_mpfr_lgamma_r): New.
(fold_builtin_2): Handle builtin gamma_r/lgamma_r.
* tree.h (CASE_FLT_FN_REENT): New.

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

From-SVN: r124849

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
gcc/tree.h

index d558fac..78ea258 100644 (file)
@@ -1,3 +1,10 @@
+2007-05-19  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
+
+       PR middle-end/30250
+       * builtins.c (do_mpfr_lgamma_r): New.
+       (fold_builtin_2): Handle builtin gamma_r/lgamma_r.
+       * tree.h (CASE_FLT_FN_REENT): New.
+
 2007-05-18  Geoffrey Keating  <geoffk@apple.com>
 
        * dwarf2out.c (print_die): Use '%ld' not '%lu' to print a 'long'.
index de62d15..9a987ea 100644 (file)
@@ -236,6 +236,7 @@ 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);
+static tree do_mpfr_lgamma_r (tree, tree, tree);
 #endif
 
 /* Return true if NODE should be considered for inline expansion regardless
@@ -9935,6 +9936,13 @@ fold_builtin_2 (tree fndecl, tree arg0, tree arg1, bool ignore)
           && validate_arg(arg1, REAL_TYPE))
         return do_mpfr_arg2 (arg0, arg1, type, mpfr_remainder);
     break;
+
+    CASE_FLT_FN_REENT (BUILT_IN_GAMMA): /* GAMMA_R */
+    CASE_FLT_FN_REENT (BUILT_IN_LGAMMA): /* LGAMMA_R */
+      if (validate_arg (arg0, REAL_TYPE)
+         && validate_arg(arg1, POINTER_TYPE))
+       return do_mpfr_lgamma_r (arg0, arg1, type);
+    break;
 #endif
 
     CASE_FLT_FN (BUILT_IN_ATAN2):
@@ -12692,4 +12700,67 @@ do_mpfr_remquo (tree arg0, tree arg1, tree arg_quo)
     }
   return result;
 }
+
+/* If ARG is a REAL_CST, call mpfr_lgamma() on it and return the
+   resulting value as a tree with type TYPE.  The mpfr precision is
+   set to the precision of TYPE.  We assume that this mpfr function
+   returns zero if the result could be calculated exactly within the
+   requested precision.  In addition, the integer pointer represented
+   by ARG_SG will be dereferenced and set to the appropriate signgam
+   (-1,1) value.  */
+
+static tree
+do_mpfr_lgamma_r (tree arg, tree arg_sg, tree type)
+{
+  tree result = NULL_TREE;
+
+  STRIP_NOPS (arg);
+  
+  /* To proceed, MPFR must exactly represent the target floating point
+     format, which only happens when the target base equals two.  Also
+     verify ARG is a constant and that ARG_SG is an int pointer.  */
+  if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2
+      && TREE_CODE (arg) == REAL_CST && !TREE_OVERFLOW (arg)
+      && TREE_CODE (TREE_TYPE (arg_sg)) == POINTER_TYPE
+      && TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (arg_sg))) == integer_type_node)
+    {
+      const REAL_VALUE_TYPE *const ra = TREE_REAL_CST_PTR (arg);
+
+      /* In addition to NaN and Inf, the argument cannot be zero or a
+        negative integer.  */
+      if (!real_isnan (ra) && !real_isinf (ra)
+         && ra->cl != rvc_zero
+         && !(real_isneg(ra) && real_isinteger(ra, TYPE_MODE (type))))
+        {
+         const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+         int inexact, sg;
+         mpfr_t m;
+         tree result_lg;
+
+         mpfr_init2 (m, prec);
+         mpfr_from_real (m, ra, GMP_RNDN);
+         mpfr_clear_flags ();
+         inexact = mpfr_lgamma (m, &sg, m, GMP_RNDN);
+         result_lg = do_mpfr_ckconv (m, type, inexact);
+         mpfr_clear (m);
+         if (result_lg)
+           {
+             tree result_sg;
+
+             /* Dereference the arg_sg pointer argument.  */
+             arg_sg = build_fold_indirect_ref (arg_sg);
+             /* Assign the signgam value into *arg_sg. */
+             result_sg = fold_build2 (MODIFY_EXPR,
+                                      TREE_TYPE (arg_sg), arg_sg,
+                                      build_int_cst (NULL, sg));
+             TREE_SIDE_EFFECTS (result_sg) = 1;
+             /* Combine the signgam assignment with the lgamma result.  */
+             result = non_lvalue (fold_build2 (COMPOUND_EXPR, type,
+                                               result_sg, result_lg));
+           }
+       }
+    }
+
+  return result;
+}
 #endif
index 7ec3848..4cf062c 100644 (file)
@@ -1,3 +1,8 @@
+2007-05-19  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>
+
+       * gcc.dg/torture/builtin-math-2.c: Add gamma_r/lgamma_r tests.
+       * gcc.dg/torture/builtin-math-4.c: Likewise.
+
 2007-05-18  Jerry DeLisle  <jvdelisle@gcc.gnu.org>
 
        PR libfortran/31964
index 81608b8..214a3ef 100644 (file)
@@ -49,6 +49,13 @@ extern void fool (long double);
   fool (__builtin_remquol (ARG1##L, ARG2##L, &quo)); \
 } while (0)
 
+#define TESTIT_REENT(FUNC,ARG1) do { \
+  int sg; \
+  foof (__builtin_##FUNC##f_r (ARG1##F, &sg)); \
+  foo (__builtin_##FUNC##_r (ARG1, &sg)); \
+  fool (__builtin_##FUNC##l_r (ARG1##L, &sg)); \
+} while (0)
+
 void bar()
 {
   /* An argument of NaN is not evaluated at compile-time.  */
@@ -266,6 +273,21 @@ void bar()
   TESTIT2 (remainder, 1.0, -0.0);
   TESTIT2 (drem, 1.0, 0.0);
   TESTIT2 (drem, 1.0, -0.0);
+
+  /* The argument to lgamma* cannot be zero or a negative integer.  */
+  TESTIT_REENT (lgamma, -4.0); /* lgamma_r */
+  TESTIT_REENT (lgamma, -3.0); /* lgamma_r */
+  TESTIT_REENT (lgamma, -2.0); /* lgamma_r */
+  TESTIT_REENT (lgamma, -1.0); /* lgamma_r */
+  TESTIT_REENT (lgamma, -0.0); /* lgamma_r */
+  TESTIT_REENT (lgamma, 0.0); /* lgamma_r */
+  
+  TESTIT_REENT (gamma, -4.0); /* gamma_r */
+  TESTIT_REENT (gamma, -3.0); /* gamma_r */
+  TESTIT_REENT (gamma, -2.0); /* gamma_r */
+  TESTIT_REENT (gamma, -1.0); /* gamma_r */
+  TESTIT_REENT (gamma, -0.0); /* gamma_r */
+  TESTIT_REENT (gamma, 0.0); /* gamma_r */
 }
 
 /* { dg-final { scan-tree-dump-times "exp2 " 9 "original" } } */
@@ -340,4 +362,10 @@ void bar()
 /* { 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 { scan-tree-dump-times "lgamma_r " 6 "original" } } */
+/* { dg-final { scan-tree-dump-times "lgammaf_r" 6 "original" } } */
+/* { dg-final { scan-tree-dump-times "lgammal_r" 6 "original" } } */
+/* { 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 { cleanup-tree-dump "original" } } */
index e91f14c..99307dd 100644 (file)
@@ -103,6 +103,44 @@ extern void link_error(int);
     link_error(__LINE__); \
   } while (0)
 
+/* Test that FUNC(ARG,&SG) == (RES) && SG == RES_SG.  */
+#define TESTIT_LGAMMA_REENT(FUNC,ARG,RES,RES_SG) do { \
+  int sg; \
+  sg = 123; \
+  if (__builtin_##FUNC##f_r(ARG##F,&sg) != RES##F \
+      || sg != RES_SG \
+      || CKSGN_F(__builtin_##FUNC##f_r(ARG##F,&sg),RES##F)) \
+    link_error(__LINE__); \
+  sg = 123; \
+  if (__builtin_##FUNC##_r(ARG,&sg) != RES \
+      || sg != RES_SG \
+      || CKSGN(__builtin_##FUNC##_r(ARG,&sg),RES)) \
+    link_error(__LINE__); \
+  sg = 123; \
+  if (__builtin_##FUNC##l_r(ARG##L,&sg) != RES##L \
+      || sg != RES_SG \
+      || CKSGN_L(__builtin_##FUNC##l_r(ARG##L,&sg),RES##L)) \
+    link_error(__LINE__); \
+  } while (0)
+
+/* Range test, check that (LOW) < FUNC(ARG,&SG) < (HI), and also test
+   that SG == RES_SG.  */
+#define TESTIT_LGAMMA_REENT_R(FUNC,ARG,LOW,HI,RES_SG) do { \
+  int sg; \
+  sg = 123; \
+  if (__builtin_##FUNC##f_r(ARG,&sg) <= (LOW) || __builtin_##FUNC##f_r(ARG,&sg) >= (HI) \
+      || sg != RES_SG) \
+    link_error(__LINE__); \
+  sg = 123; \
+  if (__builtin_##FUNC##_r(ARG,&sg) <= (LOW) || __builtin_##FUNC##_r(ARG,&sg) >= (HI) \
+      || sg != RES_SG) \
+    link_error(__LINE__); \
+  sg = 123; \
+  if (__builtin_##FUNC##l_r(ARG,&sg) <= (LOW) || __builtin_##FUNC##l_r(ARG,&sg) >= (HI) \
+      || sg != RES_SG) \
+    link_error(__LINE__); \
+  } while (0)
+
 int main (void)
 {
 #ifdef __OPTIMIZE__
@@ -249,7 +287,27 @@ int main (void)
     MAXIT(remquol, -(__INT_MAX__+1.0L), 0);
     MAXIT(remquol, -(__INT_MAX__+2.0L), -1);
   }
-#endif
 
+  /* These tests rely on propagating the variable sg which contains
+     signgam.  This happens only when optimization is turned on.  */
+  TESTIT_LGAMMA_REENT_R (lgamma, -2.5, -0.06, -0.05, -1); /* lgamma_r(-2.5) == -0.056... */
+  TESTIT_LGAMMA_REENT_R (lgamma, -1.5, 0.86, 0.87, 1); /* lgamma_r(-1.5) == 0.860... */
+  TESTIT_LGAMMA_REENT_R (lgamma, -0.5, 1.26, 1.27, -1); /* lgamma_r(-0.5) == 1.265... */
+  TESTIT_LGAMMA_REENT_R (lgamma, 0.5, 0.57, 0.58, 1); /* lgamma_r(0.5) == 0.572... */
+  TESTIT_LGAMMA_REENT (lgamma, 1.0, 0.0, 1); /* lgamma_r(1) == 0 */
+  TESTIT_LGAMMA_REENT_R (lgamma, 1.5, -0.13, -0.12, 1); /* lgamma_r(1.5) == -0.120... */
+  TESTIT_LGAMMA_REENT (lgamma, 2.0, 0.0, 1); /* lgamma_r(2) == 0 */
+  TESTIT_LGAMMA_REENT_R (lgamma, 2.5, 0.28, 0.29, 1); /* lgamma_r(2.5) == 0.284... */
+
+  TESTIT_LGAMMA_REENT_R (gamma, -2.5, -0.06, -0.05, -1); /* gamma_r(-2.5) == -0.056... */
+  TESTIT_LGAMMA_REENT_R (gamma, -1.5, 0.86, 0.87, 1); /* gamma_r(-1.5) == 0.860... */
+  TESTIT_LGAMMA_REENT_R (gamma, -0.5, 1.26, 1.27, -1); /* gamma_r(-0.5) == 1.265... */
+  TESTIT_LGAMMA_REENT_R (gamma, 0.5, 0.57, 0.58, 1); /* gamma_r(0.5) == 0.572... */
+  TESTIT_LGAMMA_REENT (gamma, 1.0, 0.0, 1); /* gamma_r(1) == 0 */
+  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... */
+#endif
+  
   return 0;
 }
index 0c65e3b..0d31884 100644 (file)
@@ -273,6 +273,7 @@ extern const char * built_in_names[(int) END_BUILTINS];
 #define BUILTIN_ROOT_P(FN) (BUILTIN_SQRT_P (FN) || BUILTIN_CBRT_P (FN))
 
 #define CASE_FLT_FN(FN) case FN: case FN##F: case FN##L
+#define CASE_FLT_FN_REENT(FN) case FN##_R: case FN##F_R: case FN##L_R
 #define CASE_INT_FN(FN) case FN: case FN##L: case FN##LL
 
 /* An array of _DECL trees for the above.  */