From: Jason Merrill <jason@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>,
"Joseph S. Myers" <joseph@codesourcery.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++: Implement excess precision support for C++ [PR107097, PR323]
Date: Wed, 12 Oct 2022 14:08:20 -0400 [thread overview]
Message-ID: <f96ebb2a-47f8-7211-8a3c-990bd1263117@redhat.com> (raw)
In-Reply-To: <Y0VwowKL1r/QXhLo@tucnak>
On 10/11/22 09:33, Jakub Jelinek wrote:
> Hi!
>
> The following patch implements excess precision support for C++.
Great!
> 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.
> In general I've tried to follow the C99 handling, C11+ relies on the
> C standard saying that in case of integral conversions excess precision
> can be used (see PR87390 for more details), but I don't see anything similar
> on the C++ standard side.
https://eel.is/c++draft/expr#pre-6 seems identical to C99 (apart from a
stray "the"?); presumably nobody has proposed to copy the N1531
clarifications. But since those are clarifications, I'd prefer to use
our C11+ semantics to avoid divergence between the default modes of the
C and C++ front ends.
> 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.
That sounds right.
> 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.
Makes sense.
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
>
> 2022-10-11 Jakub Jelinek <jakub@redhat.com>
>
> PR middle-end/323
> PR c++/107097
> gcc/c-family/
> * 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.
> (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 (convert_arguments): For magic 1 remove
> EXCESS_PRECISION_EXPR.
> (cp_build_binary_op): Add excess precision support.
> (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-8.C: New test.
> * g++.target/i386/excess-precision-9.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/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.
>
> --- gcc/c-family/c-opts.cc.jj 2022-10-11 10:00:07.108129689 +0200
> +++ gcc/c-family/c-opts.cc 2022-10-11 10:06:55.784412531 +0200
> @@ -812,17 +812,9 @@ c_common_post_options (const char **pfil
> C_COMMON_OVERRIDE_OPTIONS;
> #endif
>
> - /* Excess precision other than "fast" requires front-end
> - support. */
> - if (c_dialect_cxx ())
> - {
> - if (flag_excess_precision == 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
> --- gcc/c-family/c-lex.cc.jj 2022-10-11 09:59:39.300518701 +0200
> +++ gcc/c-family/c-lex.cc 2022-10-11 10:06:55.784412531 +0200
> @@ -1008,10 +1008,7 @@ interpret_float (const cpp_token *token,
> 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;
>
> --- gcc/cp/parser.cc.jj 2022-10-10 11:57:40.158723041 +0200
> +++ gcc/cp/parser.cc 2022-10-11 11:56:50.815463848 +0200
> @@ -5583,7 +5583,9 @@ cp_parser_primary_expression (cp_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)
> {
> --- gcc/cp/cvt.cc.jj 2022-10-11 09:59:39.633514043 +0200
> +++ gcc/cp/cvt.cc 2022-10-11 10:06:55.785412518 +0200
> @@ -684,6 +684,33 @@ cp_convert_and_check (tree type, tree ex
> 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. */
> --- gcc/cp/call.cc.jj 2022-10-11 09:59:39.424516967 +0200
> +++ gcc/cp/call.cc 2022-10-11 10:06:55.787412490 +0200
> @@ -5374,6 +5374,7 @@ build_conditional_expr (const op_locatio
> 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;
> @@ -5407,6 +5408,9 @@ build_conditional_expr (const op_locatio
> 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);
> }
> @@ -5565,6 +5569,46 @@ build_conditional_expr (const op_locatio
> 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_CODE (arg2_type) == REAL_TYPE
> + && TREE_CODE (arg3_type) == REAL_TYPE
> + && (extended_float_type_p (arg2_type)
> + || extended_float_type_p (arg3_type))
What if semantic_result_type is error_mark_node and the other conditions
don't hold? That seems impossible, so maybe the other conditions should
move into a gcc_checking_assert? (And likewise for result_type below)
> + && cp_compare_floating_point_conversion_ranks (arg2_type,
> + arg3_type) == 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
> @@ -5572,8 +5616,6 @@ build_conditional_expr (const op_locatio
> 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
> @@ -5937,6 +5979,10 @@ build_conditional_expr (const op_locatio
> }
> }
>
> + 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);
> }
> @@ -6024,9 +6070,15 @@ build_conditional_expr (const op_locatio
> /* 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;
> }
> @@ -7890,7 +7942,7 @@ maybe_warn_array_conv (location_t loc, c
> }
>
> /* 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
> @@ -7906,7 +7958,7 @@ static tree convert_like (conversion *,
> 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;
> @@ -7983,7 +8035,8 @@ convert_like_internal (conversion *convs
> 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,
> @@ -7998,13 +8051,15 @@ convert_like_internal (conversion *convs
> {
> 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;
> }
> @@ -8124,6 +8179,8 @@ convert_like_internal (conversion *convs
>
> 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
> @@ -8163,7 +8220,8 @@ convert_like_internal (conversion *convs
> 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)
> @@ -8231,7 +8289,7 @@ convert_like_internal (conversion *convs
> 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;
>
> @@ -8493,7 +8551,15 @@ convert_like_internal (conversion *convs
> 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);
> @@ -8527,7 +8593,7 @@ conv_unsafe_in_template_p (tree to, tree
>
> 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,
> @@ -8550,7 +8616,8 @@ convert_like (conversion *convs, tree ex
> 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;
> @@ -8563,7 +8630,7 @@ convert_like (conversion *convs, tree ex
> {
> 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. */
> @@ -8574,7 +8641,7 @@ convert_like_with_context (conversion *c
> {
> 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
> @@ -8605,6 +8672,8 @@ convert_arg_to_ellipsis (tree arg, tsubs
> "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);
> }
> @@ -8911,9 +8980,9 @@ convert_for_arg_passing (tree type, tree
> /* 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)
> @@ -8932,7 +9001,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;
> }
> @@ -9764,7 +9841,7 @@ build_over_call (struct z_candidate *can
> 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);
> @@ -9772,8 +9849,12 @@ build_over_call (struct z_candidate *can
> return error_mark_node;
> }
> else if (magic != 0)
> - /* For other magic varargs only do decay_conversion. */
> - a = decay_conversion (a, complain);
> + {
> + if (magic == 1 && TREE_CODE (a) == EXCESS_PRECISION_EXPR)
> + a = TREE_OPERAND (a, 0);
It was confusing me that this mentions 1, and the magic_varargs_p
comment above mentions 2: Let's add a comment
/* Don't truncate excess precision to the semantic type. */
to clarify.
> + /* 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)))
> @@ -13051,7 +13132,7 @@ perform_direct_initialization_if_possibl
> 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);
> --- gcc/cp/constexpr.cc.jj 2022-10-11 09:59:39.481516169 +0200
> +++ gcc/cp/constexpr.cc 2022-10-11 10:06:55.788412476 +0200
> @@ -7618,6 +7618,19 @@ cxx_eval_constant_expression (const cons
> }
> 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. */
> @@ -8918,6 +8931,9 @@ potential_constant_expression_1 (tree t,
> 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))
> {
> --- gcc/cp/pt.cc.jj 2022-10-11 09:59:39.698513133 +0200
> +++ gcc/cp/pt.cc 2022-10-11 10:06:55.792412420 +0200
> @@ -17401,6 +17401,15 @@ tsubst_copy (tree t, tree args, tsubst_f
> 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)
> + return op0;
> + return build1_loc (EXPR_LOCATION (t), code, type, op0);
> + }
> +
> case COMPONENT_REF:
> {
> tree object;
> @@ -20429,6 +20438,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 (input_location, EXCESS_PRECISION_EXPR,
Why not EXPR_LOCATION (t)?
> + type, op0));
> + }
> +
> case FIX_TRUNC_EXPR:
> /* convert_like should have created an IMPLICIT_CONV_EXPR. */
> gcc_unreachable ();
> --- gcc/cp/cp-tree.h.jj 2022-10-11 09:59:39.512515736 +0200
> +++ gcc/cp/cp-tree.h 2022-10-11 10:06:55.793412406 +0200
> @@ -6766,6 +6766,8 @@ extern tree ocp_convert (tree, tree,
> 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);
> --- gcc/cp/cp-gimplify.cc.jj 2022-10-11 09:59:39.482516155 +0200
> +++ gcc/cp/cp-gimplify.cc 2022-10-11 10:06:55.794412392 +0200
> @@ -2522,6 +2522,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))
> --- gcc/cp/typeck.cc.jj 2022-10-11 09:59:39.738512574 +0200
> +++ gcc/cp/typeck.cc 2022-10-11 10:24:31.875679017 +0200
> @@ -4603,11 +4603,16 @@ convert_arguments (tree typelist, vec<tr
> }
> 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)
> + {
> + if (magic == 1 && TREE_CODE (val) == EXCESS_PRECISION_EXPR)
> + val = TREE_OPERAND (val, 0);
Same magic == 1 issue here.
> + /* 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 +5062,7 @@ cp_build_binary_op (const op_location_t
> {
> 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 +5074,10 @@ cp_build_binary_op (const op_location_t
> 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 +5125,10 @@ cp_build_binary_op (const op_location_t
> /* 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 +5180,8 @@ cp_build_binary_op (const op_location_t
> }
> }
>
> - 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 +5214,47 @@ cp_build_binary_op (const op_location_t
> 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 +5294,7 @@ cp_build_binary_op (const op_location_t
> 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 +5304,7 @@ cp_build_binary_op (const op_location_t
> 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;
> @@ -6091,6 +6145,27 @@ cp_build_binary_op (const op_location_t
> 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
> + && code0 == REAL_TYPE
> + && code1 == REAL_TYPE
> + && (extended_float_type_p (orig_type0)
> + || extended_float_type_p (orig_type1))
> + && cp_compare_floating_point_conversion_ranks (orig_type0,
> + orig_type1) == 3)
> + {
> + if (complain & tf_error)
> + {
> + rich_location richloc (line_table, location);
> + binary_op_error (&richloc, code, type0, type1);
> + }
> + return error_mark_node;
> + }
Same error_mark_node issue here.
> + }
>
> if (code == SPACESHIP_EXPR)
> {
> @@ -6181,6 +6256,8 @@ cp_build_binary_op (const op_location_t
> 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 +6345,9 @@ cp_build_binary_op (const op_location_t
> }
> }
> 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 +6443,11 @@ cp_build_binary_op (const op_location_t
> {
> 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 +6517,9 @@ cp_build_binary_op (const op_location_t
> 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 +7246,7 @@ cp_build_unary_op (enum tree_code code,
> 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 +7267,12 @@ cp_build_unary_op (enum tree_code code,
> 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 +7368,11 @@ cp_build_unary_op (enum tree_code code,
>
> 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 +7383,7 @@ cp_build_unary_op (enum tree_code code,
>
> val = unary_complex_lvalue (code, arg);
> if (val != 0)
> - return val;
> + goto return_build_unary_op;
>
> arg = mark_lvalue_use (arg);
>
> @@ -7304,8 +7399,8 @@ cp_build_unary_op (enum tree_code code,
> 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 +7563,7 @@ cp_build_unary_op (enum tree_code code,
> 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 +7579,11 @@ cp_build_unary_op (enum tree_code code,
> {
> 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 +7974,15 @@ cp_build_compound_expr (tree lhs, tree r
> 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 +7993,8 @@ cp_build_compound_expr (tree lhs, tree r
> 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 +8006,10 @@ cp_build_compound_expr (tree lhs, tree r
> 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 +8293,11 @@ build_static_cast_1 (location_t loc, tre
>
> 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 +8376,8 @@ build_static_cast_1 (location_t loc, tre
> {
> 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);
> }
>
> --- gcc/testsuite/gcc.target/i386/excess-precision-1.c.jj 2022-10-11 09:59:39.861510854 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-1.c 2022-10-11 10:06:55.795412378 +0200
> @@ -5,8 +5,14 @@
>
> #include <float.h>
>
> +#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;
> --- gcc/testsuite/gcc.target/i386/excess-precision-2.c.jj 2022-10-11 09:59:39.877510630 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-2.c 2022-10-11 10:06:55.796412364 +0200
> @@ -4,8 +4,14 @@
>
> #include <float.h>
>
> +#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;
> --- gcc/testsuite/gcc.target/i386/excess-precision-3.c.jj 2022-10-11 09:59:39.894510392 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-3.c 2022-10-11 10:06:55.796412364 +0200
> @@ -6,8 +6,14 @@
> #include <float.h>
> #include <stdarg.h>
>
> +#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);
> }
> --- gcc/testsuite/gcc.target/i386/excess-precision-7.c.jj 2022-10-11 09:59:39.917510070 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-7.c 2022-10-11 10:06:55.796412364 +0200
> @@ -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)
> --- gcc/testsuite/gcc.target/i386/excess-precision-9.c.jj 2022-10-11 09:59:39.947509650 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-9.c 2022-10-11 10:06:55.796412364 +0200
> @@ -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)
> --- gcc/testsuite/g++.target/i386/excess-precision-1.C.jj 2022-10-11 10:06:55.796412364 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-1.C 2022-10-11 10:06:55.796412364 +0200
> @@ -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"
> --- gcc/testsuite/g++.target/i386/excess-precision-2.C.jj 2022-10-11 10:06:55.796412364 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-2.C 2022-10-11 10:06:55.796412364 +0200
> @@ -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"
> --- gcc/testsuite/g++.target/i386/excess-precision-3.C.jj 2022-10-11 10:06:55.796412364 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-3.C 2022-10-11 10:06:55.796412364 +0200
> @@ -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"
> --- gcc/testsuite/g++.target/i386/excess-precision-4.C.jj 2022-10-11 10:06:55.796412364 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-4.C 2022-10-11 10:06:55.796412364 +0200
> @@ -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'" }
> --- gcc/testsuite/g++.target/i386/excess-precision-5.C.jj 2022-10-11 10:06:55.797412351 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-5.C 2022-10-11 10:06:55.797412351 +0200
> @@ -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<typename T, T v> struct integral_constant {
> + static constexpr T value = v;
> + };
> + typedef integral_constant<bool, false> false_type;
> + typedef integral_constant<bool, true> true_type;
> + template<class T, class U>
> + struct is_same : std::false_type {};
> + template <class T>
> + struct is_same<T, T> : std::true_type {};
> +}
> +
> +float f;
> +double d;
> +
> +void
> +test_types (void)
> +{
> +#define CHECK_FLOAT(E) static_assert (std::is_same <float, decltype (E)>::value, "")
> +#define CHECK_DOUBLE(E) static_assert (std::is_same <double, decltype (E)>::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);
> +}
> --- gcc/testsuite/g++.target/i386/excess-precision-6.C.jj 2022-10-11 10:06:55.797412351 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-6.C 2022-10-11 10:06:55.797412351 +0200
> @@ -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" } }
> --- gcc/testsuite/g++.target/i386/excess-precision-7.C.jj 2022-10-11 10:06:55.797412351 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-7.C 2022-10-11 10:06:55.797412351 +0200
> @@ -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"
> --- gcc/testsuite/g++.target/i386/excess-precision-8.C.jj 2022-10-11 10:06:55.797412351 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-8.C 2022-10-11 10:06:55.797412351 +0200
> @@ -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 <float.h>
> +#include <stdarg.h>
> +
> +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<S> (f1 + f2);
> + T t2 = static_cast<T> (f1 + f2);
> + T t3 = static_cast<T> (d1 + d2 + d3);
> +}
> +
> +int
> +main ()
> +{
> + test_call ();
> +}
> --- gcc/testsuite/g++.target/i386/excess-precision-9.C.jj 2022-10-11 10:06:55.797412351 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-9.C 2022-10-11 10:06:55.797412351 +0200
> @@ -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"
> --- gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c.jj 2020-01-12 11:54:37.003404507 +0100
> +++ gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c 2022-10-11 11:20:40.511702027 +0200
> @@ -1,4 +1,5 @@
> /* This test assumes IEEE float and double. */
> +/* { dg-additional-options "-fexcess-precision=fast" } */
>
> #include "convert.h"
>
> --- gcc/testsuite/c-c++-common/dfp/compare-eq-const.c.jj 2020-01-12 11:54:37.003404507 +0100
> +++ gcc/testsuite/c-c++-common/dfp/compare-eq-const.c 2022-10-11 11:18:31.432502023 +0200
> @@ -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"
>
> --- gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C.jj 2020-09-03 20:11:34.160825160 +0200
> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C 2022-10-11 11:51:31.563910365 +0200
> @@ -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;
> --- gcc/testsuite/g++.dg/cpp1z/decomp12.C.jj 2020-01-12 11:54:37.128402621 +0100
> +++ gcc/testsuite/g++.dg/cpp1z/decomp12.C 2022-10-11 11:30:05.737825169 +0200
> @@ -7,13 +7,13 @@ template <typename, typename> struct sam
> template <typename T> struct same_type<T, T> {};
>
> 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<std::tuple_element<0, decltype(tuple)>::type, decltype(i)>{};
> same_type<decltype(i), int>{};
> same_type<decltype(c), char>{};
> same_type<decltype(d), double>{};
> same_type<decltype(b), bool>{};
> - if (i != 1 || c != 'a' || d != 2.3 || b != true)
> + if (i != 1 || c != 'a' || d != 2.25 || b != true)
> __builtin_abort ();
> }
> --- gcc/testsuite/g++.dg/other/thunk1.C.jj 2020-01-12 11:54:37.218401263 +0100
> +++ gcc/testsuite/g++.dg/other/thunk1.C 2022-10-11 11:30:37.350384645 +0200
> @@ -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);
>
> --- gcc/testsuite/g++.dg/vect/pr64410.cc.jj 2020-01-12 11:54:37.279400343 +0100
> +++ gcc/testsuite/g++.dg/vect/pr64410.cc 2022-10-11 11:31:54.970302984 +0200
> @@ -1,5 +1,6 @@
> // { dg-do compile }
> // { dg-require-effective-target vect_double }
> +// { dg-additional-options "-fexcess-precision=fast" }
>
> #include <vector>
> #include <complex>
> --- gcc/testsuite/g++.dg/cpp1y/pr68180.C.jj 2020-01-12 11:54:37.121402727 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/pr68180.C 2022-10-11 11:50:42.324596315 +0200
> @@ -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) {
> --- gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C.jj 2022-05-20 11:45:17.801744787 +0200
> +++ gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C 2022-10-11 11:28:34.282099648 +0200
> @@ -1,4 +1,5 @@
> // { dg-do run { target c++11 } }
> +// { dg-additional-options "-fexcess-precision=fast" }
> // An implementation of TR1's <tuple> using variadic teplates
> // Contributed by Douglas Gregor <doug.gregor@gmail.com>
>
> --- gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C.jj 2020-01-12 11:54:37.087403240 +0100
> +++ gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C 2022-10-11 11:27:50.009716597 +0200
> @@ -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();
> }
> --- gcc/testsuite/g++.old-deja/g++.brendan/copy9.C.jj 2020-01-11 16:31:54.872295939 +0100
> +++ gcc/testsuite/g++.old-deja/g++.brendan/copy9.C 2022-10-11 11:10:40.589067853 +0200
> @@ -1,4 +1,5 @@
> // { dg-do run }
> +// { dg-additional-options "-fexcess-precision=fast" }
> // GROUPS passed copy-ctors
> #include <iostream>
>
> --- gcc/testsuite/g++.old-deja/g++.brendan/overload7.C.jj 2020-01-11 16:31:54.877295864 +0100
> +++ gcc/testsuite/g++.old-deja/g++.brendan/overload7.C 2022-10-11 11:11:45.238166331 +0200
> @@ -1,4 +1,5 @@
> // { dg-do run }
> +// { dg-additional-options "-fexcess-precision=fast" }
> // GROUPS passed overloading
> extern "C" int printf (const char *, ...);
>
>
> Jakub
>
next prev parent reply other threads:[~2022-10-12 18:08 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-11 13:33 Jakub Jelinek
2022-10-12 18:08 ` Jason Merrill [this message]
2022-10-13 16:40 ` [PATCH] c++, v2: " Jakub Jelinek
2022-10-13 19:28 ` Jason Merrill
2022-10-12 18:17 ` [PATCH] c++: " Marek Polacek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f96ebb2a-47f8-7211-8a3c-990bd1263117@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jakub@redhat.com \
--cc=joseph@codesourcery.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).