From 752b7d38db4314d146297c6736ec9ab65068ffa8 Mon Sep 17 00:00:00 2001 From: "Kaveh R. Ghazi" Date: Sat, 19 May 2007 04:18:05 +0000 Subject: [PATCH] re PR middle-end/30250 (Evaluate lgamma/gamma at compile-time) 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 | 7 +++ gcc/builtins.c | 71 +++++++++++++++++++++++++++ gcc/testsuite/ChangeLog | 5 ++ gcc/testsuite/gcc.dg/torture/builtin-math-2.c | 28 +++++++++++ gcc/testsuite/gcc.dg/torture/builtin-math-4.c | 60 +++++++++++++++++++++- gcc/tree.h | 1 + 6 files changed, 171 insertions(+), 1 deletion(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d558fac..78ea258 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2007-05-19 Kaveh R. Ghazi + + 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 * dwarf2out.c (print_die): Use '%ld' not '%lu' to print a 'long'. diff --git a/gcc/builtins.c b/gcc/builtins.c index de62d15..9a987ea 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7ec3848..4cf062c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2007-05-19 Kaveh R. Ghazi + + * 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 PR libfortran/31964 diff --git a/gcc/testsuite/gcc.dg/torture/builtin-math-2.c b/gcc/testsuite/gcc.dg/torture/builtin-math-2.c index 81608b8..214a3ef 100644 --- a/gcc/testsuite/gcc.dg/torture/builtin-math-2.c +++ b/gcc/testsuite/gcc.dg/torture/builtin-math-2.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/builtin-math-4.c b/gcc/testsuite/gcc.dg/torture/builtin-math-4.c index e91f14c..99307dd 100644 --- a/gcc/testsuite/gcc.dg/torture/builtin-math-4.c +++ b/gcc/testsuite/gcc.dg/torture/builtin-math-4.c @@ -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; } diff --git a/gcc/tree.h b/gcc/tree.h index 0c65e3b..0d31884 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -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. */ -- 2.7.4