public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [4.5 C] C99-conforming excess precision (fix PR 323 for C)
@ 2008-11-04  2:08 Joseph S. Myers
  2008-11-05 15:44 ` Ian Lance Taylor
  0 siblings, 1 reply; 6+ messages in thread
From: Joseph S. Myers @ 2008-11-04  2:08 UTC (permalink / raw)
  To: gcc-patches

This patch (for 4.5 Stage 1, and against c-4_5-branch) implements
C99-conforming excess precision, so fixing bug 323 for C.

Recall that x87 floating point does not support operations on the
"float" and "double" types; that is, all floating-point operations
round the result to the range and precision of long double, and still
round to the range of long double even if the precision has been set
to 24 or 53 by linking with -mpc32 or -mpc64.  (Such link-time
precision settings do not change GCC's internal compile-time
understanding of the long double format so are not near to conforming
modes.  In what follows, I refer to "excess precision" as a shorthand
covering both range and precision.)

C99 allows evaluation with excess range and precision following
certain rules.  These are outlined in 5.2.4.2.2 paragraph 8:

    Except for assignment and cast (which remove all extra range and
    precision), the values of operations with floating operands and
    values subject to the usual arithmetic conversions and of floating
    constants are evaluated to a format whose range and precision may
    be greater than required by the type. The use of evaluation
    formats is characterized by the implementation-defined value of
    FLT_EVAL_METHOD:

There are various other statements about excess precision in the
standard, although they do not always clearly define what must be done
in all cases.  N1321 (approved for C1x) splits constants out of
FLT_EVAL_METHOD to be controlled by a separate macro.

GCC defines FLT_EVAL_METHOD to 2 when using x87 floating point.  Its
implementation, however, does not conform to the C99 requirements for
FLT_EVAL_METHOD == 2, since it is implemented by the back end
pretending that the processor supports operations on SFmode and
DFmode:

* Sometimes, depending on optimization, a value may be spilled to
memory in SFmode or DFmode, so losing excess precision unpredictably
and in places other than when C99 specifies that it is lost.

* An assignment will not generally lose excess precision, although
-ffloat-store may make it more likely that it does.

* A cast to the same type will not be treated as having any semantic
effect at all, although it is required to remove excess precision.

* Complex multiplication and division will call libgcc functions for
SCmode and DCmode and so lose excess precision through calling
functions, although the complex types should act like the
corresponding real types for excess precision (and so call the
functions for XCmode if in conforming excess precision mode).  (This
is separate from the issue in bug 34071 that these library functions
for SCmode and DCmode should use XFmode internally on x86 and x86_64
because the excess precision is beneficial there.)

* Conversions from integer types to float and double just convert to
long double and so may result in a value not actually representable in
float or double, but I believe C99 requires the results of a cast to
be representable in the resulting type.

I believe the only way to implement semantics conforming to a
reasonable interpretation of C99, or to implement any semantics that
are predictable at the source language level and do not depend on
optimization, is to implement excess precision in the front end, which
this patch does.

With this patch, the C and Objective-C front ends, in conforming mode
on x86 using x87 floating point, will generate GIMPLE that does not
contain float and double operations that the processor does not
support, and the back end will not pretend to support such operations
(I have tried to identify the right set of insn patterns to disable;
it seems safest to disable them to catch any cases where the rest of
the compiler acts incorrectly and so tries to expand such operations);
the back end is also fixed to handle conversions from integers to
float and double properly (with an extra truncation step in this
conformance mode).  The front ends use the definition of
TARGET_FLT_EVAL_METHOD to determine the correct intermediate types to
use for each operation.

This is controlled by a new option, -fexcess-precision, with possible
arguments "fast" and "standard".  In "fast" mode, the default, the
back end pretends (as now) to have SFmode and DFmode operations; in
"standard" mode the back end does not pretend and the front end
arranges for arithmetic to be done on supported modes.  For C,
selecting a conformance mode (with e.g. -ansi or -std=c99) changes the
default to -fexcess-precision=standard; although C90 knows nothing of
excess precision, handling it like C99 seems better for C90
conformance mode than acting unpredictably.

For languages other than C, and for processors without excess
precision problems (including x86 with -mfpmath=sse),
-fexcess-precision=standard is quietly mapped to
-fexcess-precision=fast.  This is also done for
-funsafe-math-optimizations, since that enables larger
unpredictability of floating-point results, and for -mfpmath=sse+387,
since FLT_EVAL_METHOD is defined to -1 with that option.

It would be possible to implement the option for non-C languages, to
provide whatever predictable semantics are appropriate for those
languages (whether or not described in their standards).  Note that
bug 323 was originally reported with a C++ testcase.  If implemented
for all languages, the option might supersede -ffloat-store.  Right
now, -ffloat-store checks are scattered about the optimizers and it
seems unlikely that -ffloat-store really implements any form of
predictable semantics now; such semantic effect as it was intended to
have could be better represented as an alias for a
-fexcess-precision=standard option supported for all languages.  It
would probably be most appropriate not to close bug 323 without having
some form of predictable semantics available for each language.

The option naming leaves open the possibility of other options such as
"none" if someone wishes to implement them.  ("none" would involve the
compiler doing mode switching and adding code to deal with extra range
and double rounding of subnormals.  There could also be an option to
have extra range but not precision, again with the compiler inserting
mode switches as needed.)  I am however doubtful of the use of such
options; this patch deals with a major area where -std=c99 fails to
implement standard semantics in default x86 configurations, but I'd
expect people wanting particular semantics beyond a predictable
version of C99 would use SSE.

I have not changed the m68k back end in this patch.  Thus the option
may not be fully effective for the affected m68k processors (classic
m68k with 68881, before 68040, only, not ColdFire, not 68040 or
later).  If anyone wishes to make it fully effective for such
processors they should copy the testcases to gcc.target/m68k/ and go
through m68k insn patterns appropriately adjusting them.

As noted above, C99 leaves some matters open to interpretation.  I
have applied the interpretation that conversions from integers to
floating-point types (whether or not using explicit casts) must result
in a value representable in the new type, since the operand in such a
case is integer, not floating-point, and since at least casts seem to
be expected to remove excess precision.  I have applied the
interpretation that argument passing and return must lose excess
precision, since depending on the ABI operational in a particular case
it may not be possible to do otherwise, and have interpreted this as I
think correct for non-prototyped and variadic functions.  (For
type-generic built-ins, C99 explicitly specifies such removal of
excess precision for classification macros, but does not specify it
for comparison macros; I've implemented this accordingly.)

If DRs or C1x ultimately lead to contrary interpretations the
implementation can be adjusted accordingly, but I think what's most
important for this feature is that the semantics can in all cases be
predicted from the source code without depending on optimization
level.  Some of the interpretation issues are being discussed right
now (coincidentally) on the WG14 reflector.

With this patch, excess precision will be removed as part of
evaluating an expression statement; this normally has no effect (GCC
doesn't try to preserve such conversions with no apparent side
effects, although they could generate exceptions so ought to be
preserved for that with -ftrapping-math) but does mean that the value
of a statement expression has no excess precision.  There is no good
language design reason for this, simply convenience of implementation;
it may change in future if GCC gets better at preserving exceptions or
statement expressions end up in a future C standard.

Bootstrapped with no regressions on i686-pc-linux-gnu.  Are the
non-C-front-end parts OK for 4.5?

2008-11-03  Joseph Myers  <joseph@codesourcery.com>

	PR rtl-optimization/323
	* c-common.c (convert_and_check, c_common_truthvalue_conversion):
	Handle EXCESS_PRECISION_EXPR.
	* c-common.def (EXCESS_PRECISION_EXPR): New.
	* c-cppbuiltin.c (builtin_define_float_constants): Define
	constants with enough digits for long double.
	* c-lex.c (interpret_float): Interpret constant with excess
	precision where appropriate.
	* c-opts.c (c_common_post_options): Set
	flag_excess_precision_cmdline.
	* c-parser.c (c_parser_conditional_expression): Handle excess
	precision in condition.
	* c-typeck.c (c_fully_fold): Handle EXCESS_PRECISION_EXPR.
	(c_fully_fold_internal): Disallow EXCESS_PRECISION_EXPR.
	(convert_arguments): Handle arguments with excess precision.
	(build_unary_op): Move excess precision outside operation.
	(build_conditional_expr): Likewise.
	(build_compound_expr): Likewise.
	(build_c_cast): Do cast on operand of EXCESS_PRECISION_EXPR.
	(build_modify_expr): Handle excess precision in RHS.
	(convert_for_assignment): Handle excess precision in converted
	value.
	(digest_init, output_init_element, process_init_element): Handle
	excess precision in initializer.
	(c_finish_return): Handle excess precision in return value.
	(build_binary_op): Handle excess precision in operands and add
	excess precision as needed for operation.
	* common.opt (-fexcess-precision=): New option.
	* config/i386/i386.h (X87_ENABLE_ARITH, X87_ENABLE_FLOAT): New.
	* config/i386/i386.md (float<SSEMODEI24:mode><X87MODEF:mode>2):
	For standard excess precision, output explicit conversion to and
	truncation from XFmode.
	(*float<SSEMODEI24:mode><X87MODEF:mode>2_1,
	*float<SSEMODEI24:mode><X87MODEF:mode>2_i387_with_temp,
	*float<SSEMODEI24:mode><X87MODEF:mode>2_i387, two unnamed
	define_splits, floatdi<X87MODEF:mode>2_i387_with_xmm, two unnamed
	define_splits, *floatunssi<mode>2_1, two unnamed define_splits,
	floatunssi<mode>2, add<mode>3, sub<mode>3, mul<mode>3, divdf3,
	divsf3, *fop_<mode>_comm_i387, *fop_<mode>_1_i387,
	*fop_<MODEF:mode>_2_i387, *fop_<MODEF:mode>_3_i387,
	*fop_df_4_i387, *fop_df_5_i387, *fop_df_6_i387, two unnamed
	define_splits, sqrt<mode>2): Disable where appropriate for
	standard excess precision.
	* convert.c (convert_to_real): Do not shorten arithmetic to type
	for which excess precision would be used.
	* doc/invoke.texi (-fexcess-precision=): Document option.
	(-mfpmath=): Correct index entry.
	* flags.h (enum excess_precision, flag_excess_precision_cmdline,
	flag_excess_precision): New.
	* langhooks.c (lhd_post_options): Set
	flag_excess_precision_cmdline.
	* opts.c (common_handle_option): Handle -fexcess-precision=.
	* toplev.c (flag_excess_precision_cmdline, flag_excess_precision,
	init_excess_precision): New.
	(lang_dependent_init_target): Call init_excess_precision.
	* tree.c (excess_precision_type): New.
	* tree.h (excess_precision_type): Declare.

ada:
2008-11-03  Joseph Myers  <joseph@codesourcery.com>

	PR rtl-optimization/323
	* gcc-interface/misc.c (gnat_post_options): Set
	flag_excess_precision_cmdline.

fortran:
2008-11-03  Joseph Myers  <joseph@codesourcery.com>

	PR rtl-optimization/323
	* options.c (gfc_post_options): Set flag_excess_precision_cmdline.

java:
2008-11-03  Joseph Myers  <joseph@codesourcery.com>

	PR rtl-optimization/323
	* lang.c (java_post_options): Set flag_excess_precision_cmdline.

testsuite:
2008-11-03  Joseph Myers  <joseph@codesourcery.com>

	PR rtl-optimization/323
	* gcc.target/i386/excess-precision-1.c,
	gcc.target/i386/excess-precision-2.c,
	gcc.target/i386/excess-precision-3.c,
	gcc.target/i386/excess-precision-4.c,
	gcc.target/i386/excess-precision-5.c,
	gcc.target/i386/excess-precision-6.c: New tests.

Index: doc/invoke.texi
===================================================================
--- doc/invoke.texi	(revision 141547)
+++ doc/invoke.texi	(working copy)
@@ -331,8 +331,9 @@ Objective-C and Objective-C++ Dialects}.
 -fdata-sections -fdce -fdce @gol
 -fdelayed-branch -fdelete-null-pointer-checks -fdse -fdse @gol
 -fearly-inlining -fexpensive-optimizations -ffast-math @gol
--ffinite-math-only -ffloat-store -fforward-propagate @gol
--ffunction-sections -fgcse -fgcse-after-reload -fgcse-las -fgcse-lm @gol
+-ffinite-math-only -ffloat-store -fexcess-precision=@var{style} @gol
+-fforward-propagate -ffunction-sections @gol
+-fgcse -fgcse-after-reload -fgcse-las -fgcse-lm @gol
 -fgcse-sm -fif-conversion -fif-conversion2 -findirect-inlining @gol
 -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
 -finline-small-functions -fipa-cp -fipa-cp-clone -fipa-marix-reorg -fipa-pta @gol 
@@ -6631,6 +6632,31 @@ good, but a few programs rely on the pre
 point.  Use @option{-ffloat-store} for such programs, after modifying
 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 will follow 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
+conformance option such as @option{-std=c99} is used.
+
+@opindex mfpmath
+This option has no effect for languages other than C, or if
+@option{-funsafe-math-optimizations} or @option{-ffast-math} is
+specified.  On the x86, it also has no effect if @option{-mfpmath=sse}
+or @option{-mfpmath=sse+387} is specified; in the former case, IEEE
+semantics apply without excess precision, and in the latter, rounding
+is unpredictable.
+
 @item -ffast-math
 @opindex ffast-math
 Sets @option{-fno-math-errno}, @option{-funsafe-math-optimizations},
@@ -10772,7 +10798,7 @@ specifying @option{-march=@var{cpu-type}
 A deprecated synonym for @option{-mtune}.
 
 @item -mfpmath=@var{unit}
-@opindex march
+@opindex mfpmath
 Generate floating point arithmetics for selected unit @var{unit}.  The choices
 for @var{unit} are:
 
Index: flags.h
===================================================================
--- flags.h	(revision 141547)
+++ flags.h	(working copy)
@@ -218,6 +218,21 @@ extern enum ira_algorithm flag_ira_algor
 
 extern unsigned int flag_ira_verbose;
 
+/* The options for excess precision.  */
+enum excess_precision
+{
+  EXCESS_PRECISION_DEFAULT,
+  EXCESS_PRECISION_FAST,
+  EXCESS_PRECISION_STANDARD
+};
+
+/* The excess precision specified on the command line, or defaulted by
+   the front end.  */
+extern enum excess_precision flag_excess_precision_cmdline;
+
+/* The excess precision currently in effect.  */
+extern enum excess_precision flag_excess_precision;
+
 \f
 /* Other basic status info about current function.  */
 
Index: java/lang.c
===================================================================
--- java/lang.c	(revision 141547)
+++ java/lang.c	(working copy)
@@ -528,6 +528,10 @@ java_post_options (const char **pfilenam
 {
   const char *filename = *pfilename;
 
+  /* Excess precision other than "fast" requires front-end
+     support.  */
+  flag_excess_precision_cmdline = EXCESS_PRECISION_FAST;
+
   /* An absolute requirement: if we're not using indirect dispatch, we
      must always verify everything.  */
   if (! flag_indirect_dispatch)
Index: c-lex.c
===================================================================
--- c-lex.c	(revision 141547)
+++ c-lex.c	(working copy)
@@ -605,8 +605,10 @@ static tree
 interpret_float (const cpp_token *token, unsigned int flags)
 {
   tree type;
+  tree const_type;
   tree value;
   REAL_VALUE_TYPE real;
+  REAL_VALUE_TYPE real_trunc;
   char *copy;
   size_t copylen;
 
@@ -655,6 +657,10 @@ interpret_float (const cpp_token *token,
     else
       type = double_type_node;
 
+  const_type = excess_precision_type (type);
+  if (!const_type)
+    const_type = type;
+
   /* Copy the constant to a nul-terminated buffer.  If the constant
      has any suffixes, cut them off; REAL_VALUE_ATOF/ REAL_VALUE_HTOF
      can't handle them.  */
@@ -675,13 +681,21 @@ interpret_float (const cpp_token *token,
   memcpy (copy, token->val.str.text, copylen);
   copy[copylen] = '\0';
 
-  real_from_string3 (&real, copy, TYPE_MODE (type));
+  real_from_string3 (&real, copy, TYPE_MODE (const_type));
+  if (const_type != type)
+    /* Diagnosing if the result of converting the value with excess
+       precision to the semantic type would overflow (with associated
+       double rounding) is more appropriate than diagnosing if the
+       result of converting the string directly to the semantic type
+       would overflow.  */
+    real_convert (&real_trunc, TYPE_MODE (type), &real);
 
   /* Both C and C++ require a diagnostic for a floating constant
      outside the range of representable values of its type.  Since we
      have __builtin_inf* to produce an infinity, this is now a
      mandatory pedwarn if the target does not support infinities.  */
-  if (REAL_VALUE_ISINF (real)) 
+  if (REAL_VALUE_ISINF (real)
+      || (const_type != type && REAL_VALUE_ISINF (real_trunc)))
     {
       if (!MODE_HAS_INFINITIES (TYPE_MODE (type)))
 	pedwarn (input_location, 0, "floating constant exceeds range of %qT", type);
@@ -689,7 +703,8 @@ interpret_float (const cpp_token *token,
 	warning (OPT_Woverflow, "floating constant exceeds range of %qT", type);
     }
   /* We also give a warning if the value underflows.  */
-  else if (REAL_VALUES_EQUAL (real, dconst0))
+  else if (REAL_VALUES_EQUAL (real, dconst0)
+	   || (const_type != type && REAL_VALUES_EQUAL (real_trunc, dconst0)))
     {
       REAL_VALUE_TYPE realvoidmode;
       int overflow = real_from_string (&realvoidmode, copy);
@@ -698,9 +713,13 @@ interpret_float (const cpp_token *token,
     }
 
   /* Create a node with determined type and value.  */
-  value = build_real (type, real);
+  value = build_real (const_type, real);
   if (flags & CPP_N_IMAGINARY)
-    value = build_complex (NULL_TREE, convert (type, integer_zero_node), value);
+    value = build_complex (NULL_TREE, convert (const_type, integer_zero_node),
+			   value);
+
+  if (type != const_type)
+    value = build1 (EXCESS_PRECISION_EXPR, type, value);
 
   return value;
 }
Index: tree.c
===================================================================
--- tree.c	(revision 141547)
+++ tree.c	(working copy)
@@ -6228,6 +6228,61 @@ build_complex_type (tree component_type)
 
   return build_qualified_type (t, TYPE_QUALS (component_type));
 }
+
+/* If TYPE is a real or complex floating-point type and the target
+   does not directly support arithmetic on TYPE then return the wider
+   type to be used for arithmetic on TYPE.  Otherwise, return
+   NULL_TREE.  */
+
+tree
+excess_precision_type (tree type)
+{
+  if (flag_excess_precision != EXCESS_PRECISION_FAST)
+    {
+      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;
+	}
+    }
+  return NULL_TREE;
+}
 \f
 /* Return OP, stripped of any conversions to wider types as much as is safe.
    Converting the value back to OP's type makes a value equivalent to OP.
Index: tree.h
===================================================================
--- tree.h	(revision 141547)
+++ tree.h	(working copy)
@@ -4026,6 +4026,7 @@ extern bool tree_expr_nonnegative_p (tre
 extern bool tree_expr_nonnegative_warnv_p (tree, bool *);
 extern bool may_negate_without_overflow_p (const_tree);
 extern tree strip_array_types (tree);
+extern tree excess_precision_type (tree);
 
 /* Construct various nodes representing fract or accum data types.  */
 
Index: toplev.c
===================================================================
--- toplev.c	(revision 141547)
+++ toplev.c	(working copy)
@@ -279,6 +279,11 @@ enum ira_algorithm flag_ira_algorithm = 
 
 unsigned int flag_ira_verbose = 5;
 
+/* Set the default for excess precision.  */
+
+enum excess_precision flag_excess_precision_cmdline = EXCESS_PRECISION_DEFAULT;
+enum excess_precision flag_excess_precision = EXCESS_PRECISION_DEFAULT;
+
 /* Nonzero means change certain warnings into errors.
    Usually these are warnings about failure to conform to some standard.  */
 
@@ -2031,11 +2036,51 @@ backend_init (void)
   backend_init_target ();
 }
 
+/* Initialize excess precision settings.  */
+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.
    This function can be called more than once if target parameters change.  */
 static void
 lang_dependent_init_target (void)
 {
+  /* This determines excess precision settings.  */
+  init_excess_precision ();
+
   /* This creates various _DECL nodes, so needs to be called after the
      front end is initialized.  It also depends on the HAVE_xxx macros
      generated from the target machine description.  */
Index: c-cppbuiltin.c
===================================================================
--- c-cppbuiltin.c	(revision 141547)
+++ c-cppbuiltin.c	(working copy)
@@ -98,6 +98,7 @@ builtin_define_float_constants (const ch
   const double log10_2 = .30102999566398119521;
   double log10_b;
   const struct real_format *fmt;
+  const struct real_format *ldfmt;
 
   char name[64], buf[128];
   int dig, min_10_exp, max_10_exp;
@@ -105,6 +106,8 @@ builtin_define_float_constants (const ch
 
   fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
   gcc_assert (fmt->b != 10);
+  ldfmt = REAL_MODE_FORMAT (TYPE_MODE (long_double_type_node));
+  gcc_assert (ldfmt->b != 10);
 
   /* The radix of the exponent representation.  */
   if (type == float_type_node)
@@ -187,7 +190,8 @@ builtin_define_float_constants (const ch
      The only macro we care about is this number for the widest supported
      floating type, but we want this value for rendering constants below.  */
   {
-    double d_decimal_dig = 1 + fmt->p * log10_b;
+    double d_decimal_dig
+      = 1 + (fmt->p < ldfmt->p ? ldfmt->p : fmt->p) * log10_b;
     decimal_dig = d_decimal_dig;
     if (decimal_dig < d_decimal_dig)
       decimal_dig++;
Index: testsuite/gcc.target/i386/excess-precision-1.c
===================================================================
--- testsuite/gcc.target/i386/excess-precision-1.c	(revision 0)
+++ testsuite/gcc.target/i386/excess-precision-1.c	(revision 0)
@@ -0,0 +1,186 @@
+/* Excess precision tests.  Test that excess precision is carried
+   through various operations.  */
+/* { dg-do run } */
+/* { dg-require-effective-target ilp32 } */
+/* { dg-options "-O2 -fexcess-precision=standard" } */
+
+#include <float.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile float f1 = 1.0f;
+volatile float f2 = 0x1.0p-30f;
+volatile float f3 = 0x1.0p-60f;
+volatile double d1 = 1.0;
+volatile double d2 = 0x1.0p-30;
+volatile double d3 = 0x1.0p-60;
+volatile float fadd1 = 1.0f + 0x1.0p-30f;
+volatile double dadd2 = 1.0 + 0x1.0p-30 + 0x1.0p-60;
+volatile long double ldadd1 = 1.0l + 0x1.0p-30l;
+volatile long double ldadd2 = 1.0l + 0x1.0p-30l + 0x1.0p-60l;
+
+void
+test_add (void)
+{
+  if (f1 + f2 != ldadd1)
+    abort ();
+  if (f1 + f2 + f3 != ldadd2)
+    abort ();
+  if (d1 + d2 != ldadd1)
+    abort ();
+  if (d1 + d2 + d3 != ldadd2)
+    abort ();
+  if (f1 + d2 + f3 != ldadd2)
+    abort ();
+  if (f1 + f2 == fadd1)
+    abort ();
+  if (f1 + f2 <= fadd1)
+    abort ();
+  if (f1 + f2 < fadd1)
+    abort ();
+  if (d1 + d2 + d3 == dadd2)
+    abort ();
+  if (!(d1 + d2 + d3 > dadd2))
+    abort ();
+  if (!(d1 + d2 + d3 >= dadd2))
+    abort ();
+}
+
+volatile long double ldsub1 = 1.0l - 0x1.0p-30l;
+volatile long double ldsub2 = 1.0l - 0x1.0p-30l - 0x1.0p-60l;
+
+void
+test_sub (void)
+{
+  if (f1 - f2 != ldsub1)
+    abort ();
+  if (f1 - f2 - f3 != ldsub2)
+    abort ();
+  if (d1 - d2 != ldsub1)
+    abort ();
+  if (d1 - d2 - d3 != ldsub2)
+    abort ();
+  if (f1 - d2 - f3 != ldsub2)
+    abort ();
+  if (+(f1 - d2 - f3) != ldsub2)
+    abort ();
+  if (-(f1 - d2 - f3) != -ldsub2)
+    abort ();
+}
+
+volatile float flt_min = FLT_MIN;
+volatile double dbl_min = DBL_MIN;
+volatile long double flt_min2 = (long double)FLT_MIN * (long double)FLT_MIN;
+volatile long double dbl_min3 = (long double)DBL_MIN * (long double)DBL_MIN * (long double)DBL_MIN;
+
+void
+test_mul (void)
+{
+  if (flt_min * flt_min != flt_min2)
+    abort ();
+  if (flt_min * flt_min == 0)
+    abort ();
+  if (flt_min * flt_min == 0)
+    abort ();
+  if (!(flt_min * flt_min))
+    abort ();
+  if (dbl_min * dbl_min * dbl_min != dbl_min3)
+    abort ();
+  if ((long double)(dbl_min * dbl_min * dbl_min) != dbl_min3)
+    abort ();
+  if ((0, dbl_min * dbl_min * dbl_min) != dbl_min3)
+    abort ();
+  if (dbl_min * dbl_min * dbl_min == 0)
+    abort ();
+  if ((flt_min * flt_min ? dbl_min * dbl_min * dbl_min : 0) == 0)
+    abort ();
+  if ((flt_min * flt_min ? : 0) == 0)
+    abort ();
+}
+
+volatile float f4 = 0x1.0p100f;
+volatile double d4 = 0x1.0p100;
+volatile long double flt_div = 0x1.0p100l / (long double) FLT_MIN;
+volatile long double dbl_div = 0x1.0p100l / (long double) DBL_MIN;
+
+void
+test_div (void)
+{
+  if (f4 / flt_min != flt_div)
+    abort ();
+  if (d4 / dbl_min != dbl_div)
+    abort ();
+}
+
+volatile float f5 = 0x1.0p30;
+
+void
+test_cast (void)
+{
+  if ((int)(f1 + f5) != 0x40000001)
+    abort ();
+}
+
+volatile float _Complex f1c = 1.0f + 1.0if;
+volatile float _Complex f2c = 0x1.0p-30f + 0x1.0p-31if;
+volatile float _Complex f3c = 0x1.0p-60f + 0x1.0p-59if;
+volatile double _Complex d1c = 1.0 + 1.0i;
+volatile double _Complex d2c = 0x1.0p-30 + 0x1.0p-31i;
+volatile double _Complex d3c = 0x1.0p-60 + 0x1.0p-59i;
+volatile long double _Complex ldadd1c = 1.0l + 0x1.0p-30l + 1.0il + 0x1.0p-31il;
+volatile long double _Complex ldadd2c = 1.0l + 0x1.0p-30l + 0x1.0p-60l + 1.0il + 0x1.0p-31il + 0x1.0p-59il;
+volatile long double _Complex ldadd2cc = 1.0l + 0x1.0p-30l + 0x1.0p-60l - 1.0il - 0x1.0p-31il - 0x1.0p-59il;
+volatile float _Complex flt_minc = FLT_MIN;
+volatile double _Complex dbl_minc = DBL_MIN;
+volatile float _Complex f4c = 0x1.0p100f;
+volatile double _Complex d4c = 0x1.0p100;
+
+void
+test_complex (void)
+{
+  if (f1c + f2c != ldadd1c)
+    abort ();
+  if (f1c + f2c + f3c != ldadd2c)
+    abort ();
+  if (d1c + d2c != ldadd1c)
+    abort ();
+  if (d1c + d2c + d3c != ldadd2c)
+    abort ();
+  if (__real__ (f1c + f2c + f3c) != ldadd2)
+    abort ();
+  if (__imag__ (d1c + d2c + d3c) != __imag__ ldadd2c)
+    abort ();
+  if (~(d1c + d2c + d3c) != ldadd2cc)
+    abort ();
+  /* The following call libgcc functions and so would fail unless they
+     call those for long double.  */
+  if (flt_minc * flt_minc != flt_min2)
+    abort ();
+  if (dbl_minc * dbl_minc * dbl_minc != dbl_min3)
+    abort ();
+  if (f4c / flt_minc != flt_div)
+    abort ();
+  if (d4c / dbl_minc != dbl_div)
+    abort ();
+  if (f4 / flt_minc != flt_div)
+    abort ();
+  if (d4 / dbl_minc != dbl_div)
+    abort ();
+  if (f4c / flt_min != flt_div)
+    abort ();
+  if (d4c / dbl_min != dbl_div)
+    abort ();
+}
+
+int
+main (void)
+{
+  test_add ();
+  test_sub ();
+  test_mul ();
+  test_div ();
+  test_cast ();
+  test_complex ();
+  exit (0);
+}
Index: testsuite/gcc.target/i386/excess-precision-3.c
===================================================================
--- testsuite/gcc.target/i386/excess-precision-3.c	(revision 0)
+++ testsuite/gcc.target/i386/excess-precision-3.c	(revision 0)
@@ -0,0 +1,219 @@
+/* Excess precision tests.  Test excess precision is removed when
+   necessary.  */
+/* { dg-do run } */
+/* { dg-require-effective-target ilp32 } */
+/* { dg-options "-O2 -fexcess-precision=standard" } */
+
+#include <float.h>
+#include <stdarg.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile float f1 = 1.0f;
+volatile float f2 = 0x1.0p-30f;
+volatile float f3 = 0x1.0p-60f;
+volatile double d1 = 1.0;
+volatile double d2 = 0x1.0p-30;
+volatile double d3 = 0x1.0p-60;
+volatile float fadd1 = 1.0f + 0x1.0p-30f;
+volatile double dadd2 = 1.0 + 0x1.0p-30 + 0x1.0p-60;
+volatile double dh = 0x1.0p-24;
+volatile float fha = 1.0f + 0x1.0p-23f;
+
+void
+test_assign (void)
+{
+  float f;
+  double d;
+  f = f1 + f2;
+  if (f != fadd1)
+    abort ();
+  d = f1 + f2;
+  if (d != dadd2)
+    abort ();
+  d = d1 + d2 + d3;
+  if (d != dadd2)
+    abort ();
+  /* Verify rounding direct to float without double rounding.  */
+  f = d1 + dh + d3;
+  if (f != fha)
+    abort ();
+}
+
+void
+test_init (void)
+{
+  float f = f1 + f2;
+  double d = d1 + d2 + d3;
+  if (f != fadd1)
+    abort ();
+  if (d != dadd2)
+    abort ();
+}
+
+volatile int i1 = 0x40000001;
+volatile unsigned int u1 = 0x80000001u;
+volatile long long ll1 = 0x4000000000000001ll;
+volatile unsigned long long ull1 = 0x8000000000000001ull;
+
+void
+test_cast (void)
+{
+  if ((float)(f1 + f2) != fadd1)
+    abort ();
+  if ((double)(d1 + d2 + d3) != dadd2)
+    abort ();
+  if ((double)(f1 + f2 + f3) != dadd2)
+    abort ();
+  if ((float)i1 != 0x1.0p30f)
+    abort ();
+  if ((float)u1 != 0x1.0p31f)
+    abort ();
+  if ((float)ll1 != 0x1.0p62f)
+    abort ();
+  if ((float)ull1 != 0x1.0p63f)
+    abort ();
+  if ((double)ll1 != 0x1.0p62)
+    abort ();
+  if ((double)ull1 != 0x1.0p63)
+    abort ();
+}
+
+static inline void
+check_float (float f)
+{
+  if (f != fadd1)
+    abort ();
+}
+
+static inline void
+check_double (double d)
+{
+  if (d != dadd2)
+    abort ();
+}
+
+static inline void
+check_float_nonproto (f)
+     float f;
+{
+  if (f != fadd1)
+    abort ();
+}
+
+static inline void
+check_double_nonproto (d)
+     double d;
+{
+  if (d != dadd2)
+    abort ();
+}
+
+static void
+check_double_va (int i, ...)
+{
+  va_list ap;
+  va_start (ap, i);
+  if (va_arg (ap, double) != dadd2)
+    abort ();
+  va_end (ap);
+}
+
+void
+test_call (void)
+{
+  check_float (f1 + f2);
+  check_double (d1 + d2 + d3);
+  check_double (f1 + f2 + f3);
+  check_float_nonproto (f1 + f2);
+  check_double_nonproto (d1 + d2 + d3);
+  check_double_nonproto (f1 + f2 + f3);
+  check_double_va (0, d1 + d2 + d3);
+  check_double_va (0, f1 + f2 + f3);
+}
+
+static inline float
+return_float (void)
+{
+  return f1 + f2;
+}
+
+static inline double
+return_double1 (void)
+{
+  return d1 + d2 + d3;
+}
+
+static inline double
+return_double2 (void)
+{
+  return f1 + f2 + f3;
+}
+
+void
+test_return (void)
+{
+  if (return_float () != fadd1)
+    abort ();
+  if (return_double1 () != dadd2)
+    abort ();
+  if (return_double2 () != dadd2)
+    abort ();
+}
+
+volatile float flt_min = FLT_MIN;
+volatile double dbl_min = DBL_MIN;
+volatile float flt_max = FLT_MAX;
+volatile double dbl_max = DBL_MAX;
+
+void
+test_builtin (void)
+{
+  /* Classification macros convert to the semantic type.  signbit and
+     comparison macros do not.  */
+  if (!__builtin_isinf (flt_max * flt_max))
+    abort ();
+  if (!__builtin_isinf (dbl_max * dbl_max))
+    abort ();
+  if (__builtin_isnormal (flt_max * flt_max))
+    abort ();
+  if (__builtin_isnormal (dbl_max * dbl_max))
+    abort ();
+  if (__builtin_isfinite (flt_max * flt_max))
+    abort ();
+  if (__builtin_isfinite (dbl_max * dbl_max))
+    abort ();
+  if (!__builtin_isgreater (flt_min * flt_min, 0.0f))
+    abort ();
+  if (!__builtin_isgreaterequal (flt_min * flt_min, 0.0f))
+    abort ();
+  if (!__builtin_isless (0.0f, flt_min * flt_min))
+    abort ();
+  if (__builtin_islessequal (flt_min * flt_min, 0.0f))
+    abort ();
+  if (!__builtin_islessgreater (flt_min * flt_min, 0.0f))
+    abort ();
+  if (!__builtin_isgreater (dbl_min * dbl_min, 0.0))
+    abort ();
+  if (!__builtin_isgreaterequal (dbl_min * dbl_min, 0.0))
+    abort ();
+  if (!__builtin_isless (0.0, dbl_min * dbl_min))
+    abort ();
+  if (__builtin_islessequal (dbl_min * dbl_min, 0.0))
+    abort ();
+  if (!__builtin_islessgreater (dbl_min * dbl_min, 0.0))
+    abort ();
+}
+
+int
+main (void)
+{
+  test_assign ();
+  test_init ();
+  test_cast ();
+  test_call ();
+  test_return ();
+  test_builtin ();
+  exit (0);
+}
Index: testsuite/gcc.target/i386/excess-precision-5.c
===================================================================
--- testsuite/gcc.target/i386/excess-precision-5.c	(revision 0)
+++ testsuite/gcc.target/i386/excess-precision-5.c	(revision 0)
@@ -0,0 +1,23 @@
+/* Excess precision tests.  Verify excess precision doesn't affect
+   actual types.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target ilp32 } */
+/* { dg-options "-fexcess-precision=standard" } */
+
+float f;
+double d;
+
+void
+test_types (void)
+{
+  float *fp;
+  double *dp;
+#define CHECK_FLOAT(E) fp = &(typeof(E)){0}
+#define CHECK_DOUBLE(E) dp = &(typeof(E)){0}
+  CHECK_FLOAT (f + f);
+  CHECK_DOUBLE (d + d);
+  CHECK_FLOAT (f * f / f);
+  CHECK_DOUBLE (d * d / d);
+  CHECK_FLOAT (f ? f - f : f);
+  CHECK_DOUBLE (d ? d - d : d);
+}
Index: testsuite/gcc.target/i386/excess-precision-2.c
===================================================================
--- testsuite/gcc.target/i386/excess-precision-2.c	(revision 0)
+++ testsuite/gcc.target/i386/excess-precision-2.c	(revision 0)
@@ -0,0 +1,34 @@
+/* Excess precision tests.  Test excess precision of constants.  */
+/* { dg-do run } */
+/* { dg-require-effective-target ilp32 } */
+/* { dg-options "-O2 -fexcess-precision=standard" } */
+
+#include <float.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile long double ldadd1 = 1.0l + 0x1.0p-30l;
+volatile long double ld11f = 1.1f;
+volatile long double ld11d = 1.1;
+volatile long double ld11 = 1.1;
+
+void
+test_const (void)
+{
+  if (1.0f + 0x1.0p-30f != ldadd1)
+    abort ();
+  if (ld11f != ld11)
+    abort ();
+  if (ld11d != ld11)
+    abort ();
+  if (1.1f != ld11)
+    abort ();
+}
+
+int
+main (void)
+{
+  test_const ();
+  exit (0);
+}
Index: testsuite/gcc.target/i386/excess-precision-4.c
===================================================================
--- testsuite/gcc.target/i386/excess-precision-4.c	(revision 0)
+++ testsuite/gcc.target/i386/excess-precision-4.c	(revision 0)
@@ -0,0 +1,8 @@
+/* Excess precision tests.  Test diagnostics for excess precision of
+   constants.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target ilp32 } */
+/* { dg-options "-fexcess-precision=standard" } */
+
+float f = 0.0f * 1e50f; /* { dg-warning "floating constant exceeds range of 'float'" } */
+double d = 0.0 * 1e400; /* { dg-warning "floating constant exceeds range of 'double'" } */
Index: testsuite/gcc.target/i386/excess-precision-6.c
===================================================================
--- testsuite/gcc.target/i386/excess-precision-6.c	(revision 0)
+++ testsuite/gcc.target/i386/excess-precision-6.c	(revision 0)
@@ -0,0 +1,20 @@
+/* Excess precision tests.  Make sure sqrt is not inlined for float or
+   double.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target ilp32 } */
+/* { dg-options "-O2 -fno-math-errno -fexcess-precision=standard" } */
+
+float f;
+double d;
+
+float fr;
+double dr;
+
+void
+test_builtins (void)
+{
+  fr = __builtin_sqrtf (f);
+  dr = __builtin_sqrt (d);
+}
+
+/* { dg-final { scan-assembler-not "fsqrt" } } */
Index: opts.c
===================================================================
--- opts.c	(revision 141547)
+++ opts.c	(working copy)
@@ -1743,6 +1743,15 @@ common_handle_option (size_t scode, cons
 	return 0;
       break;
 
+    case OPT_fexcess_precision_:
+      if (!strcmp (arg, "fast"))
+	flag_excess_precision_cmdline = EXCESS_PRECISION_FAST;
+      else if (!strcmp (arg, "standard"))
+	flag_excess_precision_cmdline = EXCESS_PRECISION_STANDARD;
+      else
+	error ("unknown excess precision style \"%s\"", arg);
+      break;
+
     case OPT_ffast_math:
       set_fast_math_flags (value);
       break;
Index: ada/gcc-interface/misc.c
===================================================================
--- ada/gcc-interface/misc.c	(revision 141547)
+++ ada/gcc-interface/misc.c	(working copy)
@@ -337,6 +337,10 @@ gnat_init_options (unsigned int argc, co
 bool
 gnat_post_options (const char **pfilename ATTRIBUTE_UNUSED)
 {
+  /* Excess precision other than "fast" requires front-end
+     support.  */
+  flag_excess_precision_cmdline = EXCESS_PRECISION_FAST;
+
   /* ??? The warning machinery is outsmarted by Ada.  */
   warn_unused_parameter = 0;
 
Index: fortran/options.c
===================================================================
--- fortran/options.c	(revision 141547)
+++ fortran/options.c	(working copy)
@@ -227,6 +227,10 @@ gfc_post_options (const char **pfilename
   char *source_path;
   int i;
 
+  /* Excess precision other than "fast" requires front-end
+     support.  */
+  flag_excess_precision_cmdline = EXCESS_PRECISION_FAST;
+
   /* Issue an error if -fwhole-program was used.  */
   if (flag_whole_program)
     gfc_fatal_error ("Option -fwhole-program is not supported for Fortran");
Index: langhooks.c
===================================================================
--- langhooks.c	(revision 141547)
+++ langhooks.c	(working copy)
@@ -105,6 +105,9 @@ lhd_return_null_const_tree (const_tree A
 bool
 lhd_post_options (const char ** ARG_UNUSED (pfilename))
 {
+  /* Excess precision other than "fast" requires front-end
+     support.  */
+  flag_excess_precision_cmdline = EXCESS_PRECISION_FAST;
   return false;
 }
 
Index: c-typeck.c
===================================================================
--- c-typeck.c	(revision 141547)
+++ c-typeck.c	(working copy)
@@ -143,13 +143,21 @@ tree
 c_fully_fold (tree expr, bool in_init, bool *maybe_const)
 {
   tree ret;
+  tree eptype = NULL_TREE;
   bool dummy = true;
   bool maybe_const_itself = true;
 
   if (!maybe_const)
     maybe_const = &dummy;
+  if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR)
+    {
+      eptype = TREE_TYPE (expr);
+      expr = TREE_OPERAND (expr, 0);
+    }
   ret = c_fully_fold_internal (expr, in_init, maybe_const,
 			       &maybe_const_itself);
+  if (eptype)
+    ret = fold_convert (eptype, ret);
   *maybe_const &= maybe_const_itself;
   return ret;
 }
@@ -444,6 +452,15 @@ c_fully_fold_internal (tree expr, bool i
 	*maybe_const_itself &= op2_const_self;
       goto out;
 
+    case EXCESS_PRECISION_EXPR:
+      /* Each case where an operand with excess precision may be
+	 encountered must remove the EXCESS_PRECISION_EXPR around
+	 inner operands and possibly put one around the whole
+	 expression or possibly convert to the semantic type (which
+	 c_fully_fold does); we cannot tell at this stage which is
+	 appropriate in any particular case.  */
+      gcc_unreachable ();
+
     default:
       /* Various codes may appear through folding built-in functions
 	 and their arguments.  */
@@ -2874,6 +2891,7 @@ convert_arguments (int nargs, tree *arga
   int parmnum;
   const bool type_generic = fundecl
     && lookup_attribute ("type generic", TYPE_ATTRIBUTES(TREE_TYPE (fundecl)));
+  bool type_generic_remove_excess_precision = false;
   tree selector;
 
   /* Change pointer to function to the function itself for
@@ -2885,6 +2903,30 @@ convert_arguments (int nargs, tree *arga
   /* Handle an ObjC selector specially for diagnostics.  */
   selector = objc_message_selector ();
 
+  /* For type-generic built-in functions, determine whether excess
+     precision should be removed (classification) or not
+     (comparison).  */
+  if (type_generic
+      && DECL_BUILT_IN (fundecl)
+      && DECL_BUILT_IN_CLASS (fundecl) == BUILT_IN_NORMAL)
+    {
+      switch (DECL_FUNCTION_CODE (fundecl))
+	{
+	case BUILT_IN_ISFINITE:
+	case BUILT_IN_ISINF:
+	case BUILT_IN_ISINF_SIGN:
+	case BUILT_IN_ISNAN:
+	case BUILT_IN_ISNORMAL:
+	case BUILT_IN_FPCLASSIFY:
+	  type_generic_remove_excess_precision = true;
+	  break;
+
+	default:
+	  type_generic_remove_excess_precision = false;
+	  break;
+	}
+    }
+
   /* Scan the given expressions and types, producing individual
      converted arguments and storing them in ARGARRAY.  */
 
@@ -2894,9 +2936,11 @@ convert_arguments (int nargs, tree *arga
     {
       tree type = typetail ? TREE_VALUE (typetail) : 0;
       tree val = TREE_VALUE (valtail);
+      tree valtype = TREE_TYPE (val);
       tree rname = function;
       int argnum = parmnum + 1;
       const char *invalid_func_diag;
+      bool excess_precision = false;
       bool npc;
 
       if (type == void_type_node)
@@ -2912,6 +2956,19 @@ convert_arguments (int nargs, tree *arga
 	}
 
       npc = null_pointer_constant_p (val);
+
+      /* If there is excess precision and a prototype, convert once to
+	 the required type rather than converting via the semantic
+	 type.  Likewise without a prototype a float value represented
+	 as long double should be converted once to double.  But for
+	 type-generic classification functions excess precision must
+	 be removed here.  */
+      if (TREE_CODE (val) == EXCESS_PRECISION_EXPR
+	  && (type || !type_generic || !type_generic_remove_excess_precision))
+	{
+	  val = TREE_OPERAND (val, 0);
+	  excess_precision = true;
+	}
       val = c_fully_fold (val, false, NULL);
       STRIP_TYPE_NOPS (val);
 
@@ -2936,32 +2993,32 @@ convert_arguments (int nargs, tree *arga
 		  unsigned int formal_prec = TYPE_PRECISION (type);
 
 		  if (INTEGRAL_TYPE_P (type)
-		      && TREE_CODE (TREE_TYPE (val)) == REAL_TYPE)
+		      && TREE_CODE (valtype) == REAL_TYPE)
 		    warning (0, "passing argument %d of %qE as integer "
 			     "rather than floating due to prototype",
 			     argnum, rname);
 		  if (INTEGRAL_TYPE_P (type)
-		      && TREE_CODE (TREE_TYPE (val)) == COMPLEX_TYPE)
+		      && TREE_CODE (valtype) == COMPLEX_TYPE)
 		    warning (0, "passing argument %d of %qE as integer "
 			     "rather than complex due to prototype",
 			     argnum, rname);
 		  else if (TREE_CODE (type) == COMPLEX_TYPE
-			   && TREE_CODE (TREE_TYPE (val)) == REAL_TYPE)
+			   && TREE_CODE (valtype) == REAL_TYPE)
 		    warning (0, "passing argument %d of %qE as complex "
 			     "rather than floating due to prototype",
 			     argnum, rname);
 		  else if (TREE_CODE (type) == REAL_TYPE
-			   && INTEGRAL_TYPE_P (TREE_TYPE (val)))
+			   && INTEGRAL_TYPE_P (valtype))
 		    warning (0, "passing argument %d of %qE as floating "
 			     "rather than integer due to prototype",
 			     argnum, rname);
 		  else if (TREE_CODE (type) == COMPLEX_TYPE
-			   && INTEGRAL_TYPE_P (TREE_TYPE (val)))
+			   && INTEGRAL_TYPE_P (valtype))
 		    warning (0, "passing argument %d of %qE as complex "
 			     "rather than integer due to prototype",
 			     argnum, rname);
 		  else if (TREE_CODE (type) == REAL_TYPE
-			   && TREE_CODE (TREE_TYPE (val)) == COMPLEX_TYPE)
+			   && TREE_CODE (valtype) == COMPLEX_TYPE)
 		    warning (0, "passing argument %d of %qE as floating "
 			     "rather than complex due to prototype",
 			     argnum, rname);
@@ -2969,7 +3026,7 @@ convert_arguments (int nargs, tree *arga
 		     conversions between complex types, but that's too messy
 		     to do now.  */
 		  else if (TREE_CODE (type) == REAL_TYPE
-			   && TREE_CODE (TREE_TYPE (val)) == REAL_TYPE)
+			   && TREE_CODE (valtype) == REAL_TYPE)
 		    {
 		      /* Warn if any argument is passed as `float',
 			 since without a prototype it would be `double'.  */
@@ -2983,40 +3040,40 @@ convert_arguments (int nargs, tree *arga
 			 for decimal float types.  Warn of conversions with
 			 binary float types and of precision narrowing due to
 			 prototype. */
- 		      else if (type != TREE_TYPE (val)
+ 		      else if (type != valtype
 			       && (type == dfloat32_type_node
 				   || type == dfloat64_type_node
 				   || type == dfloat128_type_node
-				   || TREE_TYPE (val) == dfloat32_type_node
-				   || TREE_TYPE (val) == dfloat64_type_node
-				   || TREE_TYPE (val) == dfloat128_type_node)
+				   || valtype == dfloat32_type_node
+				   || valtype == dfloat64_type_node
+				   || valtype == dfloat128_type_node)
 			       && (formal_prec
-				   <= TYPE_PRECISION (TREE_TYPE (val))
+				   <= TYPE_PRECISION (valtype)
 				   || (type == dfloat128_type_node
-				       && (TREE_TYPE (val)
+				       && (valtype
 					   != dfloat64_type_node
-					   && (TREE_TYPE (val)
+					   && (valtype
 					       != dfloat32_type_node)))
 				   || (type == dfloat64_type_node
-				       && (TREE_TYPE (val)
+				       && (valtype
 					   != dfloat32_type_node))))
 			warning (0, "passing argument %d of %qE as %qT "
 				 "rather than %qT due to prototype",
-				 argnum, rname, type, TREE_TYPE (val));
+				 argnum, rname, type, valtype);
 
 		    }
 		  /* Detect integer changing in width or signedness.
 		     These warnings are only activated with
 		     -Wtraditional-conversion, not with -Wtraditional.  */
 		  else if (warn_traditional_conversion && INTEGRAL_TYPE_P (type)
-			   && INTEGRAL_TYPE_P (TREE_TYPE (val)))
+			   && INTEGRAL_TYPE_P (valtype))
 		    {
 		      tree would_have_been = default_conversion (val);
 		      tree type1 = TREE_TYPE (would_have_been);
 
 		      if (TREE_CODE (type) == ENUMERAL_TYPE
 			  && (TYPE_MAIN_VARIANT (type)
-			      == TYPE_MAIN_VARIANT (TREE_TYPE (val))))
+			      == TYPE_MAIN_VARIANT (valtype)))
 			/* No warning if function asks for enum
 			   and the actual arg is that enum type.  */
 			;
@@ -3040,8 +3097,8 @@ convert_arguments (int nargs, tree *arga
 			 unsigned type, it doesn't matter whether we
 			 pass it as signed or unsigned; the value
 			 certainly is the same either way.  */
-		      else if (TYPE_PRECISION (TREE_TYPE (val)) < TYPE_PRECISION (type)
-			       && TYPE_UNSIGNED (TREE_TYPE (val)))
+		      else if (TYPE_PRECISION (valtype) < TYPE_PRECISION (type)
+			       && TYPE_UNSIGNED (valtype))
 			;
 		      else if (TYPE_UNSIGNED (type))
 			warning (OPT_Wtraditional_conversion, "passing argument %d of %qE "
@@ -3053,6 +3110,10 @@ convert_arguments (int nargs, tree *arga
 		    }
 		}
 
+	      /* Possibly restore an EXCESS_PRECISION_EXPR for the
+		 sake of better warnings from convert_and_check.  */
+	      if (excess_precision)
+		val = build1 (EXCESS_PRECISION_EXPR, valtype, val);
 	      parmval = convert_for_assignment (type, val, ic_argpass, npc,
 						fundecl, function,
 						parmnum + 1);
@@ -3064,10 +3125,10 @@ convert_arguments (int nargs, tree *arga
 	    }
 	  argarray[parmnum] = parmval;
 	}
-      else if (TREE_CODE (TREE_TYPE (val)) == REAL_TYPE
-	       && (TYPE_PRECISION (TREE_TYPE (val))
+      else if (TREE_CODE (valtype) == REAL_TYPE
+	       && (TYPE_PRECISION (valtype)
 		   < TYPE_PRECISION (double_type_node))
-	       && !DECIMAL_FLOAT_MODE_P (TYPE_MODE (TREE_TYPE (val))))
+	       && !DECIMAL_FLOAT_MODE_P (TYPE_MODE (valtype)))
         {
 	  if (type_generic)
 	    argarray[parmnum] = val;
@@ -3075,6 +3136,10 @@ convert_arguments (int nargs, tree *arga
 	    /* Convert `float' to `double'.  */
 	    argarray[parmnum] = convert (double_type_node, val);
 	}
+      else if (excess_precision && !type_generic)
+	/* A "double" argument with excess precision being passed
+	   without a prototype or in variable arguments.  */
+	argarray[parmnum] = convert (valtype, val);
       else if ((invalid_func_diag =
 		targetm.calls.invalid_arg_for_unprototyped_fn (typelist, fundecl, val)))
 	{
@@ -3280,6 +3345,7 @@ build_unary_op (location_t location,
   enum tree_code typecode;
   tree val;
   tree ret = error_mark_node;
+  tree eptype = NULL_TREE;
   int noconvert = flag;
   const char *invalid_op_diag;
   bool int_operands;
@@ -3302,6 +3368,12 @@ build_unary_op (location_t location,
       return error_mark_node;
     }
 
+  if (TREE_CODE (arg) == EXCESS_PRECISION_EXPR)
+    {
+      eptype = TREE_TYPE (arg);
+      arg = TREE_OPERAND (arg, 0);
+    }
+
   switch (code)
     {
     case CONVERT_EXPR:
@@ -3398,6 +3470,8 @@ build_unary_op (location_t location,
 	ret = fold_build1 (REALPART_EXPR, TREE_TYPE (TREE_TYPE (arg)), arg);
       else
 	ret = arg;
+      if (eptype && TREE_CODE (eptype) == COMPLEX_TYPE)
+	eptype = TREE_TYPE (eptype);
       goto return_build_unary_op;
 
     case IMAGPART_EXPR:
@@ -3407,6 +3481,8 @@ build_unary_op (location_t location,
 	ret = fold_build1 (IMAGPART_EXPR, TREE_TYPE (TREE_TYPE (arg)), arg);
       else
 	ret = omit_one_operand (TREE_TYPE (arg), integer_zero_node, arg);
+      if (eptype && TREE_CODE (eptype) == COMPLEX_TYPE)
+	eptype = TREE_TYPE (eptype);
       goto return_build_unary_op;
 
     case PREINCREMENT_EXPR:
@@ -3657,6 +3733,8 @@ build_unary_op (location_t location,
     ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
   else if (TREE_CODE (ret) != INTEGER_CST && int_operands)
     ret = note_integer_operands (ret);
+  if (eptype)
+    ret = build1 (EXCESS_PRECISION_EXPR, eptype, ret);
   protected_set_expr_location (ret, location);
   return ret;
 }
@@ -3836,6 +3914,7 @@ build_conditional_expr (tree ifexp, bool
   enum tree_code code1;
   enum tree_code code2;
   tree result_type = NULL;
+  tree ep_result_type = NULL;
   tree orig_op1 = op1, orig_op2 = op2;
   bool int_const, op1_int_operands, op2_int_operands, int_operands;
   tree ret;
@@ -3865,6 +3944,28 @@ build_conditional_expr (tree ifexp, bool
       return error_mark_node;
     }
 
+  if ((TREE_CODE (op1) == EXCESS_PRECISION_EXPR
+       || TREE_CODE (op2) == EXCESS_PRECISION_EXPR)
+      && (code1 == INTEGER_TYPE || code1 == REAL_TYPE
+	  || code1 == COMPLEX_TYPE)
+      && (code2 == INTEGER_TYPE || code2 == REAL_TYPE
+	  || code2 == COMPLEX_TYPE))
+    {
+      ep_result_type = c_common_type (type1, type2);
+      if (TREE_CODE (op1) == EXCESS_PRECISION_EXPR)
+	{
+	  op1 = TREE_OPERAND (op1, 0);
+	  type1 = TREE_TYPE (op1);
+	  gcc_assert (TREE_CODE (type1) == code1);
+	}
+      if (TREE_CODE (op2) == EXCESS_PRECISION_EXPR)
+	{
+	  op2 = TREE_OPERAND (op2, 0);
+	  type2 = TREE_TYPE (op2);
+	  gcc_assert (TREE_CODE (type2) == code2);
+	}
+    }
+
   /* Quickly detect the usual case where op1 and op2 have the same type
      after promotion.  */
   if (TYPE_MAIN_VARIANT (type1) == TYPE_MAIN_VARIANT (type2))
@@ -4030,6 +4131,8 @@ build_conditional_expr (tree ifexp, bool
       if (int_operands)
 	ret = note_integer_operands (ret);
     }
+  if (ep_result_type)
+    ret = build1 (EXCESS_PRECISION_EXPR, ep_result_type, ret);
 
   return ret;
 }
@@ -4040,8 +4143,17 @@ build_conditional_expr (tree ifexp, bool
 tree
 build_compound_expr (tree expr1, tree expr2)
 {
+  tree eptype = NULL_TREE;
   tree ret;
 
+  if (TREE_CODE (expr1) == EXCESS_PRECISION_EXPR)
+    expr1 = TREE_OPERAND (expr1, 0);
+  if (TREE_CODE (expr2) == EXCESS_PRECISION_EXPR)
+    {
+      eptype = TREE_TYPE (expr2);
+      expr2 = TREE_OPERAND (expr2, 0);
+    }
+
   if (!TREE_SIDE_EFFECTS (expr1))
     {
       /* The left-hand operand of a comma expression is like an expression
@@ -4079,6 +4191,9 @@ build_compound_expr (tree expr1, tree ex
       && EXPR_INT_CONST_OPERANDS (expr2))
     ret = note_integer_operands (ret);
 
+  if (eptype)
+    ret = build1 (EXCESS_PRECISION_EXPR, eptype, ret);
+
   return ret;
 }
 
@@ -4087,7 +4202,12 @@ build_compound_expr (tree expr1, tree ex
 tree
 build_c_cast (tree type, tree expr)
 {
-  tree value = expr;
+  tree value;
+
+  if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR)
+    expr = TREE_OPERAND (expr, 0);
+
+  value = expr;
 
   if (type == error_mark_node || expr == error_mark_node)
     return error_mark_node;
@@ -4347,6 +4467,7 @@ build_modify_expr (location_t location,
 {
   tree result;
   tree newrhs;
+  tree rhs_semantic_type = NULL_TREE;
   tree lhstype = TREE_TYPE (lhs);
   tree olhstype = lhstype;
   bool npc;
@@ -4361,6 +4482,12 @@ build_modify_expr (location_t location,
   if (!lvalue_or_else (lhs, lv_assign))
     return error_mark_node;
 
+  if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
+    {
+      rhs_semantic_type = TREE_TYPE (rhs);
+      rhs = TREE_OPERAND (rhs, 0);
+    }
+
   newrhs = rhs;
 
   if (TREE_CODE (lhs) == C_MAYBE_CONST_EXPR)
@@ -4420,11 +4547,14 @@ build_modify_expr (location_t location,
       TREE_TYPE (lhs) = lhstype;
     }
 
-  /* Convert new value to destination type.  Fold it first for the
-     sake of conversion warnings.  */
+  /* Convert new value to destination type.  Fold it first, then
+     restore any excess precision information, for the sake of
+     conversion warnings.  */
 
   npc = null_pointer_constant_p (newrhs);
   newrhs = c_fully_fold (newrhs, false, NULL);
+  if (rhs_semantic_type)
+    newrhs = build1 (EXCESS_PRECISION_EXPR, rhs_semantic_type, newrhs);
   newrhs = convert_for_assignment (lhstype, newrhs, ic_assign, npc,
 				   NULL_TREE, NULL_TREE, 0);
   if (TREE_CODE (newrhs) == ERROR_MARK)
@@ -4479,6 +4609,7 @@ convert_for_assignment (tree type, tree 
 			tree fundecl, tree function, int parmnum)
 {
   enum tree_code codel = TREE_CODE (type);
+  tree orig_rhs = rhs;
   tree rhstype;
   enum tree_code coder;
   tree rname = NULL_TREE;
@@ -4531,6 +4662,9 @@ convert_for_assignment (tree type, tree 
       }                                                                  \
   } while (0)
 
+  if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
+    rhs = TREE_OPERAND (rhs, 0);
+
   rhstype = TREE_TYPE (rhs);
   coder = TREE_CODE (rhstype);
 
@@ -4618,7 +4752,7 @@ convert_for_assignment (tree type, tree 
 	       || coder == FIXED_POINT_TYPE
 	       || coder == ENUMERAL_TYPE || coder == COMPLEX_TYPE
 	       || coder == BOOLEAN_TYPE))
-    return convert_and_check (type, rhs);
+    return convert_and_check (type, orig_rhs);
 
   /* Aggregates in different TUs might need conversion.  */
   if ((codel == RECORD_TYPE || codel == UNION_TYPE)
@@ -5252,6 +5386,7 @@ digest_init (tree type, tree init, bool 
 {
   enum tree_code code = TREE_CODE (type);
   tree inside_init = init;
+  tree semantic_type = NULL_TREE;
   bool maybe_const = true;
 
   if (type == error_mark_node
@@ -5262,6 +5397,11 @@ digest_init (tree type, tree init, bool 
 
   STRIP_TYPE_NOPS (inside_init);
 
+  if (TREE_CODE (inside_init) == EXCESS_PRECISION_EXPR)
+    {
+      semantic_type = TREE_TYPE (inside_init);
+      inside_init = TREE_OPERAND (inside_init, 0);
+    }
   inside_init = c_fully_fold (inside_init, require_constant, &maybe_const);
   inside_init = decl_constant_value_for_optimization (inside_init);
 
@@ -5479,6 +5619,9 @@ digest_init (tree type, tree init, bool 
 	  && (TREE_CODE (init) == STRING_CST
 	      || TREE_CODE (init) == COMPOUND_LITERAL_EXPR))
 	inside_init = init = array_to_pointer_conversion (init);
+      if (semantic_type)
+	inside_init = build1 (EXCESS_PRECISION_EXPR, semantic_type,
+			      inside_init);
       inside_init
 	= convert_for_assignment (type, inside_init, ic_init,
 				  null_pointer_constant,
@@ -6844,6 +6987,7 @@ static void
 output_init_element (tree value, bool strict_string, tree type, tree field,
 		     int pending)
 {
+  tree semantic_type = NULL_TREE;
   constructor_elt *celt;
   bool maybe_const = true;
   bool npc;
@@ -6874,6 +7018,11 @@ output_init_element (tree value, bool st
     }
 
   npc = null_pointer_constant_p (value);
+  if (TREE_CODE (value) == EXCESS_PRECISION_EXPR)
+    {
+      semantic_type = TREE_TYPE (value);
+      value = TREE_OPERAND (value, 0);
+    }
   value = c_fully_fold (value, require_constant_value, &maybe_const);
 
   if (value == error_mark_node)
@@ -6915,6 +7064,8 @@ output_init_element (tree value, bool st
 		  || TREE_CHAIN (field)))))
     return;
 
+  if (semantic_type)
+    value = build1 (EXCESS_PRECISION_EXPR, semantic_type, value);
   value = digest_init (type, value, npc, strict_string,
 		       require_constant_value);
   if (value == error_mark_node)
@@ -7220,7 +7371,18 @@ process_init_element (struct c_expr valu
       if (TREE_CODE (value.value) != COMPOUND_LITERAL_EXPR
 	  || !require_constant_value
 	  || flag_isoc99)
-	value.value = c_save_expr (value.value);
+	{
+	  tree semantic_type = NULL_TREE;
+	  if (TREE_CODE (value.value) == EXCESS_PRECISION_EXPR)
+	    {
+	      semantic_type = TREE_TYPE (value.value);
+	      value.value = TREE_OPERAND (value.value, 0);
+	    }
+	  value.value = c_save_expr (value.value);
+	  if (semantic_type)
+	    value.value = build1 (EXCESS_PRECISION_EXPR, semantic_type,
+				  value.value);
+	}
     }
 
   while (1)
@@ -7713,8 +7875,16 @@ c_finish_return (tree retval)
 
   if (retval)
     {
+      tree semantic_type = NULL_TREE;
       npc = null_pointer_constant_p (retval);
+      if (TREE_CODE (retval) == EXCESS_PRECISION_EXPR)
+	{
+	  semantic_type = TREE_TYPE (retval);
+	  retval = TREE_OPERAND (retval, 0);
+	}
       retval = c_fully_fold (retval, false, NULL);
+      if (semantic_type)
+	retval = build1 (EXCESS_PRECISION_EXPR, semantic_type, retval);
     }
 
   if (!retval)
@@ -8527,7 +8697,8 @@ tree
 build_binary_op (location_t location, enum tree_code code,
 		 tree orig_op0, tree orig_op1, int convert_p)
 {
-  tree type0, type1;
+  tree type0, type1, orig_type0, orig_type1;
+  tree eptype;
   enum tree_code code0, code1;
   tree op0, op1;
   tree ret = error_mark_node;
@@ -8543,6 +8714,10 @@ build_binary_op (location_t location, en
      In the simplest cases this is the common type of the arguments.  */
   tree result_type = NULL;
 
+  /* When the computation is in excess precision, the type of the
+     final EXCESS_PRECISION_EXPR.  */
+  tree real_result_type = NULL;
+
   /* Nonzero means operands have already been type-converted
      in whatever way is necessary.
      Zero means they need to be converted to RESULT_TYPE.  */
@@ -8579,6 +8754,10 @@ build_binary_op (location_t location, en
   /* True means types are compatible as far as ObjC is concerned.  */
   bool objc_ok;
 
+  /* True means this is an arithmetic operation that may need excess
+     precision.  */
+  bool may_need_excess_precision;
+
   if (location == UNKNOWN_LOCATION)
     location = input_location;
 
@@ -8606,8 +8785,8 @@ build_binary_op (location_t location, en
       op1 = orig_op1;
     }
 
-  type0 = TREE_TYPE (op0);
-  type1 = TREE_TYPE (op1);
+  orig_type0 = type0 = TREE_TYPE (op0);
+  orig_type1 = type1 = TREE_TYPE (op1);
 
   /* The expression codes of the data types of the arguments tell us
      whether the arguments are integers, floating, pointers, etc.  */
@@ -8631,6 +8810,45 @@ build_binary_op (location_t location, en
       return error_mark_node;
     }
 
+  switch (code)
+    {
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case MULT_EXPR:
+    case TRUNC_DIV_EXPR:
+    case CEIL_DIV_EXPR:
+    case FLOOR_DIV_EXPR:
+    case ROUND_DIV_EXPR:
+    case EXACT_DIV_EXPR:
+      may_need_excess_precision = true;
+      break;
+    default:
+      may_need_excess_precision = false;
+      break;
+    }
+  if (TREE_CODE (op0) == EXCESS_PRECISION_EXPR)
+    {
+      op0 = TREE_OPERAND (op0, 0);
+      type0 = TREE_TYPE (op0);
+    }
+  else if (may_need_excess_precision
+	   && (eptype = excess_precision_type (type0)) != NULL_TREE)
+    {
+      type0 = eptype;
+      op0 = convert (eptype, op0);
+    }
+  if (TREE_CODE (op1) == EXCESS_PRECISION_EXPR)
+    {
+      op1 = TREE_OPERAND (op1, 0);
+      type1 = TREE_TYPE (op1);
+    }
+  else if (may_need_excess_precision
+	   && (eptype = excess_precision_type (type1)) != NULL_TREE)
+    {
+      type1 = eptype;
+      op1 = convert (eptype, op1);
+    }
+
   objc_ok = objc_compare_types (type0, type1, -3, NULL_TREE);
 
   switch (code)
@@ -9131,7 +9349,14 @@ build_binary_op (location_t location, en
     }
 
   if (build_type == NULL_TREE)
-    build_type = result_type;
+    {
+      build_type = result_type;
+      if (type0 != orig_type0 || type1 != orig_type1)
+	{
+	  gcc_assert (may_need_excess_precision && common);
+	  real_result_type = c_common_type (orig_type0, orig_type1);
+	}
+    }
 
   /* Treat expressions in initializers specially as they can't trap.  */
   if (int_const_or_overflow)
@@ -9151,6 +9376,8 @@ build_binary_op (location_t location, en
 	   : build1 (NOP_EXPR, TREE_TYPE (ret), ret));
   else if (TREE_CODE (ret) != INTEGER_CST && int_operands)
     ret = note_integer_operands (ret);
+  if (real_result_type)
+    ret = build1 (EXCESS_PRECISION_EXPR, real_result_type, ret);
   protected_set_expr_location (ret, location);
   return ret;
 }
Index: c-opts.c
===================================================================
--- c-opts.c	(revision 141547)
+++ c-opts.c	(working copy)
@@ -1017,6 +1017,15 @@ c_common_post_options (const char **pfil
   C_COMMON_OVERRIDE_OPTIONS;
 #endif
 
+  /* Excess precision other than "fast" requires front-end
+     support.  */
+  if (c_dialect_cxx ())
+    flag_excess_precision_cmdline = EXCESS_PRECISION_FAST;
+  else if (flag_excess_precision_cmdline == EXCESS_PRECISION_DEFAULT)
+    flag_excess_precision_cmdline = (flag_iso
+				     ? EXCESS_PRECISION_STANDARD
+				     : EXCESS_PRECISION_FAST);
+
   /* By default we use C99 inline semantics in GNU99 or C99 mode.  C99
      inline semantics are not supported in GNU89 or C89 mode.  */
   if (flag_gnu89_inline == -1)
Index: common.opt
===================================================================
--- common.opt	(revision 141547)
+++ common.opt	(working copy)
@@ -495,6 +495,10 @@ fexpensive-optimizations
 Common Report Var(flag_expensive_optimizations) Optimization
 Perform a number of minor, expensive optimizations
 
+fexcess-precision=
+Common Joined RejectNegative
+-fexcess-precision=[fast|standard]	Specify handling of excess floating-point precision
+
 ffast-math
 Common
 
Index: c-common.c
===================================================================
--- c-common.c	(revision 141547)
+++ c-common.c	(working copy)
@@ -1783,6 +1783,21 @@ tree
 convert_and_check (tree type, tree expr)
 {
   tree result;
+  tree expr_for_warning;
+
+  /* Convert from a value with possible excess precision rather than
+     via the semantic type, but do not warn about values not fitting
+     exactly in the semantic type.  */
+  if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR)
+    {
+      tree orig_type = TREE_TYPE (expr);
+      expr = TREE_OPERAND (expr, 0);
+      expr_for_warning = convert (orig_type, expr);
+      if (orig_type == type)
+	return expr_for_warning;
+    }
+  else
+    expr_for_warning = expr;
 
   if (TREE_TYPE (expr) == type)
     return expr;
@@ -1790,7 +1805,7 @@ convert_and_check (tree type, tree expr)
   result = convert (type, expr);
 
   if (!skip_evaluation && !TREE_OVERFLOW_P (expr) && result != error_mark_node)
-    warnings_for_convert_and_check (type, expr, result);
+    warnings_for_convert_and_check (type, expr_for_warning, result);
 
   return result;
 }
@@ -3476,6 +3491,7 @@ c_common_truthvalue_conversion (location
     case NEGATE_EXPR:
     case ABS_EXPR:
     case FLOAT_EXPR:
+    case EXCESS_PRECISION_EXPR:
       /* These don't change whether an object is nonzero or zero.  */
       return c_common_truthvalue_conversion (location, TREE_OPERAND (expr, 0));
 
Index: c-common.def
===================================================================
--- c-common.def	(revision 141547)
+++ c-common.def	(working copy)
@@ -46,6 +46,13 @@ DEFTREECODE (COMPOUND_LITERAL_EXPR, "com
    not.  */
 DEFTREECODE (C_MAYBE_CONST_EXPR, "c_maybe_const_expr", tcc_expression, 2)
 
+/* An EXCESS_PRECISION_EXPR, currently only used for C and Objective
+   C, represents an expression evaluated in greater range or precision
+   than its type.  The type of the EXCESS_PRECISION_EXPR is the
+   semantic type while the operand represents what is actually being
+   evaluated.  */
+DEFTREECODE (EXCESS_PRECISION_EXPR, "excess_precision_expr", tcc_expression, 1)
+
 /*
 Local variables:
 mode:c
Index: c-parser.c
===================================================================
--- c-parser.c	(revision 141547)
+++ c-parser.c	(working copy)
@@ -4456,10 +4456,18 @@ c_parser_conditional_expression (c_parse
   c_parser_consume_token (parser);
   if (c_parser_next_token_is (parser, CPP_COLON))
     {
+      tree eptype = NULL_TREE;
       pedwarn (c_parser_peek_token (parser)->location, OPT_pedantic, 
 	       "ISO C forbids omitting the middle term of a ?: expression");
+      if (TREE_CODE (cond.value) == EXCESS_PRECISION_EXPR)
+	{
+	  eptype = TREE_TYPE (cond.value);
+	  cond.value = TREE_OPERAND (cond.value, 0);
+	}
       /* Make sure first operand is calculated only once.  */
       exp1.value = c_save_expr (default_conversion (cond.value));
+      if (eptype)
+	exp1.value = build1 (EXCESS_PRECISION_EXPR, eptype, exp1.value);
       cond.value = c_objc_common_truthvalue_conversion (cond_loc, exp1.value);
       skip_evaluation += cond.value == truthvalue_true_node;
     }
Index: config/i386/i386.h
===================================================================
--- config/i386/i386.h	(revision 141547)
+++ config/i386/i386.h	(working copy)
@@ -608,6 +608,20 @@ enum target_cpu_default
 #define TARGET_FLT_EVAL_METHOD \
   (TARGET_MIX_SSE_I387 ? -1 : TARGET_SSE_MATH ? 0 : 2)
 
+/* Whether to allow x87 floating-point arithmetic on MODE (one of
+   SFmode, DFmode and XFmode) in the current excess precision
+   configuration.  */
+#define X87_ENABLE_ARITH(MODE) \
+  (flag_excess_precision == EXCESS_PRECISION_FAST || (MODE) == XFmode)
+
+/* Likewise, whether to allow direct conversions from integer mode
+   IMODE (HImode, SImode or DImode) to MODE.  */
+#define X87_ENABLE_FLOAT(MODE, IMODE)			\
+  (flag_excess_precision == EXCESS_PRECISION_FAST	\
+   || (MODE) == XFmode					\
+   || ((MODE) == DFmode && (IMODE) == SImode)		\
+   || (IMODE) == HImode)
+
 /* target machine storage layout */
 
 #define SHORT_TYPE_SIZE 16
Index: config/i386/i386.md
===================================================================
--- config/i386/i386.md	(revision 141547)
+++ config/i386/i386.md	(working copy)
@@ -5122,13 +5122,28 @@
   "TARGET_80387
    || ((<SSEMODEI24:MODE>mode != DImode || TARGET_64BIT)
        && SSE_FLOAT_MODE_P (<X87MODEF:MODE>mode) && TARGET_SSE_MATH)"
-  "")
+  "
+{
+  if (!((<SSEMODEI24:MODE>mode != DImode || TARGET_64BIT)
+	&& SSE_FLOAT_MODE_P (<X87MODEF:MODE>mode) && TARGET_SSE_MATH)
+      && !X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, <SSEMODEI24:MODE>mode))
+    {
+      rtx reg = gen_reg_rtx (XFmode);
+      emit_insn (gen_float<SSEMODEI24:mode>xf2 (reg, operands[1]));
+/* Avoid references to nonexistent function in dead code in XFmode case.  */
+#define gen_truncxfxf2 gen_truncxfdf2
+      emit_insn (gen_truncxf<X87MODEF:mode>2 (operands[0], reg));
+#undef gen_truncxfxf2
+      DONE;
+    }
+}")
 
 ;; Pre-reload splitter to add memory clobber to the pattern.
 (define_insn_and_split "*float<SSEMODEI24:mode><X87MODEF:mode>2_1"
   [(set (match_operand:X87MODEF 0 "register_operand" "")
 	(float:X87MODEF (match_operand:SSEMODEI24 1 "register_operand" "")))]
   "((TARGET_80387
+     && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, <SSEMODEI24:MODE>mode)
      && (!((<SSEMODEI24:MODE>mode != DImode || TARGET_64BIT)
 	   && SSE_FLOAT_MODE_P (<X87MODEF:MODE>mode) && TARGET_SSE_MATH)
 	 || TARGET_MIX_SSE_I387))
@@ -5509,7 +5524,8 @@
 	(float:X87MODEF
 	  (match_operand:SSEMODEI24 1 "nonimmediate_operand" "m,?r")))
   (clobber (match_operand:SSEMODEI24 2 "memory_operand" "=X,m"))]
-  "TARGET_80387"
+  "TARGET_80387
+   && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, <SSEMODEI24:MODE>mode)"
   "@
    fild%z1\t%1
    #"
@@ -5522,7 +5538,8 @@
   [(set (match_operand:X87MODEF 0 "register_operand" "=f")
 	(float:X87MODEF
 	  (match_operand:SSEMODEI24 1 "memory_operand" "m")))]
-  "TARGET_80387"
+  "TARGET_80387
+   && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, <SSEMODEI24:MODE>mode)"
   "fild%z1\t%1"
   [(set_attr "type" "fmov")
    (set_attr "mode" "<X87MODEF:MODE>")
@@ -5533,6 +5550,7 @@
 	(float:X87MODEF (match_operand:SSEMODEI24 1 "register_operand" "")))
    (clobber (match_operand:SSEMODEI24 2 "memory_operand" ""))]
   "TARGET_80387
+   && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, <SSEMODEI24:MODE>mode)
    && reload_completed
    && FP_REG_P (operands[0])"
   [(set (match_dup 2) (match_dup 1))
@@ -5544,6 +5562,7 @@
 	(float:X87MODEF (match_operand:SSEMODEI24 1 "memory_operand" "")))
    (clobber (match_operand:SSEMODEI24 2 "memory_operand" ""))]
   "TARGET_80387
+   && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, <SSEMODEI24:MODE>mode)
    && reload_completed
    && FP_REG_P (operands[0])"
   [(set (match_dup 0) (float:X87MODEF (match_dup 1)))]
@@ -5559,7 +5578,8 @@
    (clobber (match_scratch:V4SI 3 "=X,x"))
    (clobber (match_scratch:V4SI 4 "=X,x"))
    (clobber (match_operand:DI 2 "memory_operand" "=X,m"))]
-  "TARGET_80387 && TARGET_SSE2 && TARGET_INTER_UNIT_MOVES
+  "TARGET_80387 && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, DImode)
+   && TARGET_SSE2 && TARGET_INTER_UNIT_MOVES
    && !TARGET_64BIT && optimize_function_for_speed_p (cfun)"
   "#"
   [(set_attr "type" "multi")
@@ -5573,7 +5593,8 @@
    (clobber (match_scratch:V4SI 3 ""))
    (clobber (match_scratch:V4SI 4 ""))
    (clobber (match_operand:DI 2 "memory_operand" ""))]
-  "TARGET_80387 && TARGET_SSE2 && TARGET_INTER_UNIT_MOVES
+  "TARGET_80387 && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, DImode)
+   && TARGET_SSE2 && TARGET_INTER_UNIT_MOVES
    && !TARGET_64BIT && optimize_function_for_speed_p (cfun)
    && reload_completed
    && FP_REG_P (operands[0])"
@@ -5597,7 +5618,8 @@
    (clobber (match_scratch:V4SI 3 ""))
    (clobber (match_scratch:V4SI 4 ""))
    (clobber (match_operand:DI 2 "memory_operand" ""))]
-  "TARGET_80387 && TARGET_SSE2 && TARGET_INTER_UNIT_MOVES
+  "TARGET_80387 && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, DImode)
+   && TARGET_SSE2 && TARGET_INTER_UNIT_MOVES
    && !TARGET_64BIT && optimize_function_for_speed_p (cfun)
    && reload_completed
    && FP_REG_P (operands[0])"
@@ -5617,7 +5639,8 @@
    (clobber (match_operand:DI 2 "memory_operand" "=m,m"))
    (clobber (match_scratch:SI 3 "=X,x"))]
   "!TARGET_64BIT
-   && TARGET_80387 && TARGET_SSE"
+   && TARGET_80387 && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, SImode)
+   && TARGET_SSE"
   "#"
   [(set_attr "type" "multi")
    (set_attr "mode" "<MODE>")])
@@ -5629,7 +5652,8 @@
    (clobber (match_operand:DI 2 "memory_operand" ""))
    (clobber (match_scratch:SI 3 ""))]
   "!TARGET_64BIT
-   && TARGET_80387 && TARGET_SSE
+   && TARGET_80387 && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, SImode)
+   && TARGET_SSE
    && reload_completed"
   [(set (match_dup 2) (match_dup 1))
    (set (match_dup 0)
@@ -5643,7 +5667,8 @@
    (clobber (match_operand:DI 2 "memory_operand" ""))
    (clobber (match_scratch:SI 3 ""))]
   "!TARGET_64BIT
-   && TARGET_80387 && TARGET_SSE
+   && TARGET_80387 && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, SImode)
+   && TARGET_SSE
    && reload_completed"
   [(set (match_dup 2) (match_dup 3))
    (set (match_dup 0)
@@ -5661,7 +5686,8 @@
       (clobber (match_dup 2))
       (clobber (match_scratch:SI 3 ""))])]
   "!TARGET_64BIT
-   && ((TARGET_80387 && TARGET_SSE)
+   && ((TARGET_80387 && X87_ENABLE_FLOAT (<X87MODEF:MODE>mode, SImode)
+	&& TARGET_SSE)
        || (SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH))"
 {
   if (SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH)
@@ -7460,7 +7486,8 @@
   [(set (match_operand:MODEF 0 "register_operand" "")
 	(plus:MODEF (match_operand:MODEF 1 "register_operand" "")
 		    (match_operand:MODEF 2 "nonimmediate_operand" "")))]
-  "TARGET_80387 || (SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH)"
+  "(TARGET_80387 && X87_ENABLE_ARITH (<MODE>mode))
+    || (SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH)"
   "")
 \f
 ;; Subtract instructions
@@ -7820,7 +7847,8 @@
   [(set (match_operand:MODEF 0 "register_operand" "")
 	(minus:MODEF (match_operand:MODEF 1 "register_operand" "")
 		     (match_operand:MODEF 2 "nonimmediate_operand" "")))]
-  "TARGET_80387 || (SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH)"
+  "(TARGET_80387 && X87_ENABLE_ARITH (<MODE>mode))
+    || (SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH)"
   "")
 \f
 ;; Multiply instructions
@@ -8375,7 +8403,8 @@
   [(set (match_operand:MODEF 0 "register_operand" "")
 	(mult:MODEF (match_operand:MODEF 1 "register_operand" "")
 		    (match_operand:MODEF 2 "nonimmediate_operand" "")))]
-  "TARGET_80387 || (SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH)"
+  "(TARGET_80387 && X87_ENABLE_ARITH (<MODE>mode))
+    || (SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH)"
   "")
 
 ;; SSE5 scalar multiply/add instructions are defined in sse.md.
@@ -8416,14 +8445,16 @@
   [(set (match_operand:DF 0 "register_operand" "")
  	(div:DF (match_operand:DF 1 "register_operand" "")
  		(match_operand:DF 2 "nonimmediate_operand" "")))]
-   "TARGET_80387 || (TARGET_SSE2 && TARGET_SSE_MATH)"
+   "(TARGET_80387 && X87_ENABLE_ARITH (DFmode))
+    || (TARGET_SSE2 && TARGET_SSE_MATH)"
    "")
 
 (define_expand "divsf3"
   [(set (match_operand:SF 0 "register_operand" "")
 	(div:SF (match_operand:SF 1 "register_operand" "")
 		(match_operand:SF 2 "nonimmediate_operand" "")))]
-  "TARGET_80387 || TARGET_SSE_MATH"
+  "(TARGET_80387 && X87_ENABLE_ARITH (SFmode))
+    || TARGET_SSE_MATH"
 {
   if (TARGET_SSE_MATH && TARGET_RECIP && optimize_insn_for_speed_p ()
       && flag_finite_math_only && !flag_trapping_math
@@ -16278,7 +16309,7 @@
 	(match_operator:MODEF 3 "binary_fp_operator"
 	  [(match_operand:MODEF 1 "nonimmediate_operand" "%0")
 	   (match_operand:MODEF 2 "nonimmediate_operand" "fm")]))]
-  "TARGET_80387
+  "TARGET_80387 && X87_ENABLE_ARITH (<MODE>mode)
    && COMMUTATIVE_ARITH_P (operands[3])
    && !(MEM_P (operands[1]) && MEM_P (operands[2]))"
   "* return output_387_binary_op (insn, operands);"
@@ -16392,7 +16423,8 @@
 	(match_operator:MODEF 3 "binary_fp_operator"
 	  [(match_operand:MODEF 1 "nonimmediate_operand" "0,fm")
 	   (match_operand:MODEF 2 "nonimmediate_operand" "fm,0")]))]
-  "TARGET_80387 && !(SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH)
+  "TARGET_80387 && X87_ENABLE_ARITH (<MODE>mode)
+   && !(SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH)
    && !COMMUTATIVE_ARITH_P (operands[3])
    && !(MEM_P (operands[1]) && MEM_P (operands[2]))"
   "* return output_387_binary_op (insn, operands);"
@@ -16412,7 +16444,8 @@
 	  [(float:MODEF
 	     (match_operand:X87MODEI12 1 "nonimmediate_operand" "m,?r"))
 	   (match_operand:MODEF 2 "register_operand" "0,0")]))]
-  "TARGET_80387 && !(SSE_FLOAT_MODE_P (<MODEF:MODE>mode) && TARGET_SSE_MATH)
+  "TARGET_80387 && X87_ENABLE_FLOAT (<MODEF:MODE>mode, <X87MODEI12:MODE>mode)
+   && !(SSE_FLOAT_MODE_P (<MODEF:MODE>mode) && TARGET_SSE_MATH)
    && (TARGET_USE_<X87MODEI12:MODE>MODE_FIOP || optimize_function_for_size_p (cfun))"
   "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);"
   [(set (attr "type")
@@ -16431,7 +16464,8 @@
 	  [(match_operand:MODEF 1 "register_operand" "0,0")
 	   (float:MODEF
 	     (match_operand:X87MODEI12 2 "nonimmediate_operand" "m,?r"))]))]
-  "TARGET_80387 && !(SSE_FLOAT_MODE_P (<MODEF:MODE>mode) && TARGET_SSE_MATH)
+  "TARGET_80387 && X87_ENABLE_FLOAT (<MODEF:MODE>mode, <X87MODEI12:MODE>mode)
+   && !(SSE_FLOAT_MODE_P (<MODEF:MODE>mode) && TARGET_SSE_MATH)
    && (TARGET_USE_<X87MODEI12:MODE>MODE_FIOP || optimize_function_for_size_p (cfun))"
   "* return which_alternative ? \"#\" : output_387_binary_op (insn, operands);"
   [(set (attr "type")
@@ -16450,7 +16484,8 @@
 	   [(float_extend:DF
 	     (match_operand:SF 1 "nonimmediate_operand" "fm,0"))
 	    (match_operand:DF 2 "register_operand" "0,f")]))]
-  "TARGET_80387 && !(TARGET_SSE2 && TARGET_SSE_MATH)
+  "TARGET_80387 && X87_ENABLE_ARITH (DFmode)
+   && !(TARGET_SSE2 && TARGET_SSE_MATH)
    && !(MEM_P (operands[1]) && MEM_P (operands[2]))"
   "* return output_387_binary_op (insn, operands);"
   [(set (attr "type")
@@ -16468,7 +16503,8 @@
 	  [(match_operand:DF 1 "register_operand" "0,f")
 	   (float_extend:DF
 	    (match_operand:SF 2 "nonimmediate_operand" "fm,0"))]))]
-  "TARGET_80387 && !(TARGET_SSE2 && TARGET_SSE_MATH)"
+  "TARGET_80387 && X87_ENABLE_ARITH (DFmode)
+   && !(TARGET_SSE2 && TARGET_SSE_MATH)"
   "* return output_387_binary_op (insn, operands);"
   [(set (attr "type")
         (cond [(match_operand:DF 3 "mult_operator" "")
@@ -16486,7 +16522,8 @@
 	    (match_operand:SF 1 "register_operand" "0,f"))
 	   (float_extend:DF
 	    (match_operand:SF 2 "nonimmediate_operand" "fm,0"))]))]
-  "TARGET_80387 && !(TARGET_SSE2 && TARGET_SSE_MATH)"
+  "TARGET_80387 && X87_ENABLE_ARITH (DFmode)
+   && !(TARGET_SSE2 && TARGET_SSE_MATH)"
   "* return output_387_binary_op (insn, operands);"
   [(set (attr "type")
         (cond [(match_operand:DF 3 "mult_operator" "")
@@ -16622,7 +16659,8 @@
 	   [(float (match_operand:X87MODEI12 1 "register_operand" ""))
 	    (match_operand 2 "register_operand" "")]))]
   "reload_completed
-   && X87_FLOAT_MODE_P (GET_MODE (operands[0]))"
+   && X87_FLOAT_MODE_P (GET_MODE (operands[0]))
+   && X87_ENABLE_FLOAT (GET_MODE (operands[0]), GET_MODE (operands[1]))"
   [(const_int 0)]
 {
   operands[4] = ix86_force_to_memory (GET_MODE (operands[1]), operands[1]);
@@ -16642,7 +16680,8 @@
 	   [(match_operand 1 "register_operand" "")
 	    (float (match_operand:X87MODEI12 2 "register_operand" ""))]))]
   "reload_completed
-   && X87_FLOAT_MODE_P (GET_MODE (operands[0]))"
+   && X87_FLOAT_MODE_P (GET_MODE (operands[0]))
+   && X87_ENABLE_FLOAT (GET_MODE (operands[0]), GET_MODE (operands[2]))"
   [(const_int 0)]
 {
   operands[4] = ix86_force_to_memory (GET_MODE (operands[2]), operands[2]);
@@ -16728,7 +16767,7 @@
   [(set (match_operand:MODEF 0 "register_operand" "")
 	(sqrt:MODEF
 	  (match_operand:MODEF 1 "nonimmediate_operand" "")))]
-  "TARGET_USE_FANCY_MATH_387
+  "(TARGET_USE_FANCY_MATH_387 && X87_ENABLE_ARITH (<MODE>mode))
    || (SSE_FLOAT_MODE_P (<MODE>mode) && TARGET_SSE_MATH)"
 {
   if (<MODE>mode == SFmode
Index: convert.c
===================================================================
--- convert.c	(revision 141547)
+++ convert.c	(working copy)
@@ -321,7 +321,8 @@ convert_to_real (tree type, tree expr)
 		      && (flag_unsafe_math_optimizations
 			  || (TYPE_PRECISION (newtype) == TYPE_PRECISION (type)
 			      && real_can_shorten_arithmetic (TYPE_MODE (itype),
-							      TYPE_MODE (type)))))
+							      TYPE_MODE (type))
+			      && !excess_precision_type (newtype))))
 		    {
 		      expr = build2 (TREE_CODE (expr), newtype,
 				     fold (convert_to_real (newtype, arg0)),

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2008-11-10 18:29 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-11-04  2:08 [4.5 C] C99-conforming excess precision (fix PR 323 for C) Joseph S. Myers
2008-11-05 15:44 ` Ian Lance Taylor
2008-11-05 17:24   ` Joseph S. Myers
2008-11-05 18:37     ` Ian Lance Taylor
2008-11-09 21:11       ` Joseph S. Myers
2008-11-10 19:14         ` Ian Lance Taylor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).