diff mbox

[6/17] Migrate excess precision logic to use TARGET_EXCESS_PRECISION

Message ID 1478878647-22547-7-git-send-email-james.greenhalgh@arm.com
State New
Headers show

Commit Message

James Greenhalgh Nov. 11, 2016, 3:37 p.m. UTC
---
This patch has already been approved:
  https://gcc.gnu.org/ml/gcc-patches/2016-11/msg00784.html
---

This patch moves the logic for excess precision from using the
TARGET_FLT_EVAL_METHOD macro to the TARGET_EXCESS_PRECISION hook
introduced earlier in the patch series.

Briefly; we have four things to change.

  1) The logic in tree.c::excess_precision_type .
  Here we want to ask the target which excess preicion it would like for
  whichever of -fexcess-precision=standard or -fexcess-precision=fast is
  in use, then apply that.

  2) The logic in c-family/c-cppbuiltin.c::c_cpp_flt_eval_method_iec_559 .
  We want to update this to return TRUE if the excess precision proposed
  by the front-end is the excess precision that will actually be used.

  3) The logic in c-family/c-cppbuiltin.c::c_cpp_builtin for setting
  __FLT_EVAL_METHOD__ .
  Which is now little more complicated, and makes use of
  -fpermitted-flt-eval-methods from patch 5. We also set
  __FLT_EVAL_METHOD_TS_18661_3__.

  4) The logic in c-family/c-cppbuiltin.c::c_cpp_builtin for setting
  __LIBGCC_*_EXCESS_PRECISION__ .

Having moved the logic in to those areas, we can simplify
toplev.c::init_excess_precision , which now only retains the assert that
-fexcess-precision=default has been rewritten by the language front-end, and
the set from the command-line variable to the internal variable.

Finally we need to update <float.h> to return the appropriate value for
FLT_EVAL_METHOD based on the new macros we add.

The documentation in invoke.texi is not quite right for the impact of
-fexcess-precision, so I've rewritten the text to read a little more
generic.

Thanks,
James

---
gcc/

2016-11-09  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__.

gcc/c-family/

2016-11-09  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__.

gcc/testsuite/

2016-11-09  James Greenhalgh  <james.greenhalgh@arm.com>

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

Patch

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 307862b..9f0b4a6 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -7944,4 +7944,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"
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 547bab2..602a07e 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1539,6 +1539,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);
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index 70eade1..a735ca0 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -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)
@@ -1044,9 +1070,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__",
@@ -1187,25 +1226,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__"));
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 75ff8ec..a1eb168 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -8961,15 +8961,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
diff --git a/gcc/ginclude/float.h b/gcc/ginclude/float.h
index 421f735..3df2889 100644
--- a/gcc/ginclude/float.h
+++ b/gcc/ginclude/float.h
@@ -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
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
index 0000000..c7bd756
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fpermitted-flt-eval-methods_3.c
@@ -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
index 0000000..a7bbb65
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fpermitted-flt-eval-methods_4.c
@@ -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;
+    }
+}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 59b84eb..d02ca6c 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1685,41 +1685,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.
diff --git a/gcc/tree.c b/gcc/tree.c
index c155d56..0e497fd 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -8852,50 +8852,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;
 }