public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
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
> 


  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).