From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2153) id 5AABD3858C56; Fri, 14 Oct 2022 07:32:01 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5AABD3858C56 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1665732721; bh=4im0JQJItUbHQuZht15ZlJstWtSv9zapk5I0OyzJYaQ=; h=From:To:Subject:Date:From; b=tJtPPlhA/vZC2kWxaJ6J5f+bzvPYnNZHGVAR9i2byeUX4zmLiJaPaycE0aEvRH/aY AMnFyPm5OWu3n41uY4+kJttEJoxy2Q6SzdaBi6aueBxcDWRd3dof5e3LFm+h035a7A SWucAvSBdRxwLBjhAGxEo+2m2piY+3msAu5XdwFQ= MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="utf-8" From: Jakub Jelinek To: gcc-cvs@gcc.gnu.org Subject: [gcc r13-3290] c++: Implement excess precision support for C++ [PR107097, PR323] X-Act-Checkin: gcc X-Git-Author: Jakub Jelinek X-Git-Refname: refs/heads/master X-Git-Oldrev: 18981635127c6701733dc052aa054e569271b733 X-Git-Newrev: 98e341130f87984af07c884fea773c0bb3cc8821 Message-Id: <20221014073201.5AABD3858C56@sourceware.org> Date: Fri, 14 Oct 2022 07:32:01 +0000 (GMT) List-Id: https://gcc.gnu.org/g:98e341130f87984af07c884fea773c0bb3cc8821 commit r13-3290-g98e341130f87984af07c884fea773c0bb3cc8821 Author: Jakub Jelinek Date: Fri Oct 14 09:28:57 2022 +0200 c++: Implement excess precision support for C++ [PR107097, PR323] The following patch implements excess precision support for C++. Like for C, it uses EXCESS_PRECISION_EXPR tree to say that its operand is evaluated in excess precision and what the semantic type of the expression is. In most places I've followed what the C FE does in similar spots, so e.g. for binary ops if one or both operands are already EXCESS_PRECISION_EXPR, strip those away or for operations that might need excess precision (+, -, *, /) check if the operands should use excess precision and convert to that type and at the end wrap into EXCESS_PRECISION_EXPR with the common semantic type. This patch follows the C99 handling where it differs from C11 handling. There are some cases which needed to be handled differently, the C FE can just strip EXCESS_PRECISION_EXPR (replace it with its operand) when handling explicit cast, but that IMHO isn't right for C++ - the discovery what exact conversion should be used (e.g. if user conversion or standard or their sequence) should be decided based on the semantic type (i.e. type of EXCESS_PRECISION_EXPR), and that decision continues in convert_like* where we pick the right user conversion, again, if say some class has ctor from double and long double and we are on ia32 with standard excess precision promoting float/double to long double, then we should pick the ctor from double. Or when some other class has ctor from just double, and EXCESS_PRECISION_EXPR semantic type is float, we should choose the user ctor from double, but actually just convert the long double excess precision to double and not to float first. We need to make sure even identity conversion converts from excess precision to the semantic one though, but if identity is chained with other conversions, we don't want the identity next_conversion to drop to semantic precision only to widen afterwards. The existing testcases tweaks were for cases on i686-linux where excess precision breaks those tests, e.g. if we have double d = 4.2; if (d == 4.2) then it does the expected thing only with -fexcess-precision=fast, because with -fexcess-precision=standard it is actually double d = 4.2; if ((long double) d == 4.2L) where 4.2L is different from 4.2. I've added -fexcess-precision=fast to some tests and changed other tests to use constants that are exactly representable and don't suffer from these excess precision issues. There is one exception, pr68180.C looks like a bug in the patch which is also present in the C FE (so I'd like to get it resolved incrementally in both). Reduced testcase: typedef float __attribute__((vector_size (16))) float32x4_t; float32x4_t foo(float32x4_t x, float y) { return x + y; } with -m32 -std=c11 -Wno-psabi or -m32 -std=c++17 -Wno-psabi it is rejected with: pr68180.c:2:52: error: conversion of scalar ‘long double’ to vector ‘float32x4_t’ {aka ‘__vector(4) float’} involves truncation but without excess precision (say just -std=c11 -Wno-psabi or -std=c++17 -Wno-psabi) it is accepted. Perhaps we should pass down the semantic type to scalar_to_vector and use the semantic type rather than excess precision type in the diagnostics. 2022-10-14 Jakub Jelinek PR middle-end/323 PR c++/107097 gcc/ * doc/invoke.texi (-fexcess-precision=standard): Mention that the option now also works in C++. gcc/c-family/ * c-common.def (EXCESS_PRECISION_EXPR): Remove comment part about the tree being specific to C/ObjC. * c-opts.cc (c_common_post_options): Handle flag_excess_precision in C++ the same as in C. * c-lex.cc (interpret_float): Set const_type to excess_precision () even for C++. gcc/cp/ * parser.cc (cp_parser_primary_expression): Handle EXCESS_PRECISION_EXPR with REAL_CST operand the same as REAL_CST. * cvt.cc (cp_ep_convert_and_check): New function. * call.cc (build_conditional_expr): Add excess precision support. When type_after_usual_arithmetic_conversions returns error_mark_node, use gcc_checking_assert that it is because of uncomparable floating point ranks instead of checking all those conditions and make it work also with complex types. (convert_like_internal): Likewise. Add NESTED_P argument, pass true to recursive calls to convert_like. (convert_like): Add NESTED_P argument, pass it through to convert_like_internal. For other overload pass false to it. (convert_like_with_context): Pass false to NESTED_P. (convert_arg_to_ellipsis): Add excess precision support. (magic_varargs_p): For __builtin_is{finite,inf,inf_sign,nan,normal} and __builtin_fpclassify return 2 instead of 1, document what it means. (build_over_call): Don't handle former magic 2 which is no longer used, instead for magic 1 remove EXCESS_PRECISION_EXPR. (perform_direct_initialization_if_possible): Pass false to NESTED_P convert_like argument. * constexpr.cc (cxx_eval_constant_expression): Handle EXCESS_PRECISION_EXPR. (potential_constant_expression_1): Likewise. * pt.cc (tsubst_copy, tsubst_copy_and_build): Likewise. * cp-tree.h (cp_ep_convert_and_check): Declare. * cp-gimplify.cc (cp_fold): Handle EXCESS_PRECISION_EXPR. * typeck.cc (cp_common_type): For COMPLEX_TYPEs, return error_mark_node if recursive call returned it. (convert_arguments): For magic 1 remove EXCESS_PRECISION_EXPR. (cp_build_binary_op): Add excess precision support. When cp_common_type returns error_mark_node, use gcc_checking_assert that it is because of uncomparable floating point ranks instead of checking all those conditions and make it work also with complex types. (cp_build_unary_op): Likewise. (cp_build_compound_expr): Likewise. (build_static_cast_1): Remove EXCESS_PRECISION_EXPR. gcc/testsuite/ * gcc.target/i386/excess-precision-1.c: For C++ wrap abort and exit declarations into extern "C" block. * gcc.target/i386/excess-precision-2.c: Likewise. * gcc.target/i386/excess-precision-3.c: Likewise. Remove check_float_nonproto and check_double_nonproto tests for C++. * gcc.target/i386/excess-precision-7.c: For C++ wrap abort and exit declarations into extern "C" block. * gcc.target/i386/excess-precision-9.c: Likewise. * g++.target/i386/excess-precision-1.C: New test. * g++.target/i386/excess-precision-2.C: New test. * g++.target/i386/excess-precision-3.C: New test. * g++.target/i386/excess-precision-4.C: New test. * g++.target/i386/excess-precision-5.C: New test. * g++.target/i386/excess-precision-6.C: New test. * g++.target/i386/excess-precision-7.C: New test. * g++.target/i386/excess-precision-9.C: New test. * g++.target/i386/excess-precision-11.C: New test. * c-c++-common/dfp/convert-bfp-10.c: Add -fexcess-precision=fast as dg-additional-options. * c-c++-common/dfp/compare-eq-const.c: Likewise. * g++.dg/cpp1z/constexpr-96862.C: Likewise. * g++.dg/cpp1z/decomp12.C (main): Use 2.25 instead of 2.3 to avoid excess precision differences. * g++.dg/other/thunk1.C: Add -fexcess-precision=fast as dg-additional-options. * g++.dg/vect/pr64410.cc: Likewise. * g++.dg/cpp1y/pr68180.C: Likewise. * g++.dg/vect/pr89653.cc: Likewise. * g++.dg/cpp0x/variadic-tuple.C: Likewise. * g++.dg/cpp0x/nsdmi-union1.C: Use 4.25 instead of 4.2 to avoid excess precision differences. * g++.old-deja/g++.brendan/copy9.C: Add -fexcess-precision=fast as dg-additional-options. * g++.old-deja/g++.brendan/overload7.C: Likewise. Diff: --- gcc/c-family/c-common.def | 7 +- gcc/c-family/c-lex.cc | 5 +- gcc/c-family/c-opts.cc | 12 +- gcc/cp/call.cc | 154 +++++++++++++---- gcc/cp/constexpr.cc | 16 ++ gcc/cp/cp-gimplify.cc | 5 + gcc/cp/cp-tree.h | 2 + gcc/cp/cvt.cc | 27 +++ gcc/cp/parser.cc | 4 +- gcc/cp/pt.cc | 22 +++ gcc/cp/typeck.cc | 188 ++++++++++++++++++--- gcc/doc/invoke.texi | 10 +- gcc/testsuite/c-c++-common/dfp/compare-eq-const.c | 1 + gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c | 1 + gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C | 4 +- gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C | 1 + gcc/testsuite/g++.dg/cpp1y/pr68180.C | 2 +- gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C | 2 +- gcc/testsuite/g++.dg/cpp1z/decomp12.C | 4 +- gcc/testsuite/g++.dg/other/thunk1.C | 1 + gcc/testsuite/g++.dg/vect/pr64410.cc | 1 + gcc/testsuite/g++.dg/vect/pr89653.cc | 1 + gcc/testsuite/g++.old-deja/g++.brendan/copy9.C | 1 + gcc/testsuite/g++.old-deja/g++.brendan/overload7.C | 1 + gcc/testsuite/g++.target/i386/excess-precision-1.C | 6 + .../g++.target/i386/excess-precision-11.C | 105 ++++++++++++ gcc/testsuite/g++.target/i386/excess-precision-2.C | 5 + gcc/testsuite/g++.target/i386/excess-precision-3.C | 6 + gcc/testsuite/g++.target/i386/excess-precision-4.C | 7 + gcc/testsuite/g++.target/i386/excess-precision-5.C | 32 ++++ gcc/testsuite/g++.target/i386/excess-precision-6.C | 19 +++ gcc/testsuite/g++.target/i386/excess-precision-7.C | 7 + gcc/testsuite/g++.target/i386/excess-precision-9.C | 6 + gcc/testsuite/gcc.target/i386/excess-precision-1.c | 6 + gcc/testsuite/gcc.target/i386/excess-precision-2.c | 6 + gcc/testsuite/gcc.target/i386/excess-precision-3.c | 10 ++ gcc/testsuite/gcc.target/i386/excess-precision-7.c | 6 + gcc/testsuite/gcc.target/i386/excess-precision-9.c | 6 + 38 files changed, 613 insertions(+), 86 deletions(-) diff --git a/gcc/c-family/c-common.def b/gcc/c-family/c-common.def index 0759ace5fc6..dd8be7fdd3d 100644 --- a/gcc/c-family/c-common.def +++ b/gcc/c-family/c-common.def @@ -38,10 +38,9 @@ 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 +/* An EXCESS_PRECISION_EXPR 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) diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc index 050fa775a95..11f2b784738 100644 --- a/gcc/c-family/c-lex.cc +++ b/gcc/c-family/c-lex.cc @@ -1008,10 +1008,7 @@ interpret_float (const cpp_token *token, unsigned int flags, else type = double_type_node; - if (c_dialect_cxx ()) - const_type = NULL_TREE; - else - const_type = excess_precision_type (type); + const_type = excess_precision_type (type); if (!const_type) const_type = type; diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc index 55cebf68f9c..32b929e3ece 100644 --- a/gcc/c-family/c-opts.cc +++ b/gcc/c-family/c-opts.cc @@ -812,17 +812,9 @@ 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 == EXCESS_PRECISION_STANDARD) - sorry ("%<-fexcess-precision=standard%> for C++"); - flag_excess_precision = EXCESS_PRECISION_FAST; - } - else if (flag_excess_precision == EXCESS_PRECISION_DEFAULT) + if (flag_excess_precision == EXCESS_PRECISION_DEFAULT) flag_excess_precision = (flag_iso ? EXCESS_PRECISION_STANDARD - : EXCESS_PRECISION_FAST); + : EXCESS_PRECISION_FAST); /* ISO C restricts floating-point expression contraction to within source-language expressions (-ffp-contract=on, currently an alias diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index b9c08d628bc..709afc63eb7 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -5359,6 +5359,7 @@ build_conditional_expr (const op_location_t &loc, tree arg3_type; tree result = NULL_TREE; tree result_type = NULL_TREE; + tree semantic_result_type = NULL_TREE; bool is_glvalue = true; struct z_candidate *candidates = 0; struct z_candidate *cand; @@ -5392,6 +5393,9 @@ build_conditional_expr (const op_location_t &loc, expression, since it needs to be materialized for the conversion to bool, so treat it as an xvalue in arg2. */ arg2 = move (TARGET_EXPR_SLOT (arg1)); + else if (TREE_CODE (arg1) == EXCESS_PRECISION_EXPR) + arg2 = arg1 = build1 (EXCESS_PRECISION_EXPR, TREE_TYPE (arg1), + cp_save_expr (TREE_OPERAND (arg1, 0))); else arg2 = arg1 = cp_save_expr (arg1); } @@ -5550,6 +5554,52 @@ build_conditional_expr (const op_location_t &loc, if (error_operand_p (arg1)) return error_mark_node; + arg2_type = unlowered_expr_type (arg2); + arg3_type = unlowered_expr_type (arg3); + + if ((TREE_CODE (arg2) == EXCESS_PRECISION_EXPR + || TREE_CODE (arg3) == EXCESS_PRECISION_EXPR) + && (TREE_CODE (arg2_type) == INTEGER_TYPE + || TREE_CODE (arg2_type) == REAL_TYPE + || TREE_CODE (arg2_type) == COMPLEX_TYPE) + && (TREE_CODE (arg3_type) == INTEGER_TYPE + || TREE_CODE (arg3_type) == REAL_TYPE + || TREE_CODE (arg3_type) == COMPLEX_TYPE)) + { + semantic_result_type + = type_after_usual_arithmetic_conversions (arg2_type, arg3_type); + if (semantic_result_type == error_mark_node) + { + tree t1 = arg2_type; + tree t2 = arg3_type; + if (TREE_CODE (t1) == COMPLEX_TYPE) + t1 = TREE_TYPE (t1); + if (TREE_CODE (t2) == COMPLEX_TYPE) + t2 = TREE_TYPE (t2); + gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE + && TREE_CODE (t2) == REAL_TYPE + && (extended_float_type_p (t1) + || extended_float_type_p (t2)) + && cp_compare_floating_point_conversion_ranks + (t1, t2) == 3); + if (complain & tf_error) + error_at (loc, "operands to % of types %qT and %qT " + "have unordered conversion rank", + arg2_type, arg3_type); + return error_mark_node; + } + if (TREE_CODE (arg2) == EXCESS_PRECISION_EXPR) + { + arg2 = TREE_OPERAND (arg2, 0); + arg2_type = TREE_TYPE (arg2); + } + if (TREE_CODE (arg3) == EXCESS_PRECISION_EXPR) + { + arg3 = TREE_OPERAND (arg3, 0); + arg3_type = TREE_TYPE (arg3); + } + } + /* [expr.cond] If either the second or the third operand has type (possibly @@ -5557,8 +5607,6 @@ build_conditional_expr (const op_location_t &loc, array-to-pointer (_conv.array_), and function-to-pointer (_conv.func_) standard conversions are performed on the second and third operands. */ - arg2_type = unlowered_expr_type (arg2); - arg3_type = unlowered_expr_type (arg3); if (VOID_TYPE_P (arg2_type) || VOID_TYPE_P (arg3_type)) { /* 'void' won't help in resolving an overloaded expression on the @@ -5850,14 +5898,20 @@ build_conditional_expr (const op_location_t &loc, /* In this case, there is always a common type. */ result_type = type_after_usual_arithmetic_conversions (arg2_type, arg3_type); - if (result_type == error_mark_node - && TREE_CODE (arg2_type) == REAL_TYPE - && TREE_CODE (arg3_type) == REAL_TYPE - && (extended_float_type_p (arg2_type) - || extended_float_type_p (arg3_type)) - && cp_compare_floating_point_conversion_ranks (arg2_type, - arg3_type) == 3) + if (result_type == error_mark_node) { + tree t1 = arg2_type; + tree t2 = arg3_type; + if (TREE_CODE (t1) == COMPLEX_TYPE) + t1 = TREE_TYPE (t1); + if (TREE_CODE (t2) == COMPLEX_TYPE) + t2 = TREE_TYPE (t2); + gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE + && TREE_CODE (t2) == REAL_TYPE + && (extended_float_type_p (t1) + || extended_float_type_p (t2)) + && cp_compare_floating_point_conversion_ranks + (t1, t2) == 3); if (complain & tf_error) error_at (loc, "operands to % of types %qT and %qT " "have unordered conversion rank", @@ -5922,6 +5976,10 @@ build_conditional_expr (const op_location_t &loc, } } + if (semantic_result_type && INTEGRAL_TYPE_P (arg2_type)) + arg2 = perform_implicit_conversion (semantic_result_type, arg2, complain); + else if (semantic_result_type && INTEGRAL_TYPE_P (arg3_type)) + arg3 = perform_implicit_conversion (semantic_result_type, arg3, complain); arg2 = perform_implicit_conversion (result_type, arg2, complain); arg3 = perform_implicit_conversion (result_type, arg3, complain); } @@ -6009,9 +6067,15 @@ build_conditional_expr (const op_location_t &loc, /* If this expression is an rvalue, but might be mistaken for an lvalue, we must add a NON_LVALUE_EXPR. */ result = rvalue (result); + if (semantic_result_type) + result = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, + result); } else - result = force_paren_expr (result); + { + result = force_paren_expr (result); + gcc_assert (semantic_result_type == NULL_TREE); + } return result; } @@ -7875,7 +7939,7 @@ maybe_warn_array_conv (location_t loc, conversion *c, tree expr) } /* We call this recursively in convert_like_internal. */ -static tree convert_like (conversion *, tree, tree, int, bool, bool, +static tree convert_like (conversion *, tree, tree, int, bool, bool, bool, tsubst_flags_t); /* Perform the conversions in CONVS on the expression EXPR. FN and @@ -7891,7 +7955,7 @@ static tree convert_like (conversion *, tree, tree, int, bool, bool, static tree convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, bool issue_conversion_warnings, bool c_cast_p, - tsubst_flags_t complain) + bool nested_p, tsubst_flags_t complain) { tree totype = convs->type; diagnostic_t diag_kind; @@ -7968,7 +8032,8 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, print_z_candidate (loc, N_("candidate is:"), t->cand); expr = convert_like (t, expr, fn, argnum, /*issue_conversion_warnings=*/false, - /*c_cast_p=*/false, complain); + /*c_cast_p=*/false, /*nested_p=*/true, + complain); if (convs->kind == ck_ref_bind) expr = convert_to_reference (totype, expr, CONV_IMPLICIT, LOOKUP_NORMAL, NULL_TREE, @@ -7983,13 +8048,15 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, { expr = convert_like (t, expr, fn, argnum, /*issue_conversion_warnings=*/false, - /*c_cast_p=*/false, complain); + /*c_cast_p=*/false, /*nested_p=*/true, + complain); break; } else if (t->kind == ck_ambig) return convert_like (t, expr, fn, argnum, /*issue_conversion_warnings=*/false, - /*c_cast_p=*/false, complain); + /*c_cast_p=*/false, /*nested_p=*/true, + complain); else if (t->kind == ck_identity) break; } @@ -8109,6 +8176,8 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, if (type_unknown_p (expr)) expr = instantiate_type (totype, expr, complain); + if (!nested_p && TREE_CODE (expr) == EXCESS_PRECISION_EXPR) + expr = cp_convert (totype, TREE_OPERAND (expr, 0), complain); if (expr == null_node && INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (totype)) /* If __null has been converted to an integer type, we do not want to @@ -8148,7 +8217,8 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), ix, val) { tree sub = convert_like (convs->u.list[ix], val, fn, - argnum, false, false, complain); + argnum, false, false, + /*nested_p=*/true, complain); if (sub == error_mark_node) return sub; if (!BRACE_ENCLOSED_INITIALIZER_P (val) @@ -8216,7 +8286,7 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, expr = convert_like (next_conversion (convs), expr, fn, argnum, convs->kind == ck_ref_bind ? issue_conversion_warnings : false, - c_cast_p, complain & ~tf_no_cleanup); + c_cast_p, /*nested_p=*/true, complain & ~tf_no_cleanup); if (expr == error_mark_node) return error_mark_node; @@ -8475,7 +8545,15 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, return error_mark_node; warning_sentinel w (warn_zero_as_null_pointer_constant); - if (issue_conversion_warnings) + if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR) + { + if (issue_conversion_warnings) + expr = cp_ep_convert_and_check (totype, TREE_OPERAND (expr, 0), + TREE_TYPE (expr), complain); + else + expr = cp_convert (totype, TREE_OPERAND (expr, 0), complain); + } + else if (issue_conversion_warnings) expr = cp_convert_and_check (totype, expr, complain); else expr = cp_convert (totype, expr, complain); @@ -8509,7 +8587,7 @@ conv_unsafe_in_template_p (tree to, tree from) static tree convert_like (conversion *convs, tree expr, tree fn, int argnum, - bool issue_conversion_warnings, bool c_cast_p, + bool issue_conversion_warnings, bool c_cast_p, bool nested_p, tsubst_flags_t complain) { /* Creating &TARGET_EXPR<> in a template breaks when substituting, @@ -8532,7 +8610,8 @@ convert_like (conversion *convs, tree expr, tree fn, int argnum, error_mark_node. */ } expr = convert_like_internal (convs, expr, fn, argnum, - issue_conversion_warnings, c_cast_p, complain); + issue_conversion_warnings, c_cast_p, + nested_p, complain); if (expr == error_mark_node) return error_mark_node; return conv_expr ? conv_expr : expr; @@ -8545,7 +8624,7 @@ convert_like (conversion *convs, tree expr, tsubst_flags_t complain) { return convert_like (convs, expr, NULL_TREE, 0, /*issue_conversion_warnings=*/true, - /*c_cast_p=*/false, complain); + /*c_cast_p=*/false, /*nested_p=*/false, complain); } /* Convenience wrapper for convert_like. */ @@ -8556,7 +8635,7 @@ convert_like_with_context (conversion *convs, tree expr, tree fn, int argnum, { return convert_like (convs, expr, fn, argnum, /*issue_conversion_warnings=*/true, - /*c_cast_p=*/false, complain); + /*c_cast_p=*/false, /*nested_p=*/false, complain); } /* ARG is being passed to a varargs function. Perform any conversions @@ -8587,6 +8666,8 @@ convert_arg_to_ellipsis (tree arg, tsubst_flags_t complain) "implicit conversion from %qH to %qI when passing " "argument to function", arg_type, double_type_node); + if (TREE_CODE (arg) == EXCESS_PRECISION_EXPR) + arg = TREE_OPERAND (arg, 0); arg = mark_rvalue_use (arg); arg = convert_to_real_nofold (double_type_node, arg); } @@ -8893,9 +8974,9 @@ convert_for_arg_passing (tree type, tree val, tsubst_flags_t complain) /* Returns non-zero iff FN is a function with magic varargs, i.e. ones for which just decay_conversion or no conversions at all should be done. This is true for some builtins which don't act like normal functions. - Return 2 if no conversions at all should be done, 1 if just - decay_conversion. Return 3 for special treatment of the 3rd argument - for __builtin_*_overflow_p. */ + Return 2 if just decay_conversion and removal of excess precision should + be done, 1 if just decay_conversion. Return 3 for special treatment of + the 3rd argument for __builtin_*_overflow_p. */ int magic_varargs_p (tree fn) @@ -8914,7 +8995,15 @@ magic_varargs_p (tree fn) case BUILT_IN_MUL_OVERFLOW_P: return 3; - default:; + 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: + return 2; + + default: return lookup_attribute ("type generic", TYPE_ATTRIBUTES (TREE_TYPE (fn))) != 0; } @@ -9717,7 +9806,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) for (; arg_index < vec_safe_length (args); ++arg_index) { tree a = (*args)[arg_index]; - if ((magic == 3 && arg_index == 2) || magic == 2) + if (magic == 3 && arg_index == 2) { /* Do no conversions for certain magic varargs. */ a = mark_type_use (a); @@ -9725,8 +9814,13 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) return error_mark_node; } else if (magic != 0) - /* For other magic varargs only do decay_conversion. */ - a = decay_conversion (a, complain); + { + /* Don't truncate excess precision to the semantic type. */ + if (magic == 1 && TREE_CODE (a) == EXCESS_PRECISION_EXPR) + a = TREE_OPERAND (a, 0); + /* For other magic varargs only do decay_conversion. */ + a = decay_conversion (a, complain); + } else if (DECL_CONSTRUCTOR_P (fn) && same_type_ignoring_top_level_qualifiers_p (DECL_CONTEXT (fn), TREE_TYPE (a))) @@ -13004,7 +13098,7 @@ perform_direct_initialization_if_possible (tree type, else expr = convert_like (conv, expr, NULL_TREE, 0, /*issue_conversion_warnings=*/false, - c_cast_p, complain); + c_cast_p, /*nested_p=*/false, complain); /* Free all the conversions we allocated. */ obstack_free (&conversion_obstack, p); diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 2038f43425b..fb171a2784f 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -7598,6 +7598,19 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, } break; + case EXCESS_PRECISION_EXPR: + { + tree oldop = TREE_OPERAND (t, 0); + + tree op = cxx_eval_constant_expression (ctx, oldop, + lval, + non_constant_p, overflow_p); + if (*non_constant_p) + return t; + r = fold_convert (TREE_TYPE (t), op); + break; + } + case EMPTY_CLASS_EXPR: /* Handle EMPTY_CLASS_EXPR produced by build_call_a by lowering it to an appropriate CONSTRUCTOR. */ @@ -8898,6 +8911,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, sub-object of such an object; */ return RECUR (TREE_OPERAND (t, 0), rval); + case EXCESS_PRECISION_EXPR: + return RECUR (TREE_OPERAND (t, 0), rval); + case VAR_DECL: if (DECL_HAS_VALUE_EXPR_P (t)) { diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index a937060b029..2608475897c 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -2515,6 +2515,11 @@ cp_fold (tree x) break; + case EXCESS_PRECISION_EXPR: + op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops); + x = fold_convert_loc (EXPR_LOCATION (x), TREE_TYPE (x), op0); + break; + case INDIRECT_REF: /* We don't need the decltype(auto) obfuscation anymore. */ if (REF_PARENTHESIZED_P (x)) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 1534c875693..ceaadba33fa 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6793,6 +6793,8 @@ extern tree ocp_convert (tree, tree, int, int, tsubst_flags_t); extern tree cp_convert (tree, tree, tsubst_flags_t); extern tree cp_convert_and_check (tree, tree, tsubst_flags_t); +extern tree cp_ep_convert_and_check (tree, tree, tree, + tsubst_flags_t); extern tree cp_fold_convert (tree, tree); extern tree cp_get_callee (tree); extern tree cp_get_callee_fndecl (tree); diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc index 434d306961f..4da04e1cad7 100644 --- a/gcc/cp/cvt.cc +++ b/gcc/cp/cvt.cc @@ -684,6 +684,33 @@ cp_convert_and_check (tree type, tree expr, tsubst_flags_t complain) return result; } +/* Similarly, but deal with excess precision. SEMANTIC_TYPE is the type this + conversion would use without excess precision. If SEMANTIC_TYPE is NULL, + this function is equivalent to cp_convert_and_check. This function is + a wrapper that handles conversions that may be different than the usual + ones because of excess precision. */ + +tree +cp_ep_convert_and_check (tree type, tree expr, tree semantic_type, + tsubst_flags_t complain) +{ + if (TREE_TYPE (expr) == type) + return expr; + if (expr == error_mark_node) + return expr; + if (!semantic_type) + return cp_convert_and_check (type, expr, complain); + + if (TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE + && TREE_TYPE (expr) != semantic_type) + /* For integers, we need to check the real conversion, not + the conversion to the excess precision type. */ + expr = cp_convert_and_check (semantic_type, expr, complain); + /* Result type is the excess precision type, which should be + large enough, so do not check. */ + return cp_convert (type, expr, complain); +} + /* Conversion... FLAGS indicates how we should behave. */ diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 10d303884fb..9ddfb027ff9 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -5583,7 +5583,9 @@ cp_parser_primary_expression (cp_parser *parser, /* Floating-point literals are only allowed in an integral constant expression if they are cast to an integral or enumeration type. */ - if (TREE_CODE (token->u.value) == REAL_CST + if ((TREE_CODE (token->u.value) == REAL_CST + || (TREE_CODE (token->u.value) == EXCESS_PRECISION_EXPR + && TREE_CODE (TREE_OPERAND (token->u.value, 0)) == REAL_CST)) && parser->integral_constant_expression_p && pedantic) { diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index f6800857dcf..e4dca9d4f9d 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -17414,6 +17414,18 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) return r; } + case EXCESS_PRECISION_EXPR: + { + tree type = tsubst (TREE_TYPE (t), args, complain, in_decl); + tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl); + if (TREE_CODE (op0) == EXCESS_PRECISION_EXPR) + { + gcc_checking_assert (same_type_p (type, TREE_TYPE (op0))); + return op0; + } + return build1_loc (EXPR_LOCATION (t), code, type, op0); + } + case COMPONENT_REF: { tree object; @@ -20442,6 +20454,16 @@ tsubst_copy_and_build (tree t, templated_operator_saved_lookups (t), complain|decltype_flag)); + case EXCESS_PRECISION_EXPR: + { + tree type = tsubst (TREE_TYPE (t), args, complain, in_decl); + tree op0 = RECUR (TREE_OPERAND (t, 0)); + if (TREE_CODE (op0) == EXCESS_PRECISION_EXPR) + RETURN (op0); + RETURN (build1_loc (EXPR_LOCATION (t), EXCESS_PRECISION_EXPR, + type, op0)); + } + case FIX_TRUNC_EXPR: /* convert_like should have created an IMPLICIT_CONV_EXPR. */ gcc_unreachable (); diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 69c378dbcf0..c3503b69f77 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -439,6 +439,8 @@ cp_common_type (tree t1, tree t2) tree subtype = type_after_usual_arithmetic_conversions (subtype1, subtype2); + if (subtype == error_mark_node) + return subtype; if (code1 == COMPLEX_TYPE && TREE_TYPE (t1) == subtype) return build_type_attribute_variant (t1, attributes); else if (code2 == COMPLEX_TYPE && TREE_TYPE (t2) == subtype) @@ -4603,11 +4605,17 @@ convert_arguments (tree typelist, vec **values, tree fndecl, } else { - if (fndecl && magic_varargs_p (fndecl)) - /* Don't do ellipsis conversion for __built_in_constant_p - as this will result in spurious errors for non-trivial - types. */ - val = require_complete_type (val, complain); + int magic = fndecl ? magic_varargs_p (fndecl) : 0; + if (magic) + { + /* Don't truncate excess precision to the semantic type. */ + if (magic == 1 && TREE_CODE (val) == EXCESS_PRECISION_EXPR) + val = TREE_OPERAND (val, 0); + /* Don't do ellipsis conversion for __built_in_constant_p + as this will result in spurious errors for non-trivial + types. */ + val = require_complete_type (val, complain); + } else val = convert_arg_to_ellipsis (val, complain); @@ -5057,7 +5065,7 @@ cp_build_binary_op (const op_location_t &location, { tree op0, op1; enum tree_code code0, code1; - tree type0, type1; + tree type0, type1, orig_type0, orig_type1; const char *invalid_op_diag; /* Expression code to give to the expression when it is built. @@ -5069,6 +5077,10 @@ cp_build_binary_op (const op_location_t &location, In the simplest cases this is the common type of the arguments. */ tree result_type = NULL_TREE; + /* When the computation is in excess precision, the type of the + final EXCESS_PRECISION_EXPR. */ + tree semantic_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. */ @@ -5116,6 +5128,10 @@ cp_build_binary_op (const op_location_t &location, /* Tree holding instrumentation expression. */ tree instrument_expr = NULL_TREE; + /* True means this is an arithmetic operation that may need excess + precision. */ + bool may_need_excess_precision; + /* Apply default conversions. */ op0 = resolve_nondeduced_context (orig_op0, complain); op1 = resolve_nondeduced_context (orig_op1, complain); @@ -5167,8 +5183,8 @@ cp_build_binary_op (const op_location_t &location, } } - 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. */ @@ -5201,6 +5217,47 @@ cp_build_binary_op (const op_location_t &location, 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 + && (code0 == REAL_TYPE || code0 == COMPLEX_TYPE)) + if (tree eptype = excess_precision_type (type0)) + { + 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 + && (code1 == REAL_TYPE || code1 == COMPLEX_TYPE)) + if (tree eptype = excess_precision_type (type1)) + { + type1 = eptype; + op1 = convert (eptype, op1); + } + /* Issue warnings about peculiar, but valid, uses of NULL. */ if ((null_node_p (orig_op0) || null_node_p (orig_op1)) /* It's reasonable to use pointer values as operands of && @@ -5240,7 +5297,7 @@ cp_build_binary_op (const op_location_t &location, op0 = convert (TREE_TYPE (type1), op0); op0 = save_expr (op0); op0 = build_vector_from_val (type1, op0); - type0 = TREE_TYPE (op0); + orig_type0 = type0 = TREE_TYPE (op0); code0 = TREE_CODE (type0); converted = 1; break; @@ -5250,7 +5307,7 @@ cp_build_binary_op (const op_location_t &location, op1 = convert (TREE_TYPE (type0), op1); op1 = save_expr (op1); op1 = build_vector_from_val (type0, op1); - type1 = TREE_TYPE (op1); + orig_type1 = type1 = TREE_TYPE (op1); code1 = TREE_CODE (type1); converted = 1; break; @@ -6067,12 +6124,20 @@ cp_build_binary_op (const op_location_t &location, && (shorten || common || short_compare)) { result_type = cp_common_type (type0, type1); - if (result_type == error_mark_node - && code0 == REAL_TYPE - && code1 == REAL_TYPE - && (extended_float_type_p (type0) || extended_float_type_p (type1)) - && cp_compare_floating_point_conversion_ranks (type0, type1) == 3) + if (result_type == error_mark_node) { + tree t1 = type0; + tree t2 = type1; + if (TREE_CODE (t1) == COMPLEX_TYPE) + t1 = TREE_TYPE (t1); + if (TREE_CODE (t2) == COMPLEX_TYPE) + t2 = TREE_TYPE (t2); + gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE + && TREE_CODE (t2) == REAL_TYPE + && (extended_float_type_p (t1) + || extended_float_type_p (t2)) + && cp_compare_floating_point_conversion_ranks + (t1, t2) == 3); if (complain & tf_error) { rich_location richloc (line_table, location); @@ -6091,6 +6156,33 @@ cp_build_binary_op (const op_location_t &location, TREE_TYPE (orig_op1)); } } + if (may_need_excess_precision + && (orig_type0 != type0 || orig_type1 != type1)) + { + gcc_assert (common); + semantic_result_type = cp_common_type (orig_type0, orig_type1); + if (semantic_result_type == error_mark_node) + { + tree t1 = orig_type0; + tree t2 = orig_type1; + if (TREE_CODE (t1) == COMPLEX_TYPE) + t1 = TREE_TYPE (t1); + if (TREE_CODE (t2) == COMPLEX_TYPE) + t2 = TREE_TYPE (t2); + gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE + && TREE_CODE (t2) == REAL_TYPE + && (extended_float_type_p (t1) + || extended_float_type_p (t2)) + && cp_compare_floating_point_conversion_ranks + (t1, t2) == 3); + if (complain & tf_error) + { + rich_location richloc (line_table, location); + binary_op_error (&richloc, code, type0, type1); + } + return error_mark_node; + } + } if (code == SPACESHIP_EXPR) { @@ -6181,6 +6273,8 @@ cp_build_binary_op (const op_location_t &location, build_type ? build_type : result_type, NULL_TREE, op1); TREE_OPERAND (tmp, 0) = op0; + if (semantic_result_type) + tmp = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, tmp); return tmp; } @@ -6268,6 +6362,9 @@ cp_build_binary_op (const op_location_t &location, } } result = build2 (COMPLEX_EXPR, result_type, real, imag); + if (semantic_result_type) + result = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, + result); return result; } @@ -6363,9 +6460,11 @@ cp_build_binary_op (const op_location_t &location, { warning_sentinel w (warn_sign_conversion, short_compare); if (!same_type_p (TREE_TYPE (op0), result_type)) - op0 = cp_convert_and_check (result_type, op0, complain); + op0 = cp_ep_convert_and_check (result_type, op0, + semantic_result_type, complain); if (!same_type_p (TREE_TYPE (op1), result_type)) - op1 = cp_convert_and_check (result_type, op1, complain); + op1 = cp_ep_convert_and_check (result_type, op1, + semantic_result_type, complain); if (op0 == error_mark_node || op1 == error_mark_node) return error_mark_node; @@ -6435,6 +6534,9 @@ cp_build_binary_op (const op_location_t &location, if (resultcode == SPACESHIP_EXPR && !processing_template_decl) result = get_target_expr (result, complain); + if (semantic_result_type) + result = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, result); + if (!c_inhibit_evaluation_warnings) { if (!processing_template_decl) @@ -7161,6 +7263,7 @@ cp_build_unary_op (enum tree_code code, tree xarg, bool noconvert, tree arg = xarg; location_t location = cp_expr_loc_or_input_loc (arg); tree argtype = 0; + tree eptype = NULL_TREE; const char *errstring = NULL; tree val; const char *invalid_op_diag; @@ -7181,6 +7284,12 @@ cp_build_unary_op (enum tree_code code, tree xarg, bool noconvert, return error_mark_node; } + if (TREE_CODE (arg) == EXCESS_PRECISION_EXPR) + { + eptype = TREE_TYPE (arg); + arg = TREE_OPERAND (arg, 0); + } + switch (code) { case UNARY_PLUS_EXPR: @@ -7276,8 +7385,11 @@ cp_build_unary_op (enum tree_code code, tree xarg, bool noconvert, case REALPART_EXPR: case IMAGPART_EXPR: - arg = build_real_imag_expr (input_location, code, arg); - return arg; + val = build_real_imag_expr (input_location, code, arg); + if (eptype && TREE_CODE (eptype) == COMPLEX_EXPR) + val = build1_loc (input_location, EXCESS_PRECISION_EXPR, + TREE_TYPE (eptype), val); + return val; case PREINCREMENT_EXPR: case POSTINCREMENT_EXPR: @@ -7288,7 +7400,7 @@ cp_build_unary_op (enum tree_code code, tree xarg, bool noconvert, val = unary_complex_lvalue (code, arg); if (val != 0) - return val; + goto return_build_unary_op; arg = mark_lvalue_use (arg); @@ -7304,8 +7416,8 @@ cp_build_unary_op (enum tree_code code, tree xarg, bool noconvert, real = cp_build_unary_op (code, real, true, complain); if (real == error_mark_node || imag == error_mark_node) return error_mark_node; - return build2 (COMPLEX_EXPR, TREE_TYPE (arg), - real, imag); + val = build2 (COMPLEX_EXPR, TREE_TYPE (arg), real, imag); + goto return_build_unary_op; } /* Report invalid types. */ @@ -7468,7 +7580,7 @@ cp_build_unary_op (enum tree_code code, tree xarg, bool noconvert, val = build2 (code, TREE_TYPE (arg), arg, inc); TREE_SIDE_EFFECTS (val) = 1; - return val; + goto return_build_unary_op; } case ADDR_EXPR: @@ -7484,7 +7596,11 @@ cp_build_unary_op (enum tree_code code, tree xarg, bool noconvert, { if (argtype == 0) argtype = TREE_TYPE (arg); - return build1 (code, argtype, arg); + val = build1 (code, argtype, arg); + return_build_unary_op: + if (eptype) + val = build1 (EXCESS_PRECISION_EXPR, eptype, val); + return val; } if (complain & tf_error) @@ -7875,6 +7991,15 @@ cp_build_compound_expr (tree lhs, tree rhs, tsubst_flags_t complain) if (lhs == error_mark_node || rhs == error_mark_node) return error_mark_node; + if (TREE_CODE (lhs) == EXCESS_PRECISION_EXPR) + lhs = TREE_OPERAND (lhs, 0); + tree eptype = NULL_TREE; + if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR) + { + eptype = TREE_TYPE (rhs); + rhs = TREE_OPERAND (rhs, 0); + } + if (TREE_CODE (rhs) == TARGET_EXPR) { /* If the rhs is a TARGET_EXPR, then build the compound @@ -7885,6 +8010,8 @@ cp_build_compound_expr (tree lhs, tree rhs, tsubst_flags_t complain) init = build2 (COMPOUND_EXPR, TREE_TYPE (init), lhs, init); TREE_OPERAND (rhs, 1) = init; + if (eptype) + rhs = build1 (EXCESS_PRECISION_EXPR, eptype, rhs); return rhs; } @@ -7896,7 +8023,10 @@ cp_build_compound_expr (tree lhs, tree rhs, tsubst_flags_t complain) return error_mark_node; } - return build2 (COMPOUND_EXPR, TREE_TYPE (rhs), lhs, rhs); + tree ret = build2 (COMPOUND_EXPR, TREE_TYPE (rhs), lhs, rhs); + if (eptype) + ret = build1 (EXCESS_PRECISION_EXPR, eptype, ret); + return ret; } /* Issue a diagnostic message if casting from SRC_TYPE to DEST_TYPE @@ -8180,7 +8310,11 @@ build_static_cast_1 (location_t loc, tree type, tree expr, bool c_cast_p, Any expression can be explicitly converted to type cv void. */ if (VOID_TYPE_P (type)) - return convert_to_void (expr, ICV_CAST, complain); + { + if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR) + expr = TREE_OPERAND (expr, 0); + return convert_to_void (expr, ICV_CAST, complain); + } /* [class.abstract] An abstract class shall not be used ... as the type of an explicit @@ -8259,6 +8393,8 @@ build_static_cast_1 (location_t loc, tree type, tree expr, bool c_cast_p, { if (processing_template_decl) return expr; + if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR) + expr = TREE_OPERAND (expr, 0); return ocp_convert (type, expr, CONV_C_CAST, LOOKUP_NORMAL, complain); } diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index a9ecc4426a4..c176e2dc646 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -13785,18 +13785,18 @@ default, @option{-fexcess-precision=fast} is in effect; this means that operations may be carried out in a wider precision than the types specified in the source if that would result in faster code, and it is unpredictable when rounding to the types specified in the source code takes place. -When compiling C, if @option{-fexcess-precision=standard} is specified then -excess precision follows the rules specified in ISO C99; in particular, +When compiling C or C++, if @option{-fexcess-precision=standard} is specified +then excess precision follows the rules specified in ISO C99 or C++; 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. +assignments). This option is enabled by default for C or C++ if a strict +conformance option such as @option{-std=c99} or @option{-std=c++17} is used. @option{-ffast-math} enables @option{-fexcess-precision=fast} by default regardless of whether a strict conformance option is used. @opindex mfpmath @option{-fexcess-precision=standard} is not implemented for languages -other than C. On the x86, it has no effect if @option{-mfpmath=sse} +other than C or C++. On the x86, it 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. diff --git a/gcc/testsuite/c-c++-common/dfp/compare-eq-const.c b/gcc/testsuite/c-c++-common/dfp/compare-eq-const.c index 0c9ff200b28..4bc2189f53c 100644 --- a/gcc/testsuite/c-c++-common/dfp/compare-eq-const.c +++ b/gcc/testsuite/c-c++-common/dfp/compare-eq-const.c @@ -1,5 +1,6 @@ /* C99 6.5.9 Equality operators. Compare decimal float constants against each other. */ +/* { dg-additional-options "-fexcess-precision=fast" } */ #include "dfp-dbg.h" diff --git a/gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c b/gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c index 3fef98a3384..2cce3e6a4e7 100644 --- a/gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c +++ b/gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c @@ -1,4 +1,5 @@ /* This test assumes IEEE float and double. */ +/* { dg-additional-options "-fexcess-precision=fast" } */ #include "convert.h" diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C index 11bdd88eea2..c5acc4a789a 100644 --- a/gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C +++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C @@ -18,8 +18,8 @@ int main() { Test t; B b; - B b2(4.2); + B b2(4.25); - if (t.a != 4 || b.i != 42 || b2.d != 4.2) + if (t.a != 4 || b.i != 42 || b2.d != 4.25) __builtin_abort(); } diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C b/gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C index e2699d94ebf..5164598439b 100644 --- a/gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C +++ b/gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C @@ -1,4 +1,5 @@ // { dg-do run { target c++11 } } +// { dg-additional-options "-fexcess-precision=fast" } // An implementation of TR1's using variadic teplates // Contributed by Douglas Gregor diff --git a/gcc/testsuite/g++.dg/cpp1y/pr68180.C b/gcc/testsuite/g++.dg/cpp1y/pr68180.C index 9e6e5e984f9..64d613ee5a7 100644 --- a/gcc/testsuite/g++.dg/cpp1y/pr68180.C +++ b/gcc/testsuite/g++.dg/cpp1y/pr68180.C @@ -1,6 +1,6 @@ // PR c++/68180 // { dg-do compile { target c++14 } } -// { dg-additional-options "-Wno-psabi" } +// { dg-additional-options "-Wno-psabi -fexcess-precision=fast" } typedef float __attribute__( ( vector_size( 16 ) ) ) float32x4_t; constexpr float32x4_t fill(float x) { diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C index 0b5c2e81bd1..daefca94a63 100644 --- a/gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C @@ -1,6 +1,6 @@ // PR c++/96862 // { dg-do compile { target c++17 } } -// { dg-additional-options "-frounding-math" } +// { dg-additional-options "-frounding-math -fexcess-precision=fast" } constexpr double a = 0x1.0p+100 + 0x1.0p-100; const double b = 0x1.0p+100 + 0x1.0p-100; diff --git a/gcc/testsuite/g++.dg/cpp1z/decomp12.C b/gcc/testsuite/g++.dg/cpp1z/decomp12.C index 56fd4987165..a65aaf903a2 100644 --- a/gcc/testsuite/g++.dg/cpp1z/decomp12.C +++ b/gcc/testsuite/g++.dg/cpp1z/decomp12.C @@ -7,13 +7,13 @@ template struct same_type; template struct same_type {}; int main() { - std::tuple tuple = { 1, 'a', 2.3, true }; + std::tuple tuple = { 1, 'a', 2.25, true }; auto[i, c, d, b] = tuple; same_type::type, decltype(i)>{}; same_type{}; same_type{}; same_type{}; same_type{}; - if (i != 1 || c != 'a' || d != 2.3 || b != true) + if (i != 1 || c != 'a' || d != 2.25 || b != true) __builtin_abort (); } diff --git a/gcc/testsuite/g++.dg/other/thunk1.C b/gcc/testsuite/g++.dg/other/thunk1.C index 90165628b45..77581116291 100644 --- a/gcc/testsuite/g++.dg/other/thunk1.C +++ b/gcc/testsuite/g++.dg/other/thunk1.C @@ -1,5 +1,6 @@ // PR c++/12007 Multiple inheritance float pass by value fails // { dg-do run } +// { dg-additional-options "-fexcess-precision=fast" } extern "C" void abort (void); diff --git a/gcc/testsuite/g++.dg/vect/pr64410.cc b/gcc/testsuite/g++.dg/vect/pr64410.cc index cbc2accc2a1..ad4b085b5e0 100644 --- a/gcc/testsuite/g++.dg/vect/pr64410.cc +++ b/gcc/testsuite/g++.dg/vect/pr64410.cc @@ -1,5 +1,6 @@ // { dg-do compile } // { dg-require-effective-target vect_double } +// { dg-additional-options "-fexcess-precision=fast" } #include #include diff --git a/gcc/testsuite/g++.dg/vect/pr89653.cc b/gcc/testsuite/g++.dg/vect/pr89653.cc index d38b49a5497..f5a071fb9e8 100644 --- a/gcc/testsuite/g++.dg/vect/pr89653.cc +++ b/gcc/testsuite/g++.dg/vect/pr89653.cc @@ -1,5 +1,6 @@ // { dg-do compile } // { dg-require-effective-target vect_double } +// { dg-additional-options "-fexcess-precision=fast" } #include diff --git a/gcc/testsuite/g++.old-deja/g++.brendan/copy9.C b/gcc/testsuite/g++.old-deja/g++.brendan/copy9.C index f05b1943a9c..79339c13de9 100644 --- a/gcc/testsuite/g++.old-deja/g++.brendan/copy9.C +++ b/gcc/testsuite/g++.old-deja/g++.brendan/copy9.C @@ -1,4 +1,5 @@ // { dg-do run } +// { dg-additional-options "-fexcess-precision=fast" } // GROUPS passed copy-ctors #include diff --git a/gcc/testsuite/g++.old-deja/g++.brendan/overload7.C b/gcc/testsuite/g++.old-deja/g++.brendan/overload7.C index 29a90601656..08f220c6447 100644 --- a/gcc/testsuite/g++.old-deja/g++.brendan/overload7.C +++ b/gcc/testsuite/g++.old-deja/g++.brendan/overload7.C @@ -1,4 +1,5 @@ // { dg-do run } +// { dg-additional-options "-fexcess-precision=fast" } // GROUPS passed overloading extern "C" int printf (const char *, ...); diff --git a/gcc/testsuite/g++.target/i386/excess-precision-1.C b/gcc/testsuite/g++.target/i386/excess-precision-1.C new file mode 100644 index 00000000000..0519d630542 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/excess-precision-1.C @@ -0,0 +1,6 @@ +// Excess precision tests. Test that excess precision is carried +// through various operations. +// { dg-do run } +// { dg-options "-O2 -mfpmath=387 -fexcess-precision=standard" } + +#include "../../gcc.target/i386/excess-precision-1.c" diff --git a/gcc/testsuite/g++.target/i386/excess-precision-11.C b/gcc/testsuite/g++.target/i386/excess-precision-11.C new file mode 100644 index 00000000000..119b6667f29 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/excess-precision-11.C @@ -0,0 +1,105 @@ +// Excess precision tests. Test excess precision is removed when +// necessary. +// { dg-do run } +// { dg-options "-O2 -mfpmath=387 -fexcess-precision=standard" } + +#include +#include + +extern "C" void abort (); + +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 double d3d = 0x1.0p-52; +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; + +static inline void +check_float (float f) +{ + if (f != fadd1) + abort (); +} + +static inline void +check_float (double) +{ + abort (); +} + +static inline void +check_float (long double) +{ + abort (); +} + +static inline void +check_double (double d) +{ + if (d != dadd2) + abort (); +} + +static inline void +check_double (long double) +{ + abort (); +} + +static inline void +check_float2 (float f) +{ + if (f != fha) + abort (); +} + +struct S { + S () {} + S (float f) { if (f != fadd1) abort (); } +}; + +struct T { + T () {} + T (double d) { if (d != dadd2) abort (); } +}; + +static inline void +check_float3 (S) +{ +} + +static inline void +check_double2 (T) +{ +} + +void +test_call () +{ + check_float (f1 + f2); + check_double (f1 + f2); + check_double (d1 + d2 + d3); + /* Verify rounding direct to float without double rounding. */ + if (sizeof (long double) > sizeof (double)) + check_float2 (d1 + dh + d3); + else + check_float2 (d1 + dh + d3d); + check_float3 (f1 + f2); + check_double2 (f1 + f2); + check_double2 (d1 + d2 + d3); + S s1 = static_cast (f1 + f2); + T t2 = static_cast (f1 + f2); + T t3 = static_cast (d1 + d2 + d3); +} + +int +main () +{ + test_call (); +} diff --git a/gcc/testsuite/g++.target/i386/excess-precision-2.C b/gcc/testsuite/g++.target/i386/excess-precision-2.C new file mode 100644 index 00000000000..afb21976589 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/excess-precision-2.C @@ -0,0 +1,5 @@ +// Excess precision tests. Test excess precision of constants. +// { dg-do run } +// { dg-options "-O2 -mfpmath=387 -fexcess-precision=standard" } + +#include "../../gcc.target/i386/excess-precision-2.c" diff --git a/gcc/testsuite/g++.target/i386/excess-precision-3.C b/gcc/testsuite/g++.target/i386/excess-precision-3.C new file mode 100644 index 00000000000..be8cc423019 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/excess-precision-3.C @@ -0,0 +1,6 @@ +// Excess precision tests. Test excess precision is removed when +// necessary. +// { dg-do run } +// { dg-options "-O2 -mfpmath=387 -fexcess-precision=standard" } + +#include "../../gcc.target/i386/excess-precision-3.c" diff --git a/gcc/testsuite/g++.target/i386/excess-precision-4.C b/gcc/testsuite/g++.target/i386/excess-precision-4.C new file mode 100644 index 00000000000..30606b1d655 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/excess-precision-4.C @@ -0,0 +1,7 @@ +// Excess precision tests. Test diagnostics for excess precision of +// constants. +// { dg-do compile } +// { dg-options "-mfpmath=387 -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/g++.target/i386/excess-precision-5.C b/gcc/testsuite/g++.target/i386/excess-precision-5.C new file mode 100644 index 00000000000..fa15753e451 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/excess-precision-5.C @@ -0,0 +1,32 @@ +// Excess precision tests. Verify excess precision doesn't affect +// actual types. +// { dg-do compile { target c++11 } } +// { dg-options "-mfpmath=387 -fexcess-precision=standard" } + +namespace std { + template struct integral_constant { + static constexpr T value = v; + }; + typedef integral_constant false_type; + typedef integral_constant true_type; + template + struct is_same : std::false_type {}; + template + struct is_same : std::true_type {}; +} + +float f; +double d; + +void +test_types (void) +{ +#define CHECK_FLOAT(E) static_assert (std::is_same ::value, "") +#define CHECK_DOUBLE(E) static_assert (std::is_same ::value, "") + 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/g++.target/i386/excess-precision-6.C b/gcc/testsuite/g++.target/i386/excess-precision-6.C new file mode 100644 index 00000000000..06e9a93058b --- /dev/null +++ b/gcc/testsuite/g++.target/i386/excess-precision-6.C @@ -0,0 +1,19 @@ +// Excess precision tests. Make sure sqrt is not inlined for float or +// double. +// { dg-do compile } +// { dg-options "-mfpmath=387 -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/testsuite/g++.target/i386/excess-precision-7.C b/gcc/testsuite/g++.target/i386/excess-precision-7.C new file mode 100644 index 00000000000..5df0d9d8c1f --- /dev/null +++ b/gcc/testsuite/g++.target/i386/excess-precision-7.C @@ -0,0 +1,7 @@ +// Excess precision tests. Test C99 semantics for conversions from +// integers to floating point: no excess precision for either explicit +// or implicit conversions. +// { dg-do run } +// { dg-options "-mfpmath=387 -fexcess-precision=standard" } + +#include "../../gcc.target/i386/excess-precision-7.c" diff --git a/gcc/testsuite/g++.target/i386/excess-precision-9.C b/gcc/testsuite/g++.target/i386/excess-precision-9.C new file mode 100644 index 00000000000..1fcadb94c1f --- /dev/null +++ b/gcc/testsuite/g++.target/i386/excess-precision-9.C @@ -0,0 +1,6 @@ +// Excess precision tests. Test implicit conversions in comparisons: +// no excess precision in C++. +// { dg-do run } +// { dg-options "-mfpmath=387 -fexcess-precision=standard" } + +#include "../../gcc.target/i386/excess-precision-9.c" diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-1.c b/gcc/testsuite/gcc.target/i386/excess-precision-1.c index 1bd3b8868bb..388cee72c13 100644 --- a/gcc/testsuite/gcc.target/i386/excess-precision-1.c +++ b/gcc/testsuite/gcc.target/i386/excess-precision-1.c @@ -5,8 +5,14 @@ #include +#ifdef __cplusplus +extern "C" { +#endif extern void abort (void); extern void exit (int); +#ifdef __cplusplus +} +#endif volatile float f1 = 1.0f; volatile float f2 = 0x1.0p-30f; diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-2.c b/gcc/testsuite/gcc.target/i386/excess-precision-2.c index b5035e5a1fe..c14784e0af9 100644 --- a/gcc/testsuite/gcc.target/i386/excess-precision-2.c +++ b/gcc/testsuite/gcc.target/i386/excess-precision-2.c @@ -4,8 +4,14 @@ #include +#ifdef __cplusplus +extern "C" { +#endif extern void abort (void); extern void exit (int); +#ifdef __cplusplus +} +#endif volatile long double ldadd1 = 1.0l + 0x1.0p-30l; volatile long double ld11f = 1.1f; diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-3.c b/gcc/testsuite/gcc.target/i386/excess-precision-3.c index 1fd038a8707..e8e9abc6957 100644 --- a/gcc/testsuite/gcc.target/i386/excess-precision-3.c +++ b/gcc/testsuite/gcc.target/i386/excess-precision-3.c @@ -6,8 +6,14 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif extern void abort (void); extern void exit (int); +#ifdef __cplusplus +} +#endif volatile float f1 = 1.0f; volatile float f2 = 0x1.0p-30f; @@ -100,6 +106,7 @@ check_double (double d) abort (); } +#ifndef __cplusplus static inline void check_float_nonproto (f) float f; @@ -115,6 +122,7 @@ check_double_nonproto (d) if (d != dadd2) abort (); } +#endif static void check_double_va (int i, ...) @@ -132,9 +140,11 @@ test_call (void) check_float (f1 + f2); check_double (d1 + d2 + d3); check_double (f1 + f2 + f3); +#ifndef __cplusplus check_float_nonproto (f1 + f2); check_double_nonproto (d1 + d2 + d3); check_double_nonproto (f1 + f2 + f3); +#endif check_double_va (0, d1 + d2 + d3); check_double_va (0, f1 + f2 + f3); } diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-7.c b/gcc/testsuite/gcc.target/i386/excess-precision-7.c index 0cdd932ce7f..4c3b950f105 100644 --- a/gcc/testsuite/gcc.target/i386/excess-precision-7.c +++ b/gcc/testsuite/gcc.target/i386/excess-precision-7.c @@ -4,8 +4,14 @@ /* { dg-do run } */ /* { dg-options "-std=c99 -mfpmath=387 -fexcess-precision=standard" } */ +#ifdef __cplusplus +extern "C" { +#endif extern void abort (void); extern void exit (int); +#ifdef __cplusplus +} +#endif int main (void) diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-9.c b/gcc/testsuite/gcc.target/i386/excess-precision-9.c index 61e5fc104ad..50081642615 100644 --- a/gcc/testsuite/gcc.target/i386/excess-precision-9.c +++ b/gcc/testsuite/gcc.target/i386/excess-precision-9.c @@ -3,8 +3,14 @@ /* { dg-do run } */ /* { dg-options "-std=c99 -mfpmath=387 -fexcess-precision=standard" } */ +#ifdef __cplusplus +extern "C" { +#endif extern void abort (void); extern void exit (int); +#ifdef __cplusplus +} +#endif int main (void)