re PR middle-end/323 (optimized code gives strange floating point results)
authorJoseph Myers <joseph@codesourcery.com>
Mon, 30 Mar 2009 01:50:44 +0000 (02:50 +0100)
committerJoseph Myers <jsm28@gcc.gnu.org>
Mon, 30 Mar 2009 01:50:44 +0000 (02:50 +0100)
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.

ada:
* 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.

fortran:
* options.c (gfc_post_options): Set
flag_excess_precision_cmdline.  Give an error for
-fexcess-precision=standard for processors where the option is
significant.

java:
* lang.c (java_post_options): Set flag_excess_precision_cmdline.
Give an error for -fexcess-precision=standard for processors where
the option is significant.

testsuite:
* 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.

From-SVN: r145272

33 files changed:
gcc/ChangeLog
gcc/ada/ChangeLog
gcc/ada/gcc-interface/misc.c
gcc/c-common.c
gcc/c-common.def
gcc/c-cppbuiltin.c
gcc/c-lex.c
gcc/c-opts.c
gcc/c-parser.c
gcc/c-typeck.c
gcc/common.opt
gcc/config/i386/i386.h
gcc/config/i386/i386.md
gcc/convert.c
gcc/defaults.h
gcc/doc/invoke.texi
gcc/flags.h
gcc/fortran/ChangeLog
gcc/fortran/options.c
gcc/java/ChangeLog
gcc/java/lang.c
gcc/langhooks.c
gcc/opts.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/i386/excess-precision-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/excess-precision-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/excess-precision-3.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/excess-precision-4.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/excess-precision-5.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/excess-precision-6.c [new file with mode: 0644]
gcc/toplev.c
gcc/tree.c
gcc/tree.h

index 49bbb0f..db553e9 100644 (file)
@@ -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 97bd260..0b16cae 100644 (file)
@@ -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 ca672f8..329f68e 100644 (file)
@@ -337,6 +337,13 @@ gnat_init_options (unsigned int argc, const char **argv)
 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 9abd006..b1ac9bc 100644 (file)
@@ -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, bool *maybe_const)
 
   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 in_init, bool *maybe_const_operands,
        *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_t location, tree expr)
     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 c4027f7..1c59363 100644 (file)
@@ -39,6 +39,13 @@ along with GCC; see the file COPYING3.  If not see
    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 c6112d8..5d6033d 100644 (file)
@@ -98,6 +98,7 @@ builtin_define_float_constants (const char *name_prefix,
   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 char *name_prefix,
 
   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 char *name_prefix,
      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 5b71c3b..72a9590 100644 (file)
@@ -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, unsigned int flags)
     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, unsigned int flags)
   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, unsigned int flags)
        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, unsigned int flags)
     }
 
   /* 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 334577a..3953991 100644 (file)
@@ -1013,6 +1013,20 @@ c_common_post_options (const char **pfilename)
   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 4d1b4d5..03a7194 100644 (file)
@@ -4465,10 +4465,18 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after)
   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 2559b1d..8702373 100644 (file)
@@ -2553,6 +2553,7 @@ convert_arguments (int nargs, tree *argarray,
   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 *argarray,
   /* 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 *argarray,
     {
       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 *argarray,
        }
 
       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 *argarray,
                  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 *argarray,
                     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 *argarray,
                         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 *argarray,
                         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 *argarray,
                    }
                }
 
+             /* 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 *argarray,
            }
          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 *argarray,
            /* 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 ifexp_bcp, tree op1, tree op2)
   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 ifexp_bcp, tree op1, tree op2)
 
   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 ifexp_bcp, tree op1, tree op2)
       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 ifexp_bcp, tree op1, tree op2)
 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 expr2)
       && 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 expr2)
 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 rhs, enum impl_conv errtype,
                        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 rhs, enum impl_conv errtype,
       }                                                                  \
   } 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 rhs, enum impl_conv errtype,
       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 null_pointer_constant,
 {
   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 null_pointer_constant,
 
   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 null_pointer_constant,
          && (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 strict_string, tree type, tree field,
     }
 
   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 strict_string, tree type, tree field,
                  || 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 value, bool implicit)
       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, enum tree_code code,
      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, enum tree_code code,
   /* 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, enum tree_code code,
       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, enum tree_code code,
       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, enum tree_code code,
     }
 
   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, enum tree_code code,
   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 023d773..3626c9e 100644 (file)
@@ -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 e4d4463..89e26f6 100644 (file)
@@ -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 a112198..9592f91 100644 (file)
   "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))
        (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
    #"
   [(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>")
        (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))
        (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)))]
    (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")
    (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])"
    (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])"
    (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>")])
    (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)
    (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)
       (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)
   [(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
   [(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
   [(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.
   [(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
        (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);"
        (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);"
          [(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")
          [(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")
           [(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")
          [(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" "")
            (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" "")
           [(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]);
           [(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]);
   [(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 e98b657..b6a9d3d 100644 (file)
@@ -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)),
index 217c0d9..a1863cd 100644 (file)
@@ -688,8 +688,11 @@ along with GCC; see the file COPYING3.  If not see
 #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 4e2d931..19ac308 100644 (file)
@@ -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 precise definition of IEEE floating
 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}} implies @option{-mtune=@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 e406bf1..e606f60 100644 (file)
@@ -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;
+
 \f
 /* Other basic status info about current function.  */
 
index 34d31ff..d4a24dd 100644 (file)
@@ -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 587fb36..17c577d 100644 (file)
@@ -32,6 +32,8 @@ along with GCC; see the file COPYING3.  If not see
 #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 6a7e44f..a72a2f9 100644 (file)
@@ -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 2a962c0..c431141 100644 (file)
@@ -528,6 +528,13 @@ java_post_options (const char **pfilename)
 {
   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 bd01c3f..1964137 100644 (file)
@@ -105,6 +105,9 @@ lhd_return_null_const_tree (const_tree ARG_UNUSED (t))
 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 90f34df..e318790 100644 (file)
@@ -1728,6 +1728,15 @@ common_handle_option (size_t scode, const char *arg, int value,
        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 23701aa..37dcc21 100644 (file)
@@ -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.
 
diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-1.c b/gcc/testsuite/gcc.target/i386/excess-precision-1.c
new file mode 100644 (file)
index 0000000..3d5e7d2
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-2.c b/gcc/testsuite/gcc.target/i386/excess-precision-2.c
new file mode 100644 (file)
index 0000000..1075cd0
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-3.c b/gcc/testsuite/gcc.target/i386/excess-precision-3.c
new file mode 100644 (file)
index 0000000..0cdcb3d
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-4.c b/gcc/testsuite/gcc.target/i386/excess-precision-4.c
new file mode 100644 (file)
index 0000000..db44b0f
--- /dev/null
@@ -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'" } */
diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-5.c b/gcc/testsuite/gcc.target/i386/excess-precision-5.c
new file mode 100644 (file)
index 0000000..9c76592
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-6.c b/gcc/testsuite/gcc.target/i386/excess-precision-6.c
new file mode 100644 (file)
index 0000000..1d421c9
--- /dev/null
@@ -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 8417cfe..72f3d30 100644 (file)
@@ -281,6 +281,11 @@ enum ira_region flag_ira_region = IRA_REGION_MIXED;
 
 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 9a6e000..76cba27 100644 (file)
@@ -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;
+}
 \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 0eb2dc6..dbd0faa 100644 (file)
@@ -4040,6 +4040,7 @@ extern bool tree_expr_nonnegative_p (tree);
 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.  */