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: 2026-02-28 14:20:25 Functions: 91.7 % 12 11
Legend: Lines:     hit not hit

            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-2026 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      3584419 : expand_target_clones (struct cgraph_node *node, bool definition)
     260              : {
     261              :   /* Parsing target attributes separated by TARGET_CLONES_ATTR_SEPARATOR.  */
     262      3584419 :   tree attr_target = lookup_attribute ("target_clones",
     263      3584419 :                                        DECL_ATTRIBUTES (node->decl));
     264              :   /* No targets specified.  */
     265      3584419 :   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      3584888 : redirect_to_specific_clone (cgraph_node *node)
     432              : {
     433      3584888 :   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      8121103 :   for (cgraph_edge *e = node->callees; e ; e = e->next_callee)
     438              :     {
     439              :       /* Only if this is a call to a dispatched symbol.  */
     440      5348293 :       if (!e->callee->dispatcher_function)
     441      5348207 :         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          444 :            ver;
     471          373 :            ver = ver->next)
     472          373 :         if (targetm.target_option.functions_b_resolvable_from_a
     473          373 :               (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       459709 : ipa_target_clone (bool early)
     550              : {
     551       459709 :   struct cgraph_node *node;
     552       459709 :   auto_vec<cgraph_node *> to_dispatch;
     553              : 
     554              :   /* Don't need to do anything early for target attribute semantics.  */
     555       459709 :   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      7628520 :   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      3584419 :       if (early == !is_simple_target_clones_case (node))
     585      3584419 :         if (expand_target_clones (node, node->definition)
     586      3584419 :             && 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       229925 :   for (unsigned i = 0; i < to_dispatch.length (); i++)
     606           84 :     create_dispatcher_calls (to_dispatch[i]);
     607              : 
     608      3814729 :   FOR_EACH_FUNCTION (node)
     609      3584888 :     redirect_to_specific_clone (node);
     610              : 
     611              :   return 0;
     612       459709 : }
     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       571444 :   pass_target_clone (gcc::context *ctxt)
     633      1142888 :     : simple_ipa_opt_pass (pass_data_target_clone, ctxt), early_p (false)
     634              :   {}
     635              :   bool early_p;
     636              : 
     637       571444 :   void set_pass_param (unsigned int n, bool param) final override
     638              :     {
     639       571444 :       gcc_assert (n == 0);
     640       571444 :       early_p = param;
     641       571444 :     }
     642              :   /* opt_pass methods: */
     643              :   bool gate (function *) final override;
     644       285722 :   opt_pass * clone () final override { return new pass_target_clone (m_ctxt); }
     645       459709 :   unsigned int execute (function *) final override
     646              :   {
     647       459709 :     return ipa_target_clone (early_p);
     648              :   }
     649              : };
     650              : 
     651              : bool
     652       459920 : pass_target_clone::gate (function *)
     653              : {
     654              :   /* If there were any errors avoid pass property verification errors.  */
     655       459920 :   return !seen_error ();
     656              : }
     657              : 
     658              : } // anon namespace
     659              : 
     660              : simple_ipa_opt_pass *
     661       285722 : make_pass_target_clone (gcc::context *ctxt)
     662              : {
     663       285722 :   return new pass_target_clone (ctxt);
     664              : }
        

Generated by: LCOV version 2.4-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.