This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PR 323 patch committed


I have committed my patch to implement C99-conforming excess precision for 
C to trunk after bootstrap with no regressions on i686-pc-linux-gnu.  This 
is <http://gcc.gnu.org/ml/gcc-patches/2008-11/msg00105.html> (which 
message contains further discussion of the background and approach taken), 
together with the subsequent changes to make -fexcess-precision=standard 
give an error for non-C languages when used for a target that may have 
excess precision issues.

Index: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi	(revision 145271)
+++ gcc/doc/invoke.texi	(working copy)
@@ -329,8 +329,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-matrix-reorg -fipa-pta @gol 
@@ -6783,6 +6784,32 @@ 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
+@option{-fexcess-precision=standard} is not implemented for languages
+other than C, and has no effect 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},
@@ -11000,7 +11027,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: gcc/flags.h
===================================================================
--- gcc/flags.h	(revision 145271)
+++ gcc/flags.h	(working copy)
@@ -227,6 +227,21 @@ extern enum ira_region flag_ira_region;
 
 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;
+
 
 /* Other basic status info about current function.  */
 
Index: gcc/java/ChangeLog
===================================================================
--- gcc/java/ChangeLog	(revision 145271)
+++ gcc/java/ChangeLog	(working copy)
@@ -1,3 +1,10 @@
+2009-03-30  Joseph Myers  <joseph@codesourcery.com>
+
+	PR rtl-optimization/323
+	* lang.c (java_post_options): Set flag_excess_precision_cmdline.
+	Give an error for -fexcess-precision=standard for processors where
+	the option is significant.
+
 2009-03-18  Ralf Wildenhues  <Ralf.Wildenhues@gmx.de>
 
 	* lang.opt: Unify help text for -Wdeprecated.
Index: gcc/java/lang.c
===================================================================
--- gcc/java/lang.c	(revision 145271)
+++ gcc/java/lang.c	(working copy)
@@ -528,6 +528,13 @@ java_post_options (const char **pfilenam
 {
   const char *filename = *pfilename;
 
+  /* Excess precision other than "fast" requires front-end
+     support.  */
+  if (flag_excess_precision_cmdline == EXCESS_PRECISION_STANDARD
+      && TARGET_FLT_EVAL_METHOD_NON_DEFAULT)
+    sorry ("-fexcess-precision=standard for Java");
+  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: gcc/c-lex.c
===================================================================
--- gcc/c-lex.c	(revision 145271)
+++ gcc/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: gcc/defaults.h
===================================================================
--- gcc/defaults.h	(revision 145271)
+++ gcc/defaults.h	(working copy)
@@ -688,8 +688,11 @@ along with GCC; see the file COPYING3.  
 #define FLOAT_WORDS_BIG_ENDIAN WORDS_BIG_ENDIAN
 #endif
 
-#ifndef TARGET_FLT_EVAL_METHOD
+#ifdef TARGET_FLT_EVAL_METHOD
+#define TARGET_FLT_EVAL_METHOD_NON_DEFAULT 1
+#else
 #define TARGET_FLT_EVAL_METHOD 0
+#define TARGET_FLT_EVAL_METHOD_NON_DEFAULT 0
 #endif
 
 #ifndef TARGET_DEC_EVAL_METHOD
Index: gcc/tree.c
===================================================================
--- gcc/tree.c	(revision 145271)
+++ gcc/tree.c	(working copy)
@@ -6258,6 +6258,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;
+}
 
 /* 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: gcc/tree.h
===================================================================
--- gcc/tree.h	(revision 145271)
+++ gcc/tree.h	(working copy)
@@ -4040,6 +4040,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: gcc/toplev.c
===================================================================
--- gcc/toplev.c	(revision 145271)
+++ gcc/toplev.c	(working copy)
@@ -281,6 +281,11 @@ enum ira_region flag_ira_region = IRA_RE
 
 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.  */
 
@@ -2033,11 +2038,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: gcc/c-cppbuiltin.c
===================================================================
--- gcc/c-cppbuiltin.c	(revision 145271)
+++ gcc/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: gcc/ChangeLog
===================================================================
--- gcc/ChangeLog	(revision 145271)
+++ gcc/ChangeLog	(working copy)
@@ -1,5 +1,68 @@
 2009-03-30  Joseph Myers  <joseph@codesourcery.com>
 
+	PR rtl-optimization/323
+	* c-common.c (c_fully_fold, convert_and_check,
+	c_common_truthvalue_conversion): Handle EXCESS_PRECISION_EXPR.
+	(c_fully_fold_internal): Disallow 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.  Give an error for
+	-fexcess-precision=standard for C++ for processors where the
+	option is significant.
+	* c-parser.c (c_parser_conditional_expression): Handle excess
+	precision in condition.
+	* c-typeck.c (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.
+	* defaults.h (TARGET_FLT_EVAL_METHOD_NON_DEFAULT): Define.
+	* 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.
+
+2009-03-30  Joseph Myers  <joseph@codesourcery.com>
+
 	PR c/35235
 	* c-typeck.c (build_component_ref): Do not copy qualifiers from
 	non-lvalue to component.
Index: gcc/testsuite/gcc.target/i386/excess-precision-1.c
===================================================================
--- gcc/testsuite/gcc.target/i386/excess-precision-1.c	(revision 0)
+++ gcc/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: gcc/testsuite/gcc.target/i386/excess-precision-3.c
===================================================================
--- gcc/testsuite/gcc.target/i386/excess-precision-3.c	(revision 0)
+++ gcc/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: gcc/testsuite/gcc.target/i386/excess-precision-5.c
===================================================================
--- gcc/testsuite/gcc.target/i386/excess-precision-5.c	(revision 0)
+++ gcc/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: gcc/testsuite/gcc.target/i386/excess-precision-2.c
===================================================================
--- gcc/testsuite/gcc.target/i386/excess-precision-2.c	(revision 0)
+++ gcc/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: gcc/testsuite/gcc.target/i386/excess-precision-4.c
===================================================================
--- gcc/testsuite/gcc.target/i386/excess-precision-4.c	(revision 0)
+++ gcc/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: gcc/testsuite/gcc.target/i386/excess-precision-6.c
===================================================================
--- gcc/testsuite/gcc.target/i386/excess-precision-6.c	(revision 0)
+++ gcc/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: gcc/testsuite/ChangeLog
===================================================================
--- gcc/testsuite/ChangeLog	(revision 145271)
+++ gcc/testsuite/ChangeLog	(working copy)
@@ -1,5 +1,15 @@
 2009-03-30  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.
+
+2009-03-30  Joseph Myers  <joseph@codesourcery.com>
+
 	PR c/35235
 	* gcc.dg/c99-array-lval-8.c: New test.
 
Index: gcc/opts.c
===================================================================
--- gcc/opts.c	(revision 145271)
+++ gcc/opts.c	(working copy)
@@ -1728,6 +1728,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: gcc/ada/ChangeLog
===================================================================
--- gcc/ada/ChangeLog	(revision 145271)
+++ gcc/ada/ChangeLog	(working copy)
@@ -1,3 +1,11 @@
+2009-03-30  Joseph Myers  <joseph@codesourcery.com>
+
+	PR rtl-optimization/323
+	* gcc-interface/misc.c (gnat_post_options): Set
+	flag_excess_precision_cmdline.  Give an error for
+	-fexcess-precision=standard for processors where the option is
+	significant.
+
 2009-03-27  H.J. Lu  <hongjiu.lu@intel.com>
 
 	PR c/39323
Index: gcc/ada/gcc-interface/misc.c
===================================================================
--- gcc/ada/gcc-interface/misc.c	(revision 145271)
+++ gcc/ada/gcc-interface/misc.c	(working copy)
@@ -337,6 +337,13 @@ 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.  */
+  if (flag_excess_precision_cmdline == EXCESS_PRECISION_STANDARD
+      && TARGET_FLT_EVAL_METHOD_NON_DEFAULT)
+    sorry ("-fexcess-precision=standard for Ada");
+  flag_excess_precision_cmdline = EXCESS_PRECISION_FAST;
+
   /* ??? The warning machinery is outsmarted by Ada.  */
   warn_unused_parameter = 0;
 
Index: gcc/fortran/ChangeLog
===================================================================
--- gcc/fortran/ChangeLog	(revision 145271)
+++ gcc/fortran/ChangeLog	(working copy)
@@ -1,3 +1,11 @@
+2009-03-30  Joseph Myers  <joseph@codesourcery.com>
+
+	PR rtl-optimization/323
+	* options.c (gfc_post_options): Set
+	flag_excess_precision_cmdline.  Give an error for
+	-fexcess-precision=standard for processors where the option is
+	significant.
+
 2009-03-29  Joseph Myers  <joseph@codesourcery.com>
 
 	PR preprocessor/34695
Index: gcc/fortran/options.c
===================================================================
--- gcc/fortran/options.c	(revision 145271)
+++ gcc/fortran/options.c	(working copy)
@@ -32,6 +32,8 @@ along with GCC; see the file COPYING3.  
 #include "gfortran.h"
 #include "target.h"
 #include "cpp.h"
+#include "toplev.h"
+#include "tm.h"
 
 gfc_option_t gfc_option;
 
@@ -228,6 +230,13 @@ gfc_post_options (const char **pfilename
   char *source_path;
   int i;
 
+  /* Excess precision other than "fast" requires front-end
+     support.  */
+  if (flag_excess_precision_cmdline == EXCESS_PRECISION_STANDARD
+      && TARGET_FLT_EVAL_METHOD_NON_DEFAULT)
+    sorry ("-fexcess-precision=standard for Fortran");
+  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: gcc/langhooks.c
===================================================================
--- gcc/langhooks.c	(revision 145271)
+++ gcc/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: gcc/c-typeck.c
===================================================================
--- gcc/c-typeck.c	(revision 145271)
+++ gcc/c-typeck.c	(working copy)
@@ -2553,6 +2553,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
@@ -2564,6 +2565,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.  */
 
@@ -2573,9 +2598,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)
@@ -2591,6 +2618,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);
 
@@ -2615,32 +2655,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);
@@ -2648,7 +2688,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'.  */
@@ -2662,40 +2702,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.  */
 			;
@@ -2719,8 +2759,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 "
@@ -2732,6 +2772,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);
@@ -2743,10 +2787,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;
@@ -2754,6 +2798,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)))
 	{
@@ -2959,6 +3007,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;
@@ -2981,6 +3030,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:
@@ -3077,6 +3132,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:
@@ -3086,6 +3143,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:
@@ -3333,6 +3392,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;
 }
@@ -3512,6 +3573,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;
@@ -3544,6 +3606,28 @@ build_conditional_expr (tree ifexp, bool
 
   objc_ok = objc_compare_types (type1, type2, -3, NULL_TREE);
 
+  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))
@@ -3741,6 +3825,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;
 }
@@ -3751,8 +3837,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
@@ -3790,6 +3885,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;
 }
 
@@ -3798,7 +3896,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;
@@ -4058,6 +4161,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;
@@ -4072,6 +4176,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)
@@ -4131,11 +4241,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)
@@ -4190,6 +4303,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;
@@ -4242,6 +4356,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);
 
@@ -4334,7 +4451,7 @@ convert_for_assignment (tree type, tree 
       bool save = in_late_binary_op;
       if (codel == BOOLEAN_TYPE)
 	in_late_binary_op = true;
-      ret = convert_and_check (type, rhs);
+      ret = convert_and_check (type, orig_rhs);
       if (codel == BOOLEAN_TYPE)
 	in_late_binary_op = save;
       return ret;
@@ -4979,6 +5096,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
@@ -4989,6 +5107,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);
 
@@ -5206,6 +5329,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,
@@ -6587,6 +6713,7 @@ static void
 output_init_element (tree value, bool strict_string, tree type, tree field,
 		     int pending, bool implicit)
 {
+  tree semantic_type = NULL_TREE;
   constructor_elt *celt;
   bool maybe_const = true;
   bool npc;
@@ -6617,6 +6744,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)
@@ -6658,6 +6790,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)
@@ -6972,7 +7106,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)
@@ -7465,8 +7610,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)
@@ -8281,7 +8434,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;
@@ -8297,6 +8451,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.  */
@@ -8333,6 +8491,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;
 
@@ -8360,8 +8522,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.  */
@@ -8385,6 +8547,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)
@@ -8929,7 +9130,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)
@@ -8950,6 +9158,8 @@ build_binary_op (location_t location, en
   else if (TREE_CODE (ret) != INTEGER_CST && int_operands
 	   && !in_late_binary_op)
     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: gcc/c-opts.c
===================================================================
--- gcc/c-opts.c	(revision 145271)
+++ gcc/c-opts.c	(working copy)
@@ -1013,6 +1013,20 @@ 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 ())
+    {
+      if (flag_excess_precision_cmdline == EXCESS_PRECISION_STANDARD
+	  && TARGET_FLT_EVAL_METHOD_NON_DEFAULT)
+	sorry ("-fexcess-precision=standard for C++");
+      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: gcc/common.opt
===================================================================
--- gcc/common.opt	(revision 145271)
+++ gcc/common.opt	(working copy)
@@ -491,6 +491,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: gcc/c-common.c
===================================================================
--- gcc/c-common.c	(revision 145271)
+++ gcc/c-common.c	(working copy)
@@ -1131,6 +1131,7 @@ 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;
 
@@ -1142,8 +1143,15 @@ c_fully_fold (tree expr, bool in_init, b
 
   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;
 }
@@ -1444,6 +1452,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.  */
@@ -2174,6 +2191,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;
@@ -2181,7 +2213,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;
 }
@@ -3862,6 +3894,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: gcc/c-common.def
===================================================================
--- gcc/c-common.def	(revision 145271)
+++ gcc/c-common.def	(working copy)
@@ -39,6 +39,13 @@ along with GCC; see the file COPYING3.  
    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: gcc/c-parser.c
===================================================================
--- gcc/c-parser.c	(revision 145271)
+++ gcc/c-parser.c	(working copy)
@@ -4465,10 +4465,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: gcc/config/i386/i386.h
===================================================================
--- gcc/config/i386/i386.h	(revision 145271)
+++ gcc/config/i386/i386.h	(working copy)
@@ -611,6 +611,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: gcc/config/i386/i386.md
===================================================================
--- gcc/config/i386/i386.md	(revision 145271)
+++ gcc/config/i386/i386.md	(working copy)
@@ -5137,13 +5137,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))
@@ -5524,7 +5539,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
    #"
@@ -5537,7 +5553,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>")
@@ -5548,6 +5565,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))
@@ -5559,6 +5577,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)))]
@@ -5574,7 +5593,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")
@@ -5588,7 +5608,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])"
@@ -5612,7 +5633,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])"
@@ -5632,7 +5654,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>")])
@@ -5644,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 1))
    (set (match_dup 0)
@@ -5658,7 +5682,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)
@@ -5676,7 +5701,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)
@@ -7475,7 +7501,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)"
   "")
 
 ;; Subtract instructions
@@ -7835,7 +7862,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)"
   "")
 
 ;; Multiply instructions
@@ -8390,7 +8418,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.
@@ -8431,14 +8460,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
@@ -16317,7 +16348,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);"
@@ -16431,7 +16462,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);"
@@ -16451,7 +16483,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")
@@ -16470,7 +16503,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")
@@ -16489,7 +16523,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")
@@ -16507,7 +16542,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" "")
@@ -16525,7 +16561,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" "")
@@ -16661,7 +16698,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]);
@@ -16681,7 +16719,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]);
@@ -16767,7 +16806,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: gcc/convert.c
===================================================================
--- gcc/convert.c	(revision 145271)
+++ gcc/convert.c	(working copy)
@@ -326,7 +326,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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]