[Patch 6/17] Migrate excess precision logic to use TARGET_EXCESS_PRECISION
authorJames Greenhalgh <james.greenhalgh@arm.com>
Wed, 23 Nov 2016 17:23:12 +0000 (17:23 +0000)
committerJames Greenhalgh <jgreenhalgh@gcc.gnu.org>
Wed, 23 Nov 2016 17:23:12 +0000 (17:23 +0000)
gcc/

* toplev.c (init_excess_precision): Delete most logic.
* tree.c (excess_precision_type): Rewrite to use
TARGET_EXCESS_PRECISION.
* doc/invoke.texi (-fexcess-precision): Document behaviour in a
more generic fashion.
* ginclude/float.h: Wrap definition of FLT_EVAL_METHOD in
__STDC_WANT_IEC_60559_TYPES_EXT__.

gcc/c-family/

* c-common.c (excess_precision_mode_join): New.
(c_ts18661_flt_eval_method): New.
(c_c11_flt_eval_method): Likewise.
(c_flt_eval_method): Likewise.
* c-common.h (excess_precision_mode_join): New.
(c_flt_eval_method): Likewise.
* c-cppbuiltin.c (c_cpp_flt_eval_method_iec_559): New.
(cpp_iec_559_value): Call it.
(c_cpp_builtins): Modify logic for __LIBGCC_*_EXCESS_PRECISION__,
call c_flt_eval_method to set __FLT_EVAL_METHOD__ and
__FLT_EVAL_METHOD_TS_18661_3__.

gcc/testsuite/

* gcc.dg/fpermitted-flt-eval-methods_3.c: New.
* gcc.dg/fpermitted-flt-eval-methods_4.c: Likewise.

From-SVN: r242776

12 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c-family/c-cppbuiltin.c
gcc/doc/invoke.texi
gcc/ginclude/float.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/fpermitted-flt-eval-methods_3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/fpermitted-flt-eval-methods_4.c [new file with mode: 0644]
gcc/toplev.c
gcc/tree.c

index 8ce444b384797358ab6127e05d1415cdfd9b9b0d..66045fb9fefef9b414d17135cadcfd1a3e758cd2 100644 (file)
@@ -1,3 +1,13 @@
+2016-11-23  James Greenhalgh  <james.greenhalgh@arm.com>
+
+       * toplev.c (init_excess_precision): Delete most logic.
+       * tree.c (excess_precision_type): Rewrite to use
+       TARGET_EXCESS_PRECISION.
+       * doc/invoke.texi (-fexcess-precision): Document behaviour in a
+       more generic fashion.
+       * ginclude/float.h: Wrap definition of FLT_EVAL_METHOD in
+       __STDC_WANT_IEC_60559_TYPES_EXT__.
+
 2016-11-23  James Greenhalgh  <james.greenhalgh@arm.com>
 
        * common.opt (fpermitted-flt-eval-methods): New.
index f4d81a19515aa7c024dd6ed88f660cd2c2be1f4d..b2f712f5256d50c9355a0d838407df1b2018dda3 100644 (file)
@@ -1,3 +1,17 @@
+2016-11-23  James Greenhalgh  <james.greenhalgh@arm.com>
+
+       * c-common.c (excess_precision_mode_join): New.
+       (c_ts18661_flt_eval_method): New.
+       (c_c11_flt_eval_method): Likewise.
+       (c_flt_eval_method): Likewise.
+       * c-common.h (excess_precision_mode_join): New.
+       (c_flt_eval_method): Likewise.
+       * c-cppbuiltin.c (c_cpp_flt_eval_method_iec_559): New.
+       (cpp_iec_559_value): Call it.
+       (c_cpp_builtins): Modify logic for __LIBGCC_*_EXCESS_PRECISION__,
+       call c_flt_eval_method to set __FLT_EVAL_METHOD__ and
+       __FLT_EVAL_METHOD_TS_18661_3__.
+
 2016-11-23  James Greenhalgh  <james.greenhalgh@arm.com>
 
        * c-opts.c (c_common_post_options): Add logic to handle the default
index cc18f295e00c3d2a377f8bce64e21fc3dc21009c..62174a9eaa5c708df386bc6f94f9a6f1f99a6661 100644 (file)
@@ -7950,4 +7950,86 @@ cb_get_suggestion (cpp_reader *, const char *goal,
   return bm.get_best_meaningful_candidate ();
 }
 
+/* Return the latice point which is the wider of the two FLT_EVAL_METHOD
+   modes X, Y.  This isn't just  >, as the FLT_EVAL_METHOD values added
+   by C TS 18661-3 for interchange  types that are computed in their
+   native precision are larger than the C11 values for evaluating in the
+   precision of float/double/long double.  If either mode is
+   FLT_EVAL_METHOD_UNPREDICTABLE, return that.  */
+
+enum flt_eval_method
+excess_precision_mode_join (enum flt_eval_method x,
+                           enum flt_eval_method y)
+{
+  if (x == FLT_EVAL_METHOD_UNPREDICTABLE
+      || y == FLT_EVAL_METHOD_UNPREDICTABLE)
+    return FLT_EVAL_METHOD_UNPREDICTABLE;
+
+  /* GCC only supports one interchange type right now, _Float16.  If
+     we're evaluating _Float16 in 16-bit precision, then flt_eval_method
+     will be FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16.  */
+  if (x == FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16)
+    return y;
+  if (y == FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16)
+    return x;
+
+  /* Other values for flt_eval_method are directly comparable, and we want
+     the maximum.  */
+  return MAX (x, y);
+}
+
+/* Return the value that should be set for FLT_EVAL_METHOD in the
+   context of ISO/IEC TS 18861-3.
+
+   This relates to the effective excess precision seen by the user,
+   which is the join point of the precision the target requests for
+   -fexcess-precision={standard,fast} and the implicit excess precision
+   the target uses.  */
+
+static enum flt_eval_method
+c_ts18661_flt_eval_method (void)
+{
+  enum flt_eval_method implicit
+    = targetm.c.excess_precision (EXCESS_PRECISION_TYPE_IMPLICIT);
+
+  enum excess_precision_type flag_type
+    = (flag_excess_precision_cmdline == EXCESS_PRECISION_STANDARD
+       ? EXCESS_PRECISION_TYPE_STANDARD
+       : EXCESS_PRECISION_TYPE_FAST);
+
+  enum flt_eval_method requested
+    = targetm.c.excess_precision (flag_type);
+
+  return excess_precision_mode_join (implicit, requested);
+}
+
+/* As c_cpp_ts18661_flt_eval_method, but clamps the expected values to
+   those that were permitted by C11.  That is to say, eliminates
+   FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16.  */
+
+static enum flt_eval_method
+c_c11_flt_eval_method (void)
+{
+  return excess_precision_mode_join (c_ts18661_flt_eval_method (),
+                                    FLT_EVAL_METHOD_PROMOTE_TO_FLOAT);
+}
+
+/* Return the value that should be set for FLT_EVAL_METHOD.
+   MAYBE_C11_ONLY_P is TRUE if we should check
+   FLAG_PERMITTED_EVAL_METHODS as to whether we should limit the possible
+   values we can return to those from C99/C11, and FALSE otherwise.
+   See the comments on c_ts18661_flt_eval_method for what value we choose
+   to set here.  */
+
+int
+c_flt_eval_method (bool maybe_c11_only_p)
+{
+  if (maybe_c11_only_p
+      && flag_permitted_flt_eval_methods
+         == PERMITTED_FLT_EVAL_METHODS_C11)
+    return c_c11_flt_eval_method ();
+  else
+    return c_ts18661_flt_eval_method ();
+}
+
 #include "gt-c-family-c-common.h"
index 241b3454e06478cc0a4e589263b258ceeb2aa457..a23193ee9db9dfbac6946e0c359f3bc0b28ab6cf 100644 (file)
@@ -1546,6 +1546,11 @@ extern int tm_attr_to_mask (tree);
 extern tree tm_mask_to_attr (int);
 extern tree find_tm_attribute (tree);
 
+extern enum flt_eval_method
+excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
+
+extern int c_flt_eval_method (bool ts18661_p);
+
 #if CHECKING_P
 namespace selftest {
   extern void c_format_c_tests (void);
index 5c88e91e9e34f578831afae312bc76548d5ecb81..e2419e81233101f68433109f4dff13a588a4e67a 100644 (file)
@@ -728,6 +728,31 @@ cpp_atomic_builtins (cpp_reader *pfile)
                        (have_swap[psize]? 2 : 1));
 }
 
+/* Return TRUE if the implicit excess precision in which the back-end will
+   compute floating-point calculations is not more than the explicit
+   excess precision that the front-end will apply under
+   -fexcess-precision=[standard|fast].
+
+   More intuitively, return TRUE if the excess precision proposed by the
+   front-end is the excess precision that will actually be used.  */
+
+static bool
+c_cpp_flt_eval_method_iec_559 (void)
+{
+  enum excess_precision_type front_end_ept
+    = (flag_excess_precision_cmdline == EXCESS_PRECISION_STANDARD
+       ? EXCESS_PRECISION_TYPE_STANDARD
+       : EXCESS_PRECISION_TYPE_FAST);
+
+  enum flt_eval_method back_end
+    = targetm.c.excess_precision (EXCESS_PRECISION_TYPE_IMPLICIT);
+
+  enum flt_eval_method front_end
+    = targetm.c.excess_precision (front_end_ept);
+
+  return excess_precision_mode_join (front_end, back_end) == front_end;
+}
+
 /* Return the value for __GCC_IEC_559.  */
 static int
 cpp_iec_559_value (void)
@@ -770,16 +795,17 @@ cpp_iec_559_value (void)
       || !dfmt->has_signed_zero)
     ret = 0;
 
-  /* In strict C standards conformance mode, consider unpredictable
-     excess precision to mean lack of IEEE 754 support.  The same
-     applies to unpredictable contraction.  For C++, and outside
-     strict conformance mode, do not consider these options to mean
-     lack of IEEE 754 support.  */
+  /* In strict C standards conformance mode, consider a back-end providing
+     more implicit excess precision than the explicit excess precision
+     the front-end options would require to mean a lack of IEEE 754
+     support.  For C++, and outside strict conformance mode, do not consider
+     this to mean a lack of IEEE 754 support.  */
+
   if (flag_iso
       && !c_dialect_cxx ()
-      && TARGET_FLT_EVAL_METHOD != 0
-      && flag_excess_precision_cmdline != EXCESS_PRECISION_STANDARD)
+      && !c_cpp_flt_eval_method_iec_559 ())
     ret = 0;
+
   if (flag_iso
       && !c_dialect_cxx ()
       && flag_fp_contract_mode == FP_CONTRACT_FAST)
@@ -1045,9 +1071,22 @@ c_cpp_builtins (cpp_reader *pfile)
   builtin_define_with_int_value ("__GCC_IEC_559_COMPLEX",
                                 cpp_iec_559_complex_value ());
 
-  /* float.h needs to know this.  */
+  /* float.h needs these to correctly set FLT_EVAL_METHOD
+
+     We define two values:
+
+     __FLT_EVAL_METHOD__
+       Which, depending on the value given for
+       -fpermitted-flt-eval-methods, may be limited to only those values
+       for FLT_EVAL_METHOD defined in C99/C11.
+
+     __FLT_EVAL_METHOD_TS_18661_3__
+       Which always permits the values for FLT_EVAL_METHOD defined in
+       ISO/IEC TS 18661-3.  */
   builtin_define_with_int_value ("__FLT_EVAL_METHOD__",
-                                TARGET_FLT_EVAL_METHOD);
+                                c_flt_eval_method (true));
+  builtin_define_with_int_value ("__FLT_EVAL_METHOD_TS_18661_3__",
+                                c_flt_eval_method (false));
 
   /* And decfloat.h needs this.  */
   builtin_define_with_int_value ("__DEC_EVAL_METHOD__",
@@ -1188,25 +1227,38 @@ c_cpp_builtins (cpp_reader *pfile)
              gcc_assert (found_suffix);
            }
          builtin_define_with_value (macro_name, suffix, 0);
+
+         /* The way __LIBGCC_*_EXCESS_PRECISION__ is used is about
+            eliminating excess precision from results assigned to
+            variables - meaning it should be about the implicit excess
+            precision only.  */
          bool excess_precision = false;
-         if (TARGET_FLT_EVAL_METHOD != 0
-             && mode != TYPE_MODE (long_double_type_node)
-             && (mode == TYPE_MODE (float_type_node)
-                 || mode == TYPE_MODE (double_type_node)))
-           switch (TARGET_FLT_EVAL_METHOD)
-             {
-             case -1:
-             case 2:
-               excess_precision = true;
-               break;
-
-             case 1:
-               excess_precision = mode == TYPE_MODE (float_type_node);
-               break;
-
-             default:
-               gcc_unreachable ();
-             }
+         machine_mode float16_type_mode = (float16_type_node
+                                           ? TYPE_MODE (float16_type_node)
+                                           : VOIDmode);
+         switch (targetm.c.excess_precision
+                   (EXCESS_PRECISION_TYPE_IMPLICIT))
+           {
+           case FLT_EVAL_METHOD_UNPREDICTABLE:
+           case FLT_EVAL_METHOD_PROMOTE_TO_LONG_DOUBLE:
+             excess_precision = (mode == float16_type_mode
+                                 || mode == TYPE_MODE (float_type_node)
+                                 || mode == TYPE_MODE (double_type_node));
+             break;
+
+           case FLT_EVAL_METHOD_PROMOTE_TO_DOUBLE:
+             excess_precision = (mode == float16_type_mode
+                                 || mode == TYPE_MODE (float_type_node));
+             break;
+           case FLT_EVAL_METHOD_PROMOTE_TO_FLOAT:
+             excess_precision = mode == float16_type_mode;
+             break;
+           case FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16:
+             excess_precision = false;
+             break;
+           default:
+             gcc_unreachable ();
+           }
          macro_name = (char *) alloca (strlen (name)
                                        + sizeof ("__LIBGCC__EXCESS_"
                                                  "PRECISION__"));
index a9d060999352d0e7e80b6e56778352cf972b929f..22f539d440725c005e7b7b5260043cf7d994ecb4 100644 (file)
@@ -8987,15 +8987,14 @@ them to store all pertinent intermediate computations into variables.
 @item -fexcess-precision=@var{style}
 @opindex fexcess-precision
 This option allows further control over excess precision on machines
-where floating-point registers have more precision than the IEEE
-@code{float} and @code{double} types and the processor does not
-support operations rounding to those types.  By default,
-@option{-fexcess-precision=fast} is in effect; this means that
-operations are carried out in the precision of the registers and that
-it is unpredictable when rounding to the types specified in the source
-code takes place.  When compiling C, if
-@option{-fexcess-precision=standard} is specified then excess
-precision follows the rules specified in ISO C99; in particular,
+where floating-point operations occur in a format with more precision or
+range than the IEEE standard and interchange floating-point types.  By
+default, @option{-fexcess-precision=fast} is in effect; this means that
+operations may be carried out in a wider precision than the types specified
+in the source if that would result in faster code, and it is unpredictable
+when rounding to the types specified in the source code takes place.
+When compiling C, if @option{-fexcess-precision=standard} is specified then
+excess precision follows the rules specified in ISO C99; in particular,
 both casts and assignments cause values to be rounded to their
 semantic types (whereas @option{-ffloat-store} only affects
 assignments).  This option is enabled by default for C if a strict
index 421f7351e37477760fcbcd506061739a1ce1cadd..3df2889164ffaebe575d02b3c6eec87b24a2885d 100644 (file)
@@ -129,21 +129,73 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 #if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) \
      || (defined (__cplusplus) && __cplusplus >= 201103L)
-/* The floating-point expression evaluation method.
-        -1  indeterminate
-         0  evaluate all operations and constants just to the range and
-            precision of the type
-         1  evaluate operations and constants of type float and double
-            to the range and precision of the double type, evaluate
-            long double operations and constants to the range and
-            precision of the long double type
-         2  evaluate all operations and constants to the range and
-            precision of the long double type
+/* The floating-point expression evaluation method.  The precise
+   definitions of these values are generalised to include support for
+   the interchange and extended types defined in ISO/IEC TS 18661-3.
+   Prior to this (for C99/C11) the definitions were:
+
+       -1  indeterminate
+        0  evaluate all operations and constants just to the range and
+           precision of the type
+        1  evaluate operations and constants of type float and double
+           to the range and precision of the double type, evaluate
+           long double operations and constants to the range and
+           precision of the long double type
+        2  evaluate all operations and constants to the range and
+           precision of the long double type
+
+   The TS 18661-3 definitions are:
+
+       -1  indeterminate
+        0  evaluate all operations and constants, whose semantic type has
+           at most the range and precision of float, to the range and
+           precision of float; evaluate all other operations and constants
+           to the range and precision of the semantic type.
+        1  evaluate all operations and constants, whose semantic type has
+           at most the range and precision of double, to the range and
+           precision of double; evaluate all other operations and constants
+           to the range and precision of the semantic type.
+        2  evaluate all operations and constants, whose semantic type has
+           at most the range and precision of long double, to the range and
+           precision of long double; evaluate all other operations and
+           constants to the range and precision of the semantic type.
+        N  where _FloatN  is a supported interchange floating type
+           evaluate all operations and constants, whose semantic type has
+           at most the range and precision of the _FloatN type, to the
+           range and precision of the _FloatN type; evaluate all other
+           operations and constants to the range and precision of the
+           semantic type.
+        N + 1, where _FloatNx is a supported extended floating type
+           evaluate operations and constants, whose semantic type has at
+           most the range and precision of the _FloatNx type, to the range
+           and precision of the _FloatNx type; evaluate all other
+           operations and constants to the range and precision of the
+           semantic type.
+
+   The compiler predefines two macros:
+
+      __FLT_EVAL_METHOD__
+      Which, depending on the value given for
+      -fpermitted-flt-eval-methods, may be limited to only those values
+      for FLT_EVAL_METHOD defined in C99/C11.
+
+     __FLT_EVAL_METHOD_TS_18661_3__
+      Which always permits the values for FLT_EVAL_METHOD defined in
+      ISO/IEC TS 18661-3.
+
+     Here we want to use __FLT_EVAL_METHOD__, unless
+     __STDC_WANT_IEC_60559_TYPES_EXT__ is defined, in which case the user
+     is specifically asking for the ISO/IEC TS 18661-3 types, so we use
+     __FLT_EVAL_METHOD_TS_18661_3__.
 
    ??? This ought to change with the setting of the fp control word;
    the value provided by the compiler assumes the widest setting.  */
 #undef FLT_EVAL_METHOD
+#ifdef __STDC_WANT_IEC_60559_TYPES_EXT__
+#define FLT_EVAL_METHOD __FLT_EVAL_METHOD_TS_18661_3__
+#else
 #define FLT_EVAL_METHOD        __FLT_EVAL_METHOD__
+#endif
 
 /* Number of decimal digits, n, such that any floating-point number in the
    widest supported floating type with pmax radix b digits can be rounded
index 40d00326111858d34081fd03b76959d2a6e10a2f..99c89b60d391724e5b084c42cc8c43d7ea9a55c3 100644 (file)
@@ -1,3 +1,8 @@
+2016-11-23  James Greenhalgh  <james.greenhalgh@arm.com>
+
+       * gcc.dg/fpermitted-flt-eval-methods_3.c: New.
+       * gcc.dg/fpermitted-flt-eval-methods_4.c: Likewise.
+
 2016-11-23  James Greenhalgh  <james.greenhalgh@arm.com>
 
        * gcc.dg/fpermitted-flt-eval-methods_1.c: New.
diff --git a/gcc/testsuite/gcc.dg/fpermitted-flt-eval-methods_3.c b/gcc/testsuite/gcc.dg/fpermitted-flt-eval-methods_3.c
new file mode 100644 (file)
index 0000000..c7bd756
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do run } */
+/* { dg-options "-std=c11" } */
+
+/* Test that when compiling with -std=c11, we only see the C99/C11 values
+   for FLT_EVAL_METHOD.  */
+
+#include <float.h>
+
+int main (int argc, char** argv)
+{
+  switch (FLT_EVAL_METHOD)
+    {
+      case 0:
+      case 1:
+      case 2:
+      case -1:
+       return 0;
+      default:
+       return 1;
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/fpermitted-flt-eval-methods_4.c b/gcc/testsuite/gcc.dg/fpermitted-flt-eval-methods_4.c
new file mode 100644 (file)
index 0000000..a7bbb65
--- /dev/null
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-options "-std=c11" } */
+
+/* Test that when compiling with -std=c11 and defining
+   __STDC_WANT_IEC_60559_TYPES_EXT__, we only see the ISO/IEC TS
+   18661-3 values for FLT_EVAL_METHOD.  */
+
+#define __STDC_WANT_IEC_60559_TYPES_EXT__
+
+#include <float.h>
+
+int main (int argc, char** argv)
+{
+  switch (__FLT_EVAL_METHOD__)
+    {
+      case 0:
+      case 1:
+      case 2:
+      case 16:
+      case -1:
+       return 0;
+      default:
+       return 1;
+    }
+}
index d43234a65c1d86aed729a1703f6a02f675a6c777..5af02ea34e826e3032b26c457b1c2942d9c83439 100644 (file)
@@ -1680,41 +1680,17 @@ backend_init (void)
   init_regs ();
 }
 
-/* Initialize excess precision settings.  */
+/* Initialize excess precision settings.
+
+   We have no need to modify anything here, just keep track of what the
+   user requested.  We'll figure out any appropriate relaxations
+   later.  */
+
 static void
 init_excess_precision (void)
 {
-  /* Adjust excess precision handling based on the target options.  If
-     the front end cannot handle it, flag_excess_precision_cmdline
-     will already have been set accordingly in the post_options
-     hook.  */
   gcc_assert (flag_excess_precision_cmdline != EXCESS_PRECISION_DEFAULT);
   flag_excess_precision = flag_excess_precision_cmdline;
-  if (flag_unsafe_math_optimizations)
-    flag_excess_precision = EXCESS_PRECISION_FAST;
-  if (flag_excess_precision == EXCESS_PRECISION_STANDARD)
-    {
-      int flt_eval_method = TARGET_FLT_EVAL_METHOD;
-      switch (flt_eval_method)
-       {
-       case -1:
-       case 0:
-         /* Either the target acts unpredictably (-1) or has all the
-            operations required not to have excess precision (0).  */
-         flag_excess_precision = EXCESS_PRECISION_FAST;
-         break;
-       case 1:
-       case 2:
-         /* In these cases, predictable excess precision makes
-            sense.  */
-         break;
-       default:
-         /* Any other implementation-defined FLT_EVAL_METHOD values
-            require the compiler to handle the associated excess
-            precision rules in excess_precision_type.  */
-         gcc_unreachable ();
-       }
-    }
 }
 
 /* Initialize things that are both lang-dependent and target-dependent.
index 80675dbd9f49dc6171bd5ce5f7763026fb465b87..4f3d678e08fb5ad989027c51ee7a28298f4e93a1 100644 (file)
@@ -8855,50 +8855,99 @@ build_complex_type (tree component_type, bool named)
 tree
 excess_precision_type (tree type)
 {
-  if (flag_excess_precision != EXCESS_PRECISION_FAST)
+  /* The target can give two different responses to the question of
+     which excess precision mode it would like depending on whether we
+     are in -fexcess-precision=standard or -fexcess-precision=fast.  */
+
+  enum excess_precision_type requested_type
+    = (flag_excess_precision == EXCESS_PRECISION_FAST
+       ? EXCESS_PRECISION_TYPE_FAST
+       : EXCESS_PRECISION_TYPE_STANDARD);
+
+  enum flt_eval_method target_flt_eval_method
+    = targetm.c.excess_precision (requested_type);
+
+  /* The target should not ask for unpredictable float evaluation (though
+     it might advertise that implicitly the evaluation is unpredictable,
+     but we don't care about that here, it will have been reported
+     elsewhere).  If it does ask for unpredictable evaluation, we have
+     nothing to do here.  */
+  gcc_assert (target_flt_eval_method != FLT_EVAL_METHOD_UNPREDICTABLE);
+
+  /* Nothing to do.  The target has asked for all types we know about
+     to be computed with their native precision and range.  */
+  if (target_flt_eval_method == FLT_EVAL_METHOD_PROMOTE_TO_FLOAT16)
+    return NULL_TREE;
+
+  /* The target will promote this type in a target-dependent way, so excess
+     precision ought to leave it alone.  */
+  if (targetm.promoted_type (type) != NULL_TREE)
+    return NULL_TREE;
+
+  machine_mode float16_type_mode = (float16_type_node
+                                   ? TYPE_MODE (float16_type_node)
+                                   : VOIDmode);
+  machine_mode float_type_mode = TYPE_MODE (float_type_node);
+  machine_mode double_type_mode = TYPE_MODE (double_type_node);
+
+  switch (TREE_CODE (type))
     {
-      int flt_eval_method = TARGET_FLT_EVAL_METHOD;
-      switch (TREE_CODE (type))
-       {
-       case REAL_TYPE:
-         switch (flt_eval_method)
-           {
-           case 1:
-             if (TYPE_MODE (type) == TYPE_MODE (float_type_node))
-               return double_type_node;
-             break;
-           case 2:
-             if (TYPE_MODE (type) == TYPE_MODE (float_type_node)
-                 || TYPE_MODE (type) == TYPE_MODE (double_type_node))
-               return long_double_type_node;
-             break;
-           default:
-             gcc_unreachable ();
-           }
-         break;
-       case COMPLEX_TYPE:
-         if (TREE_CODE (TREE_TYPE (type)) != REAL_TYPE)
-           return NULL_TREE;
-         switch (flt_eval_method)
-           {
-           case 1:
-             if (TYPE_MODE (TREE_TYPE (type)) == TYPE_MODE (float_type_node))
-               return complex_double_type_node;
-             break;
-           case 2:
-             if (TYPE_MODE (TREE_TYPE (type)) == TYPE_MODE (float_type_node)
-                 || (TYPE_MODE (TREE_TYPE (type))
-                     == TYPE_MODE (double_type_node)))
-               return complex_long_double_type_node;
-             break;
-           default:
-             gcc_unreachable ();
-           }
-         break;
-       default:
-         break;
-       }
+    case REAL_TYPE:
+      {
+       machine_mode type_mode = TYPE_MODE (type);
+       switch (target_flt_eval_method)
+         {
+         case FLT_EVAL_METHOD_PROMOTE_TO_FLOAT:
+           if (type_mode == float16_type_mode)
+             return float_type_node;
+           break;
+         case FLT_EVAL_METHOD_PROMOTE_TO_DOUBLE:
+           if (type_mode == float16_type_mode
+               || type_mode == float_type_mode)
+             return double_type_node;
+           break;
+         case FLT_EVAL_METHOD_PROMOTE_TO_LONG_DOUBLE:
+           if (type_mode == float16_type_mode
+               || type_mode == float_type_mode
+               || type_mode == double_type_mode)
+             return long_double_type_node;
+           break;
+         default:
+           gcc_unreachable ();
+         }
+       break;
+      }
+    case COMPLEX_TYPE:
+      {
+       if (TREE_CODE (TREE_TYPE (type)) != REAL_TYPE)
+         return NULL_TREE;
+       machine_mode type_mode = TYPE_MODE (TREE_TYPE (type));
+       switch (target_flt_eval_method)
+         {
+         case FLT_EVAL_METHOD_PROMOTE_TO_FLOAT:
+           if (type_mode == float16_type_mode)
+             return complex_float_type_node;
+           break;
+         case FLT_EVAL_METHOD_PROMOTE_TO_DOUBLE:
+           if (type_mode == float16_type_mode
+               || type_mode == float_type_mode)
+             return complex_double_type_node;
+           break;
+         case FLT_EVAL_METHOD_PROMOTE_TO_LONG_DOUBLE:
+           if (type_mode == float16_type_mode
+               || type_mode == float_type_mode
+               || type_mode == double_type_mode)
+             return complex_long_double_type_node;
+           break;
+         default:
+           gcc_unreachable ();
+         }
+       break;
+      }
+    default:
+      break;
     }
+
   return NULL_TREE;
 }
 \f