LCOV - code coverage report
Current view: top level - gcc - multiple_target.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 91.8 % 233 214
Test Date: 2025-10-18 14:39:06 Functions: 91.7 % 12 11
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* Pass for parsing functions with multiple target attributes.
       2                 :             : 
       3                 :             :    Contributed by Evgeny Stupachenko <evstupac@gmail.com>
       4                 :             : 
       5                 :             :    Copyright (C) 2015-2025 Free Software Foundation, Inc.
       6                 :             : 
       7                 :             : This file is part of GCC.
       8                 :             : 
       9                 :             : GCC is free software; you can redistribute it and/or modify it under
      10                 :             : the terms of the GNU General Public License as published by the Free
      11                 :             : Software Foundation; either version 3, or (at your option) any later
      12                 :             : version.
      13                 :             : 
      14                 :             : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      15                 :             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      16                 :             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      17                 :             : for more details.
      18                 :             : 
      19                 :             : You should have received a copy of the GNU General Public License
      20                 :             : along with GCC; see the file COPYING3.  If not see
      21                 :             : <http://www.gnu.org/licenses/>.  */
      22                 :             : 
      23                 :             : #include "config.h"
      24                 :             : #include "system.h"
      25                 :             : #include "coretypes.h"
      26                 :             : #include "backend.h"
      27                 :             : #include "tree.h"
      28                 :             : #include "stringpool.h"
      29                 :             : #include "gimple.h"
      30                 :             : #include "diagnostic-core.h"
      31                 :             : #include "gimple-ssa.h"
      32                 :             : #include "cgraph.h"
      33                 :             : #include "tree-pass.h"
      34                 :             : #include "target.h"
      35                 :             : #include "attribs.h"
      36                 :             : #include "pretty-print.h"
      37                 :             : #include "gimple-iterator.h"
      38                 :             : #include "gimple-walk.h"
      39                 :             : #include "tree-inline.h"
      40                 :             : #include "intl.h"
      41                 :             : 
      42                 :             : /* Walker callback that replaces all FUNCTION_DECL of a function that's
      43                 :             :    going to be versioned.  */
      44                 :             : 
      45                 :             : static tree
      46                 :         127 : replace_function_decl (tree *op, int *walk_subtrees, void *data)
      47                 :             : {
      48                 :         127 :   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
      49                 :         127 :   cgraph_function_version_info *info = (cgraph_function_version_info *)wi->info;
      50                 :             : 
      51                 :         127 :   if (TREE_CODE (*op) == FUNCTION_DECL
      52                 :          50 :       && info->this_node->decl == *op)
      53                 :             :     {
      54                 :          26 :       *op = info->dispatcher_resolver;
      55                 :          26 :       *walk_subtrees = 0;
      56                 :             :     }
      57                 :             : 
      58                 :         127 :   return NULL;
      59                 :             : }
      60                 :             : 
      61                 :             : /* In target FMV attributes, if the call in NODE has multiple target attribute
      62                 :             :    with multiple fields, replace it with calls to the dispatched symbol and
      63                 :             :    create the dispatcher body (once).
      64                 :             : 
      65                 :             :    In target_version semantics, if it is a lone annotated default, then
      66                 :             :    the dispatched symbol is changed to be an alias and no resolver is
      67                 :             :    required.  Otherwise, redirect all calls and references to the dispatched
      68                 :             :    symbol, but only create the resolver body if the default version is
      69                 :             :    implemented.  */
      70                 :             : 
      71                 :             : static void
      72                 :          84 : create_dispatcher_calls (struct cgraph_node *node)
      73                 :             : {
      74                 :          84 :   ipa_ref *ref;
      75                 :             : 
      76                 :          84 :   if (!targetm.has_ifunc_p ())
      77                 :             :     {
      78                 :           0 :       error_at (DECL_SOURCE_LOCATION (node->decl),
      79                 :             :                 "the call requires %<ifunc%>, which is not"
      80                 :             :                 " supported by this target");
      81                 :           0 :       return;
      82                 :             :     }
      83                 :          84 :   else if (!targetm.get_function_versions_dispatcher)
      84                 :             :     {
      85                 :           0 :       error_at (DECL_SOURCE_LOCATION (node->decl),
      86                 :             :                 "target does not support function version dispatcher");
      87                 :           0 :       return;
      88                 :             :     }
      89                 :             : 
      90                 :          84 :   tree idecl = targetm.get_function_versions_dispatcher (node->decl);
      91                 :          84 :   if (!idecl)
      92                 :             :     {
      93                 :           0 :       error_at (DECL_SOURCE_LOCATION (node->decl),
      94                 :             :                 "default %<target_clones%> attribute was not set");
      95                 :           0 :       return;
      96                 :             :     }
      97                 :             : 
      98                 :          84 :   cgraph_node *inode = cgraph_node::get (idecl);
      99                 :          84 :   gcc_assert (inode);
     100                 :          84 :   cgraph_function_version_info *inode_info = inode->function_version ();
     101                 :          84 :   gcc_assert (inode_info);
     102                 :             : 
     103                 :          84 :   tree resolver_decl = NULL;
     104                 :             : 
     105                 :             :   /* For target_version semantics, if there is a lone default declaration
     106                 :             :      it needs to be mangled, with an alias from the dispatched symbol to the
     107                 :             :      default version.  */
     108                 :          84 :   if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
     109                 :             :       && TREE_STATIC (node->decl)
     110                 :             :       && inode_info->next
     111                 :             :       && !inode_info->next->next)
     112                 :             :     {
     113                 :             :       inode->alias = true;
     114                 :             :       inode->alias_target = inode_info->next->this_node->decl;
     115                 :             :       inode->externally_visible = true;
     116                 :             :       if (!inode->analyzed)
     117                 :             :         inode->resolve_alias
     118                 :             :           (cgraph_node::get (inode_info->next->this_node->decl));
     119                 :             : 
     120                 :             :       DECL_ATTRIBUTES (idecl)
     121                 :             :         = make_attribute ("alias",
     122                 :             :                           IDENTIFIER_POINTER
     123                 :             :                             (DECL_ASSEMBLER_NAME
     124                 :             :                                (inode_info->next->this_node->decl)),
     125                 :             :                           DECL_ATTRIBUTES (node->decl));
     126                 :             :       TREE_USED (idecl) = true;
     127                 :             :       DECL_EXTERNAL (idecl) = false;
     128                 :             :       TREE_STATIC (idecl) = true;
     129                 :             :       return;
     130                 :             :     }
     131                 :             :   /* In target_version semantics, only create the resolver if the
     132                 :             :      default node is implemented.  */
     133                 :          84 :   else if (TARGET_HAS_FMV_TARGET_ATTRIBUTE || TREE_STATIC (node->decl))
     134                 :             :     {
     135                 :          84 :       resolver_decl = targetm.generate_version_dispatcher_body (inode);
     136                 :             :       /* Update aliases.  */
     137                 :          84 :       inode->alias = true;
     138                 :          84 :       inode->alias_target = resolver_decl;
     139                 :          84 :       if (!inode->analyzed)
     140                 :           0 :         inode->resolve_alias (cgraph_node::get (resolver_decl));
     141                 :             :     }
     142                 :             : 
     143                 :          84 :   auto_vec<cgraph_edge *> edges_to_redirect;
     144                 :             :   /* We need to capture the references by value rather than just pointers to them
     145                 :             :      and remove them right away, as removing them later would invalidate what
     146                 :             :      some other reference pointers point to.  */
     147                 :          84 :   auto_vec<ipa_ref> references_to_redirect;
     148                 :             : 
     149                 :         198 :   while (node->iterate_referring (0, ref))
     150                 :             :     {
     151                 :         114 :       references_to_redirect.safe_push (*ref);
     152                 :         114 :       ref->remove_reference ();
     153                 :             :     }
     154                 :             : 
     155                 :             :   /* We need to remember NEXT_CALLER as it could be modified in the loop.  */
     156                 :         147 :   for (cgraph_edge *e = node->callers; e ; e = e->next_caller)
     157                 :          63 :     edges_to_redirect.safe_push (e);
     158                 :             : 
     159                 :          84 :   if (!edges_to_redirect.is_empty () || !references_to_redirect.is_empty ())
     160                 :             :     {
     161                 :             :       /* Redirect edges.  */
     162                 :             :       unsigned i;
     163                 :             :       cgraph_edge *e;
     164                 :         147 :       FOR_EACH_VEC_ELT (edges_to_redirect, i, e)
     165                 :             :         {
     166                 :          63 :           e->redirect_callee (inode);
     167                 :          63 :           cgraph_edge::redirect_call_stmt_to_callee (e);
     168                 :             :         }
     169                 :             : 
     170                 :             :       /* Redirect references.  */
     171                 :         198 :       FOR_EACH_VEC_ELT (references_to_redirect, i, ref)
     172                 :             :         {
     173                 :         114 :           if (ref->use == IPA_REF_ADDR)
     174                 :             :             {
     175                 :         110 :               struct walk_stmt_info wi;
     176                 :         110 :               memset (&wi, 0, sizeof (wi));
     177                 :         110 :               wi.info = (void *)node->function_version ();
     178                 :             : 
     179                 :         110 :               if (dyn_cast<varpool_node *> (ref->referring))
     180                 :             :                 {
     181                 :           1 :                   hash_set<tree> visited_nodes;
     182                 :           1 :                   walk_tree (&DECL_INITIAL (ref->referring->decl),
     183                 :             :                              replace_function_decl, &wi, &visited_nodes);
     184                 :           1 :                 }
     185                 :             :               else
     186                 :             :                 {
     187                 :         109 :                   gimple_stmt_iterator it = gsi_for_stmt (ref->stmt);
     188                 :         109 :                   if (ref->referring->decl != resolver_decl)
     189                 :          25 :                     walk_gimple_stmt (&it, NULL, replace_function_decl, &wi);
     190                 :             :                 }
     191                 :             : 
     192                 :         110 :               symtab_node *source = ref->referring;
     193                 :         110 :               source->create_reference (inode, IPA_REF_ADDR);
     194                 :             :             }
     195                 :           4 :           else if (ref->use == IPA_REF_ALIAS)
     196                 :             :             {
     197                 :           4 :               symtab_node *source = ref->referring;
     198                 :           4 :               source->create_reference (inode, IPA_REF_ALIAS);
     199                 :           4 :               if (inode->get_comdat_group ())
     200                 :             :                 {
     201                 :           3 :                   if (source->same_comdat_group)
     202                 :           0 :                     source->remove_from_same_comdat_group ();
     203                 :           3 :                   source->add_to_same_comdat_group (inode);
     204                 :             :                 }
     205                 :             :             }
     206                 :             :           else
     207                 :           0 :             gcc_unreachable ();
     208                 :             :         }
     209                 :             :     }
     210                 :             : 
     211                 :          84 :   if (node->definition)
     212                 :             :     {
     213                 :             :       /* FIXME: copy of cgraph_node::make_local that should be cleaned up
     214                 :             :                 in next stage1.  */
     215                 :          76 :       node->make_decl_local ();
     216                 :          76 :       node->set_section (NULL);
     217                 :          76 :       node->set_comdat_group (NULL);
     218                 :          76 :       node->externally_visible = false;
     219                 :          76 :       node->forced_by_abi = false;
     220                 :             : 
     221                 :          76 :       DECL_ARTIFICIAL (node->decl) = 1;
     222                 :          76 :       node->force_output = true;
     223                 :             :     }
     224                 :          84 : }
     225                 :             : 
     226                 :             : /*  Creates target clone of NODE.  */
     227                 :             : 
     228                 :             : static cgraph_node *
     229                 :         155 : create_target_clone (cgraph_node *node, bool definition, char *name,
     230                 :             :                      tree attributes)
     231                 :             : {
     232                 :         155 :   cgraph_node *new_node;
     233                 :             : 
     234                 :         155 :   if (definition)
     235                 :             :     {
     236                 :         124 :       new_node
     237                 :         124 :         = node->create_version_clone_with_body (vNULL, NULL, NULL, NULL, NULL,
     238                 :             :                                                 name, attributes, false);
     239                 :         124 :       if (new_node == NULL)
     240                 :             :         return NULL;
     241                 :         123 :       new_node->force_output = true;
     242                 :             :     }
     243                 :             :   else
     244                 :             :     {
     245                 :          31 :       tree new_decl = copy_node (node->decl);
     246                 :          31 :       new_node = cgraph_node::get_create (new_decl);
     247                 :          31 :       DECL_ATTRIBUTES (new_decl) = attributes;
     248                 :             :       /* Generate a new name for the new version.  */
     249                 :          31 :       tree fname = clone_function_name (node->decl, name);
     250                 :          31 :       symtab->change_decl_assembler_name (new_node->decl, fname);
     251                 :             :     }
     252                 :             :   return new_node;
     253                 :             : }
     254                 :             : 
     255                 :             : /* If the function in NODE has multiple target attributes
     256                 :             :    create the appropriate clone for each valid target attribute.  */
     257                 :             : 
     258                 :             : static bool
     259                 :     3587005 : expand_target_clones (struct cgraph_node *node, bool definition)
     260                 :             : {
     261                 :             :   /* Parsing target attributes separated by TARGET_CLONES_ATTR_SEPARATOR.  */
     262                 :     3587005 :   tree attr_target = lookup_attribute ("target_clones",
     263                 :     3587005 :                                        DECL_ATTRIBUTES (node->decl));
     264                 :             :   /* No targets specified.  */
     265                 :     3587005 :   if (!attr_target)
     266                 :             :     return false;
     267                 :             : 
     268                 :          94 :   int num_defaults = 0;
     269                 :          94 :   auto_vec<string_slice> attr_list = get_clone_versions (node->decl,
     270                 :          94 :                                                          &num_defaults);
     271                 :             : 
     272                 :             :   /* If the target clones list is empty after filtering, remove this node.  */
     273                 :          94 :   if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE && attr_list.is_empty ())
     274                 :             :     {
     275                 :             :       node->remove ();
     276                 :             :       return false;
     277                 :             :     }
     278                 :             : 
     279                 :             :   /* No need to clone for 1 target attribute.  */
     280                 :          94 :   if (attr_list.length () == 1 && TARGET_HAS_FMV_TARGET_ATTRIBUTE)
     281                 :             :     {
     282                 :           0 :       warning_at (DECL_SOURCE_LOCATION (node->decl),
     283                 :           0 :                   0, "single %<target_clones%> attribute is ignored");
     284                 :           0 :       return false;
     285                 :             :     }
     286                 :             : 
     287                 :             :   /* For target_version semantics, a target clone with just a default version
     288                 :             :      is the same as an unannotated decl, so can ignore.  */
     289                 :          94 :   if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
     290                 :             :       && attr_list.length () == 1
     291                 :             :       && num_defaults == 1)
     292                 :             :     return false;
     293                 :             : 
     294                 :          94 :   if (node->definition
     295                 :          94 :       && (node->alias || !tree_versionable_function_p (node->decl)))
     296                 :             :     {
     297                 :           6 :       auto_diagnostic_group d;
     298                 :           6 :       error_at (DECL_SOURCE_LOCATION (node->decl),
     299                 :             :                 "clones for %<target_clones%> attribute cannot be created");
     300                 :           6 :       const char *reason = NULL;
     301                 :           6 :       if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl)))
     302                 :             :         reason = G_("function %q+F can never be copied "
     303                 :             :                     "because it has %<noclone%> attribute");
     304                 :           3 :       else if (node->alias)
     305                 :             :         reason
     306                 :             :           = "%<target_clones%> cannot be combined with %<alias%> attribute";
     307                 :             :       else
     308                 :           2 :         reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
     309                 :           2 :       if (reason)
     310                 :           6 :         inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
     311                 :           6 :       return false;
     312                 :           6 :     }
     313                 :             : 
     314                 :             :   /* Disallow multiple defaults.  */
     315                 :          88 :   if (num_defaults > 1)
     316                 :             :     {
     317                 :           1 :       error_at (DECL_SOURCE_LOCATION (node->decl),
     318                 :             :                 "multiple %<default%> targets were set");
     319                 :           1 :       return false;
     320                 :             :     }
     321                 :             : 
     322                 :             :   /* For target FMV semantics, where target and target_clone mixing
     323                 :             :      is not supported, disallow target clones with no defaults.  */
     324                 :          87 :   if (TARGET_HAS_FMV_TARGET_ATTRIBUTE && num_defaults == 0)
     325                 :             :     {
     326                 :           1 :       error_at (DECL_SOURCE_LOCATION (node->decl),
     327                 :             :                 "%<default%> target was not set");
     328                 :           1 :       return false;
     329                 :             :     }
     330                 :             : 
     331                 :             :   /* Disallow any empty values in the clone attr.  */
     332                 :         500 :   for (string_slice attr : attr_list)
     333                 :         485 :     if (attr.empty () || !attr.is_valid ())
     334                 :             :       {
     335                 :           1 :         error_at (DECL_SOURCE_LOCATION (node->decl),
     336                 :             :                   "an empty string cannot be in %<target_clones%> attribute");
     337                 :           1 :         return false;
     338                 :             :       }
     339                 :             : 
     340                 :          85 :   string_slice new_attr_name = TARGET_HAS_FMV_TARGET_ATTRIBUTE
     341                 :             :                                ? "target"
     342                 :          85 :                                : "target_version";
     343                 :             : 
     344                 :          85 :   cgraph_function_version_info *node_v = node->function_version ();
     345                 :             : 
     346                 :          85 :   if (!node_v)
     347                 :          85 :     node_v = node->insert_new_function_version ();
     348                 :             : 
     349                 :             :   /* If this target_clones contains a default, then convert this node to the
     350                 :             :      default.  If this node does not contain default (this is only possible
     351                 :             :      in target_version semantics) then remove the node.  This is safe at this
     352                 :             :      point as only target_clones declarations containing default version is
     353                 :             :      resolvable so this decl will have no calls/references.  */
     354                 :             : 
     355                 :          85 :   tree attrs = remove_attribute ("target_clones",
     356                 :          85 :                                   DECL_ATTRIBUTES (node->decl));
     357                 :          85 :   tree assembler_name = node_v->assembler_name;
     358                 :             : 
     359                 :             :   /* Change the current node into the default node.  */
     360                 :          85 :   if (num_defaults == 1)
     361                 :             :     {
     362                 :             :       /* Setting new attribute to initial function.  */
     363                 :          85 :       tree attributes = make_attribute (new_attr_name, "default", attrs);
     364                 :          85 :       DECL_ATTRIBUTES (node->decl) = attributes;
     365                 :          85 :       DECL_FUNCTION_VERSIONED (node->decl) = true;
     366                 :             : 
     367                 :          85 :       node->is_target_clone = true;
     368                 :          85 :       node->local = false;
     369                 :             : 
     370                 :             :       /* Remangle base node after new target version string set.  */
     371                 :          85 :       tree id = targetm.mangle_decl_assembler_name (node->decl, assembler_name);
     372                 :          85 :       symtab->change_decl_assembler_name (node->decl, id);
     373                 :             :     }
     374                 :             :   else
     375                 :             :     {
     376                 :             :       /* Target clones without a default are only allowed for target_version
     377                 :             :          semantics where we can have target_clones/target_version mixing.  */
     378                 :           0 :       gcc_assert (!TARGET_HAS_FMV_TARGET_ATTRIBUTE);
     379                 :             : 
     380                 :             :       /* If there isn't a default version, can safely remove this version.
     381                 :             :          The node itself gets removed after the other versions are created.  */
     382                 :             :       cgraph_function_version_info *temp = node_v;
     383                 :             :       node_v = node_v->next ? node_v->next : node_v->prev;
     384                 :             :       cgraph_node::delete_function_version (temp);
     385                 :             :     }
     386                 :             : 
     387                 :         493 :   for (string_slice attr : attr_list)
     388                 :             :     {
     389                 :             :       /* Skip default nodes.  */
     390                 :         239 :       if (attr == "default")
     391                 :          84 :         continue;
     392                 :             : 
     393                 :             :       /* Create new target clone.  */
     394                 :         155 :       tree attributes = make_attribute (new_attr_name, attr, attrs);
     395                 :             : 
     396                 :         155 :       cgraph_node *new_node
     397                 :         155 :         = create_target_clone (node, definition, NULL, attributes);
     398                 :         155 :       if (new_node == NULL)
     399                 :           1 :         return false;
     400                 :         154 :       new_node->local = false;
     401                 :             : 
     402                 :         154 :       DECL_FUNCTION_VERSIONED (new_node->decl) = true;
     403                 :         154 :       if (!node_v)
     404                 :           0 :         node_v = new_node->insert_new_function_version ();
     405                 :             :       else
     406                 :         154 :         cgraph_node::add_function_version (node_v, new_node->decl);
     407                 :             : 
     408                 :             :       /* Use the base node's assembler name for all created nodes.  */
     409                 :         154 :       new_node->function_version ()->assembler_name = assembler_name;
     410                 :         154 :       new_node->is_target_clone = true;
     411                 :             : 
     412                 :             :       /* Mangle all new nodes.  */
     413                 :         154 :       tree id = targetm.mangle_decl_assembler_name
     414                 :         154 :         (new_node->decl, new_node->function_version ()->assembler_name);
     415                 :         154 :       symtab->change_decl_assembler_name (new_node->decl, id);
     416                 :             :     }
     417                 :             : 
     418                 :             :   /* If there are no default versions in the target_clones, this node is not
     419                 :             :      reused, so can delete this node.  */
     420                 :          84 :   if (num_defaults == 0)
     421                 :           0 :     node->remove ();
     422                 :             : 
     423                 :             :   return true;
     424                 :          94 : }
     425                 :             : 
     426                 :             : /* When NODE is part of an FMV function set, consider all callees and check if
     427                 :             :    any can provably always resolve a certain version and then call that version
     428                 :             :    directly.  */
     429                 :             : 
     430                 :             : static void
     431                 :     3587474 : redirect_to_specific_clone (cgraph_node *node)
     432                 :             : {
     433                 :     3587474 :   if (!targetm.compare_version_priority || !optimize)
     434                 :             :     return;
     435                 :             : 
     436                 :             :   /* We need to remember NEXT_CALLER as it could be modified in the loop.  */
     437                 :     8122389 :   for (cgraph_edge *e = node->callees; e ; e = e->next_callee)
     438                 :             :     {
     439                 :             :       /* Only if this is a call to a dispatched symbol.  */
     440                 :     5350783 :       if (!e->callee->dispatcher_function)
     441                 :     5350697 :         continue;
     442                 :             : 
     443                 :          86 :       cgraph_function_version_info *callee_v
     444                 :          86 :         = e->callee->function_version ();
     445                 :          86 :       cgraph_function_version_info *caller_v
     446                 :          86 :         = e->caller->function_version ();
     447                 :             : 
     448                 :          86 :       gcc_assert (callee_v);
     449                 :             : 
     450                 :             :       /* Find the default nodes for both callee and caller (if present).  */
     451                 :          86 :       cgraph_function_version_info *callee_default_v = callee_v->next;
     452                 :          86 :       cgraph_function_version_info *caller_default_v = caller_v;
     453                 :          86 :       if (caller_v)
     454                 :             :         {
     455                 :           9 :           while (caller_default_v->prev)
     456                 :             :             caller_default_v = caller_default_v->prev;
     457                 :           7 :           if (!is_function_default_version (caller_default_v->this_node->decl))
     458                 :          79 :             caller_default_v = NULL;
     459                 :             :         }
     460                 :             : 
     461                 :             :       /* If this is not the TU that contains the definition of the default
     462                 :             :          version we are not guaranteed to have visibility of all versions
     463                 :             :          so cannot reason about them.  */
     464                 :         101 :       if (!callee_default_v
     465                 :          86 :           || !callee_default_v->this_node->binds_to_current_def_p ())
     466                 :          15 :         continue;
     467                 :             : 
     468                 :          71 :       cgraph_function_version_info *highest_callable_fn = NULL;
     469                 :          71 :       for (cgraph_function_version_info *ver = callee_v->next;
     470                 :         438 :            ver;
     471                 :         367 :            ver = ver->next)
     472                 :         367 :         if (targetm.target_option.functions_b_resolvable_from_a
     473                 :         367 :               (node->decl, ver->this_node->decl, node->decl))
     474                 :           7 :           highest_callable_fn = ver;
     475                 :             : 
     476                 :          71 :       if (!highest_callable_fn)
     477                 :          64 :         continue;
     478                 :             : 
     479                 :           7 :       bool inlinable = true;
     480                 :             : 
     481                 :             :       /* If there are higher priority versions of callee and caller has no
     482                 :             :          more version information, then not callable.  */
     483                 :           7 :       if (highest_callable_fn->next)
     484                 :             :         {
     485                 :             :           /* If this is not the TU where the callee default is defined then
     486                 :             :              cannot reason about the caller versions.  */
     487                 :           5 :           if (!caller_default_v
     488                 :           5 :               || !caller_default_v->this_node->binds_to_current_def_p ())
     489                 :           0 :             continue;
     490                 :             : 
     491                 :             :           /* If every higher priority version would imply a higher priority
     492                 :             :              version of caller would have been selected, then this is
     493                 :             :              callable.  */
     494                 :           5 :           for (cgraph_function_version_info *callee_ver
     495                 :             :                = highest_callable_fn->next;
     496                 :          10 :                callee_ver; callee_ver = callee_ver->next)
     497                 :             :             {
     498                 :           5 :               bool is_possible = true;
     499                 :           5 :               for (cgraph_function_version_info *caller_ver = caller_v->next;
     500                 :           5 :                    caller_ver; caller_ver = caller_ver->next)
     501                 :           5 :                 if (targetm.target_option.functions_b_resolvable_from_a
     502                 :           5 :                       (callee_ver->this_node->decl, caller_ver->this_node->decl,
     503                 :             :                        node->decl))
     504                 :             :                   {
     505                 :             :                     is_possible = false;
     506                 :             :                     break;
     507                 :             :                   }
     508                 :           5 :               if (is_possible)
     509                 :             :                 {
     510                 :             :                   inlinable = false;
     511                 :             :                   break;
     512                 :             :                 }
     513                 :             :             }
     514                 :             :         }
     515                 :           5 :       if (inlinable)
     516                 :             :         {
     517                 :           7 :           e->redirect_callee (highest_callable_fn->this_node);
     518                 :           7 :           cgraph_edge::redirect_call_stmt_to_callee (e);
     519                 :             :         }
     520                 :             :     }
     521                 :             : }
     522                 :             : 
     523                 :             : /* Checks if NODE is in the 'simple' target_clones case, which is where NODE
     524                 :             :    is a declaration annotated with target_clones containing the default, and it
     525                 :             :    is the sole function declaration in the FMV function set.  */
     526                 :             : 
     527                 :             : static bool
     528                 :           0 : is_simple_target_clones_case (cgraph_node *node)
     529                 :             : {
     530                 :             :   /* target attribute semantics doesnt support the complex case,
     531                 :             :      so this is always true.  */
     532                 :           0 :   if (TARGET_HAS_FMV_TARGET_ATTRIBUTE)
     533                 :           0 :     return true;
     534                 :             : 
     535                 :             :   int num_defaults = 0;
     536                 :             :   auto versions = get_clone_versions (node->decl, &num_defaults);
     537                 :             :   if (versions.is_empty () || num_defaults != 1)
     538                 :             :     return false;
     539                 :             : 
     540                 :             :   cgraph_function_version_info *fv = node->function_version ();
     541                 :             : 
     542                 :             :   if (fv && (fv->next || fv->prev))
     543                 :             :     return false;
     544                 :             : 
     545                 :             :   return true;
     546                 :             : }
     547                 :             : 
     548                 :             : static unsigned int
     549                 :      462331 : ipa_target_clone (bool early)
     550                 :             : {
     551                 :      462331 :   struct cgraph_node *node;
     552                 :      462331 :   auto_vec<cgraph_node *> to_dispatch;
     553                 :             : 
     554                 :             :   /* Don't need to do anything early for target attribute semantics.  */
     555                 :      462331 :   if (early && TARGET_HAS_FMV_TARGET_ATTRIBUTE)
     556                 :             :     return 0;
     557                 :             : 
     558                 :             :   /* For target attribute semantics, this pass skips the early phase, and in
     559                 :             :      the later stage is only responsible for expanding and dispatching
     560                 :             :      target_clone declarations, as target annotated functions are dispatched
     561                 :             :      in the front end.
     562                 :             : 
     563                 :             :      The expanding and dispatching can be done at the late stage as the
     564                 :             :      target_clone functions aren't allowed to be part of a larger FMV set, so
     565                 :             :      all versions will all have the same body, so early optimisations are safe
     566                 :             :      to treat a call to a target_clones set as a call to one function.
     567                 :             : 
     568                 :             :      For target_version semantics, this pass is responsible for expanding
     569                 :             :      target_clones and dispatching all FMV function sets, including ones only
     570                 :             :      made up of target_version declarations.
     571                 :             : 
     572                 :             :      Cases where there is more than one declaration must be expanded and
     573                 :             :      dispatched at the early stage, as the declarations may have different
     574                 :             :      bodies, and so the early optimisation passes would not be valid.
     575                 :             : 
     576                 :             :      The late stage is only used for the expansion and dispatching of the simple
     577                 :             :      case where the FMV set is defined by a single target_clone attribute.  */
     578                 :             : 
     579                 :     7867466 :   FOR_EACH_FUNCTION_REMOVABLE (node)
     580                 :             :     {
     581                 :             :       /* In the early stage, we need to expand any target clone that is not
     582                 :             :          the simple case.  Simple cases are dispatched in the later stage.  */
     583                 :             : 
     584                 :     3587005 :       if (early == !is_simple_target_clones_case (node))
     585                 :     3587005 :         if (expand_target_clones (node, node->definition)
     586                 :     3587005 :             && TARGET_HAS_FMV_TARGET_ATTRIBUTE)
     587                 :             :           /* In non target_version semantics, dispatch all target clones.  */
     588                 :          84 :           to_dispatch.safe_push (node);
     589                 :             :     }
     590                 :             : 
     591                 :             :   /* In target_version semantics dispatch all FMV function sets with a default
     592                 :             :      implementation in the early stage.
     593                 :             :      Also dispatch any default versions generated by expanding target_clones
     594                 :             :      in the late stage.  */
     595                 :             : 
     596                 :             :   if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE)
     597                 :             :     FOR_EACH_FUNCTION (node)
     598                 :             :       if (is_function_default_version (node->decl)
     599                 :             :           && DECL_FUNCTION_VERSIONED (node->decl)
     600                 :             :           /* Don't dispatch target clones, as they haven't been expanded so
     601                 :             :              are simple.  */
     602                 :             :           && !lookup_attribute ("target_clones", DECL_ATTRIBUTES (node->decl)))
     603                 :             :         to_dispatch.safe_push (node);
     604                 :             : 
     605                 :      231236 :   for (unsigned i = 0; i < to_dispatch.length (); i++)
     606                 :          84 :     create_dispatcher_calls (to_dispatch[i]);
     607                 :             : 
     608                 :     7637252 :   FOR_EACH_FUNCTION (node)
     609                 :     3587474 :     redirect_to_specific_clone (node);
     610                 :             : 
     611                 :             :   return 0;
     612                 :      462331 : }
     613                 :             : 
     614                 :             : namespace {
     615                 :             : 
     616                 :             : const pass_data pass_data_target_clone =
     617                 :             : {
     618                 :             :   SIMPLE_IPA_PASS,              /* type */
     619                 :             :   "targetclone",              /* name */
     620                 :             :   OPTGROUP_NONE,                /* optinfo_flags */
     621                 :             :   TV_NONE,                      /* tv_id */
     622                 :             :   ( PROP_ssa | PROP_cfg ),      /* properties_required */
     623                 :             :   0,                            /* properties_provided */
     624                 :             :   0,                            /* properties_destroyed */
     625                 :             :   0,                            /* todo_flags_start */
     626                 :             :   TODO_update_ssa               /* todo_flags_finish */
     627                 :             : };
     628                 :             : 
     629                 :             : class pass_target_clone : public simple_ipa_opt_pass
     630                 :             : {
     631                 :             : public:
     632                 :      578160 :   pass_target_clone (gcc::context *ctxt)
     633                 :     1156320 :     : simple_ipa_opt_pass (pass_data_target_clone, ctxt), early_p (false)
     634                 :             :   {}
     635                 :             :   bool early_p;
     636                 :             : 
     637                 :      578160 :   void set_pass_param (unsigned int n, bool param) final override
     638                 :             :     {
     639                 :      578160 :       gcc_assert (n == 0);
     640                 :      578160 :       early_p = param;
     641                 :      578160 :     }
     642                 :             :   /* opt_pass methods: */
     643                 :             :   bool gate (function *) final override;
     644                 :      289080 :   opt_pass * clone () final override { return new pass_target_clone (m_ctxt); }
     645                 :      462331 :   unsigned int execute (function *) final override
     646                 :             :   {
     647                 :      462331 :     return ipa_target_clone (early_p);
     648                 :             :   }
     649                 :             : };
     650                 :             : 
     651                 :             : bool
     652                 :      462542 : pass_target_clone::gate (function *)
     653                 :             : {
     654                 :             :   /* If there were any errors avoid pass property verification errors.  */
     655                 :      462542 :   return !seen_error ();
     656                 :             : }
     657                 :             : 
     658                 :             : } // anon namespace
     659                 :             : 
     660                 :             : simple_ipa_opt_pass *
     661                 :      289080 : make_pass_target_clone (gcc::context *ctxt)
     662                 :             : {
     663                 :      289080 :   return new pass_target_clone (ctxt);
     664                 :             : }
        

Generated by: LCOV version 2.1-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.