[PATCH v2 06/16] Change function versions to be implicitly ordered.

Alfie Richards alfie.richards@arm.com
Tue Feb 18 13:31:13 GMT 2025


On 18/02/2025 12:11, Richard Sandiford wrote:
> Alfie Richards <alfie.richards@arm.com> writes:
>> This changes function version structures to maintain the default version
>> as the first declaration in the linked data structures by giving priority
>> to the set containing the default when constructing the structure.
>>
>> This allows for removing logic for moving the default to the first
>> position which was duplicated across target specific code and enables
>> easier reasoning about function sets when checking for a default.
>>
>> gcc/ChangeLog:
>>
>> 	* cgraph.cc (cgraph_node::record_function_versions): Update to
>> 	implicitly keep default first.
>> 	* config/aarch64/aarch64.cc (aarch64_get_function_versions_dispatcher):
>> 	Remove reordering.
>> 	* config/i386/i386-features.cc (ix86_get_function_versions_dispatcher):
>> 	Remove reordering.
>> 	* config/riscv/riscv.cc (riscv_get_function_versions_dispatcher):
>> 	Remove reordering.
>> 	* config/rs6000/rs6000.cc (rs6000_get_function_versions_dispatcher):
>> 	Remove reordering.
>> ---
>>   gcc/cgraph.cc                    | 27 ++++++++++++++++-----
>>   gcc/config/aarch64/aarch64.cc    | 37 +++++++---------------------
>>   gcc/config/i386/i386-features.cc | 33 ++++---------------------
>>   gcc/config/riscv/riscv.cc        | 41 +++++++-------------------------
>>   gcc/config/rs6000/rs6000.cc      | 35 +++++----------------------
>>   5 files changed, 49 insertions(+), 124 deletions(-)
>>
>> diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
>> index d0b19ad850e..bf6b43d00db 100644
>> --- a/gcc/cgraph.cc
>> +++ b/gcc/cgraph.cc
>> @@ -247,7 +247,9 @@ cgraph_node::record_function_versions (tree decl1, tree decl2)
>>     decl1_v = decl1_node->function_version ();
>>     decl2_v = decl2_node->function_version ();
>>   
>> -  if (decl1_v != NULL && decl2_v != NULL)
>> +  /* If the nodes are already linked, skip.  */
>> +  if ((decl1_v != NULL && (decl1_v->next || decl1_v->prev))
>> +      && (decl2_v != NULL && (decl2_v->next || decl2_v->prev)))
>>       return;
>>   
>>     if (decl1_v == NULL)
>> @@ -256,18 +258,31 @@ cgraph_node::record_function_versions (tree decl1, tree decl2)
>>     if (decl2_v == NULL)
>>       decl2_v = decl2_node->insert_new_function_version ();
>>   
>> -  /* Chain decl2_v and decl1_v.  All semantically identical versions
>> -     will be chained together.  */
>> +  gcc_assert (decl1_v);
>> +  gcc_assert (decl2_v);
>>   
>>     before = decl1_v;
>>     after = decl2_v;
>>   
>> +  /* Go to first after node.  */
>> +  while (after->prev != NULL)
>> +    after = after->prev;
>> +
>> +  while (before->prev != NULL)
>> +    before = before->prev;
>> +
>> +  /* Potentially swap the nodes to maintain the default always being in the
>> +     first position.  */
>> +  if (before->next
>> +      ? !is_function_default_version (before->this_node->decl)
>> +      : is_function_default_version (after->this_node->decl))
>> +    std::swap (before, after);
>> +
>> +  /* Go to last node of before.  */
>>     while (before->next != NULL)
>>       before = before->next;
>>   
>> -  while (after->prev != NULL)
>> -    after= after->prev;
>> -
>> +  /* Chain decl2_v and decl1_v.  */
> I think this can be simplified to:
>
>    before = decl1_v;
>    after = decl2_v;
>
>    /* Potentially swap the nodes to maintain the default always being in the
>       first position.  */
>    if (before->prev || before->next
>        ? is_function_default_version (after->this_node->decl)
>        : !is_function_default_version (before->this_node->decl))
>      std::swap (before, after);
>
>    while (before->next != NULL)
>      before = before->next;
>   
>    while (after->prev != NULL)
>      after = after->prev;
>
> That is, if one decl is linked (and so the other is not), we only want
> to put the other decl first if it is the default.
I see your point here, which I think relies on the assumption that 
functions get
added to the structure one by one rather than in a fractal pattern.
This assumption is already used here subtly so that makes sense.

I added this logic to at least try make this work in a slightly more 
general case as
to tell if a structure contains the default we should check the first 
element
of that structure, but it is unnecessary given that knowledge.

I would prefer to change this to make that more explicit and change this 
to be
"add_decl_to_version_into" taking a cgraph_function_version_info for the 
existing structure and a decl for the version to add to make this 
explicit. Would that change work for you?
>> [...]
>> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
>> index 9bf7713139f..e5aa99a4965 100644
>> --- a/gcc/config/riscv/riscv.cc
>> +++ b/gcc/config/riscv/riscv.cc
>> @@ -13726,7 +13726,6 @@ riscv_get_function_versions_dispatcher (void *decl)
>>     struct cgraph_node *node = NULL;
>>     struct cgraph_node *default_node = NULL;
>>     struct cgraph_function_version_info *node_v = NULL;
>> -  struct cgraph_function_version_info *first_v = NULL;
>>   
>>     tree dispatch_decl = NULL;
>>   
>> @@ -13743,41 +13742,19 @@ riscv_get_function_versions_dispatcher (void *decl)
>>     if (node_v->dispatcher_resolver != NULL)
>>       return node_v->dispatcher_resolver;
>>   
>> -  /* Find the default version and make it the first node.  */
>> -  first_v = node_v;
>> -  /* Go to the beginning of the chain.  */
>> -  while (first_v->prev != NULL)
>> -    first_v = first_v->prev;
>> -  default_version_info = first_v;
>> -
>> -  while (default_version_info != NULL)
>> -    {
>> -      struct riscv_feature_bits res;
>> -      int priority; /* Unused.  */
>> -      parse_features_for_version (default_version_info->this_node->decl,
>> -				  res, priority);
>> -      if (res.length == 0)
>> -	break;
>> -      default_version_info = default_version_info->next;
>> -    }
>> +  /* The default node is always the beginning of the chain.  */
>> +  default_version_info = node_v;
>> +  while (default_version_info->prev)
>> +    default_version_info = default_version_info->prev;
>> +  default_node = default_version_info->this_node;
>>   
>>     /* If there is no default node, just return NULL.  */
>> -  if (default_version_info == NULL)
>> +  struct riscv_feature_bits res;
>> +  int priority; /* Unused.  */
>> +  parse_features_for_version (default_node->decl, res, priority);
>> +  if (res.length != 0)
>>       return NULL;
> My comment from the previous review still stands:
>
>    I expect you were trying to avoid editorialising, but: how about
>    converting this to is_function_default_version at the same time?
>    That seems more logically consistent with the target-independent code
>    and wasn't an option open to the RISC-V code until patch 5.
>
>    To put it another way, not being able to use is_function_default_version
>    here would suggest that the "default-first" assumption doesn't in fact
>    hold for RISC-V.
>
> I.e. I think this can just be:
>
>    if (!is_function_default_version (default_node->decl))
>      return NULL;
>
> as for the others.
Apologies for this, I meant to change this and must have missed it.
I agree with your reasoning.
> OK for GCC 16 with those changes, if you agree with them.
>
> Thanks,
> Richard


More information about the Gcc-patches mailing list