Move constant folds for maths functions to new file
authorRichard Sandiford <richard.sandiford@arm.com>
Mon, 2 Nov 2015 16:34:16 +0000 (16:34 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Mon, 2 Nov 2015 16:34:16 +0000 (16:34 +0000)
The new routines operate on the built-in enum rather than on tree decls.
The idea is to extend this to handle internal functions too, with a
combined enum for both.

The patch also moves fold_fma too, with the same prototype.  The long-term
plan is to replace FMA_EXPR with an internal function, for consistency
with the way that things like SQRT will be handled.

Tested on x86_64-linux-gnu, arm-linux-gnueabi and aarch64-linux-gnu.

gcc/
* builtins.h (fold_fma): Move to fold-const-call.h.
* builtins.c: Include fold-const-call.h.
(mathfn_built_in_2): New function, split out from...
(mathfn_built_in_1): ...here.
(do_real_to_int_conversion, fold_const_builtin_pow)
(fold_const_builtin_logb, fold_const_builtin_significand)
(fold_const_builtin_load_exponent, do_mpfr_arg1, do_mpfr_arg2)
(do_mpfr_arg3, do_mpfr_sincos, do_mpfr_bessel_n, do_mpc_arg1): Delete.
(fold_builtin_sincos): Use fold_const_call to handle constants.
(fold_builtin_1, fold_builtin_2, fold_builtin_3): Add explicit
checks for ERROR_MARK.  Use fold_const_call to handle constant
folds for math functions.
(fold_fma): Move to fold-const-call.c.
* fold-const.c: Include fold-const-call.h.
* Makefile.in (OBJS): Add fold-const-call.o.
(PLUGIN_HEADERS): Add fold-const-call.h.
* realmpfr.h (real_from_mpfr): Allow the format to be specified
directly.
* realmpfr.c (real_from_mpfr): Likewise.
* fold-const-call.h, fold-const-call.c: New files.

From-SVN: r229669

gcc/ChangeLog
gcc/Makefile.in
gcc/builtins.c
gcc/builtins.h
gcc/fold-const-call.c [new file with mode: 0644]
gcc/fold-const-call.h [new file with mode: 0644]
gcc/fold-const.c
gcc/realmpfr.c
gcc/realmpfr.h

index f999fd51431b5af1b7061bd0ca5562e19c6bac7c..f4569aaaacaccf32276c739dcb8f9f1a8c58014d 100644 (file)
@@ -1,3 +1,26 @@
+2015-11-02  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * builtins.h (fold_fma): Move to fold-const-call.h.
+       * builtins.c: Include fold-const-call.h.
+       (mathfn_built_in_2): New function, split out from...
+       (mathfn_built_in_1): ...here.
+       (do_real_to_int_conversion, fold_const_builtin_pow)
+       (fold_const_builtin_logb, fold_const_builtin_significand)
+       (fold_const_builtin_load_exponent, do_mpfr_arg1, do_mpfr_arg2)
+       (do_mpfr_arg3, do_mpfr_sincos, do_mpfr_bessel_n, do_mpc_arg1): Delete.
+       (fold_builtin_sincos): Use fold_const_call to handle constants.
+       (fold_builtin_1, fold_builtin_2, fold_builtin_3): Add explicit
+       checks for ERROR_MARK.  Use fold_const_call to handle constant
+       folds for math functions.
+       (fold_fma): Move to fold-const-call.c.
+       * fold-const.c: Include fold-const-call.h.
+       * Makefile.in (OBJS): Add fold-const-call.o.
+       (PLUGIN_HEADERS): Add fold-const-call.h.
+       * realmpfr.h (real_from_mpfr): Allow the format to be specified
+       directly.
+       * realmpfr.c (real_from_mpfr): Likewise.
+       * fold-const-call.h, fold-const-call.c: New files.
+
 2015-11-02  Julian Brown  <julian@codesourcery.com>
 
        * config/arm/neon-testgen.ml (emit_epilogue): Remove extraneous
index 7d53a7dee2d03743a12780ad479f114822024739..34d23565f8914cfdd4f13804797c8cfa3aa672cb 100644 (file)
@@ -1260,6 +1260,7 @@ OBJS = \
        final.o \
        fixed-value.o \
        fold-const.o \
+       fold-const-call.o \
        function.o \
        fwprop.o \
        gcse.o \
@@ -3273,10 +3274,10 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
   prefix.h tree-inline.h $(GIMPLE_PRETTY_PRINT_H) realmpfr.h \
   $(IPA_PROP_H) $(TARGET_H) $(RTL_H) $(TM_P_H) $(CFGLOOP_H) $(EMIT_RTL_H) \
   version.h stringpool.h gimplify.h gimple-iterator.h gimple-ssa.h \
-  fold-const.h tree-cfg.h tree-into-ssa.h tree-ssanames.h print-tree.h \
-  varasm.h context.h tree-phinodes.h stor-layout.h ssa-iterators.h \
-  $(RESOURCE_H) tree-cfgcleanup.h attribs.h calls.h cfgexpand.h \
-  diagnostic-color.h gcc-symtab.h gimple-builder.h gimple-low.h \
+  fold-const.h fold-const-call.h tree-cfg.h tree-into-ssa.h tree-ssanames.h \
+  print-tree.h varasm.h context.h tree-phinodes.h stor-layout.h \
+  ssa-iterators.h $(RESOURCE_H) tree-cfgcleanup.h attribs.h calls.h \
+  cfgexpand.h diagnostic-color.h gcc-symtab.h gimple-builder.h gimple-low.h \
   gimple-walk.h gimplify-me.h pass_manager.h print-rtl.h stmt.h \
   tree-dfa.h tree-hasher.h tree-nested.h tree-object-size.h tree-outof-ssa.h \
   tree-parloops.h tree-ssa-address.h tree-ssa-coalesce.h tree-ssa-dom.h \
index bbb0e81a7dc9c12a2ec674e0c144af7c93ff30c9..8f0717c7e1b5498bc77d0f3bec9051205fb7dbbf 100644 (file)
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h"
 #include "alias.h"
 #include "fold-const.h"
+#include "fold-const-call.h"
 #include "stor-layout.h"
 #include "calls.h"
 #include "varasm.h"
@@ -63,8 +64,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl-chkp.h"
 
 
-static tree do_mpc_arg1 (tree, tree, int (*)(mpc_ptr, mpc_srcptr, mpc_rnd_t));
-
 struct target_builtins default_target_builtins;
 #if SWITCHABLE_TARGET
 struct target_builtins *this_target_builtins = &default_target_builtins;
@@ -189,16 +188,6 @@ static unsigned HOST_WIDE_INT target_s;
 char target_percent_c[3];
 char target_percent_s[3];
 char target_percent_s_newline[4];
-static tree do_mpfr_arg1 (tree, tree, int (*)(mpfr_ptr, mpfr_srcptr, mp_rnd_t),
-                         const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *, bool);
-static tree do_mpfr_arg2 (tree, tree, tree,
-                         int (*)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t));
-static tree do_mpfr_arg3 (tree, tree, tree, tree,
-                         int (*)(mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_srcptr, mp_rnd_t));
-static tree do_mpfr_sincos (tree, tree, tree);
-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);
 static void expand_builtin_sync_synchronize (void);
@@ -1795,15 +1784,16 @@ expand_builtin_classify_type (tree exp)
   fcode = BUILT_IN_MATHFN##_R; fcodef = BUILT_IN_MATHFN##F_R ; \
   fcodel = BUILT_IN_MATHFN##L_R ; break;
 
-/* Return mathematic function equivalent to FN but operating directly on TYPE,
-   if available.  If IMPLICIT is true use the implicit builtin declaration,
-   otherwise use the explicit declaration.  If we can't do the conversion,
-   return zero.  */
+/* Return a function equivalent to FN but operating on floating-point
+   values of type TYPE, or END_BUILTINS if no such function exists.
+   This is purely an operation on built-in function codes; it does not
+   guarantee that the target actually has an implementation of the
+   function.  */
 
-static tree
-mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit_p)
+static built_in_function
+mathfn_built_in_2 (tree type, built_in_function fn)
 {
-  enum built_in_function fcode, fcodef, fcodel, fcode2;
+  built_in_function fcode, fcodef, fcodel;
 
   switch (fn)
     {
@@ -1896,16 +1886,29 @@ mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit_p)
       CASE_MATHFN (BUILT_IN_YN)
 
       default:
-       return NULL_TREE;
+       return END_BUILTINS;
       }
 
   if (TYPE_MAIN_VARIANT (type) == double_type_node)
-    fcode2 = fcode;
+    return fcode;
   else if (TYPE_MAIN_VARIANT (type) == float_type_node)
-    fcode2 = fcodef;
+    return fcodef;
   else if (TYPE_MAIN_VARIANT (type) == long_double_type_node)
-    fcode2 = fcodel;
+    return fcodel;
   else
+    return END_BUILTINS;
+}
+
+/* Return mathematic function equivalent to FN but operating directly on TYPE,
+   if available.  If IMPLICIT_P is true use the implicit builtin declaration,
+   otherwise use the explicit declaration.  If we can't do the conversion,
+   return null.  */
+
+static tree
+mathfn_built_in_1 (tree type, enum built_in_function fn, bool implicit_p)
+{
+  built_in_function fcode2 = mathfn_built_in_2 (type, fn);
+  if (fcode2 == END_BUILTINS)
     return NULL_TREE;
 
   if (implicit_p && !builtin_decl_implicit_p (fcode2))
@@ -7262,35 +7265,6 @@ fold_builtin_strlen (location_t loc, tree type, tree arg)
     }
 }
 
-/* If ARG is a foldable constant real, use FN to round it to an integer
-   value and try to represent the result in integer type ITYPE.  Return
-   the value on success, otherwise return null.  */
-
-static tree
-do_real_to_int_conversion (tree itype, tree arg,
-                          void (*fn) (REAL_VALUE_TYPE *, format_helper,
-                                      const REAL_VALUE_TYPE *))
-{
-  if (TREE_CODE (arg) != REAL_CST || TREE_OVERFLOW (arg))
-    return NULL_TREE;
-
-  const REAL_VALUE_TYPE *value = TREE_REAL_CST_PTR (arg);
-  if (!real_isfinite (value))
-    return NULL_TREE;
-
-  tree ftype = TREE_TYPE (arg);
-  REAL_VALUE_TYPE rounded;
-  fn (&rounded, TYPE_MODE (ftype), value);
-
-  bool fail = false;
-  wide_int ival = real_to_integer (&rounded, &fail, TYPE_PRECISION (itype));
-  if (fail)
-    return NULL_TREE;
-
-  return wide_int_to_tree (itype, ival);
-}
-
-
 /* Fold a call to __builtin_inf or __builtin_huge_val.  */
 
 static tree
@@ -7340,7 +7314,7 @@ fold_builtin_sincos (location_t loc,
                     tree arg0, tree arg1, tree arg2)
 {
   tree type;
-  tree res, fn, call;
+  tree fndecl, call = NULL_TREE;
 
   if (!validate_arg (arg0, REAL_TYPE)
       || !validate_arg (arg1, POINTER_TYPE)
@@ -7350,26 +7324,33 @@ fold_builtin_sincos (location_t loc,
   type = TREE_TYPE (arg0);
 
   /* Calculate the result when the argument is a constant.  */
-  if ((res = do_mpfr_sincos (arg0, arg1, arg2)))
-    return res;
-
-  /* Canonicalize sincos to cexpi.  */
-  if (!targetm.libc_has_function (function_c99_math_complex))
-    return NULL_TREE;
-  fn = mathfn_built_in (type, BUILT_IN_CEXPI);
-  if (!fn)
+  built_in_function fn = mathfn_built_in_2 (type, BUILT_IN_CEXPI);
+  if (fn == END_BUILTINS)
     return NULL_TREE;
 
-  call = build_call_expr_loc (loc, fn, 1, arg0);
-  call = builtin_save_expr (call);
+  /* Canonicalize sincos to cexpi.  */
+  if (TREE_CODE (arg0) == REAL_CST)
+    {
+      tree complex_type = build_complex_type (type);
+      call = fold_const_call (fn, complex_type, arg0);
+    }
+  if (!call)
+    {
+      if (!targetm.libc_has_function (function_c99_math_complex)
+         || !builtin_decl_implicit_p (fn))
+       return NULL_TREE;
+      fndecl = builtin_decl_explicit (fn);
+      call = build_call_expr_loc (loc, fndecl, 1, arg0);
+      call = builtin_save_expr (call);
+    }
 
   return build2 (COMPOUND_EXPR, void_type_node,
                 build2 (MODIFY_EXPR, void_type_node,
                         build_fold_indirect_ref_loc (loc, arg1),
-                        build1 (IMAGPART_EXPR, type, call)),
+                        fold_build1_loc (loc, IMAGPART_EXPR, type, call)),
                 build2 (MODIFY_EXPR, void_type_node,
                         build_fold_indirect_ref_loc (loc, arg2),
-                        build1 (REALPART_EXPR, type, call)));
+                        fold_build1_loc (loc, REALPART_EXPR, type, call)));
 }
 
 /* Fold function call to builtin ffs, clz, ctz, popcount and parity
@@ -7465,49 +7446,6 @@ fold_builtin_bswap (tree fndecl, tree arg)
   return NULL_TREE;
 }
 
-/* Fold a builtin function call to pow, powf, or powl.  Return
-   NULL_TREE if no simplification can be made.  */
-static tree
-fold_const_builtin_pow (tree arg0, tree arg1, tree type)
-{
-  tree res;
-
-  if (!validate_arg (arg0, REAL_TYPE)
-       || !validate_arg (arg1, REAL_TYPE))
-    return NULL_TREE;
-
-  /* Calculate the result when the argument is a constant.  */
-  if ((res = do_mpfr_arg2 (arg0, arg1, type, mpfr_pow)))
-    return res;
-
-  /* Check for an integer exponent.  */
-  if (TREE_CODE (arg0) == REAL_CST
-      && !TREE_OVERFLOW (arg0)
-      && TREE_CODE (arg1) == REAL_CST
-      && !TREE_OVERFLOW (arg1))
-    {
-      REAL_VALUE_TYPE cint1;
-      const REAL_VALUE_TYPE *c0 = TREE_REAL_CST_PTR (arg0);
-      const REAL_VALUE_TYPE *c1 = TREE_REAL_CST_PTR (arg1);
-      HOST_WIDE_INT n1 = real_to_integer (c1);
-      real_from_integer (&cint1, VOIDmode, n1, SIGNED);
-      /* Attempt to evaluate pow at compile-time, unless this should
-        raise an exception.  */
-      if (real_identical (c1, &cint1)
-         && (n1 > 0
-             || (!flag_trapping_math && !flag_errno_math)
-             || !real_equal (c0, &dconst0)))
-       {
-         REAL_VALUE_TYPE x;
-         bool inexact = real_powi (&x, TYPE_MODE (type), c0, n1);
-         if (flag_unsafe_math_optimizations || !inexact)
-           return build_real (type, x);
-       }
-    }
-
-  return NULL_TREE;
-}
-
 /* Fold function call to builtin memchr.  ARG1, ARG2 and LEN are the
    arguments to the call, and TYPE is its return type.
    Return NULL_TREE if no simplification can be made.  */
@@ -7855,20 +7793,6 @@ fold_builtin_abs (location_t loc, tree arg, tree type)
   return fold_build1_loc (loc, ABS_EXPR, type, arg);
 }
 
-/* Fold a fma operation with arguments ARG[012].  */
-
-tree
-fold_fma (location_t loc ATTRIBUTE_UNUSED,
-         tree type, tree arg0, tree arg1, tree arg2)
-{
-  if (TREE_CODE (arg0) == REAL_CST
-      && TREE_CODE (arg1) == REAL_CST
-      && TREE_CODE (arg2) == REAL_CST)
-    return do_mpfr_arg3 (arg0, arg1, arg2, type, mpfr_fma);
-
-  return NULL_TREE;
-}
-
 /* Fold a call to fma, fmaf, or fmal with arguments ARG[012].  */
 
 static tree
@@ -7906,92 +7830,6 @@ fold_builtin_carg (location_t loc, tree arg, tree type)
   return NULL_TREE;
 }
 
-/* Fold a call to builtin logb/ilogb.  */
-
-static tree
-fold_const_builtin_logb (location_t loc, tree arg, tree rettype)
-{
-  if (! validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  if (TREE_CODE (arg) == REAL_CST && ! TREE_OVERFLOW (arg))
-    {
-      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg);
-
-      switch (value->cl)
-      {
-      case rvc_nan:
-      case rvc_inf:
-       /* If arg is Inf or NaN and we're logb, return it.  */
-       if (TREE_CODE (rettype) == REAL_TYPE)
-         {
-           /* For logb(-Inf) we have to return +Inf.  */
-           if (real_isinf (value) && real_isneg (value))
-             {
-               REAL_VALUE_TYPE tem;
-               real_inf (&tem);
-               return build_real (rettype, tem);
-             }
-           return fold_convert_loc (loc, rettype, arg);
-         }
-       /* Fall through... */
-      case rvc_zero:
-       /* Zero may set errno and/or raise an exception for logb, also
-          for ilogb we don't know FP_ILOGB0.  */
-       return NULL_TREE;
-      case rvc_normal:
-       /* For normal numbers, proceed iff radix == 2.  In GCC,
-          normalized significands are in the range [0.5, 1.0).  We
-          want the exponent as if they were [1.0, 2.0) so get the
-          exponent and subtract 1.  */
-       if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2)
-         return fold_convert_loc (loc, rettype,
-                                  build_int_cst (integer_type_node,
-                                                 REAL_EXP (value)-1));
-       break;
-      }
-    }
-
-  return NULL_TREE;
-}
-
-/* Fold a call to builtin significand, if radix == 2.  */
-
-static tree
-fold_const_builtin_significand (location_t loc, tree arg, tree rettype)
-{
-  if (! validate_arg (arg, REAL_TYPE))
-    return NULL_TREE;
-
-  if (TREE_CODE (arg) == REAL_CST && ! TREE_OVERFLOW (arg))
-    {
-      const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (arg);
-
-      switch (value->cl)
-      {
-      case rvc_zero:
-      case rvc_nan:
-      case rvc_inf:
-       /* If arg is +-0, +-Inf or +-NaN, then return it.  */
-       return fold_convert_loc (loc, rettype, arg);
-      case rvc_normal:
-       /* For normal numbers, proceed iff radix == 2.  */
-       if (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (arg)))->b == 2)
-         {
-           REAL_VALUE_TYPE result = *value;
-           /* In GCC, normalized significands are in the range [0.5,
-              1.0).  We want them to be [1.0, 2.0) so set the
-              exponent to 1.  */
-           SET_REAL_EXP (&result, 1);
-           return build_real (rettype, result);
-         }
-       break;
-      }
-    }
-
-  return NULL_TREE;
-}
-
 /* Fold a call to builtin frexp, we can assume the base is 2.  */
 
 static tree
@@ -8048,58 +7886,6 @@ fold_builtin_frexp (location_t loc, tree arg0, tree arg1, tree rettype)
   return NULL_TREE;
 }
 
-/* Fold a call to builtin ldexp or scalbn/scalbln.  If LDEXP is true
-   then we can assume the base is two.  If it's false, then we have to
-   check the mode of the TYPE parameter in certain cases.  */
-
-static tree
-fold_const_builtin_load_exponent (tree arg0, tree arg1,
-                                 tree type, bool ldexp)
-{
-  if (validate_arg (arg0, REAL_TYPE) && validate_arg (arg1, INTEGER_TYPE))
-    {
-      /* If both arguments are constant, then try to evaluate it.  */
-      if ((ldexp || REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2)
-         && TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0)
-         && tree_fits_shwi_p (arg1))
-        {
-         /* Bound the maximum adjustment to twice the range of the
-            mode's valid exponents.  Use abs to ensure the range is
-            positive as a sanity check.  */
-         const long max_exp_adj = 2 *
-           labs (REAL_MODE_FORMAT (TYPE_MODE (type))->emax
-                - REAL_MODE_FORMAT (TYPE_MODE (type))->emin);
-
-         /* Get the user-requested adjustment.  */
-         const HOST_WIDE_INT req_exp_adj = tree_to_shwi (arg1);
-
-         /* The requested adjustment must be inside this range.  This
-            is a preliminary cap to avoid things like overflow, we
-            may still fail to compute the result for other reasons.  */
-         if (-max_exp_adj < req_exp_adj && req_exp_adj < max_exp_adj)
-           {
-             REAL_VALUE_TYPE initial_result;
-
-             real_ldexp (&initial_result, &TREE_REAL_CST (arg0), req_exp_adj);
-
-             /* Ensure we didn't overflow.  */
-             if (! real_isinf (&initial_result))
-               {
-                 const REAL_VALUE_TYPE trunc_result
-                   = real_value_truncate (TYPE_MODE (type), initial_result);
-
-                 /* Only proceed if the target mode can hold the
-                    resulting value.  */
-                 if (real_equal (&initial_result, &trunc_result))
-                   return build_real (type, trunc_result);
-               }
-           }
-       }
-    }
-
-  return NULL_TREE;
-}
-
 /* Fold a call to builtin modf.  */
 
 static tree
@@ -8552,6 +8338,13 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
 {
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+
+  if (TREE_CODE (arg0) == ERROR_MARK)
+    return NULL_TREE;
+
+  if (tree ret = fold_const_call (fcode, type, arg0))
+    return ret;
+
   switch (fcode)
     {
     case BUILT_IN_CONSTANT_P:
@@ -8603,450 +8396,90 @@ fold_builtin_1 (location_t loc, tree fndecl, tree arg0)
        return non_lvalue_loc (loc, fold_build1_loc (loc, IMAGPART_EXPR, type, arg0));
     break;
 
-    CASE_FLT_FN (BUILT_IN_CCOS):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_cos);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_CCOSH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_cosh);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_CPROJ):
-      if (TREE_CODE (arg0) == COMPLEX_CST
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       {
-         const REAL_VALUE_TYPE *real
-           = TREE_REAL_CST_PTR (TREE_REALPART (arg0));
-         const REAL_VALUE_TYPE *imag
-           = TREE_REAL_CST_PTR (TREE_IMAGPART (arg0));
-
-         if (real_isinf (real) || real_isinf (imag))
-           return build_complex_inf (type, imag->sign);
-         else
-           return arg0;
-       }
-      break;
+    CASE_FLT_FN (BUILT_IN_CARG):
+      return fold_builtin_carg (loc, arg0, type);
 
-    CASE_FLT_FN (BUILT_IN_CSIN):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_sin);
-    break;
+    CASE_FLT_FN (BUILT_IN_NAN):
+    case BUILT_IN_NAND32:
+    case BUILT_IN_NAND64:
+    case BUILT_IN_NAND128:
+      return fold_builtin_nan (arg0, type, true);
 
-    CASE_FLT_FN (BUILT_IN_CSINH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_sinh);
-    break;
+    CASE_FLT_FN (BUILT_IN_NANS):
+      return fold_builtin_nan (arg0, type, false);
 
-    CASE_FLT_FN (BUILT_IN_CTAN):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_tan);
-    break;
+    case BUILT_IN_BSWAP16:
+    case BUILT_IN_BSWAP32:
+    case BUILT_IN_BSWAP64:
+      return fold_builtin_bswap (fndecl, arg0);
 
-    CASE_FLT_FN (BUILT_IN_CTANH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_tanh);
-    break;
+    CASE_INT_FN (BUILT_IN_FFS):
+    CASE_INT_FN (BUILT_IN_CLZ):
+    CASE_INT_FN (BUILT_IN_CTZ):
+    CASE_INT_FN (BUILT_IN_CLRSB):
+    CASE_INT_FN (BUILT_IN_POPCOUNT):
+    CASE_INT_FN (BUILT_IN_PARITY):
+      return fold_builtin_bitop (fndecl, arg0);
 
-    CASE_FLT_FN (BUILT_IN_CLOG):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_log);
-    break;
+    case BUILT_IN_ISASCII:
+      return fold_builtin_isascii (loc, arg0);
 
-    CASE_FLT_FN (BUILT_IN_CSQRT):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_sqrt);
-    break;
+    case BUILT_IN_TOASCII:
+      return fold_builtin_toascii (loc, arg0);
 
-    CASE_FLT_FN (BUILT_IN_CASIN):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_asin);
-    break;
+    case BUILT_IN_ISDIGIT:
+      return fold_builtin_isdigit (loc, arg0);
 
-    CASE_FLT_FN (BUILT_IN_CACOS):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_acos);
-    break;
+    CASE_FLT_FN (BUILT_IN_FINITE):
+    case BUILT_IN_FINITED32:
+    case BUILT_IN_FINITED64:
+    case BUILT_IN_FINITED128:
+    case BUILT_IN_ISFINITE:
+      {
+       tree ret = fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISFINITE);
+       if (ret)
+         return ret;
+       return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
+      }
 
-    CASE_FLT_FN (BUILT_IN_CATAN):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_atan);
-    break;
+    CASE_FLT_FN (BUILT_IN_ISINF):
+    case BUILT_IN_ISINFD32:
+    case BUILT_IN_ISINFD64:
+    case BUILT_IN_ISINFD128:
+      {
+       tree ret = fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISINF);
+       if (ret)
+         return ret;
+       return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
+      }
 
-    CASE_FLT_FN (BUILT_IN_CASINH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_asinh);
-    break;
+    case BUILT_IN_ISNORMAL:
+      return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
 
-    CASE_FLT_FN (BUILT_IN_CACOSH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_acosh);
-    break;
+    case BUILT_IN_ISINF_SIGN:
+      return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISINF_SIGN);
 
-    CASE_FLT_FN (BUILT_IN_CATANH):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_atanh);
-    break;
+    CASE_FLT_FN (BUILT_IN_ISNAN):
+    case BUILT_IN_ISNAND32:
+    case BUILT_IN_ISNAND64:
+    case BUILT_IN_ISNAND128:
+      return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISNAN);
 
-    CASE_FLT_FN (BUILT_IN_CABS):
-      if (TREE_CODE (arg0) == COMPLEX_CST
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-        return do_mpfr_arg2 (TREE_REALPART (arg0), TREE_IMAGPART (arg0),
-                            type, mpfr_hypot);
+    case BUILT_IN_FREE:
+      if (integer_zerop (arg0))
+       return build_empty_stmt (loc);
       break;
 
-    CASE_FLT_FN (BUILT_IN_CARG):
-      return fold_builtin_carg (loc, arg0, type);
-
-    CASE_FLT_FN (BUILT_IN_SQRT):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_sqrt, &dconst0, NULL, true);
+    default:
       break;
+    }
 
-    CASE_FLT_FN (BUILT_IN_CBRT):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_cbrt, NULL, NULL, 0);
-      break;
+  return NULL_TREE;
 
-    CASE_FLT_FN (BUILT_IN_ASIN):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_asin,
-                            &dconstm1, &dconst1, true);
-    break;
+}
 
-    CASE_FLT_FN (BUILT_IN_ACOS):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_acos,
-                            &dconstm1, &dconst1, true);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ATAN):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_atan, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ASINH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_asinh, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ACOSH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_acosh,
-                            &dconst1, NULL, true);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ATANH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_atanh,
-                            &dconstm1, &dconst1, false);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_SIN):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_sin, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_COS):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_cos, NULL, NULL, 0);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_TAN):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_tan, NULL, NULL, 0);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_CEXP):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE)
-       return do_mpc_arg1 (arg0, type, mpc_exp);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_CEXPI):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_sincos (arg0, NULL_TREE, NULL_TREE);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_SINH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_sinh, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_COSH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_cosh, NULL, NULL, 0);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_TANH):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_tanh, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ERF):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_erf, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_ERFC):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_erfc, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_TGAMMA):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_gamma, NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_EXP):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_exp, NULL, NULL, 0);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_EXP2):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_exp2, NULL, NULL, 0);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_EXP10):
-    CASE_FLT_FN (BUILT_IN_POW10):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_exp10, NULL, NULL, 0);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_EXPM1):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_expm1, NULL, NULL, 0);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_LOG):
-      if (validate_arg (arg0, REAL_TYPE))
-        return do_mpfr_arg1 (arg0, type, mpfr_log, &dconst0, NULL, false);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_LOG2):
-      if (validate_arg (arg0, REAL_TYPE))
-        return do_mpfr_arg1 (arg0, type, mpfr_log2, &dconst0, NULL, false);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_LOG10):
-      if (validate_arg (arg0, REAL_TYPE))
-        return do_mpfr_arg1 (arg0, type, mpfr_log10, &dconst0, NULL, false);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_LOG1P):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_log1p,
-                            &dconstm1, NULL, false);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_J0):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_j0,
-                            NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_J1):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_j1,
-                            NULL, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_Y0):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_y0,
-                            &dconst0, NULL, false);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_Y1):
-      if (validate_arg (arg0, REAL_TYPE))
-       return do_mpfr_arg1 (arg0, type, mpfr_y1,
-                            &dconst0, NULL, false);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_NAN):
-    case BUILT_IN_NAND32:
-    case BUILT_IN_NAND64:
-    case BUILT_IN_NAND128:
-      return fold_builtin_nan (arg0, type, true);
-
-    CASE_FLT_FN (BUILT_IN_NANS):
-      return fold_builtin_nan (arg0, type, false);
-
-    CASE_FLT_FN (BUILT_IN_FLOOR):
-      if (TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0))
-       {
-         REAL_VALUE_TYPE x = TREE_REAL_CST (arg0);
-         if (!REAL_VALUE_ISNAN (x) || !flag_errno_math)
-           {
-             tree type = TREE_TYPE (TREE_TYPE (fndecl));
-             REAL_VALUE_TYPE r;
-             real_floor (&r, TYPE_MODE (type), &x);
-             return build_real (type, r);
-           }
-       }
-      break;
-
-    CASE_FLT_FN (BUILT_IN_CEIL):
-      if (TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0))
-       {
-         REAL_VALUE_TYPE x = TREE_REAL_CST (arg0);
-         if (!REAL_VALUE_ISNAN (x) || !flag_errno_math)
-           {
-             tree type = TREE_TYPE (TREE_TYPE (fndecl));
-             REAL_VALUE_TYPE r;
-             real_ceil (&r, TYPE_MODE (type), &x);
-             return build_real (type, r);
-           }
-       }
-      break;
-
-    CASE_FLT_FN (BUILT_IN_TRUNC):
-      if (TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0))
-       {
-         REAL_VALUE_TYPE x = TREE_REAL_CST (arg0);
-         REAL_VALUE_TYPE r;
-         real_trunc (&r, TYPE_MODE (type), &x);
-         return build_real (type, r);
-       }
-      break;
-
-    CASE_FLT_FN (BUILT_IN_ROUND):
-      if (TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0))
-       {
-         REAL_VALUE_TYPE x = TREE_REAL_CST (arg0);
-         if (!REAL_VALUE_ISNAN (x) || !flag_errno_math)
-           {
-             tree type = TREE_TYPE (TREE_TYPE (fndecl));
-             REAL_VALUE_TYPE r;
-             real_round (&r, TYPE_MODE (type), &x);
-             return build_real (type, r);
-           }
-       }
-      break;
-
-    CASE_FLT_FN (BUILT_IN_ICEIL):
-    CASE_FLT_FN (BUILT_IN_LCEIL):
-    CASE_FLT_FN (BUILT_IN_LLCEIL):
-      return do_real_to_int_conversion (type, arg0, real_ceil);
-
-    CASE_FLT_FN (BUILT_IN_LFLOOR):
-    CASE_FLT_FN (BUILT_IN_IFLOOR):
-    CASE_FLT_FN (BUILT_IN_LLFLOOR):
-      return do_real_to_int_conversion (type, arg0, real_floor);
-
-    CASE_FLT_FN (BUILT_IN_IROUND):
-    CASE_FLT_FN (BUILT_IN_LROUND):
-    CASE_FLT_FN (BUILT_IN_LLROUND):
-      return do_real_to_int_conversion (type, arg0, real_round);
-
-    CASE_FLT_FN (BUILT_IN_IRINT):
-    CASE_FLT_FN (BUILT_IN_LRINT):
-    CASE_FLT_FN (BUILT_IN_LLRINT):
-      /* Not yet folded to a constant.  */
-      return NULL_TREE;
-
-    case BUILT_IN_BSWAP16:
-    case BUILT_IN_BSWAP32:
-    case BUILT_IN_BSWAP64:
-      return fold_builtin_bswap (fndecl, arg0);
-
-    CASE_INT_FN (BUILT_IN_FFS):
-    CASE_INT_FN (BUILT_IN_CLZ):
-    CASE_INT_FN (BUILT_IN_CTZ):
-    CASE_INT_FN (BUILT_IN_CLRSB):
-    CASE_INT_FN (BUILT_IN_POPCOUNT):
-    CASE_INT_FN (BUILT_IN_PARITY):
-      return fold_builtin_bitop (fndecl, arg0);
-
-    CASE_FLT_FN (BUILT_IN_SIGNBIT):
-      if (TREE_CODE (arg0) == REAL_CST && !TREE_OVERFLOW (arg0))
-       return (REAL_VALUE_NEGATIVE (TREE_REAL_CST (arg0))
-               ? build_one_cst (type)
-               : build_zero_cst (type));
-      break;
-
-    CASE_FLT_FN (BUILT_IN_SIGNIFICAND):
-      return fold_const_builtin_significand (loc, arg0, type);
-
-    CASE_FLT_FN (BUILT_IN_ILOGB):
-    CASE_FLT_FN (BUILT_IN_LOGB):
-      return fold_const_builtin_logb (loc, arg0, type);
-
-    case BUILT_IN_ISASCII:
-      return fold_builtin_isascii (loc, arg0);
-
-    case BUILT_IN_TOASCII:
-      return fold_builtin_toascii (loc, arg0);
-
-    case BUILT_IN_ISDIGIT:
-      return fold_builtin_isdigit (loc, arg0);
-
-    CASE_FLT_FN (BUILT_IN_FINITE):
-    case BUILT_IN_FINITED32:
-    case BUILT_IN_FINITED64:
-    case BUILT_IN_FINITED128:
-    case BUILT_IN_ISFINITE:
-      {
-       tree ret = fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISFINITE);
-       if (ret)
-         return ret;
-       return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
-      }
-
-    CASE_FLT_FN (BUILT_IN_ISINF):
-    case BUILT_IN_ISINFD32:
-    case BUILT_IN_ISINFD64:
-    case BUILT_IN_ISINFD128:
-      {
-       tree ret = fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISINF);
-       if (ret)
-         return ret;
-       return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
-      }
-
-    case BUILT_IN_ISNORMAL:
-      return fold_builtin_interclass_mathfn (loc, fndecl, arg0);
-
-    case BUILT_IN_ISINF_SIGN:
-      return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISINF_SIGN);
-
-    CASE_FLT_FN (BUILT_IN_ISNAN):
-    case BUILT_IN_ISNAND32:
-    case BUILT_IN_ISNAND64:
-    case BUILT_IN_ISNAND128:
-      return fold_builtin_classify (loc, fndecl, arg0, BUILT_IN_ISNAN);
-
-    case BUILT_IN_FREE:
-      if (integer_zerop (arg0))
-       return build_empty_stmt (loc);
-      break;
-
-    default:
-      break;
-    }
-
-  return NULL_TREE;
-
-}
-
-/* Fold a call to built-in function FNDECL with 2 arguments, ARG0 and ARG1.
-   This function returns NULL_TREE if no simplification was possible.  */
+/* Fold a call to built-in function FNDECL with 2 arguments, ARG0 and ARG1.
+   This function returns NULL_TREE if no simplification was possible.  */
 
 static tree
 fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
@@ -9054,28 +8487,15 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
 
-  switch (fcode)
-    {
-    CASE_FLT_FN (BUILT_IN_JN):
-      if (validate_arg (arg0, INTEGER_TYPE)
-         && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_bessel_n (arg0, arg1, type, mpfr_jn, NULL, 0);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_YN):
-      if (validate_arg (arg0, INTEGER_TYPE)
-         && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_bessel_n (arg0, arg1, type, mpfr_yn,
-                                &dconst0, false);
-    break;
+  if (TREE_CODE (arg0) == ERROR_MARK
+      || TREE_CODE (arg1) == ERROR_MARK)
+    return NULL_TREE;
 
-    CASE_FLT_FN (BUILT_IN_DREM):
-    CASE_FLT_FN (BUILT_IN_REMAINDER):
-      if (validate_arg (arg0, REAL_TYPE)
-          && validate_arg (arg1, REAL_TYPE))
-        return do_mpfr_arg2 (arg0, arg1, type, mpfr_remainder);
-    break;
+  if (tree ret = fold_const_call (fcode, type, arg0, arg1))
+    return ret;
 
+  switch (fcode)
+    {
     CASE_FLT_FN_REENT (BUILT_IN_GAMMA): /* GAMMA_R */
     CASE_FLT_FN_REENT (BUILT_IN_LGAMMA): /* LGAMMA_R */
       if (validate_arg (arg0, REAL_TYPE)
@@ -9083,40 +8503,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
        return do_mpfr_lgamma_r (arg0, arg1, type);
     break;
 
-    CASE_FLT_FN (BUILT_IN_ATAN2):
-      if (validate_arg (arg0, REAL_TYPE)
-         && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_arg2 (arg0, arg1, type, mpfr_atan2);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_FDIM):
-      if (validate_arg (arg0, REAL_TYPE)
-         && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_arg2 (arg0, arg1, type, mpfr_dim);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_HYPOT):
-      if (validate_arg (arg0, REAL_TYPE)
-         && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_arg2 (arg0, arg1, type, mpfr_hypot);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_CPOW):
-      if (validate_arg (arg0, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg0))) == REAL_TYPE
-         && validate_arg (arg1, COMPLEX_TYPE)
-         && TREE_CODE (TREE_TYPE (TREE_TYPE (arg1))) == REAL_TYPE)
-       return do_mpc_arg2 (arg0, arg1, type, /*do_nonfinite=*/ 0, mpc_pow);
-    break;
-
-    CASE_FLT_FN (BUILT_IN_LDEXP):
-      return fold_const_builtin_load_exponent (arg0, arg1, type,
-                                              /*ldexp=*/true);
-    CASE_FLT_FN (BUILT_IN_SCALBN):
-    CASE_FLT_FN (BUILT_IN_SCALBLN):
-      return fold_const_builtin_load_exponent (arg0, arg1, type,
-                                              /*ldexp=*/false);
-
     CASE_FLT_FN (BUILT_IN_FREXP):
       return fold_builtin_frexp (loc, arg0, arg1, type);
 
@@ -9149,43 +8535,6 @@ fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
     case BUILT_IN_EXPECT:
       return fold_builtin_expect (loc, arg0, arg1, NULL_TREE);
 
-    CASE_FLT_FN (BUILT_IN_POW):
-      return fold_const_builtin_pow (arg0, arg1, type);
-
-    CASE_FLT_FN (BUILT_IN_POWI):
-      if (TREE_CODE (arg0) == REAL_CST
-         && !TREE_OVERFLOW (arg0)
-         && tree_fits_shwi_p (arg1))
-       {
-         HOST_WIDE_INT c = tree_to_shwi (arg1);
-         REAL_VALUE_TYPE x;
-         real_powi (&x, TYPE_MODE (type), TREE_REAL_CST_PTR (arg0), c);
-         return build_real (type, x);
-       }
-      break;
-
-    CASE_FLT_FN (BUILT_IN_COPYSIGN):
-      if (TREE_CODE (arg0) == REAL_CST
-         && TREE_CODE (arg1) == REAL_CST
-         && !TREE_OVERFLOW (arg0)
-         && !TREE_OVERFLOW (arg1))
-       {
-         REAL_VALUE_TYPE c1 = TREE_REAL_CST (arg0);
-         real_copysign (&c1, TREE_REAL_CST_PTR (arg1));
-         return build_real (type, c1);
-       }
-      break;
-
-    CASE_FLT_FN (BUILT_IN_FMIN):
-      if (validate_arg (arg0, REAL_TYPE) && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_arg2 (arg0, arg1, type, mpfr_min);
-      break;
-
-    CASE_FLT_FN (BUILT_IN_FMAX):
-      if (validate_arg (arg0, REAL_TYPE) && validate_arg (arg1, REAL_TYPE))
-       return do_mpfr_arg2 (arg0, arg1, type, mpfr_max);
-      break;
-
     case BUILT_IN_ISGREATER:
       return fold_builtin_unordered_cmp (loc, fndecl,
                                         arg0, arg1, UNLE_EXPR, LE_EXPR);
@@ -9235,6 +8584,15 @@ fold_builtin_3 (location_t loc, tree fndecl,
 {
   tree type = TREE_TYPE (TREE_TYPE (fndecl));
   enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
+
+  if (TREE_CODE (arg0) == ERROR_MARK
+      || TREE_CODE (arg1) == ERROR_MARK
+      || TREE_CODE (arg2) == ERROR_MARK)
+    return NULL_TREE;
+
+  if (tree ret = fold_const_call (fcode, type, arg0, arg1, arg2))
+    return ret;
+
   switch (fcode)
     {
 
@@ -9242,8 +8600,6 @@ fold_builtin_3 (location_t loc, tree fndecl,
       return fold_builtin_sincos (loc, arg0, arg1, arg2);
 
     CASE_FLT_FN (BUILT_IN_FMA):
-      if (tree tem = fold_fma (loc, type, arg0, arg1, arg2))
-       return tem;
       return fold_builtin_fma (loc, arg0, arg1, arg2, type);
 
     CASE_FLT_FN (BUILT_IN_REMQUO):
@@ -10559,264 +9915,6 @@ do_mpc_ckconv (mpc_srcptr m, tree type, int inexact, int force_convert)
   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);
-
-  /* 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 (arg) == REAL_CST && !TREE_OVERFLOW (arg))
-    {
-      const REAL_VALUE_TYPE *const ra = &TREE_REAL_CST (arg);
-
-      if (real_isfinite (ra)
-         && (!min || real_compare (inclusive ? GE_EXPR: GT_EXPR , ra, min))
-         && (!max || real_compare (inclusive ? LE_EXPR: LT_EXPR , ra, max)))
-        {
-         const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
-         int inexact;
-         mpfr_t m;
-
-         mpfr_init2 (m, prec);
-         mpfr_from_real (m, ra, GMP_RNDN);
-         mpfr_clear_flags ();
-         inexact = func (m, m, rnd);
-         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);
-
-  /* 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 (arg1) == REAL_CST && !TREE_OVERFLOW (arg1)
-      && TREE_CODE (arg2) == REAL_CST && !TREE_OVERFLOW (arg2))
-    {
-      const REAL_VALUE_TYPE *const ra1 = &TREE_REAL_CST (arg1);
-      const REAL_VALUE_TYPE *const ra2 = &TREE_REAL_CST (arg2);
-
-      if (real_isfinite (ra1) && real_isfinite (ra2))
-        {
-         const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
-         int inexact;
-         mpfr_t m1, m2;
-
-         mpfr_inits2 (prec, m1, m2, NULL);
-         mpfr_from_real (m1, ra1, GMP_RNDN);
-         mpfr_from_real (m2, ra2, GMP_RNDN);
-         mpfr_clear_flags ();
-         inexact = func (m1, m1, m2, rnd);
-         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);
-
-  /* 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 (arg1) == REAL_CST && !TREE_OVERFLOW (arg1)
-      && TREE_CODE (arg2) == REAL_CST && !TREE_OVERFLOW (arg2)
-      && TREE_CODE (arg3) == REAL_CST && !TREE_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_isfinite (ra1) && real_isfinite (ra2) && real_isfinite (ra3))
-        {
-         const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
-         int inexact;
-         mpfr_t m1, m2, m3;
-
-         mpfr_inits2 (prec, m1, m2, m3, NULL);
-         mpfr_from_real (m1, ra1, GMP_RNDN);
-         mpfr_from_real (m2, ra2, GMP_RNDN);
-         mpfr_from_real (m3, ra3, GMP_RNDN);
-         mpfr_clear_flags ();
-         inexact = func (m1, m1, m2, m3, rnd);
-         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.
-   If ARG_SINP and ARG_COSP are NULL then the result is returned
-   as a complex value.
-   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 const type = TREE_TYPE (arg);
-  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.  */
-  if (REAL_MODE_FORMAT (TYPE_MODE (type))->b == 2
-      && TREE_CODE (arg) == REAL_CST
-      && !TREE_OVERFLOW (arg))
-    {
-      const REAL_VALUE_TYPE *const ra = &TREE_REAL_CST (arg);
-
-      if (real_isfinite (ra))
-        {
-         const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
-         tree result_s, result_c;
-         int inexact;
-         mpfr_t m, ms, mc;
-
-         mpfr_inits2 (prec, m, ms, mc, NULL);
-         mpfr_from_real (m, ra, GMP_RNDN);
-         mpfr_clear_flags ();
-         inexact = mpfr_sin_cos (ms, mc, m, rnd);
-         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)
-           {
-             /* If we are to return in a complex value do so.  */
-             if (!arg_sinp && !arg_cosp)
-               return build_complex (build_complex_type (type),
-                                     result_c, result_s);
-
-             /* 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;
-}
-
-/* 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
-      && tree_fits_shwi_p (arg1)
-      && TREE_CODE (arg2) == REAL_CST && !TREE_OVERFLOW (arg2))
-    {
-      const HOST_WIDE_INT n = tree_to_shwi (arg1);
-      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 struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero? GMP_RNDZ : GMP_RNDN;
-         int inexact;
-         mpfr_t m;
-
-         mpfr_init2 (m, prec);
-         mpfr_from_real (m, ra, GMP_RNDN);
-         mpfr_clear_flags ();
-         inexact = func (m, n, m, rnd);
-         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
@@ -10956,51 +10054,6 @@ do_mpfr_lgamma_r (tree arg, tree arg_sg, tree type)
   return result;
 }
 
-/* If argument ARG is a COMPLEX_CST, call the one-argument mpc
-   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_mpc_arg1 (tree arg, tree type, int (*func)(mpc_ptr, mpc_srcptr, mpc_rnd_t))
-{
-  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.  */
-  if (TREE_CODE (arg) == COMPLEX_CST && !TREE_OVERFLOW (arg)
-      && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == REAL_TYPE
-      && REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (TREE_TYPE (arg))))->b == 2)
-    {
-      const REAL_VALUE_TYPE *const re = TREE_REAL_CST_PTR (TREE_REALPART (arg));
-      const REAL_VALUE_TYPE *const im = TREE_REAL_CST_PTR (TREE_IMAGPART (arg));
-
-      if (real_isfinite (re) && real_isfinite (im))
-        {
-         const struct real_format *const fmt =
-           REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (type)));
-         const int prec = fmt->p;
-         const mp_rnd_t rnd = fmt->round_towards_zero ? GMP_RNDZ : GMP_RNDN;
-         const mpc_rnd_t crnd = fmt->round_towards_zero ? MPC_RNDZZ : MPC_RNDNN;
-         int inexact;
-         mpc_t m;
-
-         mpc_init2 (m, prec);
-         mpfr_from_real (mpc_realref (m), re, rnd);
-         mpfr_from_real (mpc_imagref (m), im, rnd);
-         mpfr_clear_flags ();
-         inexact = func (m, m, crnd);
-         result = do_mpc_ckconv (m, type, inexact, /*force_convert=*/ 0);
-         mpc_clear (m);
-       }
-    }
-
-  return result;
-}
-
 /* If arguments ARG0 and ARG1 are a COMPLEX_CST, call the two-argument
    mpc function FUNC on it and return the resulting value as a tree
    with type TYPE.  The mpfr precision is set to the precision of
index 5a0b57d265856043ec5cfdc37ec119165b631286..cce9e7507a287a58590e62162c4fde03815013bb 100644 (file)
@@ -73,7 +73,6 @@ extern rtx expand_builtin (tree, rtx, rtx, machine_mode, int);
 extern rtx expand_builtin_with_bounds (tree, rtx, rtx, machine_mode, int);
 extern enum built_in_function builtin_mathfn_code (const_tree);
 extern tree fold_builtin_expect (location_t, tree, tree, tree);
-extern tree fold_fma (location_t, tree, tree, tree, tree);
 extern bool avoid_folding_inline_builtin (tree);
 extern tree fold_call_expr (location_t, tree, bool);
 extern tree fold_builtin_call_array (location_t, tree, tree, int, tree *);
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
new file mode 100644 (file)
index 0000000..5af2c63
--- /dev/null
@@ -0,0 +1,1259 @@
+/* Constant folding for calls to built-in and internal functions.
+   Copyright (C) 1988-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "realmpfr.h"
+#include "tree.h"
+#include "stor-layout.h"
+#include "options.h"
+#include "fold-const-call.h"
+
+/* Functions that test for certain constant types, abstracting away the
+   decision about whether to check for overflow.  */
+
+static inline bool
+integer_cst_p (tree t)
+{
+  return TREE_CODE (t) == INTEGER_CST && !TREE_OVERFLOW (t);
+}
+
+static inline bool
+real_cst_p (tree t)
+{
+  return TREE_CODE (t) == REAL_CST && !TREE_OVERFLOW (t);
+}
+
+static inline bool
+complex_cst_p (tree t)
+{
+  return TREE_CODE (t) == COMPLEX_CST;
+}
+
+/* M is the result of trying to constant-fold an expression (starting
+   with clear MPFR flags) and INEXACT says whether the result in M is
+   exact or inexact.  Return true if M can be used as a constant-folded
+   result in format FORMAT, storing the value in *RESULT if so.  */
+
+static bool
+do_mpfr_ckconv (real_value *result, mpfr_srcptr m, bool inexact,
+               const real_format *format)
+{
+  /* 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))
+    return false;
+
+  REAL_VALUE_TYPE tmp;
+  real_from_mpfr (&tmp, m, format, GMP_RNDN);
+
+  /* Proceed iff GCC's REAL_VALUE_TYPE can hold the MPFR values.
+     If the REAL_VALUE_TYPE is zero but the mpft_t is not, then we
+     underflowed in the conversion.  */
+  if (!real_isfinite (&tmp)
+      || ((tmp.cl == rvc_zero) != (mpfr_zero_p (m) != 0)))
+    return false;
+
+  real_convert (result, format, &tmp);
+  return real_identical (result, &tmp);
+}
+
+/* Try to evaluate:
+
+      *RESULT = f (*ARG)
+
+   in format FORMAT, given that FUNC is the MPFR implementation of f.
+   Return true on success.  */
+
+static bool
+do_mpfr_arg1 (real_value *result,
+             int (*func) (mpfr_ptr, mpfr_srcptr, mpfr_rnd_t),
+             const real_value *arg, const real_format *format)
+{
+  /* To proceed, MPFR must exactly represent the target floating point
+     format, which only happens when the target base equals two.  */
+  if (format->b != 2 || !real_isfinite (arg))
+    return false;
+
+  int prec = format->p;
+  mp_rnd_t rnd = format->round_towards_zero ? GMP_RNDZ : GMP_RNDN;
+  mpfr_t m;
+
+  mpfr_init2 (m, prec);
+  mpfr_from_real (m, arg, GMP_RNDN);
+  mpfr_clear_flags ();
+  bool inexact = func (m, m, rnd);
+  bool ok = do_mpfr_ckconv (result, m, inexact, format);
+  mpfr_clear (m);
+
+  return ok;
+}
+
+/* Try to evaluate:
+
+      *RESULT_SIN = sin (*ARG);
+      *RESULT_COS = cos (*ARG);
+
+   for format FORMAT.  Return true on success.  */
+
+static bool
+do_mpfr_sincos (real_value *result_sin, real_value *result_cos,
+               const real_value *arg, const real_format *format)
+{
+  /* To proceed, MPFR must exactly represent the target floating point
+     format, which only happens when the target base equals two.  */
+  if (format->b != 2 || !real_isfinite (arg))
+    return false;
+
+  int prec = format->p;
+  mp_rnd_t rnd = format->round_towards_zero ? GMP_RNDZ : GMP_RNDN;
+  mpfr_t m, ms, mc;
+
+  mpfr_inits2 (prec, m, ms, mc, NULL);
+  mpfr_from_real (m, arg, GMP_RNDN);
+  mpfr_clear_flags ();
+  bool inexact = mpfr_sin_cos (ms, mc, m, rnd);
+  bool ok = (do_mpfr_ckconv (result_sin, ms, inexact, format)
+            && do_mpfr_ckconv (result_cos, mc, inexact, format));
+  mpfr_clears (m, ms, mc, NULL);
+
+  return ok;
+}
+
+/* Try to evaluate:
+
+      *RESULT = f (*ARG0, *ARG1)
+
+   in format FORMAT, given that FUNC is the MPFR implementation of f.
+   Return true on success.  */
+
+static bool
+do_mpfr_arg2 (real_value *result,
+             int (*func) (mpfr_ptr, mpfr_srcptr, mpfr_srcptr, mpfr_rnd_t),
+             const real_value *arg0, const real_value *arg1,
+             const real_format *format)
+{
+  /* To proceed, MPFR must exactly represent the target floating point
+     format, which only happens when the target base equals two.  */
+  if (format->b != 2 || !real_isfinite (arg0) || !real_isfinite (arg1))
+    return false;
+
+  int prec = format->p;
+  mp_rnd_t rnd = format->round_towards_zero ? GMP_RNDZ : GMP_RNDN;
+  mpfr_t m0, m1;
+
+  mpfr_inits2 (prec, m0, m1, NULL);
+  mpfr_from_real (m0, arg0, GMP_RNDN);
+  mpfr_from_real (m1, arg1, GMP_RNDN);
+  mpfr_clear_flags ();
+  bool inexact = func (m0, m0, m1, rnd);
+  bool ok = do_mpfr_ckconv (result, m0, inexact, format);
+  mpfr_clears (m0, m1, NULL);
+
+  return ok;
+}
+
+/* Try to evaluate:
+
+      *RESULT = f (ARG0, *ARG1)
+
+   in format FORMAT, given that FUNC is the MPFR implementation of f.
+   Return true on success.  */
+
+static bool
+do_mpfr_arg2 (real_value *result,
+             int (*func) (mpfr_ptr, long, mpfr_srcptr, mp_rnd_t),
+             const wide_int_ref &arg0, const real_value *arg1,
+             const real_format *format)
+{
+  if (format->b != 2 || !real_isfinite (arg1))
+    return false;
+
+  int prec = format->p;
+  mp_rnd_t rnd = format->round_towards_zero ? GMP_RNDZ : GMP_RNDN;
+  mpfr_t m;
+
+  mpfr_init2 (m, prec);
+  mpfr_from_real (m, arg1, GMP_RNDN);
+  mpfr_clear_flags ();
+  bool inexact = func (m, arg0.to_shwi (), m, rnd);
+  bool ok = do_mpfr_ckconv (result, m, inexact, format);
+  mpfr_clear (m);
+
+  return ok;
+}
+
+/* Try to evaluate:
+
+      *RESULT = f (*ARG0, *ARG1, *ARG2)
+
+   in format FORMAT, given that FUNC is the MPFR implementation of f.
+   Return true on success.  */
+
+static bool
+do_mpfr_arg3 (real_value *result,
+             int (*func) (mpfr_ptr, mpfr_srcptr, mpfr_srcptr,
+                          mpfr_srcptr, mpfr_rnd_t),
+             const real_value *arg0, const real_value *arg1,
+             const real_value *arg2, const real_format *format)
+{
+  /* To proceed, MPFR must exactly represent the target floating point
+     format, which only happens when the target base equals two.  */
+  if (format->b != 2
+      || !real_isfinite (arg0)
+      || !real_isfinite (arg1)
+      || !real_isfinite (arg2))
+    return false;
+
+  int prec = format->p;
+  mp_rnd_t rnd = format->round_towards_zero ? GMP_RNDZ : GMP_RNDN;
+  mpfr_t m0, m1, m2;
+
+  mpfr_inits2 (prec, m0, m1, m2, NULL);
+  mpfr_from_real (m0, arg0, GMP_RNDN);
+  mpfr_from_real (m1, arg1, GMP_RNDN);
+  mpfr_from_real (m2, arg2, GMP_RNDN);
+  mpfr_clear_flags ();
+  bool inexact = func (m0, m0, m1, m2, rnd);
+  bool ok = do_mpfr_ckconv (result, m0, inexact, format);
+  mpfr_clears (m0, m1, m2, NULL);
+
+  return ok;
+}
+
+/* M is the result of trying to constant-fold an expression (starting
+   with clear MPFR flags) and INEXACT says whether the result in M is
+   exact or inexact.  Return true if M can be used as a constant-folded
+   result in which the real and imaginary parts have format FORMAT.
+   Store those parts in *RESULT_REAL and *RESULT_IMAG if so.  */
+
+static bool
+do_mpc_ckconv (real_value *result_real, real_value *result_imag,
+              mpc_srcptr m, bool inexact, const real_format *format)
+{
+  /* 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 (mpc_realref (m))
+      || !mpfr_number_p (mpc_imagref (m))
+      || mpfr_overflow_p ()
+      || mpfr_underflow_p ()
+      || (flag_rounding_math && inexact))
+    return false;
+
+  REAL_VALUE_TYPE tmp_real, tmp_imag;
+  real_from_mpfr (&tmp_real, mpc_realref (m), format, GMP_RNDN);
+  real_from_mpfr (&tmp_imag, mpc_imagref (m), format, GMP_RNDN);
+
+  /* Proceed iff GCC's REAL_VALUE_TYPE can hold the MPFR values.
+     If the REAL_VALUE_TYPE is zero but the mpft_t is not, then we
+     underflowed in the conversion.  */
+  if (!real_isfinite (&tmp_real)
+      || !real_isfinite (&tmp_imag)
+      || (tmp_real.cl == rvc_zero) != (mpfr_zero_p (mpc_realref (m)) != 0)
+      || (tmp_imag.cl == rvc_zero) != (mpfr_zero_p (mpc_imagref (m)) != 0))
+    return false;
+
+  real_convert (result_real, format, &tmp_real);
+  real_convert (result_imag, format, &tmp_imag);
+
+  return (real_identical (result_real, &tmp_real)
+         && real_identical (result_imag, &tmp_imag));
+}
+
+/* Try to evaluate:
+
+      RESULT = f (ARG)
+
+   in format FORMAT, given that FUNC is the mpc implementation of f.
+   Return true on success.  Both RESULT and ARG are represented as
+   real and imaginary pairs.  */
+
+static bool
+do_mpc_arg1 (real_value *result_real, real_value *result_imag,
+            int (*func) (mpc_ptr, mpc_srcptr, mpc_rnd_t),
+            const real_value *arg_real, const real_value *arg_imag,
+            const real_format *format)
+{
+  /* To proceed, MPFR must exactly represent the target floating point
+     format, which only happens when the target base equals two.  */
+  if (format->b != 2
+      || !real_isfinite (arg_real)
+      || !real_isfinite (arg_imag))
+    return false;
+
+  int prec = format->p;
+  mpc_rnd_t crnd = format->round_towards_zero ? MPC_RNDZZ : MPC_RNDNN;
+  mpc_t m;
+
+  mpc_init2 (m, prec);
+  mpfr_from_real (mpc_realref (m), arg_real, GMP_RNDN);
+  mpfr_from_real (mpc_imagref (m), arg_imag, GMP_RNDN);
+  mpfr_clear_flags ();
+  bool inexact = func (m, m, crnd);
+  bool ok = do_mpc_ckconv (result_real, result_imag, m, inexact, format);
+  mpc_clear (m);
+
+  return ok;
+}
+
+/* Try to evaluate:
+
+      RESULT = f (ARG0, ARG1)
+
+   in format FORMAT, given that FUNC is the mpc implementation of f.
+   Return true on success.  RESULT, ARG0 and ARG1 are represented as
+   real and imaginary pairs.  */
+
+static bool
+do_mpc_arg2 (real_value *result_real, real_value *result_imag,
+            int (*func)(mpc_ptr, mpc_srcptr, mpc_srcptr, mpc_rnd_t),
+            const real_value *arg0_real, const real_value *arg0_imag,
+            const real_value *arg1_real, const real_value *arg1_imag,
+            const real_format *format)
+{
+  if (!real_isfinite (arg0_real)
+      || !real_isfinite (arg0_imag)
+      || !real_isfinite (arg1_real)
+      || !real_isfinite (arg1_imag))
+    return false;
+
+  int prec = format->p;
+  mpc_rnd_t crnd = format->round_towards_zero ? MPC_RNDZZ : MPC_RNDNN;
+  mpc_t m0, m1;
+
+  mpc_init2 (m0, prec);
+  mpc_init2 (m1, prec);
+  mpfr_from_real (mpc_realref (m0), arg0_real, GMP_RNDN);
+  mpfr_from_real (mpc_imagref (m0), arg0_imag, GMP_RNDN);
+  mpfr_from_real (mpc_realref (m1), arg1_real, GMP_RNDN);
+  mpfr_from_real (mpc_imagref (m1), arg1_imag, GMP_RNDN);
+  mpfr_clear_flags ();
+  bool inexact = func (m0, m0, m1, crnd);
+  bool ok = do_mpc_ckconv (result_real, result_imag, m0, inexact, format);
+  mpc_clear (m0);
+  mpc_clear (m1);
+
+  return ok;
+}
+
+/* Try to evaluate:
+
+      *RESULT = logb (*ARG)
+
+   in format FORMAT.  Return true on success.  */
+
+static bool
+fold_const_logb (real_value *result, const real_value *arg,
+                const real_format *format)
+{
+  switch (arg->cl)
+    {
+    case rvc_nan:
+      /* If arg is +-NaN, then return it.  */
+      *result = *arg;
+      return true;
+
+    case rvc_inf:
+      /* If arg is +-Inf, then return +Inf.  */
+      *result = *arg;
+      result->sign = 0;
+      return true;
+
+    case rvc_zero:
+      /* Zero may set errno and/or raise an exception.  */
+      return false;
+
+    case rvc_normal:
+      /* For normal numbers, proceed iff radix == 2.  In GCC,
+        normalized significands are in the range [0.5, 1.0).  We
+        want the exponent as if they were [1.0, 2.0) so get the
+        exponent and subtract 1.  */
+      if (format->b == 2)
+       {
+         real_from_integer (result, format, REAL_EXP (arg) - 1, SIGNED);
+         return true;
+       }
+      return false;
+    }
+  gcc_unreachable ();
+}
+
+/* Try to evaluate:
+
+      *RESULT = significand (*ARG)
+
+   in format FORMAT.  Return true on success.  */
+
+static bool
+fold_const_significand (real_value *result, const real_value *arg,
+                       const real_format *format)
+{
+  switch (arg->cl)
+    {
+    case rvc_zero:
+    case rvc_nan:
+    case rvc_inf:
+      /* If arg is +-0, +-Inf or +-NaN, then return it.  */
+      *result = *arg;
+      return true;
+
+    case rvc_normal:
+      /* For normal numbers, proceed iff radix == 2.  */
+      if (format->b == 2)
+       {
+         *result = *arg;
+         /* In GCC, normalized significands are in the range [0.5, 1.0).
+            We want them to be [1.0, 2.0) so set the exponent to 1.  */
+         SET_REAL_EXP (result, 1);
+         return true;
+       }
+      return false;
+    }
+  gcc_unreachable ();
+}
+
+/* Try to evaluate:
+
+      *RESULT = f (*ARG)
+
+   where FORMAT is the format of *ARG and PRECISION is the number of
+   significant bits in the result.  Return true on success.  */
+
+static bool
+fold_const_conversion (wide_int *result,
+                      void (*fn) (real_value *, format_helper,
+                                  const real_value *),
+                      const real_value *arg, unsigned int precision,
+                      const real_format *format)
+{
+  if (!real_isfinite (arg))
+    return false;
+
+  real_value rounded;
+  fn (&rounded, format, arg);
+
+  bool fail = false;
+  *result = real_to_integer (&rounded, &fail, precision);
+  return !fail;
+}
+
+/* Try to evaluate:
+
+      *RESULT = pow (*ARG0, *ARG1)
+
+   in format FORMAT.  Return true on success.  */
+
+static bool
+fold_const_pow (real_value *result, const real_value *arg0,
+               const real_value *arg1, const real_format *format)
+{
+  if (do_mpfr_arg2 (result, mpfr_pow, arg0, arg1, format))
+    return true;
+
+  /* Check for an integer exponent.  */
+  REAL_VALUE_TYPE cint1;
+  HOST_WIDE_INT n1 = real_to_integer (arg1);
+  real_from_integer (&cint1, VOIDmode, n1, SIGNED);
+  /* Attempt to evaluate pow at compile-time, unless this should
+     raise an exception.  */
+  if (real_identical (arg1, &cint1)
+      && (n1 > 0
+         || (!flag_trapping_math && !flag_errno_math)
+         || !real_equal (arg0, &dconst0)))
+    {
+      bool inexact = real_powi (result, format, arg0, n1);
+      if (flag_unsafe_math_optimizations || !inexact)
+       return true;
+    }
+
+  return false;
+}
+
+/* Try to evaluate:
+
+      *RESULT = ldexp (*ARG0, ARG1)
+
+   in format FORMAT.  Return true on success.  */
+
+static bool
+fold_const_builtin_load_exponent (real_value *result, const real_value *arg0,
+                                 const wide_int_ref &arg1,
+                                 const real_format *format)
+{
+  /* Bound the maximum adjustment to twice the range of the
+     mode's valid exponents.  Use abs to ensure the range is
+     positive as a sanity check.  */
+  int max_exp_adj = 2 * labs (format->emax - format->emin);
+
+  /* The requested adjustment must be inside this range.  This
+     is a preliminary cap to avoid things like overflow, we
+     may still fail to compute the result for other reasons.  */
+  if (wi::les_p (arg1, -max_exp_adj) || wi::ges_p (arg1, max_exp_adj))
+    return false;
+
+  REAL_VALUE_TYPE initial_result;
+  real_ldexp (&initial_result, arg0, arg1.to_shwi ());
+
+  /* Ensure we didn't overflow.  */
+  if (real_isinf (&initial_result))
+    return false;
+
+  /* Only proceed if the target mode can hold the
+     resulting value.  */
+  *result = real_value_truncate (format, initial_result);
+  return real_equal (&initial_result, result);
+}
+
+/* Try to evaluate:
+
+      *RESULT = FN (*ARG)
+
+   in format FORMAT.  Return true on success.  */
+
+static bool
+fold_const_call_ss (real_value *result, built_in_function fn,
+                   const real_value *arg, const real_format *format)
+{
+  switch (fn)
+    {
+    CASE_FLT_FN (BUILT_IN_SQRT):
+      return (real_compare (GE_EXPR, arg, &dconst0)
+             && do_mpfr_arg1 (result, mpfr_sqrt, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_CBRT):
+      return do_mpfr_arg1 (result, mpfr_cbrt, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_ASIN):
+      return (real_compare (GE_EXPR, arg, &dconstm1)
+             && real_compare (LE_EXPR, arg, &dconst1)
+             && do_mpfr_arg1 (result, mpfr_asin, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_ACOS):
+      return (real_compare (GE_EXPR, arg, &dconstm1)
+             && real_compare (LE_EXPR, arg, &dconst1)
+             && do_mpfr_arg1 (result, mpfr_acos, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_ATAN):
+      return do_mpfr_arg1 (result, mpfr_atan, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_ASINH):
+      return do_mpfr_arg1 (result, mpfr_asinh, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_ACOSH):
+      return (real_compare (GE_EXPR, arg, &dconst1)
+             && do_mpfr_arg1 (result, mpfr_acosh, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_ATANH):
+      return (real_compare (GE_EXPR, arg, &dconstm1)
+             && real_compare (LE_EXPR, arg, &dconst1)
+             && do_mpfr_arg1 (result, mpfr_atanh, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_SIN):
+      return do_mpfr_arg1 (result, mpfr_sin, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_COS):
+      return do_mpfr_arg1 (result, mpfr_cos, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_TAN):
+      return do_mpfr_arg1 (result, mpfr_tan, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_SINH):
+      return do_mpfr_arg1 (result, mpfr_sinh, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_COSH):
+      return do_mpfr_arg1 (result, mpfr_cosh, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_TANH):
+      return do_mpfr_arg1 (result, mpfr_tanh, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_ERF):
+      return do_mpfr_arg1 (result, mpfr_erf, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_ERFC):
+      return do_mpfr_arg1 (result, mpfr_erfc, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_TGAMMA):
+      return do_mpfr_arg1 (result, mpfr_gamma, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_EXP):
+      return do_mpfr_arg1 (result, mpfr_exp, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_EXP2):
+      return do_mpfr_arg1 (result, mpfr_exp2, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_EXP10):
+    CASE_FLT_FN (BUILT_IN_POW10):
+      return do_mpfr_arg1 (result, mpfr_exp10, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_EXPM1):
+      return do_mpfr_arg1 (result, mpfr_expm1, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_LOG):
+      return (real_compare (GT_EXPR, arg, &dconst0)
+             && do_mpfr_arg1 (result, mpfr_log, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_LOG2):
+      return (real_compare (GT_EXPR, arg, &dconst0)
+             && do_mpfr_arg1 (result, mpfr_log2, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_LOG10):
+      return (real_compare (GT_EXPR, arg, &dconst0)
+             && do_mpfr_arg1 (result, mpfr_log10, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_LOG1P):
+      return (real_compare (GT_EXPR, arg, &dconstm1)
+             && do_mpfr_arg1 (result, mpfr_log1p, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_J0):
+      return do_mpfr_arg1 (result, mpfr_j0, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_J1):
+      return do_mpfr_arg1 (result, mpfr_j1, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_Y0):
+      return (real_compare (GT_EXPR, arg, &dconst0)
+             && do_mpfr_arg1 (result, mpfr_y0, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_Y1):
+      return (real_compare (GT_EXPR, arg, &dconst0)
+             && do_mpfr_arg1 (result, mpfr_y1, arg, format));
+
+    CASE_FLT_FN (BUILT_IN_FLOOR):
+      if (!REAL_VALUE_ISNAN (*arg) || !flag_errno_math)
+       {
+         real_floor (result, format, arg);
+         return true;
+       }
+      return false;
+
+    CASE_FLT_FN (BUILT_IN_CEIL):
+      if (!REAL_VALUE_ISNAN (*arg) || !flag_errno_math)
+       {
+         real_ceil (result, format, arg);
+         return true;
+       }
+      return false;
+
+    CASE_FLT_FN (BUILT_IN_TRUNC):
+      real_trunc (result, format, arg);
+      return true;
+
+    CASE_FLT_FN (BUILT_IN_ROUND):
+      if (!REAL_VALUE_ISNAN (*arg) || !flag_errno_math)
+       {
+         real_round (result, format, arg);
+         return true;
+       }
+      return false;
+
+    CASE_FLT_FN (BUILT_IN_LOGB):
+      return fold_const_logb (result, arg, format);
+
+    CASE_FLT_FN (BUILT_IN_SIGNIFICAND):
+      return fold_const_significand (result, arg, format);
+
+    default:
+      return false;
+    }
+}
+
+/* Try to evaluate:
+
+      *RESULT = FN (*ARG)
+
+   where FORMAT is the format of ARG and PRECISION is the number of
+   significant bits in the result.  Return true on success.  */
+
+static bool
+fold_const_call_ss (wide_int *result, built_in_function fn,
+                   const real_value *arg, unsigned int precision,
+                   const real_format *format)
+{
+  switch (fn)
+    {
+    CASE_FLT_FN (BUILT_IN_SIGNBIT):
+      if (real_isneg (arg))
+       *result = wi::one (precision);
+      else
+       *result = wi::zero (precision);
+      return true;
+
+    CASE_FLT_FN (BUILT_IN_ILOGB):
+      /* For ilogb we don't know FP_ILOGB0, so only handle normal values.
+        Proceed iff radix == 2.  In GCC, normalized significands are in
+        the range [0.5, 1.0).  We want the exponent as if they were
+        [1.0, 2.0) so get the exponent and subtract 1.  */
+      if (arg->cl == rvc_normal && format->b == 2)
+       {
+         *result = wi::shwi (REAL_EXP (arg) - 1, precision);
+         return true;
+       }
+      return false;
+
+    CASE_FLT_FN (BUILT_IN_ICEIL):
+    CASE_FLT_FN (BUILT_IN_LCEIL):
+    CASE_FLT_FN (BUILT_IN_LLCEIL):
+      return fold_const_conversion (result, real_ceil, arg,
+                                   precision, format);
+
+    CASE_FLT_FN (BUILT_IN_LFLOOR):
+    CASE_FLT_FN (BUILT_IN_IFLOOR):
+    CASE_FLT_FN (BUILT_IN_LLFLOOR):
+      return fold_const_conversion (result, real_floor, arg,
+                                   precision, format);
+
+    CASE_FLT_FN (BUILT_IN_IROUND):
+    CASE_FLT_FN (BUILT_IN_LROUND):
+    CASE_FLT_FN (BUILT_IN_LLROUND):
+      return fold_const_conversion (result, real_round, arg,
+                                   precision, format);
+
+    CASE_FLT_FN (BUILT_IN_IRINT):
+    CASE_FLT_FN (BUILT_IN_LRINT):
+    CASE_FLT_FN (BUILT_IN_LLRINT):
+      /* Not yet folded to a constant.  */
+      return false;
+
+    default:
+      return false;
+    }
+}
+
+/* Try to evaluate:
+
+      RESULT = FN (*ARG)
+
+   where FORMAT is the format of ARG and of the real and imaginary parts
+   of RESULT, passed as RESULT_REAL and RESULT_IMAG respectively.  Return
+   true on success.  */
+
+static bool
+fold_const_call_cs (real_value *result_real, real_value *result_imag,
+                   built_in_function fn, const real_value *arg,
+                   const real_format *format)
+{
+  switch (fn)
+    {
+    CASE_FLT_FN (BUILT_IN_CEXPI):
+      /* cexpi(x+yi) = cos(x)+sin(y)*i.  */
+      return do_mpfr_sincos (result_imag, result_real, arg, format);
+
+    default:
+      return false;
+    }
+}
+
+/* Try to evaluate:
+
+      *RESULT = fn (ARG)
+
+   where FORMAT is the format of RESULT and of the real and imaginary parts
+   of ARG, passed as ARG_REAL and ARG_IMAG respectively.  Return true on
+   success.  */
+
+static bool
+fold_const_call_sc (real_value *result, built_in_function fn,
+                   const real_value *arg_real, const real_value *arg_imag,
+                   const real_format *format)
+{
+  switch (fn)
+    {
+    CASE_FLT_FN (BUILT_IN_CABS):
+      return do_mpfr_arg2 (result, mpfr_hypot, arg_real, arg_imag, format);
+
+    default:
+      return false;
+    }
+}
+
+/* Try to evaluate:
+
+      RESULT = fn (ARG)
+
+   where FORMAT is the format of the real and imaginary parts of RESULT
+   (RESULT_REAL and RESULT_IMAG) and of ARG (ARG_REAL and ARG_IMAG).
+   Return true on success.  */
+
+static bool
+fold_const_call_cc (real_value *result_real, real_value *result_imag,
+                   built_in_function fn, const real_value *arg_real,
+                   const real_value *arg_imag, const real_format *format)
+{
+  switch (fn)
+    {
+    CASE_FLT_FN (BUILT_IN_CCOS):
+      return do_mpc_arg1 (result_real, result_imag, mpc_cos,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CCOSH):
+      return do_mpc_arg1 (result_real, result_imag, mpc_cosh,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CPROJ):
+      if (real_isinf (arg_real) || real_isinf (arg_imag))
+       {
+         real_inf (result_real);
+         *result_imag = dconst0;
+         result_imag->sign = arg_imag->sign;
+       }
+      else
+       {
+         *result_real = *arg_real;
+         *result_imag = *arg_imag;
+       }
+      return true;
+
+    CASE_FLT_FN (BUILT_IN_CSIN):
+      return do_mpc_arg1 (result_real, result_imag, mpc_sin,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CSINH):
+      return do_mpc_arg1 (result_real, result_imag, mpc_sinh,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CTAN):
+      return do_mpc_arg1 (result_real, result_imag, mpc_tan,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CTANH):
+      return do_mpc_arg1 (result_real, result_imag, mpc_tanh,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CLOG):
+      return do_mpc_arg1 (result_real, result_imag, mpc_log,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CSQRT):
+      return do_mpc_arg1 (result_real, result_imag, mpc_sqrt,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CASIN):
+      return do_mpc_arg1 (result_real, result_imag, mpc_asin,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CACOS):
+      return do_mpc_arg1 (result_real, result_imag, mpc_acos,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CATAN):
+      return do_mpc_arg1 (result_real, result_imag, mpc_atan,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CASINH):
+      return do_mpc_arg1 (result_real, result_imag, mpc_asinh,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CACOSH):
+      return do_mpc_arg1 (result_real, result_imag, mpc_acosh,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CATANH):
+      return do_mpc_arg1 (result_real, result_imag, mpc_atanh,
+                         arg_real, arg_imag, format);
+
+    CASE_FLT_FN (BUILT_IN_CEXP):
+      return do_mpc_arg1 (result_real, result_imag, mpc_exp,
+                         arg_real, arg_imag, format);
+
+    default:
+      return false;
+    }
+}
+
+/* Try to fold FN (ARG) to a constant.  Return the constant on success,
+   otherwise return null.  TYPE is the type of the return value.  */
+
+tree
+fold_const_call (built_in_function fn, tree type, tree arg)
+{
+  machine_mode mode = TYPE_MODE (type);
+  machine_mode arg_mode = TYPE_MODE (TREE_TYPE (arg));
+
+  if (real_cst_p (arg))
+    {
+      gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg_mode));
+      if (mode == arg_mode)
+       {
+         /* real -> real.  */
+         REAL_VALUE_TYPE result;
+         if (fold_const_call_ss (&result, fn, TREE_REAL_CST_PTR (arg),
+                                 REAL_MODE_FORMAT (mode)))
+           return build_real (type, result);
+       }
+      else if (COMPLEX_MODE_P (mode)
+              && GET_MODE_INNER (mode) == arg_mode)
+       {
+         /* real -> complex real.  */
+         REAL_VALUE_TYPE result_real, result_imag;
+         if (fold_const_call_cs (&result_real, &result_imag, fn,
+                                 TREE_REAL_CST_PTR (arg),
+                                 REAL_MODE_FORMAT (arg_mode)))
+           return build_complex (type,
+                                 build_real (TREE_TYPE (type), result_real),
+                                 build_real (TREE_TYPE (type), result_imag));
+       }
+      else if (INTEGRAL_TYPE_P (type))
+       {
+         /* real -> int.  */
+         wide_int result;
+         if (fold_const_call_ss (&result, fn,
+                                 TREE_REAL_CST_PTR (arg),
+                                 TYPE_PRECISION (type),
+                                 REAL_MODE_FORMAT (arg_mode)))
+           return wide_int_to_tree (type, result);
+       }
+      return NULL_TREE;
+    }
+
+  if (complex_cst_p (arg))
+    {
+      gcc_checking_assert (COMPLEX_MODE_P (arg_mode));
+      machine_mode inner_mode = GET_MODE_INNER (arg_mode);
+      tree argr = TREE_REALPART (arg);
+      tree argi = TREE_IMAGPART (arg);
+      if (mode == arg_mode
+         && real_cst_p (argr)
+         && real_cst_p (argi))
+       {
+         /* complex real -> complex real.  */
+         REAL_VALUE_TYPE result_real, result_imag;
+         if (fold_const_call_cc (&result_real, &result_imag, fn,
+                                 TREE_REAL_CST_PTR (argr),
+                                 TREE_REAL_CST_PTR (argi),
+                                 REAL_MODE_FORMAT (inner_mode)))
+           return build_complex (type,
+                                 build_real (TREE_TYPE (type), result_real),
+                                 build_real (TREE_TYPE (type), result_imag));
+       }
+      if (mode == inner_mode
+         && real_cst_p (argr)
+         && real_cst_p (argi))
+       {
+         /* complex real -> real.  */
+         REAL_VALUE_TYPE result;
+         if (fold_const_call_sc (&result, fn,
+                                 TREE_REAL_CST_PTR (argr),
+                                 TREE_REAL_CST_PTR (argi),
+                                 REAL_MODE_FORMAT (inner_mode)))
+           return build_real (type, result);
+       }
+      return NULL_TREE;
+    }
+
+  return NULL_TREE;
+}
+
+/* Try to evaluate:
+
+      *RESULT = FN (*ARG0, *ARG1)
+
+   in format FORMAT.  Return true on success.  */
+
+static bool
+fold_const_call_sss (real_value *result, built_in_function fn,
+                    const real_value *arg0, const real_value *arg1,
+                    const real_format *format)
+{
+  switch (fn)
+    {
+    CASE_FLT_FN (BUILT_IN_DREM):
+    CASE_FLT_FN (BUILT_IN_REMAINDER):
+      return do_mpfr_arg2 (result, mpfr_remainder, arg0, arg1, format);
+
+    CASE_FLT_FN (BUILT_IN_ATAN2):
+      return do_mpfr_arg2 (result, mpfr_atan2, arg0, arg1, format);
+
+    CASE_FLT_FN (BUILT_IN_FDIM):
+      return do_mpfr_arg2 (result, mpfr_dim, arg0, arg1, format);
+
+    CASE_FLT_FN (BUILT_IN_HYPOT):
+      return do_mpfr_arg2 (result, mpfr_hypot, arg0, arg1, format);
+
+    CASE_FLT_FN (BUILT_IN_COPYSIGN):
+      *result = *arg0;
+      real_copysign (result, arg1);
+      return true;
+
+    CASE_FLT_FN (BUILT_IN_FMIN):
+      return do_mpfr_arg2 (result, mpfr_min, arg0, arg1, format);
+
+    CASE_FLT_FN (BUILT_IN_FMAX):
+      return do_mpfr_arg2 (result, mpfr_max, arg0, arg1, format);
+
+    CASE_FLT_FN (BUILT_IN_POW):
+      return fold_const_pow (result, arg0, arg1, format);
+
+    default:
+      return false;
+    }
+}
+
+/* Try to evaluate:
+
+      *RESULT = FN (*ARG0, ARG1)
+
+   where FORMAT is the format of *RESULT and *ARG0.  Return true on
+   success.  */
+
+static bool
+fold_const_call_sss (real_value *result, built_in_function fn,
+                    const real_value *arg0, const wide_int_ref &arg1,
+                    const real_format *format)
+{
+  switch (fn)
+    {
+    CASE_FLT_FN (BUILT_IN_LDEXP):
+      return fold_const_builtin_load_exponent (result, arg0, arg1, format);
+
+    CASE_FLT_FN (BUILT_IN_SCALBN):
+    CASE_FLT_FN (BUILT_IN_SCALBLN):
+      return (format->b == 2
+             && fold_const_builtin_load_exponent (result, arg0, arg1,
+                                                  format));
+
+    CASE_FLT_FN (BUILT_IN_POWI):
+      real_powi (result, format, arg0, arg1.to_shwi ());
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+/* Try to evaluate:
+
+      *RESULT = FN (ARG0, *ARG1)
+
+   where FORMAT is the format of *RESULT and *ARG1.  Return true on
+   success.  */
+
+static bool
+fold_const_call_sss (real_value *result, built_in_function fn,
+                    const wide_int_ref &arg0, const real_value *arg1,
+                    const real_format *format)
+{
+  switch (fn)
+    {
+    CASE_FLT_FN (BUILT_IN_JN):
+      return do_mpfr_arg2 (result, mpfr_jn, arg0, arg1, format);
+
+    CASE_FLT_FN (BUILT_IN_YN):
+      return (real_compare (GT_EXPR, arg1, &dconst0)
+             && do_mpfr_arg2 (result, mpfr_yn, arg0, arg1, format));
+
+    default:
+      return false;
+    }
+}
+
+/* Try to evaluate:
+
+      RESULT = fn (ARG0, ARG1)
+
+   where FORMAT is the format of the real and imaginary parts of RESULT
+   (RESULT_REAL and RESULT_IMAG), of ARG0 (ARG0_REAL and ARG0_IMAG)
+   and of ARG1 (ARG1_REAL and ARG1_IMAG).  Return true on success.  */
+
+static bool
+fold_const_call_ccc (real_value *result_real, real_value *result_imag,
+                    built_in_function fn, const real_value *arg0_real,
+                    const real_value *arg0_imag, const real_value *arg1_real,
+                    const real_value *arg1_imag, const real_format *format)
+{
+  switch (fn)
+    {
+    CASE_FLT_FN (BUILT_IN_CPOW):
+      return do_mpc_arg2 (result_real, result_imag, mpc_pow,
+                         arg0_real, arg0_imag, arg1_real, arg1_imag, format);
+
+    default:
+      return false;
+    }
+}
+
+/* Try to fold FN (ARG0, ARG1) to a constant.  Return the constant on success,
+   otherwise return null.  TYPE is the type of the return value.  */
+
+tree
+fold_const_call (built_in_function fn, tree type, tree arg0, tree arg1)
+{
+  machine_mode mode = TYPE_MODE (type);
+  machine_mode arg0_mode = TYPE_MODE (TREE_TYPE (arg0));
+  machine_mode arg1_mode = TYPE_MODE (TREE_TYPE (arg1));
+
+  if (arg0_mode == arg1_mode
+      && real_cst_p (arg0)
+      && real_cst_p (arg1))
+    {
+      gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg0_mode));
+      if (mode == arg0_mode)
+       {
+         /* real, real -> real.  */
+         REAL_VALUE_TYPE result;
+         if (fold_const_call_sss (&result, fn, TREE_REAL_CST_PTR (arg0),
+                                  TREE_REAL_CST_PTR (arg1),
+                                  REAL_MODE_FORMAT (mode)))
+           return build_real (type, result);
+       }
+      return NULL_TREE;
+    }
+
+  if (real_cst_p (arg0)
+      && integer_cst_p (arg1))
+    {
+      gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg0_mode));
+      if (mode == arg0_mode)
+       {
+         /* real, int -> real.  */
+         REAL_VALUE_TYPE result;
+         if (fold_const_call_sss (&result, fn, TREE_REAL_CST_PTR (arg0),
+                                  arg1, REAL_MODE_FORMAT (mode)))
+           return build_real (type, result);
+       }
+      return NULL_TREE;
+    }
+
+  if (integer_cst_p (arg0)
+      && real_cst_p (arg1))
+    {
+      gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg1_mode));
+      if (mode == arg1_mode)
+       {
+         /* int, real -> real.  */
+         REAL_VALUE_TYPE result;
+         if (fold_const_call_sss (&result, fn, arg0,
+                                  TREE_REAL_CST_PTR (arg1),
+                                  REAL_MODE_FORMAT (mode)))
+           return build_real (type, result);
+       }
+      return NULL_TREE;
+    }
+
+  if (arg0_mode == arg1_mode
+      && complex_cst_p (arg0)
+      && complex_cst_p (arg1))
+    {
+      gcc_checking_assert (COMPLEX_MODE_P (arg0_mode));
+      machine_mode inner_mode = GET_MODE_INNER (arg0_mode);
+      tree arg0r = TREE_REALPART (arg0);
+      tree arg0i = TREE_IMAGPART (arg0);
+      tree arg1r = TREE_REALPART (arg1);
+      tree arg1i = TREE_IMAGPART (arg1);
+      if (mode == arg0_mode
+         && real_cst_p (arg0r)
+         && real_cst_p (arg0i)
+         && real_cst_p (arg1r)
+         && real_cst_p (arg1i))
+       {
+         /* complex real, complex real -> complex real.  */
+         REAL_VALUE_TYPE result_real, result_imag;
+         if (fold_const_call_ccc (&result_real, &result_imag, fn,
+                                  TREE_REAL_CST_PTR (arg0r),
+                                  TREE_REAL_CST_PTR (arg0i),
+                                  TREE_REAL_CST_PTR (arg1r),
+                                  TREE_REAL_CST_PTR (arg1i),
+                                  REAL_MODE_FORMAT (inner_mode)))
+           return build_complex (type,
+                                 build_real (TREE_TYPE (type), result_real),
+                                 build_real (TREE_TYPE (type), result_imag));
+       }
+      return NULL_TREE;
+    }
+
+  return NULL_TREE;
+}
+
+/* Try to evaluate:
+
+      *RESULT = FN (*ARG0, *ARG1, *ARG2)
+
+   in format FORMAT.  Return true on success.  */
+
+static bool
+fold_const_call_ssss (real_value *result, built_in_function fn,
+                     const real_value *arg0, const real_value *arg1,
+                     const real_value *arg2, const real_format *format)
+{
+  switch (fn)
+    {
+    CASE_FLT_FN (BUILT_IN_FMA):
+      return do_mpfr_arg3 (result, mpfr_fma, arg0, arg1, arg2, format);
+
+    default:
+      return false;
+    }
+}
+
+/* Try to fold FN (ARG0, ARG1, ARG2) to a constant.  Return the constant on
+   success, otherwise return null.  TYPE is the type of the return value.  */
+
+tree
+fold_const_call (built_in_function fn, tree type, tree arg0, tree arg1,
+                tree arg2)
+{
+  machine_mode mode = TYPE_MODE (type);
+  machine_mode arg0_mode = TYPE_MODE (TREE_TYPE (arg0));
+  machine_mode arg1_mode = TYPE_MODE (TREE_TYPE (arg1));
+  machine_mode arg2_mode = TYPE_MODE (TREE_TYPE (arg2));
+
+  if (arg0_mode == arg1_mode
+      && arg0_mode == arg2_mode
+      && real_cst_p (arg0)
+      && real_cst_p (arg1)
+      && real_cst_p (arg2))
+    {
+      gcc_checking_assert (SCALAR_FLOAT_MODE_P (arg0_mode));
+      if (mode == arg0_mode)
+       {
+         /* real, real, real -> real.  */
+         REAL_VALUE_TYPE result;
+         if (fold_const_call_ssss (&result, fn, TREE_REAL_CST_PTR (arg0),
+                                   TREE_REAL_CST_PTR (arg1),
+                                   TREE_REAL_CST_PTR (arg2),
+                                   REAL_MODE_FORMAT (mode)))
+           return build_real (type, result);
+       }
+      return NULL_TREE;
+    }
+
+  return NULL_TREE;
+}
+
+/* Fold a fma operation with arguments ARG[012].  */
+
+tree
+fold_fma (location_t, tree type, tree arg0, tree arg1, tree arg2)
+{
+  REAL_VALUE_TYPE result;
+  if (real_cst_p (arg0)
+      && real_cst_p (arg1)
+      && real_cst_p (arg2)
+      && do_mpfr_arg3 (&result, mpfr_fma, TREE_REAL_CST_PTR (arg0),
+                      TREE_REAL_CST_PTR (arg1), TREE_REAL_CST_PTR (arg2),
+                      REAL_MODE_FORMAT (TYPE_MODE (type))))
+    return build_real (type, result);
+
+  return NULL_TREE;
+}
diff --git a/gcc/fold-const-call.h b/gcc/fold-const-call.h
new file mode 100644 (file)
index 0000000..6befc9b
--- /dev/null
@@ -0,0 +1,28 @@
+/* Fold calls to built-in and internal functions with constant arguments.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_FOLD_CONST_CALL_H
+#define GCC_FOLD_CONST_CALL_H
+
+tree fold_const_call (built_in_function, tree, tree);
+tree fold_const_call (built_in_function, tree, tree, tree);
+tree fold_const_call (built_in_function, tree, tree, tree, tree);
+tree fold_fma (location_t, tree, tree, tree, tree);
+
+#endif
index 1a2fb810907f354c5ef11e0f81bb1f2bbe83aa53..ee9b34910df33f223a03674b8ce56cda1c14bd7a 100644 (file)
@@ -57,6 +57,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "alias.h"
 #include "fold-const.h"
+#include "fold-const-call.h"
 #include "stor-layout.h"
 #include "calls.h"
 #include "tree-iterator.h"
index 8d6549c6c36a1427c08885e73392fe24c46c3e9e..e5f10c8d9e80a1aa7ed5f6dcbbc573a6bb22c2bc 100644 (file)
@@ -54,11 +54,12 @@ mpfr_from_real (mpfr_ptr m, const REAL_VALUE_TYPE *r, mp_rnd_t rndmode)
   gcc_assert (ret == 0);
 }
 
-/* Convert from MPFR to REAL_VALUE_TYPE, for a given type TYPE and rounding
-   mode RNDMODE.  TYPE is only relevant if M is a NaN.  */
+/* Convert from MPFR to REAL_VALUE_TYPE, for a given format FORMAT and
+   rounding mode RNDMODE.  FORMAT is only relevant if M is a NaN.  */
 
 void
-real_from_mpfr (REAL_VALUE_TYPE *r, mpfr_srcptr m, tree type, mp_rnd_t rndmode)
+real_from_mpfr (REAL_VALUE_TYPE *r, mpfr_srcptr m, const real_format *format,
+               mp_rnd_t rndmode)
 {
   /* We use a string as an intermediate type.  */
   char buf[128], *rstr;
@@ -75,7 +76,7 @@ real_from_mpfr (REAL_VALUE_TYPE *r, mpfr_srcptr m, tree type, mp_rnd_t rndmode)
 
   if (mpfr_nan_p (m))
     {
-      real_nan (r, "", 1, TYPE_MODE (type));
+      real_nan (r, "", 1, format);
       return;
     }
 
@@ -100,3 +101,13 @@ real_from_mpfr (REAL_VALUE_TYPE *r, mpfr_srcptr m, tree type, mp_rnd_t rndmode)
   real_from_string (r, buf);
 }
 
+/* Convert from MPFR to REAL_VALUE_TYPE, for a given type TYPE and rounding
+   mode RNDMODE.  TYPE is only relevant if M is a NaN.  */
+
+void
+real_from_mpfr (REAL_VALUE_TYPE *r, mpfr_srcptr m, tree type, mp_rnd_t rndmode)
+{
+  real_from_mpfr (r, m, type ? REAL_MODE_FORMAT (TYPE_MODE (type)) : NULL,
+                 rndmode);
+}
+
index 71497d7aff71e11c9614fc2ddd5158bd6687e72b..7e70db2500a0553f91109d72776bb494214650f6 100644 (file)
@@ -28,6 +28,8 @@
    responsible for initializing and clearing the MPFR parameter.  */
 
 extern void real_from_mpfr (REAL_VALUE_TYPE *, mpfr_srcptr, tree, mp_rnd_t);
+extern void real_from_mpfr (REAL_VALUE_TYPE *, mpfr_srcptr,
+                           const real_format *, mp_rnd_t);
 extern void mpfr_from_real (mpfr_ptr, const REAL_VALUE_TYPE *, mp_rnd_t);
 
 #endif /* ! GCC_REALGMP_H */