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>
Cc: "Joseph S. Myers" <joseph@codesourcery.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++, v2: Implement excess precision support for C++ [PR107097, PR323]
Date: Thu, 13 Oct 2022 15:28:02 -0400	[thread overview]
Message-ID: <c76a957a-c06a-0ce0-2d33-598213a5d041@redhat.com> (raw)
In-Reply-To: <Y0g/g0JYbV33TZiW@tucnak>

On 10/13/22 12:40, Jakub Jelinek wrote:
> On Wed, Oct 12, 2022 at 02:08:20PM -0400, Jason Merrill wrote:
>>> 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.
> 
> Ok, so that it is more readable and say if we decide to make e.g. C++98
> behave like C99 and only C++11 and later like C11, I'm sending this as
> a 2 patch series, this patch is just an updated version of the previous
> patch (your review comments, Marek's mail and missed changes to
> doc/invoke.texi) and another mail will be upgrade of this to the C11
> behavior.
> 
>>> +      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)
> 
> Changed in all places to an assert, though previously I missed
> that cp_common_type on complex type(s) could have similar problem.
> 
>>> @@ -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
> 
> That is because removing excess precision means keeping
> EXCESS_PRECISION_EXPR around and preserving excess precision
> means removing of EXCESS_PRECISION_EXPR.
> 
>>
>>   /* Don't truncate excess precision to the semantic type.  */
>>
>> to clarify.
> 
> Ok.
> 
> Here is an updated patch, bootstrapped/regtested on x86_64-linux and
> i686-linux, ok for trunk?
> 
> 2022-10-13  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR middle-end/323
> 	PR c++/107097
> gcc/
> 	* doc/invoke.texi (-fexcess-precision=standard): Mention that the
> 	option now also works in C++.
> gcc/c-family/
> 	* c-common.def (EXCESS_PRECISION_EXPR): Remove comment part about
> 	the tree being specific to C/ObjC.
> 	* c-opts.cc (c_common_post_options): Handle flag_excess_precision
> 	in C++ the same as in C.
> 	* c-lex.cc (interpret_float): Set const_type to excess_precision ()
> 	even for C++.
> gcc/cp/
> 	* parser.cc (cp_parser_primary_expression): Handle
> 	EXCESS_PRECISION_EXPR with REAL_CST operand the same as REAL_CST.
> 	* cvt.cc (cp_ep_convert_and_check): New function.
> 	* call.cc (build_conditional_expr): Add excess precision support.
> 	When type_after_usual_arithmetic_conversions returns error_mark_node,
> 	use gcc_checking_assert that it is because of uncomparable floating
> 	point ranks instead of checking all those conditions and make it
> 	work also with complex types.
> 	(convert_like_internal): Likewise.  Add NESTED_P argument, pass true
> 	to recursive calls to convert_like.
> 	(convert_like): Add NESTED_P argument, pass it through to
> 	convert_like_internal.  For other overload pass false to it.
> 	(convert_like_with_context): Pass false to NESTED_P.
> 	(convert_arg_to_ellipsis): Add excess precision support.
> 	(magic_varargs_p): For __builtin_is{finite,inf,inf_sign,nan,normal}
> 	and __builtin_fpclassify return 2 instead of 1, document what it
> 	means.
> 	(build_over_call): Don't handle former magic 2 which is no longer
> 	used, instead for magic 1 remove EXCESS_PRECISION_EXPR.
> 	(perform_direct_initialization_if_possible): Pass false to NESTED_P
> 	convert_like argument.
> 	* constexpr.cc (cxx_eval_constant_expression): Handle
> 	EXCESS_PRECISION_EXPR.
> 	(potential_constant_expression_1): Likewise.
> 	* pt.cc (tsubst_copy, tsubst_copy_and_build): Likewise.
> 	* cp-tree.h (cp_ep_convert_and_check): Declare.
> 	* cp-gimplify.cc (cp_fold): Handle EXCESS_PRECISION_EXPR.
> 	* typeck.cc (cp_common_type): For COMPLEX_TYPEs, return error_mark_node
> 	if recursive call returned it.
> 	(convert_arguments): For magic 1 remove EXCESS_PRECISION_EXPR.
> 	(cp_build_binary_op): Add excess precision support.  When
> 	cp_common_type returns error_mark_node, use gcc_checking_assert that
> 	it is because of uncomparable floating point ranks instead of checking
> 	all those conditions and make it work also with complex types.
> 	(cp_build_unary_op): Likewise.
> 	(cp_build_compound_expr): Likewise.
> 	(build_static_cast_1): Remove EXCESS_PRECISION_EXPR.
> gcc/testsuite/
> 	* gcc.target/i386/excess-precision-1.c: For C++ wrap abort and
> 	exit declarations into extern "C" block.
> 	* gcc.target/i386/excess-precision-2.c: Likewise.
> 	* gcc.target/i386/excess-precision-3.c: Likewise.  Remove
> 	check_float_nonproto and check_double_nonproto tests for C++.
> 	* gcc.target/i386/excess-precision-7.c: For C++ wrap abort and
> 	exit declarations into extern "C" block.
> 	* gcc.target/i386/excess-precision-9.c: Likewise.
> 	* g++.target/i386/excess-precision-1.C: New test.
> 	* g++.target/i386/excess-precision-2.C: New test.
> 	* g++.target/i386/excess-precision-3.C: New test.
> 	* g++.target/i386/excess-precision-4.C: New test.
> 	* g++.target/i386/excess-precision-5.C: New test.
> 	* g++.target/i386/excess-precision-6.C: New test.
> 	* g++.target/i386/excess-precision-7.C: New test.
> 	* g++.target/i386/excess-precision-9.C: New test.
> 	* g++.target/i386/excess-precision-11.C: New test.
> 	* c-c++-common/dfp/convert-bfp-10.c: Add -fexcess-precision=fast
> 	as dg-additional-options.
> 	* c-c++-common/dfp/compare-eq-const.c: Likewise.
> 	* g++.dg/cpp1z/constexpr-96862.C: Likewise.
> 	* g++.dg/cpp1z/decomp12.C (main): Use 2.25 instead of 2.3 to
> 	avoid excess precision differences.
> 	* g++.dg/other/thunk1.C: Add -fexcess-precision=fast
> 	as dg-additional-options.
> 	* g++.dg/vect/pr64410.cc: Likewise.
> 	* g++.dg/cpp1y/pr68180.C: Likewise.
> 	* g++.dg/vect/pr89653.cc: Likewise.
> 	* g++.dg/cpp0x/variadic-tuple.C: Likewise.
> 	* g++.dg/cpp0x/nsdmi-union1.C: Use 4.25 instead of 4.2 to
> 	avoid excess precision differences.
> 	* g++.old-deja/g++.brendan/copy9.C: Add -fexcess-precision=fast
> 	as dg-additional-options.
> 	* g++.old-deja/g++.brendan/overload7.C: Likewise.
> 
> --- gcc/doc/invoke.texi.jj	2022-10-12 22:06:36.029279569 +0200
> +++ gcc/doc/invoke.texi	2022-10-13 16:49:19.313221247 +0200
> @@ -13785,18 +13785,18 @@ default, @option{-fexcess-precision=fast
>   operations may be carried out in a wider precision than the types specified
>   in the source if that would result in faster code, and it is unpredictable
>   when rounding to the types specified in the source code takes place.
> -When compiling C, if @option{-fexcess-precision=standard} is specified then
> -excess precision follows the rules specified in ISO C99; in particular,
> +When compiling C or C++, if @option{-fexcess-precision=standard} is specified
> +then excess precision follows the rules specified in ISO C99 or C++; in particular,
>   both casts and assignments cause values to be rounded to their
>   semantic types (whereas @option{-ffloat-store} only affects
> -assignments).  This option is enabled by default for C if a strict
> -conformance option such as @option{-std=c99} is used.
> +assignments).  This option is enabled by default for C or C++ if a strict
> +conformance option such as @option{-std=c99} or @option{-std=c++17} is used.
>   @option{-ffast-math} enables @option{-fexcess-precision=fast} by default
>   regardless of whether a strict conformance option is used.
>   
>   @opindex mfpmath
>   @option{-fexcess-precision=standard} is not implemented for languages
> -other than C.  On the x86, it has no effect if @option{-mfpmath=sse}
> +other than C or C++.  On the x86, it has no effect if @option{-mfpmath=sse}
>   or @option{-mfpmath=sse+387} is specified; in the former case, IEEE
>   semantics apply without excess precision, and in the latter, rounding
>   is unpredictable.
> --- gcc/c-family/c-common.def.jj	2022-01-11 22:31:40.595769716 +0100
> +++ gcc/c-family/c-common.def	2022-10-13 16:44:13.408422330 +0200
> @@ -38,10 +38,9 @@ along with GCC; see the file COPYING3.
>      not.  */
>   DEFTREECODE (C_MAYBE_CONST_EXPR, "c_maybe_const_expr", tcc_expression, 2)
>   
> -/* An EXCESS_PRECISION_EXPR, currently only used for C and Objective
> -   C, represents an expression evaluated in greater range or precision
> -   than its type.  The type of the EXCESS_PRECISION_EXPR is the
> -   semantic type while the operand represents what is actually being
> +/* An EXCESS_PRECISION_EXPR represents an expression evaluated in greater
> +   range or precision than its type.  The type of the EXCESS_PRECISION_EXPR
> +   is the semantic type while the operand represents what is actually being
>      evaluated.  */
>   DEFTREECODE (EXCESS_PRECISION_EXPR, "excess_precision_expr", tcc_expression, 1)
>   
> --- gcc/c-family/c-opts.cc.jj	2022-10-11 14:49:41.396226397 +0200
> +++ gcc/c-family/c-opts.cc	2022-10-13 09:35:27.829243895 +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 14:49:41.328227329 +0200
> +++ gcc/c-family/c-lex.cc	2022-10-13 09:35:27.900242917 +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-13 08:40:37.991533161 +0200
> +++ gcc/cp/parser.cc	2022-10-13 09:35:27.912242752 +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 14:49:41.705222167 +0200
> +++ gcc/cp/cvt.cc	2022-10-13 09:35:27.956242146 +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-13 08:41:04.735165185 +0200
> +++ gcc/cp/call.cc	2022-10-13 16:01:37.241523281 +0200
> @@ -5359,6 +5359,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;
> @@ -5392,6 +5393,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);
>       }
> @@ -5550,6 +5554,52 @@ 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 t1 = arg2_type;
> +	  tree t2 = arg3_type;
> +	  if (TREE_CODE (t1) == COMPLEX_TYPE)
> +	    t1 = TREE_TYPE (t1);
> +	  if (TREE_CODE (t2) == COMPLEX_TYPE)
> +	    t2 = TREE_TYPE (t2);
> +	  gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE
> +			       && TREE_CODE (t2) == REAL_TYPE
> +			       && (extended_float_type_p (t1)
> +				   || extended_float_type_p (t2))
> +			       && cp_compare_floating_point_conversion_ranks
> +				    (t1, t2) == 3);
> +	  if (complain & tf_error)
> +	    error_at (loc, "operands to %<?:%> of types %qT and %qT "
> +			   "have unordered conversion rank",
> +		      arg2_type, arg3_type);
> +	  return error_mark_node;
> +	}
> +      if (TREE_CODE (arg2) == EXCESS_PRECISION_EXPR)
> +	{
> +	  arg2 = TREE_OPERAND (arg2, 0);
> +	  arg2_type = TREE_TYPE (arg2);
> +	}
> +      if (TREE_CODE (arg3) == EXCESS_PRECISION_EXPR)
> +	{
> +	  arg3 = TREE_OPERAND (arg3, 0);
> +	  arg3_type = TREE_TYPE (arg3);
> +	}
> +    }
> +
>     /* [expr.cond]
>   
>        If either the second or the third operand has type (possibly
> @@ -5557,8 +5607,6 @@ build_conditional_expr (const op_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
> @@ -5850,14 +5898,20 @@ build_conditional_expr (const op_locatio
>         /* In this case, there is always a common type.  */
>         result_type = type_after_usual_arithmetic_conversions (arg2_type,
>   							     arg3_type);
> -      if (result_type == error_mark_node
> -	  && TREE_CODE (arg2_type) == REAL_TYPE
> -	  && TREE_CODE (arg3_type) == REAL_TYPE
> -	  && (extended_float_type_p (arg2_type)
> -	      || extended_float_type_p (arg3_type))
> -	  && cp_compare_floating_point_conversion_ranks (arg2_type,
> -							 arg3_type) == 3)
> +      if (result_type == error_mark_node)
>   	{
> +	  tree t1 = arg2_type;
> +	  tree t2 = arg3_type;
> +	  if (TREE_CODE (t1) == COMPLEX_TYPE)
> +	    t1 = TREE_TYPE (t1);
> +	  if (TREE_CODE (t2) == COMPLEX_TYPE)
> +	    t2 = TREE_TYPE (t2);
> +	  gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE
> +			       && TREE_CODE (t2) == REAL_TYPE
> +			       && (extended_float_type_p (t1)
> +				   || extended_float_type_p (t2))
> +			       && cp_compare_floating_point_conversion_ranks
> +				    (t1, t2) == 3);
>   	  if (complain & tf_error)
>   	    error_at (loc, "operands to %<?:%> of types %qT and %qT "
>   			   "have unordered conversion rank",
> @@ -5922,6 +5976,10 @@ build_conditional_expr (const op_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);
>       }
> @@ -6009,9 +6067,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;
>   }
> @@ -7875,7 +7939,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
> @@ -7891,7 +7955,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;
> @@ -7968,7 +8032,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,
> @@ -7983,13 +8048,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;
>   	}
> @@ -8109,6 +8176,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
> @@ -8148,7 +8217,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)
> @@ -8216,7 +8286,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;
>   
> @@ -8475,7 +8545,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);
> @@ -8509,7 +8587,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,
> @@ -8532,7 +8610,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;
> @@ -8545,7 +8624,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.  */
> @@ -8556,7 +8635,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
> @@ -8587,6 +8666,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);
>       }
> @@ -8893,9 +8974,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)
> @@ -8914,7 +8995,15 @@ magic_varargs_p (tree fn)
>         case BUILT_IN_MUL_OVERFLOW_P:
>   	return 3;
>   
> -      default:;
> +      case BUILT_IN_ISFINITE:
> +      case BUILT_IN_ISINF:
> +      case BUILT_IN_ISINF_SIGN:
> +      case BUILT_IN_ISNAN:
> +      case BUILT_IN_ISNORMAL:
> +      case BUILT_IN_FPCLASSIFY:
> +	return 2;
> +
> +      default:
>   	return lookup_attribute ("type generic",
>   				 TYPE_ATTRIBUTES (TREE_TYPE (fn))) != 0;
>         }
> @@ -9717,7 +9806,7 @@ build_over_call (struct z_candidate *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);
> @@ -9725,8 +9814,13 @@ 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);
> +	{
> +	  /* Don't truncate excess precision to the semantic type.  */
> +	  if (magic == 1 && TREE_CODE (a) == EXCESS_PRECISION_EXPR)
> +	    a = TREE_OPERAND (a, 0);
> +	  /* For other magic varargs only do decay_conversion.  */
> +	  a = decay_conversion (a, complain);
> +	}
>         else if (DECL_CONSTRUCTOR_P (fn)
>   	       && same_type_ignoring_top_level_qualifiers_p (DECL_CONTEXT (fn),
>   							     TREE_TYPE (a)))
> @@ -13004,7 +13098,7 @@ perform_direct_initialization_if_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-12 17:51:00.909944772 +0200
> +++ gcc/cp/constexpr.cc	2022-10-13 09:35:27.989241691 +0200
> @@ -7598,6 +7598,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.  */
> @@ -8898,6 +8911,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-13 08:40:38.010532899 +0200
> +++ gcc/cp/pt.cc	2022-10-13 09:46:41.112966988 +0200
> @@ -17412,6 +17412,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;

I notice you're assuming this will have the right type, which seems 
likely, but I wonder about either checking_asserting that or setting its 
type to the one we just tsubsted?  OK with or without that change, along 
with the followup patch.

> +	return build1_loc (EXPR_LOCATION (t), code, type, op0);
> +      }
> +
>       case COMPONENT_REF:
>         {
>   	tree object;
> @@ -20440,6 +20449,16 @@ tsubst_copy_and_build (tree t,
>   				templated_operator_saved_lookups (t),
>   				complain|decltype_flag));
>   
> +    case EXCESS_PRECISION_EXPR:
> +      {
> +	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> +	tree op0 = RECUR (TREE_OPERAND (t, 0));
> +	if (TREE_CODE (op0) == EXCESS_PRECISION_EXPR)
> +	  RETURN (op0);
> +	RETURN (build1_loc (EXPR_LOCATION (t), EXCESS_PRECISION_EXPR,
> +			    type, op0));
> +      }
> +
>       case FIX_TRUNC_EXPR:
>         /* convert_like should have created an IMPLICIT_CONV_EXPR.  */
>         gcc_unreachable ();
> --- gcc/cp/cp-tree.h.jj	2022-10-13 08:41:04.737165157 +0200
> +++ gcc/cp/cp-tree.h	2022-10-13 09:35:27.999241554 +0200
> @@ -6793,6 +6793,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-13 08:40:37.954533670 +0200
> +++ gcc/cp/cp-gimplify.cc	2022-10-13 09:35:28.000241540 +0200
> @@ -2515,6 +2515,11 @@ cp_fold (tree x)
>   
>         break;
>   
> +    case EXCESS_PRECISION_EXPR:
> +      op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops);
> +      x = fold_convert_loc (EXPR_LOCATION (x), TREE_TYPE (x), op0);
> +      break;
> +
>       case INDIRECT_REF:
>         /* We don't need the decltype(auto) obfuscation anymore.  */
>         if (REF_PARENTHESIZED_P (x))
> --- gcc/cp/typeck.cc.jj	2022-10-13 08:41:04.780164565 +0200
> +++ gcc/cp/typeck.cc	2022-10-13 18:36:08.223309058 +0200
> @@ -439,6 +439,8 @@ cp_common_type (tree t1, tree t2)
>         tree subtype
>   	= type_after_usual_arithmetic_conversions (subtype1, subtype2);
>   
> +      if (subtype == error_mark_node)
> +	return subtype;
>         if (code1 == COMPLEX_TYPE && TREE_TYPE (t1) == subtype)
>   	return build_type_attribute_variant (t1, attributes);
>         else if (code2 == COMPLEX_TYPE && TREE_TYPE (t2) == subtype)
> @@ -4603,11 +4605,17 @@ convert_arguments (tree typelist, vec<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)
> +	    {
> +	      /* Don't truncate excess precision to the semantic type.  */
> +	      if (magic == 1 && TREE_CODE (val) == EXCESS_PRECISION_EXPR)
> +		val = TREE_OPERAND (val, 0);
> +	      /* Don't do ellipsis conversion for __built_in_constant_p
> +		 as this will result in spurious errors for non-trivial
> +		 types.  */
> +	      val = require_complete_type (val, complain);
> +	    }
>   	  else
>   	    val = convert_arg_to_ellipsis (val, complain);
>   
> @@ -5057,7 +5065,7 @@ cp_build_binary_op (const op_location_t
>   {
>     tree op0, op1;
>     enum tree_code code0, code1;
> -  tree type0, type1;
> +  tree type0, type1, orig_type0, orig_type1;
>     const char *invalid_op_diag;
>   
>     /* Expression code to give to the expression when it is built.
> @@ -5069,6 +5077,10 @@ cp_build_binary_op (const op_location_t
>        In the simplest cases this is the common type of the arguments.  */
>     tree result_type = NULL_TREE;
>   
> +  /* When the computation is in excess precision, the type of the
> +     final EXCESS_PRECISION_EXPR.  */
> +  tree semantic_result_type = NULL;
> +
>     /* Nonzero means operands have already been type-converted
>        in whatever way is necessary.
>        Zero means they need to be converted to RESULT_TYPE.  */
> @@ -5116,6 +5128,10 @@ cp_build_binary_op (const op_location_t
>     /* Tree holding instrumentation expression.  */
>     tree instrument_expr = NULL_TREE;
>   
> +  /* True means this is an arithmetic operation that may need excess
> +     precision.  */
> +  bool may_need_excess_precision;
> +
>     /* Apply default conversions.  */
>     op0 = resolve_nondeduced_context (orig_op0, complain);
>     op1 = resolve_nondeduced_context (orig_op1, complain);
> @@ -5167,8 +5183,8 @@ cp_build_binary_op (const op_location_t
>   	}
>       }
>   
> -  type0 = TREE_TYPE (op0);
> -  type1 = TREE_TYPE (op1);
> +  orig_type0 = type0 = TREE_TYPE (op0);
> +  orig_type1 = type1 = TREE_TYPE (op1);
>   
>     /* The expression codes of the data types of the arguments tell us
>        whether the arguments are integers, floating, pointers, etc.  */
> @@ -5201,6 +5217,47 @@ cp_build_binary_op (const op_location_t
>         return error_mark_node;
>       }
>   
> +  switch (code)
> +    {
> +    case PLUS_EXPR:
> +    case MINUS_EXPR:
> +    case MULT_EXPR:
> +    case TRUNC_DIV_EXPR:
> +    case CEIL_DIV_EXPR:
> +    case FLOOR_DIV_EXPR:
> +    case ROUND_DIV_EXPR:
> +    case EXACT_DIV_EXPR:
> +      may_need_excess_precision = true;
> +      break;
> +    default:
> +      may_need_excess_precision = false;
> +      break;
> +    }
> +  if (TREE_CODE (op0) == EXCESS_PRECISION_EXPR)
> +    {
> +      op0 = TREE_OPERAND (op0, 0);
> +      type0 = TREE_TYPE (op0);
> +    }
> +  else if (may_need_excess_precision
> +	   && (code0 == REAL_TYPE || code0 == COMPLEX_TYPE))
> +    if (tree eptype = excess_precision_type (type0))
> +      {
> +	type0 = eptype;
> +	op0 = convert (eptype, op0);
> +      }
> +  if (TREE_CODE (op1) == EXCESS_PRECISION_EXPR)
> +    {
> +      op1 = TREE_OPERAND (op1, 0);
> +      type1 = TREE_TYPE (op1);
> +    }
> +  else if (may_need_excess_precision
> +	   && (code1 == REAL_TYPE || code1 == COMPLEX_TYPE))
> +    if (tree eptype = excess_precision_type (type1))
> +      {
> +	type1 = eptype;
> +	op1 = convert (eptype, op1);
> +      }
> +
>     /* Issue warnings about peculiar, but valid, uses of NULL.  */
>     if ((null_node_p (orig_op0) || null_node_p (orig_op1))
>         /* It's reasonable to use pointer values as operands of &&
> @@ -5240,7 +5297,7 @@ cp_build_binary_op (const op_location_t
>                 op0 = convert (TREE_TYPE (type1), op0);
>   	      op0 = save_expr (op0);
>                 op0 = build_vector_from_val (type1, op0);
> -              type0 = TREE_TYPE (op0);
> +	      orig_type0 = type0 = TREE_TYPE (op0);
>                 code0 = TREE_CODE (type0);
>                 converted = 1;
>                 break;
> @@ -5250,7 +5307,7 @@ cp_build_binary_op (const op_location_t
>                 op1 = convert (TREE_TYPE (type0), op1);
>   	      op1 = save_expr (op1);
>                 op1 = build_vector_from_val (type0, op1);
> -              type1 = TREE_TYPE (op1);
> +	      orig_type1 = type1 = TREE_TYPE (op1);
>                 code1 = TREE_CODE (type1);
>                 converted = 1;
>                 break;
> @@ -6067,12 +6124,20 @@ cp_build_binary_op (const op_location_t
>         && (shorten || common || short_compare))
>       {
>         result_type = cp_common_type (type0, type1);
> -      if (result_type == error_mark_node
> -	  && code0 == REAL_TYPE
> -	  && code1 == REAL_TYPE
> -	  && (extended_float_type_p (type0) || extended_float_type_p (type1))
> -	  && cp_compare_floating_point_conversion_ranks (type0, type1) == 3)
> +      if (result_type == error_mark_node)
>   	{
> +	  tree t1 = type0;
> +	  tree t2 = type1;
> +	  if (TREE_CODE (t1) == COMPLEX_TYPE)
> +	    t1 = TREE_TYPE (t1);
> +	  if (TREE_CODE (t2) == COMPLEX_TYPE)
> +	    t2 = TREE_TYPE (t2);
> +	  gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE
> +			       && TREE_CODE (t2) == REAL_TYPE
> +			       && (extended_float_type_p (t1)
> +				   || extended_float_type_p (t2))
> +			       && cp_compare_floating_point_conversion_ranks
> +				    (t1, t2) == 3);
>   	  if (complain & tf_error)
>   	    {
>   	      rich_location richloc (line_table, location);
> @@ -6091,6 +6156,33 @@ cp_build_binary_op (const op_location_t
>   				    TREE_TYPE (orig_op1));
>   	}
>       }
> +  if (may_need_excess_precision
> +      && (orig_type0 != type0 || orig_type1 != type1))
> +    {
> +      gcc_assert (common);
> +      semantic_result_type = cp_common_type (orig_type0, orig_type1);
> +      if (semantic_result_type == error_mark_node)
> +	{
> +	  tree t1 = orig_type0;
> +	  tree t2 = orig_type1;
> +	  if (TREE_CODE (t1) == COMPLEX_TYPE)
> +	    t1 = TREE_TYPE (t1);
> +	  if (TREE_CODE (t2) == COMPLEX_TYPE)
> +	    t2 = TREE_TYPE (t2);
> +	  gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE
> +			       && TREE_CODE (t2) == REAL_TYPE
> +			       && (extended_float_type_p (t1)
> +				   || extended_float_type_p (t2))
> +			       && cp_compare_floating_point_conversion_ranks
> +				    (t1, t2) == 3);
> +	  if (complain & tf_error)
> +	    {
> +	      rich_location richloc (line_table, location);
> +	      binary_op_error (&richloc, code, type0, type1);
> +	    }
> +	  return error_mark_node;
> +	}
> +    }
>   
>     if (code == SPACESHIP_EXPR)
>       {
> @@ -6181,6 +6273,8 @@ cp_build_binary_op (const op_location_t
>   			 build_type ? build_type : result_type,
>   			 NULL_TREE, op1);
>         TREE_OPERAND (tmp, 0) = op0;
> +      if (semantic_result_type)
> +	tmp = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, tmp);
>         return tmp;
>       }
>   
> @@ -6268,6 +6362,9 @@ cp_build_binary_op (const op_location_t
>   		}
>   	    }
>   	  result = build2 (COMPLEX_EXPR, result_type, real, imag);
> +	  if (semantic_result_type)
> +	    result = build1 (EXCESS_PRECISION_EXPR, semantic_result_type,
> +			     result);
>   	  return result;
>   	}
>   
> @@ -6363,9 +6460,11 @@ cp_build_binary_op (const op_location_t
>       {
>         warning_sentinel w (warn_sign_conversion, short_compare);
>         if (!same_type_p (TREE_TYPE (op0), result_type))
> -	op0 = cp_convert_and_check (result_type, op0, complain);
> +	op0 = cp_ep_convert_and_check (result_type, op0,
> +				       semantic_result_type, complain);
>         if (!same_type_p (TREE_TYPE (op1), result_type))
> -	op1 = cp_convert_and_check (result_type, op1, complain);
> +	op1 = cp_ep_convert_and_check (result_type, op1,
> +				       semantic_result_type, complain);
>   
>         if (op0 == error_mark_node || op1 == error_mark_node)
>   	return error_mark_node;
> @@ -6435,6 +6534,9 @@ cp_build_binary_op (const op_location_t
>     if (resultcode == SPACESHIP_EXPR && !processing_template_decl)
>       result = get_target_expr (result, complain);
>   
> +  if (semantic_result_type)
> +    result = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, result);
> +
>     if (!c_inhibit_evaluation_warnings)
>       {
>         if (!processing_template_decl)
> @@ -7161,6 +7263,7 @@ cp_build_unary_op (enum tree_code code,
>     tree arg = xarg;
>     location_t location = cp_expr_loc_or_input_loc (arg);
>     tree argtype = 0;
> +  tree eptype = NULL_TREE;
>     const char *errstring = NULL;
>     tree val;
>     const char *invalid_op_diag;
> @@ -7181,6 +7284,12 @@ cp_build_unary_op (enum tree_code code,
>         return error_mark_node;
>       }
>   
> +  if (TREE_CODE (arg) == EXCESS_PRECISION_EXPR)
> +    {
> +      eptype = TREE_TYPE (arg);
> +      arg = TREE_OPERAND (arg, 0);
> +    }
> +
>     switch (code)
>       {
>       case UNARY_PLUS_EXPR:
> @@ -7276,8 +7385,11 @@ cp_build_unary_op (enum tree_code code,
>   
>       case REALPART_EXPR:
>       case IMAGPART_EXPR:
> -      arg = build_real_imag_expr (input_location, code, arg);
> -      return arg;
> +      val = build_real_imag_expr (input_location, code, arg);
> +      if (eptype && TREE_CODE (eptype) == COMPLEX_EXPR)
> +	val = build1_loc (input_location, EXCESS_PRECISION_EXPR,
> +			  TREE_TYPE (eptype), val);
> +      return val;
>   
>       case PREINCREMENT_EXPR:
>       case POSTINCREMENT_EXPR:
> @@ -7288,7 +7400,7 @@ cp_build_unary_op (enum tree_code code,
>   
>         val = unary_complex_lvalue (code, arg);
>         if (val != 0)
> -	return val;
> +	goto return_build_unary_op;
>   
>         arg = mark_lvalue_use (arg);
>   
> @@ -7304,8 +7416,8 @@ cp_build_unary_op (enum tree_code code,
>   	  real = cp_build_unary_op (code, real, true, complain);
>   	  if (real == error_mark_node || imag == error_mark_node)
>   	    return error_mark_node;
> -	  return build2 (COMPLEX_EXPR, TREE_TYPE (arg),
> -			 real, imag);
> +	  val = build2 (COMPLEX_EXPR, TREE_TYPE (arg), real, imag);
> +	  goto return_build_unary_op;
>   	}
>   
>         /* Report invalid types.  */
> @@ -7468,7 +7580,7 @@ cp_build_unary_op (enum tree_code code,
>   	  val = build2 (code, TREE_TYPE (arg), arg, inc);
>   
>   	TREE_SIDE_EFFECTS (val) = 1;
> -	return val;
> +	goto return_build_unary_op;
>         }
>   
>       case ADDR_EXPR:
> @@ -7484,7 +7596,11 @@ cp_build_unary_op (enum tree_code code,
>       {
>         if (argtype == 0)
>   	argtype = TREE_TYPE (arg);
> -      return build1 (code, argtype, arg);
> +      val = build1 (code, argtype, arg);
> +    return_build_unary_op:
> +      if (eptype)
> +	val = build1 (EXCESS_PRECISION_EXPR, eptype, val);
> +      return val;
>       }
>   
>     if (complain & tf_error)
> @@ -7875,6 +7991,15 @@ cp_build_compound_expr (tree lhs, tree 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 +8010,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 +8023,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 +8310,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 +8393,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 14:49:42.367213104 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-1.c	2022-10-13 09:35:28.016241320 +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 14:49:42.439212118 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-2.c	2022-10-13 09:35:28.037241031 +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 14:49:42.456211885 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-3.c	2022-10-13 09:35:28.054240796 +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 14:49:42.456211885 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-7.c	2022-10-13 09:35:28.070240576 +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 14:49:42.542210707 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-9.c	2022-10-13 09:35:28.078240466 +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-13 09:35:28.078240466 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-1.C	2022-10-13 09:35:28.078240466 +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-13 09:35:28.079240452 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-2.C	2022-10-13 09:35:28.079240452 +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-13 09:35:28.079240452 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-3.C	2022-10-13 09:35:28.079240452 +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-13 09:35:28.079240452 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-4.C	2022-10-13 09:35:28.079240452 +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-13 09:35:28.079240452 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-5.C	2022-10-13 09:35:28.079240452 +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-13 09:35:28.080240438 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-6.C	2022-10-13 09:35:28.080240438 +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-13 09:35:28.080240438 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-7.C	2022-10-13 09:35:28.080240438 +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-9.C.jj	2022-10-13 09:35:28.080240438 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-9.C	2022-10-13 09:35:28.080240438 +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/g++.target/i386/excess-precision-11.C.jj	2022-10-13 09:35:28.080240438 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-11.C	2022-10-13 09:35:28.080240438 +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/c-c++-common/dfp/convert-bfp-10.c.jj	2022-10-11 14:49:42.046217498 +0200
> +++ gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c	2022-10-13 09:35:28.092240273 +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	2022-10-11 14:49:42.024217800 +0200
> +++ gcc/testsuite/c-c++-common/dfp/compare-eq-const.c	2022-10-13 09:35:28.104240108 +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	2022-10-11 14:49:42.188215554 +0200
> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C	2022-10-13 09:35:28.112239998 +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	2022-10-11 14:49:42.193215485 +0200
> +++ gcc/testsuite/g++.dg/cpp1z/decomp12.C	2022-10-13 09:35:28.113239984 +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	2022-10-11 14:49:42.224215061 +0200
> +++ gcc/testsuite/g++.dg/other/thunk1.C	2022-10-13 09:35:28.120239887 +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	2022-10-11 14:49:42.264214513 +0200
> +++ gcc/testsuite/g++.dg/vect/pr64410.cc	2022-10-13 09:35:28.121239874 +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/vect/pr89653.cc.jj	2020-01-12 11:54:37.280400328 +0100
> +++ gcc/testsuite/g++.dg/vect/pr89653.cc	2022-10-13 10:01:35.564649879 +0200
> @@ -1,5 +1,6 @@
>   // { dg-do compile }
>   // { dg-require-effective-target vect_double }
> +// { dg-additional-options "-fexcess-precision=fast" }
>   
>   #include <algorithm>
>   
> --- gcc/testsuite/g++.dg/cpp1y/pr68180.C.jj	2022-10-11 14:49:42.168215827 +0200
> +++ gcc/testsuite/g++.dg/cpp1y/pr68180.C	2022-10-13 09:35:28.121239874 +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-10-11 14:49:42.135216279 +0200
> +++ gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C	2022-10-13 09:35:28.130239750 +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	2022-10-11 14:49:42.104216704 +0200
> +++ gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C	2022-10-13 09:35:28.140239612 +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	2022-10-11 14:49:42.304213966 +0200
> +++ gcc/testsuite/g++.old-deja/g++.brendan/copy9.C	2022-10-13 09:35:28.152239447 +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	2022-10-11 14:49:42.327213651 +0200
> +++ gcc/testsuite/g++.old-deja/g++.brendan/overload7.C	2022-10-13 09:35:28.152239447 +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-13 19:28 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-11 13:33 [PATCH] c++: " Jakub Jelinek
2022-10-12 18:08 ` Jason Merrill
2022-10-13 16:40   ` [PATCH] c++, v2: " Jakub Jelinek
2022-10-13 19:28     ` Jason Merrill [this message]
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=c76a957a-c06a-0ce0-2d33-598213a5d041@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).