+
+/* Initialize format string characters in the target charset. */
+
+static bool
+init_target_chars (void)
+{
+ static bool init;
+ if (!init)
+ {
+ target_newline = lang_hooks.to_target_charset ('\n');
+ target_percent = lang_hooks.to_target_charset ('%');
+ target_c = lang_hooks.to_target_charset ('c');
+ target_s = lang_hooks.to_target_charset ('s');
+ if (target_newline == 0 || target_percent == 0 || target_c == 0
+ || target_s == 0)
+ return false;
+
+ target_percent_c[0] = target_percent;
+ target_percent_c[1] = target_c;
+ target_percent_c[2] = '\0';
+
+ target_percent_s[0] = target_percent;
+ target_percent_s[1] = target_s;
+ target_percent_s[2] = '\0';
+
+ target_percent_s_newline[0] = target_percent;
+ target_percent_s_newline[1] = target_s;
+ target_percent_s_newline[2] = target_newline;
+ target_percent_s_newline[3] = '\0';
+
+ init = true;
+ }
+ return true;
+}
+
+/* Helper function for do_mpfr_arg*(). Ensure M is a normal number
+ and no overflow/underflow occurred. INEXACT is true if M was not
+ exacly calculated. TYPE is the tree type for the result. This
+ function assumes that you cleared the MPFR flags and then
+ calculated M to see if anything subsequently set a flag prior to
+ entering this function. Return NULL_TREE if any checks fail. */
+
+static tree
+do_mpfr_ckconv(mpfr_srcptr m, tree type, int inexact)
+{
+ /* Proceed iff we get a normal number, i.e. not NaN or Inf and no
+ overflow/underflow occurred. If -frounding-math, proceed iff the
+ result of calling FUNC was exact. */
+ if (mpfr_number_p (m) && !mpfr_overflow_p() && !mpfr_underflow_p()
+ && (!flag_rounding_math || !inexact))
+ {
+ REAL_VALUE_TYPE rr;
+
+ real_from_mpfr (&rr, m);
+ /* Proceed iff GCC's REAL_VALUE_TYPE can hold the MPFR value,
+ check for overflow/underflow. If the REAL_VALUE_TYPE is zero
+ but the mpft_t is not, then we underflowed in the
+ conversion. */
+ if (!real_isnan (&rr) && !real_isinf (&rr)
+ && (rr.cl == rvc_zero) == (mpfr_zero_p (m) != 0))
+ {
+ REAL_VALUE_TYPE rmode;
+
+ real_convert (&rmode, TYPE_MODE (type), &rr);
+ /* Proceed iff the specified mode can hold the value. */
+ if (real_identical (&rmode, &rr))
+ return build_real (type, rmode);
+ }
+ }
+ return NULL_TREE;
+}
+
+/* If argument ARG is a REAL_CST, call the one-argument mpfr function
+ FUNC on it and return the resulting value as a tree with type TYPE.
+ If MIN and/or MAX are not NULL, then the supplied ARG must be
+ within those bounds. If INCLUSIVE is true, then MIN/MAX are
+ acceptable values, otherwise they are not. 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_arg1 (tree arg, tree type, int (*func)(mpfr_ptr, mpfr_srcptr, mp_rnd_t),
+ const REAL_VALUE_TYPE *min, const REAL_VALUE_TYPE *max,
+ bool inclusive)
+{
+ tree result = NULL_TREE;
+
+ STRIP_NOPS (arg);
+
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ const REAL_VALUE_TYPE *const ra = &TREE_REAL_CST (arg);
+
+ if (!real_isnan (ra) && !real_isinf (ra)
+ && (!min || real_compare (inclusive ? GE_EXPR: GT_EXPR , ra, min))
+ && (!max || real_compare (inclusive ? LE_EXPR: LT_EXPR , ra, max)))
+ {
+ const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ int inexact;
+ mpfr_t m;
+
+ mpfr_init2 (m, prec);
+ mpfr_from_real (m, ra);
+ mpfr_clear_flags();
+ inexact = func (m, m, GMP_RNDN);
+ result = do_mpfr_ckconv (m, type, inexact);
+ mpfr_clear (m);
+ }
+ }
+
+ return result;
+}
+
+/* If argument ARG is a REAL_CST, call the two-argument mpfr function
+ FUNC 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
+ function FUNC returns zero if the result could be calculated
+ exactly within the requested precision. */
+
+static tree
+do_mpfr_arg2 (tree arg1, tree arg2, tree type,
+ int (*func)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t))
+{
+ tree result = NULL_TREE;
+
+ STRIP_NOPS (arg1);
+ STRIP_NOPS (arg2);
+
+ if (TREE_CODE (arg1) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg1)
+ && TREE_CODE (arg2) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg2))
+ {
+ const REAL_VALUE_TYPE *const ra1 = &TREE_REAL_CST (arg1);
+ const REAL_VALUE_TYPE *const ra2 = &TREE_REAL_CST (arg2);
+
+ if (!real_isnan (ra1) && !real_isinf (ra1)
+ && !real_isnan (ra2) && !real_isinf (ra2))
+ {
+ const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ int inexact;
+ mpfr_t m1, m2;
+
+ mpfr_inits2 (prec, m1, m2, NULL);
+ mpfr_from_real (m1, ra1);
+ mpfr_from_real (m2, ra2);
+ mpfr_clear_flags();
+ inexact = func (m1, m1, m2, GMP_RNDN);
+ result = do_mpfr_ckconv (m1, type, inexact);
+ mpfr_clears (m1, m2, NULL);
+ }
+ }
+
+ return result;
+}
+
+/* If argument ARG is a REAL_CST, call the three-argument mpfr function
+ FUNC 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
+ function FUNC returns zero if the result could be calculated
+ exactly within the requested precision. */
+
+static tree
+do_mpfr_arg3 (tree arg1, tree arg2, tree arg3, tree type,
+ int (*func)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t))
+{
+ tree result = NULL_TREE;
+
+ STRIP_NOPS (arg1);
+ STRIP_NOPS (arg2);
+ STRIP_NOPS (arg3);
+
+ if (TREE_CODE (arg1) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg1)
+ && TREE_CODE (arg2) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg2)
+ && TREE_CODE (arg3) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg3))
+ {
+ const REAL_VALUE_TYPE *const ra1 = &TREE_REAL_CST (arg1);
+ const REAL_VALUE_TYPE *const ra2 = &TREE_REAL_CST (arg2);
+ const REAL_VALUE_TYPE *const ra3 = &TREE_REAL_CST (arg3);
+
+ if (!real_isnan (ra1) && !real_isinf (ra1)
+ && !real_isnan (ra2) && !real_isinf (ra2)
+ && !real_isnan (ra3) && !real_isinf (ra3))
+ {
+ const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ int inexact;
+ mpfr_t m1, m2, m3;
+
+ mpfr_inits2 (prec, m1, m2, m3, NULL);
+ mpfr_from_real (m1, ra1);
+ mpfr_from_real (m2, ra2);
+ mpfr_from_real (m3, ra3);
+ mpfr_clear_flags();
+ inexact = func (m1, m1, m2, m3, GMP_RNDN);
+ result = do_mpfr_ckconv (m1, type, inexact);
+ mpfr_clears (m1, m2, m3, NULL);
+ }
+ }
+
+ return result;
+}
+
+/* If argument ARG is a REAL_CST, call mpfr_sin_cos() on it and set
+ the pointers *(ARG_SINP) and *(ARG_COSP) to the resulting values.
+ The type is taken from the type of ARG and is used for setting the
+ precision of the calculation and results. */
+
+static tree
+do_mpfr_sincos (tree arg, tree arg_sinp, tree arg_cosp)
+{
+ tree result = NULL_TREE;
+
+ STRIP_NOPS (arg);
+
+ if (TREE_CODE (arg) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg))
+ {
+ const REAL_VALUE_TYPE *const ra = &TREE_REAL_CST (arg);
+
+ if (!real_isnan (ra) && !real_isinf (ra))
+ {
+ tree const type = TREE_TYPE (arg);
+ const int prec = REAL_MODE_FORMAT (TYPE_MODE (type))->p;
+ tree result_s, result_c;
+ int inexact;
+ mpfr_t m, ms, mc;
+
+ mpfr_inits2 (prec, m, ms, mc, NULL);
+ mpfr_from_real (m, ra);
+ mpfr_clear_flags();
+ inexact = mpfr_sin_cos (ms, mc, m, GMP_RNDN);
+ result_s = do_mpfr_ckconv (ms, type, inexact);
+ result_c = do_mpfr_ckconv (mc, type, inexact);
+ mpfr_clears (m, ms, mc, NULL);
+ if (result_s && result_c)
+ {
+ /* Dereference the sin/cos pointer arguments. */
+ arg_sinp = build_fold_indirect_ref (arg_sinp);
+ arg_cosp = build_fold_indirect_ref (arg_cosp);
+ /* Proceed if valid pointer type were passed in. */
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (arg_sinp)) == TYPE_MAIN_VARIANT (type)
+ && TYPE_MAIN_VARIANT (TREE_TYPE (arg_cosp)) == TYPE_MAIN_VARIANT (type))
+ {
+ /* Set the values. */
+ result_s = fold_build2 (MODIFY_EXPR, type, arg_sinp, result_s);
+ TREE_SIDE_EFFECTS (result_s) = 1;
+ result_c = fold_build2 (MODIFY_EXPR, type, arg_cosp, result_c);
+ TREE_SIDE_EFFECTS (result_c) = 1;
+ /* Combine the assignments into a compound expr. */
+ result = non_lvalue (fold_build2 (COMPOUND_EXPR, type,
+ result_s, result_c));
+ }
+ }
+ }
+ }
+ return result;
+}