LCOV - code coverage report
Current view: top level - gcc - ipa-comdats.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 100.0 % 153 153
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Localize comdats.
       2              :    Copyright (C) 2014-2026 Free Software Foundation, Inc.
       3              : 
       4              : This file is part of GCC.
       5              : 
       6              : GCC is free software; you can redistribute it and/or modify it under
       7              : the terms of the GNU General Public License as published by the Free
       8              : Software Foundation; either version 3, or (at your option) any later
       9              : version.
      10              : 
      11              : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      12              : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13              : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14              : for more details.
      15              : 
      16              : You should have received a copy of the GNU General Public License
      17              : along with GCC; see the file COPYING3.  If not see
      18              : <http://www.gnu.org/licenses/>.  */
      19              : 
      20              : /* This is very simple pass that looks for static symbols that are used
      21              :    exclusively by symbol within one comdat group.  In this case it makes
      22              :    sense to bring the symbol itself into the group to avoid dead code
      23              :    that would arrise when the comdat group from current unit is replaced
      24              :    by a different copy.  Consider for example:
      25              : 
      26              :     static int q(void)
      27              :     {
      28              :       ....
      29              :     }
      30              :     inline int t(void)
      31              :     {
      32              :       return q();
      33              :     }
      34              : 
      35              :    if Q is used only by T, it makes sense to put Q into T's comdat group.
      36              : 
      37              :    The pass solve simple dataflow across the callgraph trying to prove what
      38              :    symbols are used exclusively from a given comdat group.
      39              : 
      40              :    The implementation maintains a queue linked by AUX pointer terminated by
      41              :    pointer value 1. Lattice values are NULL for TOP, actual comdat group, or
      42              :    ERROR_MARK_NODE for bottom.
      43              : 
      44              :    TODO: When symbol is used only by comdat symbols, but from different groups,
      45              :    it would make sense to produce a new comdat group for it with anonymous name.
      46              : 
      47              :    TODO2: We can't mix variables and functions within one group.  Currently
      48              :    we just give up on references of symbols of different types.  We also should
      49              :    handle this by anonymous comdat group section.  */
      50              : 
      51              : #include "config.h"
      52              : #include "system.h"
      53              : #include "coretypes.h"
      54              : #include "tm.h"
      55              : #include "tree.h"
      56              : #include "tree-pass.h"
      57              : #include "cgraph.h"
      58              : 
      59              : /* Main dataflow loop propagating comdat groups across
      60              :    the symbol table.  All references to SYMBOL are examined
      61              :    and NEWGROUP is updated accordingly. MAP holds current lattice
      62              :    values for individual symbols.  */
      63              : 
      64              : tree
      65        76017 : propagate_comdat_group (struct symtab_node *symbol,
      66              :                         tree newgroup, hash_map<symtab_node *, tree> &map)
      67              : {
      68        76017 :   int i;
      69        76017 :   struct ipa_ref *ref;
      70              : 
      71              :   /* Walk all references to SYMBOL, recursively dive into aliases.  */
      72              : 
      73       101485 :   for (i = 0;
      74       202970 :        symbol->iterate_referring (i, ref)
      75       101485 :        && newgroup != error_mark_node; i++)
      76              :     {
      77        47662 :       struct symtab_node *symbol2 = ref->referring;
      78              : 
      79        47662 :       if (ref->use == IPA_REF_ALIAS)
      80              :         {
      81         1179 :           newgroup = propagate_comdat_group (symbol2, newgroup, map);
      82         1179 :           continue;
      83              :         }
      84              : 
      85              :       /* One COMDAT group cannot hold both variables and functions at
      86              :          a same time.  For now we just go to BOTTOM, in future we may
      87              :          invent special comdat groups for this case.  */
      88              : 
      89        46483 :       if (symbol->type != symbol2->type)
      90              :         {
      91        22194 :           newgroup = error_mark_node;
      92        22194 :           break;
      93              :         }
      94              : 
      95              :       /* If we see inline clone, its comdat group actually
      96              :          corresponds to the comdat group of the function it is inlined
      97              :          to.  */
      98              : 
      99        24289 :       if (cgraph_node * cn = dyn_cast <cgraph_node *> (symbol2))
     100              :         {
     101        14612 :           if (cn->inlined_to)
     102         2562 :             symbol2 = cn->inlined_to;
     103              :         }
     104              : 
     105              :       /* The actual merge operation.  */
     106              : 
     107        24289 :       tree *val2 = map.get (symbol2);
     108              : 
     109        24289 :       if (val2 && *val2 != newgroup)
     110              :         {
     111        22153 :           if (!newgroup)
     112              :             newgroup = *val2;
     113              :           else
     114          188 :             newgroup = error_mark_node;
     115              :         }
     116              :     }
     117              : 
     118              :   /* If we analyze function, walk also callers.  */
     119              : 
     120        76017 :   cgraph_node *cnode = dyn_cast <cgraph_node *> (symbol);
     121              : 
     122        52547 :   if (cnode)
     123        52547 :     for (struct cgraph_edge * edge = cnode->callers;
     124       108077 :          edge && newgroup != error_mark_node; edge = edge->next_caller)
     125              :       {
     126        55530 :         struct symtab_node *symbol2 = edge->caller;
     127              : 
     128        55530 :         if (cgraph_node * cn = dyn_cast <cgraph_node *> (symbol2))
     129              :           {
     130              :             /* Thunks cannot call across section boundary.  */
     131        55530 :             if (cn->thunk)
     132          424 :               newgroup = propagate_comdat_group (symbol2, newgroup, map);
     133              :             /* If we see inline clone, its comdat group actually
     134              :                corresponds to the comdat group of the function it
     135              :                is inlined to.  */
     136        55530 :             if (cn->inlined_to)
     137        18373 :               symbol2 = cn->inlined_to;
     138              :           }
     139              : 
     140              :         /* The actual merge operation.  */
     141              : 
     142        55530 :         tree *val2 = map.get (symbol2);
     143              : 
     144        55530 :         if (val2 && *val2 != newgroup)
     145              :           {
     146        30248 :             if (!newgroup)
     147              :               newgroup = *val2;
     148              :             else
     149         4719 :               newgroup = error_mark_node;
     150              :           }
     151              :       }
     152        76017 :   return newgroup;
     153              : }
     154              : 
     155              : 
     156              : /* Add all references of SYMBOL that are defined into queue started by FIRST
     157              :    and linked by AUX pointer (unless they are already enqueued).
     158              :    Walk recursively inlined functions.  */
     159              : 
     160              : void
     161       194748 : enqueue_references (symtab_node **first,
     162              :                     symtab_node *symbol)
     163              : {
     164       194748 :   int i;
     165       194748 :   struct ipa_ref *ref = NULL;
     166              : 
     167       404149 :   for (i = 0; symbol->iterate_reference (i, ref); i++)
     168              :     {
     169       209401 :       symtab_node *node = ref->referred->ultimate_alias_target ();
     170              : 
     171              :       /* Always keep thunks in same sections as target function.  */
     172       209401 :       if (is_a <cgraph_node *>(node))
     173        25420 :         node = dyn_cast <cgraph_node *> (node)->function_symbol ();
     174       209401 :       if (!node->aux && node->definition)
     175              :         {
     176         1440 :            node->aux = *first;
     177         1440 :            *first = node;
     178              :         }
     179              :     }
     180              : 
     181       194748 :   if (cgraph_node *cnode = dyn_cast <cgraph_node *> (symbol))
     182              :     {
     183       172523 :       struct cgraph_edge *edge;
     184              : 
     185       669199 :       for (edge = cnode->callees; edge; edge = edge->next_callee)
     186       496676 :         if (!edge->inline_failed)
     187       124590 :           enqueue_references (first, edge->callee);
     188              :         else
     189              :           {
     190       372086 :             symtab_node *node = edge->callee->ultimate_alias_target ();
     191              : 
     192              :             /* Always keep thunks in same sections as target function.  */
     193       372086 :             if (is_a <cgraph_node *>(node))
     194       372086 :               node = dyn_cast <cgraph_node *> (node)->function_symbol ();
     195       372086 :             if (!node->aux && node->definition)
     196              :               {
     197         3121 :                  node->aux = *first;
     198         3121 :                  *first = node;
     199              :               }
     200              :           }
     201              :     }
     202       194748 : }
     203              : 
     204              : /* Set comdat group of SYMBOL to GROUP.
     205              :    Callback for for_node_and_aliases.  */
     206              : 
     207              : bool
     208         6894 : set_comdat_group (symtab_node *symbol,
     209              :                   void *head_p)
     210              : {
     211         6894 :   symtab_node *head = (symtab_node *)head_p;
     212              : 
     213         6894 :   gcc_assert (!symbol->get_comdat_group ());
     214         6894 :   if (symbol->real_symbol_p ())
     215              :     {
     216         6894 :       symbol->set_comdat_group (head->get_comdat_group ());
     217         6894 :       symbol->add_to_same_comdat_group (head);
     218              :     }
     219         6894 :   return false;
     220              : }
     221              : 
     222              : /* Set comdat group of SYMBOL to GROUP.
     223              :    Callback for for_node_thunks_and_aliases.  */
     224              : 
     225              : bool
     226         6888 : set_comdat_group_1 (cgraph_node *symbol,
     227              :                     void *head_p)
     228              : {
     229         6888 :   return set_comdat_group (symbol, head_p);
     230              : }
     231              : 
     232              : /* The actual pass with the main dataflow loop.  */
     233              : 
     234              : static unsigned int
     235       230063 : ipa_comdats (void)
     236              : {
     237       230063 :   hash_map<symtab_node *, tree> map (251);
     238       230063 :   hash_map<tree, symtab_node *> comdat_head_map (251);
     239       230063 :   symtab_node *symbol;
     240       230063 :   bool comdat_group_seen = false;
     241       230063 :   symtab_node *first = (symtab_node *) (void *) 1;
     242       230063 :   tree group;
     243              : 
     244              :   /* Start the dataflow by assigning comdat group to symbols that are in comdat
     245              :      groups already.  All other externally visible symbols must stay, we use
     246              :      ERROR_MARK_NODE as bottom for the propagation.  */
     247              : 
     248      5946882 :   FOR_EACH_DEFINED_SYMBOL (symbol)
     249      5716819 :     if (!symbol->real_symbol_p ())
     250              :       ;
     251      4391948 :     else if ((group = symbol->get_comdat_group ()) != NULL)
     252              :       {
     253       530321 :         map.put (symbol, group);
     254       530321 :         comdat_head_map.put (group, symbol);
     255       530321 :         comdat_group_seen = true;
     256              : 
     257              :         /* Mark the symbol so we won't waste time visiting it for dataflow.  */
     258       530321 :         symbol->aux = (symtab_node *) (void *) 1;
     259              :       }
     260              :     /* See symbols that cannot be privatized to comdats; that is externally
     261              :        visible symbols or otherwise used ones.  We also do not want to mangle
     262              :        user section names.  */
     263      3861627 :     else if (symbol->externally_visible
     264      1735857 :              || symbol->force_output
     265      1495907 :              || symbol->ref_by_asm
     266      1495902 :              || symbol->used_from_other_partition
     267      1495902 :              || TREE_THIS_VOLATILE (symbol->decl)
     268      1486595 :              || symbol->get_section ()
     269      4311297 :              || (TREE_CODE (symbol->decl) == FUNCTION_DECL
     270       183209 :                  && (DECL_STATIC_CONSTRUCTOR (symbol->decl)
     271       181204 :                      || DECL_STATIC_DESTRUCTOR (symbol->decl))))
     272              :       {
     273      3414023 :         symtab_node *target = symbol->ultimate_alias_target ();
     274              : 
     275              :         /* Always keep thunks in same sections as target function.  */
     276      3414023 :         if (is_a <cgraph_node *>(target))
     277      1129106 :           target = dyn_cast <cgraph_node *> (target)->function_symbol ();
     278      3414023 :         map.put (target, error_mark_node);
     279              : 
     280              :         /* Mark the symbol so we won't waste time visiting it for dataflow.  */
     281      3414023 :         symbol->aux = (symtab_node *) (void *) 1;
     282              :       }
     283              :     else
     284              :       {
     285              :         /* Enqueue symbol for dataflow.  */
     286       447604 :         symbol->aux = first;
     287       447604 :         first = symbol;
     288              :       }
     289              : 
     290       230063 :   if (!comdat_group_seen)
     291              :     {
     292      2700932 :       FOR_EACH_DEFINED_SYMBOL (symbol)
     293      2496774 :         symbol->aux = NULL;
     294              :       return 0;
     295              :     }
     296              : 
     297              :   /* The actual dataflow.  */
     298              : 
     299       100326 :   while (first != (void *) 1)
     300              :     {
     301        74421 :       tree group = NULL;
     302        74421 :       tree newgroup, *val;
     303              : 
     304        74421 :       symbol = first;
     305        74421 :       first = (symtab_node *)first->aux;
     306              : 
     307              :       /* Get current lattice value of SYMBOL.  */
     308        74421 :       val = map.get (symbol);
     309        74421 :       if (val)
     310          546 :         group = *val;
     311              : 
     312              :       /* If it is bottom, there is nothing to do; do not clear AUX
     313              :          so we won't re-queue the symbol.  */
     314        74421 :       if (group == error_mark_node)
     315         4263 :         continue;
     316              : 
     317        74414 :       newgroup = propagate_comdat_group (symbol, group, map);
     318              : 
     319              :       /* If nothing changed, proceed to next symbol.  */
     320        74414 :       if (newgroup == group)
     321              :         {
     322         4256 :           symbol->aux = NULL;
     323         4256 :           continue;
     324              :         }
     325              : 
     326              :       /* Update lattice value and enqueue all references for re-visiting.  */
     327        70158 :       gcc_assert (newgroup);
     328        70158 :       if (val)
     329          470 :         *val = newgroup;
     330              :       else
     331        69688 :         map.put (symbol, newgroup);
     332        70158 :       enqueue_references (&first, symbol);
     333              : 
     334              :       /* We may need to revisit the symbol unless it is BOTTOM.  */
     335        70158 :       if (newgroup != error_mark_node)
     336         7486 :         symbol->aux = NULL;
     337              :     }
     338              : 
     339              :   /* Finally assign symbols to the sections.  */
     340              : 
     341      3245950 :   FOR_EACH_DEFINED_SYMBOL (symbol)
     342              :     {
     343      3220045 :       struct cgraph_node *fun;
     344      3220045 :       symbol->aux = NULL;
     345      3220045 :       if (!symbol->get_comdat_group ()
     346      2588503 :           && !symbol->alias
     347      2578307 :           && (!(fun = dyn_cast <cgraph_node *> (symbol))
     348      1334575 :               || !fun->thunk)
     349      5797735 :           && symbol->real_symbol_p ())
     350              :         {
     351      1587873 :           tree *val = map.get (symbol);
     352              : 
     353              :           /* A NULL here means that SYMBOL is unreachable in the definition
     354              :              of ipa-comdats. Either ipa-comdats is wrong about this or someone
     355              :              forgot to cleanup and remove unreachable functions earlier.  */
     356      1587873 :           gcc_assert (val);
     357              : 
     358      1587873 :           tree group = *val;
     359              : 
     360      1587873 :           if (group == error_mark_node)
     361      1580991 :             continue;
     362         6882 :           if (dump_file)
     363              :             {
     364            3 :               fprintf (dump_file, "Localizing symbol\n");
     365            3 :               symbol->dump (dump_file);
     366            3 :               fprintf (dump_file, "To group: %s\n", IDENTIFIER_POINTER (group));
     367              :             }
     368         6882 :           if (is_a <cgraph_node *> (symbol))
     369         6876 :            dyn_cast <cgraph_node *>(symbol)->call_for_symbol_thunks_and_aliases
     370         6876 :                   (set_comdat_group_1,
     371         6876 :                    *comdat_head_map.get (group),
     372              :                    true);
     373              :           else
     374            6 :            symbol->call_for_symbol_and_aliases
     375            6 :                   (set_comdat_group,
     376            6 :                    *comdat_head_map.get (group),
     377              :                    true);
     378              :         }
     379              :     }
     380              : 
     381              : #if 0
     382              :   /* Recompute calls comdat local flag.  This need to be done after all changes
     383              :      are made.  */
     384              :   cgraph_node *function;
     385              :   FOR_EACH_DEFINED_FUNCTION (function)
     386              :     if (function->get_comdat_group ())
     387              :       function->calls_comdat_local = function->check_calls_comdat_local_p ();
     388              : #endif
     389              :   return 0;
     390       230063 : }
     391              : 
     392              : namespace {
     393              : 
     394              : const pass_data pass_data_ipa_comdats =
     395              : {
     396              :   IPA_PASS, /* type */
     397              :   "comdats", /* name */
     398              :   OPTGROUP_NONE, /* optinfo_flags */
     399              :   TV_IPA_COMDATS, /* tv_id */
     400              :   0, /* properties_required */
     401              :   0, /* properties_provided */
     402              :   0, /* properties_destroyed */
     403              :   0, /* todo_flags_start */
     404              :   0, /* todo_flags_finish */
     405              : };
     406              : 
     407              : class pass_ipa_comdats : public ipa_opt_pass_d
     408              : {
     409              : public:
     410       285722 :   pass_ipa_comdats (gcc::context *ctxt)
     411              :     : ipa_opt_pass_d (pass_data_ipa_comdats, ctxt,
     412              :                       NULL, /* generate_summary */
     413              :                       NULL, /* write_summary */
     414              :                       NULL, /* read_summary */
     415              :                       NULL, /* write_optimization_summary */
     416              :                       NULL, /* read_optimization_summary */
     417              :                       NULL, /* stmt_fixup */
     418              :                       0, /* function_transform_todo_flags_start */
     419              :                       NULL, /* function_transform */
     420       285722 :                       NULL) /* variable_transform */
     421       285722 :   {}
     422              : 
     423              :   /* opt_pass methods: */
     424              :   bool gate (function *) final override;
     425       230063 :   unsigned int execute (function *) final override { return ipa_comdats (); }
     426              : 
     427              : }; // class pass_ipa_comdats
     428              : 
     429              : bool
     430       563719 : pass_ipa_comdats::gate (function *)
     431              : {
     432       563719 :   return HAVE_COMDAT_GROUP;
     433              : }
     434              : 
     435              : } // anon namespace
     436              : 
     437              : ipa_opt_pass_d *
     438       285722 : make_pass_ipa_comdats (gcc::context *ctxt)
     439              : {
     440       285722 :   return new pass_ipa_comdats (ctxt);
     441              : }
        

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.