+
+#if MPFR_VERSION >= MPFR_VERSION_NUM(2,3,0)
+/* If argument ARG1 is an INTEGER_CST and ARG2 is a REAL_CST, call the
+ two-argument mpfr order N Bessel function FUNC on them and return
+ the resulting value as a tree with type TYPE. The mpfr precision
+ is set to the precision of TYPE. We assume that function FUNC
+ returns zero if the result could be calculated exactly within the
+ requested precision. */
+static tree
+do_mpfr_bessel_n (tree arg1, tree arg2, tree type,
+ int (*func)(mpfr_ptr, long, mpfr_srcptr, mp_rnd_t),
+ const REAL_VALUE_TYPE *min, bool inclusive)
+{
+ tree result = NULL_TREE;
+
+ STRIP_NOPS (arg1);
+ STRIP_NOPS (arg2);
+
+ /* 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
+ && host_integerp (arg1, 0)
+ && TREE_CODE (arg2) == REAL_CST && !TREE_OVERFLOW (arg2))
+ {
+ const HOST_WIDE_INT n = tree_low_cst(arg1, 0);
+ const REAL_VALUE_TYPE *const ra = &TREE_REAL_CST (arg2);
+
+ if (n == (long)n
+ && real_isfinite (ra)
+ && (!min || real_compare (inclusive ? GE_EXPR: GT_EXPR , ra, min)))
+ {
+ const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ int inexact;
+ mpfr_t m;
+
+ mpfr_init2 (m, prec);
+ mpfr_from_real (m, ra, GMP_RNDN);
+ mpfr_clear_flags ();
+ inexact = func (m, n, m, GMP_RNDN);
+ result = do_mpfr_ckconv (m, type, inexact);
+ mpfr_clear (m);
+ }
+ }
+
+ 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_isfinite (ra0) && real_isfinite (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;
+}
+
+/* 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_isfinite (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