From 8ce94e44465bcc958dc11270d9dee1775c7a4f43 Mon Sep 17 00:00:00 2001 From: Joseph Myers Date: Mon, 30 Mar 2009 02:50:44 +0100 Subject: re PR middle-end/323 (optimized code gives strange floating point results) 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 (float2): For standard excess precision, output explicit conversion to and truncation from XFmode. (*float2_1, *float2_i387_with_temp, *float2_i387, two unnamed define_splits, floatdi2_i387_with_xmm, two unnamed define_splits, *floatunssi2_1, two unnamed define_splits, floatunssi2, add3, sub3, mul3, divdf3, divsf3, *fop__comm_i387, *fop__1_i387, *fop__2_i387, *fop__3_i387, *fop_df_4_i387, *fop_df_5_i387, *fop_df_6_i387, two unnamed define_splits, sqrt2): 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 --- gcc/ChangeLog | 63 +++++ gcc/ada/ChangeLog | 8 + gcc/ada/gcc-interface/misc.c | 7 + gcc/c-common.c | 35 ++- gcc/c-common.def | 7 + gcc/c-cppbuiltin.c | 6 +- gcc/c-lex.c | 29 ++- gcc/c-opts.c | 14 ++ gcc/c-parser.c | 8 + gcc/c-typeck.c | 274 ++++++++++++++++++--- gcc/common.opt | 4 + gcc/config/i386/i386.h | 14 ++ gcc/config/i386/i386.md | 89 +++++-- gcc/convert.c | 3 +- gcc/defaults.h | 5 +- gcc/doc/invoke.texi | 33 ++- gcc/flags.h | 15 ++ gcc/fortran/ChangeLog | 8 + gcc/fortran/options.c | 9 + gcc/java/ChangeLog | 7 + gcc/java/lang.c | 7 + gcc/langhooks.c | 3 + gcc/opts.c | 9 + gcc/testsuite/ChangeLog | 10 + gcc/testsuite/gcc.target/i386/excess-precision-1.c | 186 ++++++++++++++ gcc/testsuite/gcc.target/i386/excess-precision-2.c | 34 +++ gcc/testsuite/gcc.target/i386/excess-precision-3.c | 219 ++++++++++++++++ gcc/testsuite/gcc.target/i386/excess-precision-4.c | 8 + gcc/testsuite/gcc.target/i386/excess-precision-5.c | 23 ++ gcc/testsuite/gcc.target/i386/excess-precision-6.c | 20 ++ gcc/toplev.c | 45 ++++ gcc/tree.c | 55 +++++ gcc/tree.h | 1 + 33 files changed, 1189 insertions(+), 69 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/excess-precision-1.c create mode 100644 gcc/testsuite/gcc.target/i386/excess-precision-2.c create mode 100644 gcc/testsuite/gcc.target/i386/excess-precision-3.c create mode 100644 gcc/testsuite/gcc.target/i386/excess-precision-4.c create mode 100644 gcc/testsuite/gcc.target/i386/excess-precision-5.c create mode 100644 gcc/testsuite/gcc.target/i386/excess-precision-6.c (limited to 'gcc') diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 49bbb0f..db553e9 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,68 @@ 2009-03-30 Joseph Myers + 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 (float2): + For standard excess precision, output explicit conversion to and + truncation from XFmode. + (*float2_1, + *float2_i387_with_temp, + *float2_i387, two unnamed + define_splits, floatdi2_i387_with_xmm, two unnamed + define_splits, *floatunssi2_1, two unnamed define_splits, + floatunssi2, add3, sub3, mul3, divdf3, + divsf3, *fop__comm_i387, *fop__1_i387, + *fop__2_i387, *fop__3_i387, + *fop_df_4_i387, *fop_df_5_i387, *fop_df_6_i387, two unnamed + define_splits, sqrt2): 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 + PR c/35235 * c-typeck.c (build_component_ref): Do not copy qualifiers from non-lvalue to component. diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index 97bd2609..0b16cae 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,3 +1,11 @@ +2009-03-30 Joseph Myers + + 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 PR c/39323 diff --git a/gcc/ada/gcc-interface/misc.c b/gcc/ada/gcc-interface/misc.c index ca672f8..329f68e 100644 --- a/gcc/ada/gcc-interface/misc.c +++ b/gcc/ada/gcc-interface/misc.c @@ -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; diff --git a/gcc/c-common.c b/gcc/c-common.c index 9abd006..b1ac9bc 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -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)); diff --git a/gcc/c-common.def b/gcc/c-common.def index c4027f7..1c59363 100644 --- a/gcc/c-common.def +++ b/gcc/c-common.def @@ -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 diff --git a/gcc/c-cppbuiltin.c b/gcc/c-cppbuiltin.c index c6112d8..5d6033d 100644 --- a/gcc/c-cppbuiltin.c +++ b/gcc/c-cppbuiltin.c @@ -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++; diff --git a/gcc/c-lex.c b/gcc/c-lex.c index 5b71c3b..72a9590 100644 --- a/gcc/c-lex.c +++ b/gcc/c-lex.c @@ -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; } diff --git a/gcc/c-opts.c b/gcc/c-opts.c index 334577a..3953991 100644 --- a/gcc/c-opts.c +++ b/gcc/c-opts.c @@ -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) diff --git a/gcc/c-parser.c b/gcc/c-parser.c index 4d1b4d5..03a7194 100644 --- a/gcc/c-parser.c +++ b/gcc/c-parser.c @@ -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; } diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c index 2559b1d..8702373 100644 --- a/gcc/c-typeck.c +++ b/gcc/c-typeck.c @@ -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; } diff --git a/gcc/common.opt b/gcc/common.opt index 023d773..3626c9e 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -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 diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index e4d4463..89e26f6 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -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 diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index a112198..9592f91 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -5137,13 +5137,28 @@ "TARGET_80387 || ((mode != DImode || TARGET_64BIT) && SSE_FLOAT_MODE_P (mode) && TARGET_SSE_MATH)" - "") + " +{ + if (!((mode != DImode || TARGET_64BIT) + && SSE_FLOAT_MODE_P (mode) && TARGET_SSE_MATH) + && !X87_ENABLE_FLOAT (mode, mode)) + { + rtx reg = gen_reg_rtx (XFmode); + emit_insn (gen_floatxf2 (reg, operands[1])); +/* Avoid references to nonexistent function in dead code in XFmode case. */ +#define gen_truncxfxf2 gen_truncxfdf2 + emit_insn (gen_truncxf2 (operands[0], reg)); +#undef gen_truncxfxf2 + DONE; + } +}") ;; Pre-reload splitter to add memory clobber to the pattern. (define_insn_and_split "*float2_1" [(set (match_operand:X87MODEF 0 "register_operand" "") (float:X87MODEF (match_operand:SSEMODEI24 1 "register_operand" "")))] "((TARGET_80387 + && X87_ENABLE_FLOAT (mode, mode) && (!((mode != DImode || TARGET_64BIT) && SSE_FLOAT_MODE_P (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 (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 (mode, mode)" "fild%z1\t%1" [(set_attr "type" "fmov") (set_attr "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 (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 (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 (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 (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 (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 (mode, SImode) + && TARGET_SSE" "#" [(set_attr "type" "multi") (set_attr "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 (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 (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 (mode, SImode) + && TARGET_SSE) || (SSE_FLOAT_MODE_P (mode) && TARGET_SSE_MATH))" { if (SSE_FLOAT_MODE_P (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) && TARGET_SSE_MATH)" + "(TARGET_80387 && X87_ENABLE_ARITH (mode)) + || (SSE_FLOAT_MODE_P (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) && TARGET_SSE_MATH)" + "(TARGET_80387 && X87_ENABLE_ARITH (mode)) + || (SSE_FLOAT_MODE_P (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) && TARGET_SSE_MATH)" + "(TARGET_80387 && X87_ENABLE_ARITH (mode)) + || (SSE_FLOAT_MODE_P (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) && 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) && TARGET_SSE_MATH) + "TARGET_80387 && X87_ENABLE_ARITH (mode) + && !(SSE_FLOAT_MODE_P (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 (mode) && TARGET_SSE_MATH) + "TARGET_80387 && X87_ENABLE_FLOAT (mode, mode) + && !(SSE_FLOAT_MODE_P (mode) && TARGET_SSE_MATH) && (TARGET_USE_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 (mode) && TARGET_SSE_MATH) + "TARGET_80387 && X87_ENABLE_FLOAT (mode, mode) + && !(SSE_FLOAT_MODE_P (mode) && TARGET_SSE_MATH) && (TARGET_USE_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)) || (SSE_FLOAT_MODE_P (mode) && TARGET_SSE_MATH)" { if (mode == SFmode diff --git a/gcc/convert.c b/gcc/convert.c index e98b657..b6a9d3d 100644 --- a/gcc/convert.c +++ b/gcc/convert.c @@ -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)), diff --git a/gcc/defaults.h b/gcc/defaults.h index 217c0d9..a1863cd 100644 --- a/gcc/defaults.h +++ b/gcc/defaults.h @@ -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 diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 4e2d931..19ac308 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -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: diff --git a/gcc/flags.h b/gcc/flags.h index e406bf1..e606f60 100644 --- a/gcc/flags.h +++ b/gcc/flags.h @@ -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. */ diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 34d31ff..d4a24ddc 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,11 @@ +2009-03-30 Joseph Myers + + 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 PR preprocessor/34695 diff --git a/gcc/fortran/options.c b/gcc/fortran/options.c index 587fb36..17c577d 100644 --- a/gcc/fortran/options.c +++ b/gcc/fortran/options.c @@ -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"); diff --git a/gcc/java/ChangeLog b/gcc/java/ChangeLog index 6a7e44f..a72a2f9 100644 --- a/gcc/java/ChangeLog +++ b/gcc/java/ChangeLog @@ -1,3 +1,10 @@ +2009-03-30 Joseph Myers + + 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 * lang.opt: Unify help text for -Wdeprecated. diff --git a/gcc/java/lang.c b/gcc/java/lang.c index 2a962c0..c431141 100644 --- a/gcc/java/lang.c +++ b/gcc/java/lang.c @@ -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) diff --git a/gcc/langhooks.c b/gcc/langhooks.c index bd01c3f..19641379 100644 --- a/gcc/langhooks.c +++ b/gcc/langhooks.c @@ -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; } diff --git a/gcc/opts.c b/gcc/opts.c index 90f34df..e318790 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -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; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 23701aa..37dcc21 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,15 @@ 2009-03-30 Joseph Myers + 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 + 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 index 0000000..3d5e7d2 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/excess-precision-1.c @@ -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 + +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 index 0000000..1075cd0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/excess-precision-2.c @@ -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 + +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 index 0000000..0cdcb3d --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/excess-precision-3.c @@ -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 +#include + +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 index 0000000..db44b0f --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/excess-precision-4.c @@ -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 index 0000000..9c76592 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/excess-precision-5.c @@ -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 index 0000000..1d421c9 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/excess-precision-6.c @@ -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" } } */ diff --git a/gcc/toplev.c b/gcc/toplev.c index 8417cfe..72f3d30 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -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. */ diff --git a/gcc/tree.c b/gcc/tree.c index 9a6e000..76cba27 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -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. diff --git a/gcc/tree.h b/gcc/tree.h index 0eb2dc6..dbd0faa 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -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. */ -- cgit v1.1