public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jason Merrill <jason@redhat.com>
To: Patrick Palka <ppalka@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++: redundant targ coercion for var/alias tmpls
Date: Tue, 18 Jul 2023 12:52:06 -0400	[thread overview]
Message-ID: <1ac00b10-bef7-7f35-9da1-5a27b4a5f15c@redhat.com> (raw)
In-Reply-To: <daeaa160-e0ec-4c38-4989-56ff8ef654e5@idea>

On 7/17/23 17:29, Patrick Palka wrote:
> On Fri, 14 Jul 2023, Jason Merrill wrote:
> 
>> On 7/14/23 14:07, Patrick Palka wrote:
>>> On Thu, 13 Jul 2023, Jason Merrill wrote:
>>>
>>>> On 7/13/23 11:48, Patrick Palka wrote:
>>>>> On Wed, 28 Jun 2023, Patrick Palka wrote:
>>>>>
>>>>>> On Wed, Jun 28, 2023 at 11:50 AM Jason Merrill <jason@redhat.com>
>>>>>> wrote:
>>>>>>>
>>>>>>> On 6/23/23 12:23, Patrick Palka wrote:
>>>>>>>> On Fri, 23 Jun 2023, Jason Merrill wrote:
>>>>>>>>
>>>>>>>>> On 6/21/23 13:19, Patrick Palka wrote:
>>>>>>>>>> When stepping through the variable/alias template
>>>>>>>>>> specialization
>>>>>>>>>> code
>>>>>>>>>> paths, I noticed we perform template argument coercion twice:
>>>>>>>>>> first from
>>>>>>>>>> instantiate_alias_template / finish_template_variable and
>>>>>>>>>> again
>>>>>>>>>> from
>>>>>>>>>> tsubst_decl (during instantiate_template).  It should suffice
>>>>>>>>>> to
>>>>>>>>>> perform
>>>>>>>>>> coercion once.
>>>>>>>>>>
>>>>>>>>>> To that end patch elides this second coercion from tsubst_decl
>>>>>>>>>> when
>>>>>>>>>> possible.  We can't get rid of it completely because we don't
>>>>>>>>>> always
>>>>>>>>>> specialize a variable template from finish_template_variable:
>>>>>>>>>> we
>>>>>>>>>> could
>>>>>>>>>> also be doing so directly from instantiate_template during
>>>>>>>>>> variable
>>>>>>>>>> template partial specialization selection, in which case the
>>>>>>>>>> coercion
>>>>>>>>>> from tsubst_decl would be the first and only coercion.
>>>>>>>>>
>>>>>>>>> Perhaps we should be coercing in lookup_template_variable rather
>>>>>>>>> than
>>>>>>>>> finish_template_variable?
>>>>>>>>
>>>>>>>> Ah yes, there's a patch for that at
>>>>>>>> https://gcc.gnu.org/pipermail/gcc-patches/2023-May/617377.html :)
>>>>>>>
>>>>>>> So after that patch, can we get rid of the second coercion
>>>>>>> completely?
>>>>>>
>>>>>> On second thought it should be possible to get rid of it, if we
>>>>>> rearrange things to always pass the primary arguments to tsubst_decl,
>>>>>> and perform partial specialization selection from there instead of
>>>>>> instantiate_template.  Let me try...
>>>>>
>>>>> Like so?  Bootstrapped and regtested on x86_64-pc-linux-gnu.
>>>>>
>>>>> -- >8 --
>>>>>
>>>>> When stepping through the variable/alias template specialization code
>>>>> paths, I noticed we perform template argument coercion twice: first from
>>>>> instantiate_alias_template / finish_template_variable and again from
>>>>> tsubst_decl (during instantiate_template).  It'd be good to avoid this
>>>>> redundant coercion.
>>>>>
>>>>> It turns out that this coercion could be safely elided whenever
>>>>> specializing a primary variable/alias template, because we can rely on
>>>>> lookup_template_variable and instantiate_alias_template to already have
>>>>> coerced the arguments.
>>>>>
>>>>> The other situation to consider is when fully specializing a partial
>>>>> variable template specialization (from instantiate_template), in which
>>>>> case the passed 'args' are the (already coerced) arguments relative to
>>>>> the partial template and 'argvec', the result of substitution into
>>>>> DECL_TI_ARGS, are the (uncoerced) arguments relative to the primary
>>>>> template, so coercion is still necessary.  We can still avoid this
>>>>> coercion however if we always pass the primary variable template to
>>>>> tsubst_decl from instantiate_template, and instead perform partial
>>>>> specialization selection directly from tsubst_decl.  This patch
>>>>> implements this approach.
>>>>
>>>> The relationship between instantiate_template and tsubst_decl is pretty
>>>> tangled.  We use the former to substitute (often deduced) template
>>>> arguments
>>>> into a template, and the latter to substitute template arguments into a
>>>> use of
>>>> a template...and also to implement the former.
>>>>
>>>> For substitution of uses of a template, we expect to need to coerce the
>>>> arguments after substitution.  But we avoid this issue for variable
>>>> templates
>>>> by keeping them as TEMPLATE_ID_EXPR until substitution time, so if we see
>>>> a
>>>> VAR_DECL in tsubst_decl it's either a non-template variable or under
>>>> instantiate_template.
>>>
>>> FWIW it seems we could also be in tsubst_decl for a VAR_DECL if
>>>
>>>     * we're partially instantiating a class-scope variable template
>>>       during instantiation of the class
>>
>> Hmm, why don't partial instantiations stay as TEMPLATE_ID_EXPR?
> 
> Whoops, I accidentally omitted a crucial word.  The situation is when
> partially instantiating a class-scope variable template _declaration_,
> e.g. for
> 
>    template<class T>
>    struct A {
>      template<class U> static int v;
>    };
> 
>    template struct A<int>;
> 
> we call tsubst_decl from instantiate_class_template with T=int for the
> VAR_DECL for v.
> 
>>
>>>     * we're substituting a use of an already non-dependent variable
>>>       template specialization
>>
>> Sure.
>>
>>>> So it seems like the current coercion for variable templates is only
>>>> needed in
>>>> this case to support the redundant hash table lookup that we just did in
>>>> instantiate_template.  Perhaps instead of doing coercion here or moving
>>>> the
>>>> partial spec lookup, we could skip the hash table lookup for the case of a
>>>> variable template?
>>>
>>> It seems we'd then also have to make instantiate_template responsible
>>> for registering the variable template specialization since tsubst_decl
>>> no longer necessarily has the arguments relative to the primary template
>>> ('args' could be relative to the partial template).
>>>
>>> Like so?  The following makes us perform all the specialization table
>>> manipulation in instantiate_template instead of tsubst_decl for variable
>>> template specializations.
>>
>> Looks good.
>>
>>> I wonder if we might want to do this for alias template specializations too?
>>
>> That would make sense.
> 
> Sounds good, I went ahead and made us do it for function template
> specializations too.
> 
>>
>>> @@ -15222,20 +15230,21 @@ tsubst_decl (tree t, tree args, tsubst_flags_t
>>> complain)
>>>    	      {
>>>    		tmpl = DECL_TI_TEMPLATE (t);
>>>    		gen_tmpl = most_general_template (tmpl);
>>> -		argvec = tsubst (DECL_TI_ARGS (t), args, complain, in_decl);
>>> -		if (argvec != error_mark_node
>>> -		    && PRIMARY_TEMPLATE_P (gen_tmpl)
>>> -		    && TMPL_ARGS_DEPTH (args) >= TMPL_ARGS_DEPTH (argvec))
>>> -		  /* We're fully specializing a template declaration, so
>>> -		     we need to coerce the innermost arguments corresponding
>>> to
>>> -		     the template.  */
>>> -		  argvec = (coerce_template_parms
>>> -			    (DECL_TEMPLATE_PARMS (gen_tmpl),
>>> -			     argvec, tmpl, complain));
>>> -		if (argvec == error_mark_node)
>>> -		  RETURN (error_mark_node);
>>> -		hash = spec_hasher::hash (gen_tmpl, argvec);
>>> -		spec = retrieve_specialization (gen_tmpl, argvec, hash);
>>> +		if (variable_template_p (tmpl)
>>> +		    && (TMPL_ARGS_DEPTH (args)
>>> +			>= TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (gen_tmpl))))
>>
>> Do we still need to compare depths?  If not, we could also skip computing
>> gen_tmpl in this case.
> 
> It was necessary to distinguish the desired instantiate_template case
> the "partially specializing a class-scope variable template declaration"
> case I clarified above, but not anymore with the new version of the
> patch which adds a new parameter to tsubst_decl to control this behavior
> instead of inferring it from the other arguments.
> 
> How does the following look?  Bootstrapped and regtested on
> x86_64-pc-linux-gnu.  Patch formatted with -w to ignore whitespace
> changes.

OK, thanks.

> -- >8 --
> 
> Subject: [PATCH] c++: redundant targ coercion for var/alias tmpls
> 
> gcc/cp/ChangeLog:
> 
> 	* pt.cc (tsubst_function_decl): Add defaulted 'use_spec_table'
> 	flag parameter.  Don't look up or insert into the the
> 	specializations table if 'use_spec_table' is false.
> 	(tsubst_decl): Add defaulted 'use_spec_table' flag parameter.
> 	Check for error_mark_node.
> 	<case FUNCTION_DECL>: Pass 'use_spec_table' to
> 	tsubst_function_decl.
> 	<case TYPE/VAR_DECL>: Don't call coerce_template_parms.
> 	Don't look up or insert into the specializations table if
> 	'use_spec_table' is false.  Exit earlier if the substituted
> 	type is erroneous and we're not complaining.
> 	(instantiate_template): Pass false as 'use_spec_table'
> 	to tsubst_decl.  Call register_specialization.
> ---
>   gcc/cp/pt.cc | 65 ++++++++++++++++++++++++++++++++--------------------
>   1 file changed, 40 insertions(+), 25 deletions(-)
> 
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 255d18b9539..f788127a90f 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -204,7 +204,7 @@ static bool invalid_nontype_parm_type_p (tree, tsubst_flags_t);
>   static bool dependent_template_arg_p (tree);
>   static bool dependent_type_p_r (tree);
>   static tree tsubst_copy	(tree, tree, tsubst_flags_t, tree);
> -static tree tsubst_decl (tree, tree, tsubst_flags_t);
> +static tree tsubst_decl (tree, tree, tsubst_flags_t, bool = true);
>   static tree tsubst_scope (tree, tree, tsubst_flags_t, tree);
>   static void perform_instantiation_time_access_checks (tree, tree);
>   static tree listify (tree);
> @@ -14304,7 +14304,7 @@ maybe_rebuild_function_decl_type (tree decl)
>   
>   static tree
>   tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
> -		      tree lambda_fntype)
> +		      tree lambda_fntype, bool use_spec_table = true)
>   {
>     tree gen_tmpl = NULL_TREE, argvec = NULL_TREE;
>     hashval_t hash = 0;
> @@ -14345,6 +14345,8 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
>   
>         /* Calculate the complete set of arguments used to
>   	 specialize R.  */
> +      if (use_spec_table)
> +	{
>   	  argvec = tsubst_template_args (DECL_TI_ARGS
>   					 (DECL_TEMPLATE_RESULT
>   					  (DECL_TI_TEMPLATE (t))),
> @@ -14362,6 +14364,9 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
>   		return STRIP_TEMPLATE (spec);
>   	    }
>   	}
> +      else
> +	argvec = args;
> +    }
>     else
>       {
>         /* This special case arises when we have something like this:
> @@ -14527,12 +14532,15 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
>   	= build_template_info (gen_tmpl, argvec);
>         SET_DECL_IMPLICIT_INSTANTIATION (r);
>   
> +      if (use_spec_table)
> +	{
>   	  tree new_r
>   	    = register_specialization (r, gen_tmpl, argvec, false, hash);
>   	  if (new_r != r)
>   	    /* We instantiated this while substituting into
>   	       the type earlier (template/friend54.C).  */
>   	    return new_r;
> +	}
>   
>         /* We're not supposed to instantiate default arguments
>   	 until they are called, for a template.  But, for a
> @@ -14855,10 +14863,13 @@ enclosing_instantiation_of (tree tctx)
>   
>   /* Substitute the ARGS into the T, which is a _DECL.  Return the
>      result of the substitution.  Issue error and warning messages under
> -   control of COMPLAIN.  */
> +   control of COMPLAIN.  The flag USE_SPEC_TABLE controls if we look up
> +   and/or register the specialization in the specializations table or
> +   if we can assume it's the caller's responsibility.  */
>   
>   static tree
> -tsubst_decl (tree t, tree args, tsubst_flags_t complain)
> +tsubst_decl (tree t, tree args, tsubst_flags_t complain,
> +	     bool use_spec_table /* = true */)
>   {
>   #define RETURN(EXP) do { r = (EXP); goto out; } while(0)
>     location_t saved_loc;
> @@ -14866,6 +14877,9 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
>     tree in_decl = t;
>     hashval_t hash = 0;
>   
> +  if (t == error_mark_node)
> +    return error_mark_node;
> +
>     /* Set the filename and linenumber to improve error-reporting.  */
>     saved_loc = input_location;
>     input_location = DECL_SOURCE_LOCATION (t);
> @@ -14879,7 +14893,8 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
>         break;
>   
>       case FUNCTION_DECL:
> -      r = tsubst_function_decl (t, args, complain, /*lambda*/NULL_TREE);
> +      r = tsubst_function_decl (t, args, complain, /*lambda*/NULL_TREE,
> +				use_spec_table);
>         break;
>   
>       case PARM_DECL:
> @@ -15228,22 +15243,18 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
>   	    if (!spec)
>   	      {
>   		tmpl = DECL_TI_TEMPLATE (t);
> -		gen_tmpl = most_general_template (tmpl);
> +		if (use_spec_table)
> +		  {
>   		    argvec = tsubst (DECL_TI_ARGS (t), args, complain, in_decl);
> -		if (argvec != error_mark_node
> -		    && PRIMARY_TEMPLATE_P (gen_tmpl)
> -		    && TMPL_ARGS_DEPTH (args) >= TMPL_ARGS_DEPTH (argvec))
> -		  /* We're fully specializing a template declaration, so
> -		     we need to coerce the innermost arguments corresponding to
> -		     the template.  */
> -		  argvec = (coerce_template_parms
> -			    (DECL_TEMPLATE_PARMS (gen_tmpl),
> -			     argvec, tmpl, complain));
>   		    if (argvec == error_mark_node)
>   		      RETURN (error_mark_node);
> +		    gen_tmpl = most_general_template (tmpl);
>   		    hash = spec_hasher::hash (gen_tmpl, argvec);
>   		    spec = retrieve_specialization (gen_tmpl, argvec, hash);
>   		  }
> +		else
> +		  argvec = args;
> +	      }
>   	  }
>   	else
>   	  {
> @@ -15287,20 +15298,20 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
>   	    type = tsubst (type, args, tcomplain, in_decl);
>   	    /* Substituting the type might have recursively instantiated this
>   	       same alias (c++/86171).  */
> -	    if (gen_tmpl && DECL_ALIAS_TEMPLATE_P (gen_tmpl)
> +	    if (use_spec_table && gen_tmpl && DECL_ALIAS_TEMPLATE_P (gen_tmpl)
>   		&& (spec = retrieve_specialization (gen_tmpl, argvec, hash)))
>   	      {
>   		r = spec;
>   		break;
>   	      }
>   	  }
> +	if (type == error_mark_node && !(complain & tf_error))
> +	  RETURN (error_mark_node);
>   	r = copy_decl (t);
>   	if (VAR_P (r))
>   	  {
>   	    DECL_INITIALIZED_P (r) = 0;
>   	    DECL_TEMPLATE_INSTANTIATED (r) = 0;
> -	    if (type == error_mark_node)
> -	      RETURN (error_mark_node);
>   	    if (TREE_CODE (type) == FUNCTION_TYPE)
>   	      {
>   		/* It may seem that this case cannot occur, since:
> @@ -15404,7 +15415,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
>   
>   	    DECL_TEMPLATE_INFO (r) = build_template_info (tmpl, argvec);
>   	    SET_DECL_IMPLICIT_INSTANTIATION (r);
> -	    if (!error_operand_p (r) || (complain & tf_error))
> +	    if (use_spec_table)
>   	      register_specialization (r, gen_tmpl, argvec, false, hash);
>   	  }
>   	else
> @@ -21991,7 +22002,6 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
>     tree targ_ptr = orig_args;
>     tree fndecl;
>     tree gen_tmpl;
> -  tree spec;
>     bool access_ok = true;
>   
>     if (tmpl == error_mark_node)
> @@ -22038,9 +22048,8 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
>   		(DECL_TI_ARGS (DECL_TEMPLATE_RESULT (tmpl)),
>   		 targ_ptr));
>   
> -  /* It would be nice to avoid hashing here and then again in tsubst_decl,
> -     but it doesn't seem to be on the hot path.  */
> -  spec = retrieve_specialization (gen_tmpl, targ_ptr, 0);
> +  hashval_t hash = spec_hasher::hash (gen_tmpl, targ_ptr);
> +  tree spec = retrieve_specialization (gen_tmpl, targ_ptr, hash);
>   
>     gcc_checking_assert (tmpl == gen_tmpl
>   		       || ((fndecl
> @@ -22108,13 +22117,14 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
>   	  tree partial_tmpl = TI_TEMPLATE (partial_ti);
>   	  tree partial_args = TI_ARGS (partial_ti);
>   	  tree partial_pat = DECL_TEMPLATE_RESULT (partial_tmpl);
> -	  fndecl = tsubst (partial_pat, partial_args, complain, gen_tmpl);
> +	  fndecl = tsubst_decl (partial_pat, partial_args, complain,
> +				/*use_spec_table=*/false);
>   	}
>       }
>   
>     /* Substitute template parameters to obtain the specialization.  */
>     if (fndecl == NULL_TREE)
> -    fndecl = tsubst (pattern, targ_ptr, complain, gen_tmpl);
> +    fndecl = tsubst_decl (pattern, targ_ptr, complain, /*use_spec_table=*/false);
>     if (DECL_CLASS_SCOPE_P (gen_tmpl))
>       pop_nested_class ();
>     pop_from_top_level ();
> @@ -22134,6 +22144,11 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
>          remember the result of most_specialized_partial_spec for it.  */
>       TI_PARTIAL_INFO (DECL_TEMPLATE_INFO (fndecl)) = partial_ti;
>   
> +  /* Register the specialization which we prevented tsubst_decl from doing.  */
> +  fndecl = register_specialization (fndecl, gen_tmpl, targ_ptr, false, hash);
> +  if (fndecl == error_mark_node)
> +    return error_mark_node;
> +
>     set_instantiating_module (fndecl);
>   
>     /* Now we know the specialization, compute access previously


      reply	other threads:[~2023-07-18 16:52 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-21 17:19 Patrick Palka
2023-06-23 15:11 ` Jason Merrill
2023-06-23 16:23   ` Patrick Palka
2023-06-28 15:50     ` Jason Merrill
2023-06-28 20:14       ` Patrick Palka
2023-07-13 15:48         ` Patrick Palka
2023-07-13 20:44           ` Jason Merrill
2023-07-14 18:07             ` Patrick Palka
2023-07-14 21:17               ` Jason Merrill
2023-07-17 21:29                 ` Patrick Palka
2023-07-18 16:52                   ` Jason Merrill [this message]

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=1ac00b10-bef7-7f35-9da1-5a27b4a5f15c@redhat.com \
    --to=jason@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=ppalka@redhat.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).