123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- /* Localize comdats.
- Copyright (C) 2014-2015 Free Software Foundation, Inc.
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 3, or (at your option) any later
- version.
- GCC is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
- You should have received a copy of the GNU General Public License
- along with GCC; see the file COPYING3. If not see
- <http://www.gnu.org/licenses/>. */
- /* This is very simple pass that looks for static symbols that are used
- exlusively by symbol within one comdat group. In this case it makes
- sense to bring the symbol itself into the group to avoid dead code
- that would arrise when the comdat group from current unit is replaced
- by a different copy. Consider for example:
- static int q(void)
- {
- ....
- }
- inline int t(void)
- {
- return q();
- }
- if Q is used only by T, it makes sense to put Q into T's comdat group.
- The pass solve simple dataflow across the callgraph trying to prove what
- symbols are used exclusively from a given comdat group.
- The implementation maintains a queue linked by AUX pointer terminated by
- pointer value 1. Lattice values are NULL for TOP, actual comdat group, or
- ERROR_MARK_NODE for bottom.
- TODO: When symbol is used only by comdat symbols, but from different groups,
- it would make sense to produce a new comdat group for it with anonymous name.
- TODO2: We can't mix variables and functions within one group. Currently
- we just give up on references of symbols of different types. We also should
- handle this by anonymous comdat group section. */
- #include "config.h"
- #include "system.h"
- #include "coretypes.h"
- #include "tm.h"
- #include "hash-set.h"
- #include "machmode.h"
- #include "vec.h"
- #include "double-int.h"
- #include "input.h"
- #include "alias.h"
- #include "symtab.h"
- #include "wide-int.h"
- #include "inchash.h"
- #include "tree.h"
- #include "hash-map.h"
- #include "is-a.h"
- #include "plugin-api.h"
- #include "vec.h"
- #include "hard-reg-set.h"
- #include "input.h"
- #include "function.h"
- #include "ipa-ref.h"
- #include "cgraph.h"
- #include "tree-pass.h"
- /* Main dataflow loop propagating comdat groups across
- the symbol table. All references to SYMBOL are examined
- and NEWGROUP is updated accordingly. MAP holds current lattice
- values for individual symbols. */
- tree
- propagate_comdat_group (struct symtab_node *symbol,
- tree newgroup, hash_map<symtab_node *, tree> &map)
- {
- int i;
- struct ipa_ref *ref;
- /* Walk all references to SYMBOL, recursively dive into aliases. */
- for (i = 0;
- symbol->iterate_referring (i, ref)
- && newgroup != error_mark_node; i++)
- {
- struct symtab_node *symbol2 = ref->referring;
- if (ref->use == IPA_REF_ALIAS)
- {
- newgroup = propagate_comdat_group (symbol2, newgroup, map);
- continue;
- }
- /* One COMDAT group can not hold both variables and functions at
- a same time. For now we just go to BOTTOM, in future we may
- invent special comdat groups for this case. */
- if (symbol->type != symbol2->type)
- {
- newgroup = error_mark_node;
- break;
- }
- /* If we see inline clone, its comdat group actually
- corresponds to the comdat group of the function it is inlined
- to. */
- if (cgraph_node * cn = dyn_cast <cgraph_node *> (symbol2))
- {
- if (cn->global.inlined_to)
- symbol2 = cn->global.inlined_to;
- }
- /* The actual merge operation. */
- tree *val2 = map.get (symbol2);
- if (val2 && *val2 != newgroup)
- {
- if (!newgroup)
- newgroup = *val2;
- else
- newgroup = error_mark_node;
- }
- }
- /* If we analyze function, walk also callers. */
- cgraph_node *cnode = dyn_cast <cgraph_node *> (symbol);
- if (cnode)
- for (struct cgraph_edge * edge = cnode->callers;
- edge && newgroup != error_mark_node; edge = edge->next_caller)
- {
- struct symtab_node *symbol2 = edge->caller;
- if (cgraph_node * cn = dyn_cast <cgraph_node *> (symbol2))
- {
- /* Thunks can not call across section boundary. */
- if (cn->thunk.thunk_p)
- newgroup = propagate_comdat_group (symbol2, newgroup, map);
- /* If we see inline clone, its comdat group actually
- corresponds to the comdat group of the function it
- is inlined to. */
- if (cn->global.inlined_to)
- symbol2 = cn->global.inlined_to;
- }
- /* The actual merge operation. */
- tree *val2 = map.get (symbol2);
- if (val2 && *val2 != newgroup)
- {
- if (!newgroup)
- newgroup = *val2;
- else
- newgroup = error_mark_node;
- }
- }
- return newgroup;
- }
- /* Add all references of SYMBOL that are defined into queue started by FIRST
- and linked by AUX pointer (unless they are already enqueued).
- Walk recursively inlined functions. */
- void
- enqueue_references (symtab_node **first,
- symtab_node *symbol)
- {
- int i;
- struct ipa_ref *ref = NULL;
- for (i = 0; symbol->iterate_reference (i, ref); i++)
- {
- symtab_node *node = ref->referred->ultimate_alias_target ();
- /* Always keep thunks in same sections as target function. */
- if (is_a <cgraph_node *>(node))
- node = dyn_cast <cgraph_node *> (node)->function_symbol ();
- if (!node->aux && node->definition)
- {
- node->aux = *first;
- *first = node;
- }
- }
- if (cgraph_node *cnode = dyn_cast <cgraph_node *> (symbol))
- {
- struct cgraph_edge *edge;
- for (edge = cnode->callees; edge; edge = edge->next_callee)
- if (!edge->inline_failed)
- enqueue_references (first, edge->callee);
- else
- {
- symtab_node *node = edge->callee->ultimate_alias_target ();
- /* Always keep thunks in same sections as target function. */
- if (is_a <cgraph_node *>(node))
- node = dyn_cast <cgraph_node *> (node)->function_symbol ();
- if (!node->aux && node->definition)
- {
- node->aux = *first;
- *first = node;
- }
- }
- }
- }
- /* Set comdat group of SYMBOL to GROUP.
- Callback for for_node_and_aliases. */
- bool
- set_comdat_group (symtab_node *symbol,
- void *head_p)
- {
- symtab_node *head = (symtab_node *)head_p;
- gcc_assert (!symbol->get_comdat_group ());
- symbol->set_comdat_group (head->get_comdat_group ());
- symbol->add_to_same_comdat_group (head);
- return false;
- }
- /* Set comdat group of SYMBOL to GROUP.
- Callback for for_node_thunks_and_aliases. */
- bool
- set_comdat_group_1 (cgraph_node *symbol,
- void *head_p)
- {
- return set_comdat_group (symbol, head_p);
- }
- /* The actual pass with the main dataflow loop. */
- static unsigned int
- ipa_comdats (void)
- {
- hash_map<symtab_node *, tree> map (251);
- hash_map<tree, symtab_node *> comdat_head_map (251);
- symtab_node *symbol;
- bool comdat_group_seen = false;
- symtab_node *first = (symtab_node *) (void *) 1;
- tree group;
- /* Start the dataflow by assigning comdat group to symbols that are in comdat
- groups already. All other externally visible symbols must stay, we use
- ERROR_MARK_NODE as bottom for the propagation. */
- FOR_EACH_DEFINED_SYMBOL (symbol)
- if (!symbol->real_symbol_p ())
- ;
- else if ((group = symbol->get_comdat_group ()) != NULL)
- {
- map.put (symbol, group);
- comdat_head_map.put (group, symbol);
- comdat_group_seen = true;
- /* Mark the symbol so we won't waste time visiting it for dataflow. */
- symbol->aux = (symtab_node *) (void *) 1;
- }
- /* See symbols that can not be privatized to comdats; that is externally
- visible symbols or otherwise used ones. We also do not want to mangle
- user section names. */
- else if (symbol->externally_visible
- || symbol->force_output
- || symbol->used_from_other_partition
- || TREE_THIS_VOLATILE (symbol->decl)
- || symbol->get_section ()
- || (TREE_CODE (symbol->decl) == FUNCTION_DECL
- && (DECL_STATIC_CONSTRUCTOR (symbol->decl)
- || DECL_STATIC_DESTRUCTOR (symbol->decl))))
- {
- symtab_node *target = symbol->ultimate_alias_target ();
- /* Always keep thunks in same sections as target function. */
- if (is_a <cgraph_node *>(target))
- target = dyn_cast <cgraph_node *> (target)->function_symbol ();
- map.put (target, error_mark_node);
- /* Mark the symbol so we won't waste time visiting it for dataflow. */
- symbol->aux = (symtab_node *) (void *) 1;
- }
- else
- {
- /* Enqueue symbol for dataflow. */
- symbol->aux = first;
- first = symbol;
- }
- if (!comdat_group_seen)
- {
- FOR_EACH_DEFINED_SYMBOL (symbol)
- symbol->aux = NULL;
- return 0;
- }
- /* The actual dataflow. */
- while (first != (void *) 1)
- {
- tree group = NULL;
- tree newgroup, *val;
- symbol = first;
- first = (symtab_node *)first->aux;
- /* Get current lattice value of SYMBOL. */
- val = map.get (symbol);
- if (val)
- group = *val;
- /* If it is bottom, there is nothing to do; do not clear AUX
- so we won't re-queue the symbol. */
- if (group == error_mark_node)
- continue;
- newgroup = propagate_comdat_group (symbol, group, map);
- /* If nothing changed, proceed to next symbol. */
- if (newgroup == group)
- {
- symbol->aux = NULL;
- continue;
- }
- /* Update lattice value and enqueue all references for re-visiting. */
- gcc_assert (newgroup);
- if (val)
- *val = newgroup;
- else
- map.put (symbol, newgroup);
- enqueue_references (&first, symbol);
- /* We may need to revisit the symbol unless it is BOTTOM. */
- if (newgroup != error_mark_node)
- symbol->aux = NULL;
- }
- /* Finally assign symbols to the sections. */
- FOR_EACH_DEFINED_SYMBOL (symbol)
- {
- struct cgraph_node *fun;
- symbol->aux = NULL;
- if (!symbol->get_comdat_group ()
- && !symbol->alias
- && (!(fun = dyn_cast <cgraph_node *> (symbol))
- || !fun->thunk.thunk_p)
- && symbol->real_symbol_p ())
- {
- tree *val = map.get (symbol);
- /* A NULL here means that SYMBOL is unreachable in the definition
- of ipa-comdats. Either ipa-comdats is wrong about this or someone
- forgot to cleanup and remove unreachable functions earlier. */
- gcc_assert (val);
- tree group = *val;
- if (group == error_mark_node)
- continue;
- if (dump_file)
- {
- fprintf (dump_file, "Localizing symbol\n");
- symbol->dump (dump_file);
- fprintf (dump_file, "To group: %s\n", IDENTIFIER_POINTER (group));
- }
- if (is_a <cgraph_node *> (symbol))
- dyn_cast <cgraph_node *>(symbol)->call_for_symbol_thunks_and_aliases
- (set_comdat_group_1,
- *comdat_head_map.get (group),
- true);
- else
- symbol->call_for_symbol_and_aliases
- (set_comdat_group,
- *comdat_head_map.get (group),
- true);
- }
- }
- return 0;
- }
- namespace {
- const pass_data pass_data_ipa_comdats =
- {
- IPA_PASS, /* type */
- "comdats", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- TV_IPA_COMDATS, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- 0, /* todo_flags_finish */
- };
- class pass_ipa_comdats : public ipa_opt_pass_d
- {
- public:
- pass_ipa_comdats (gcc::context *ctxt)
- : ipa_opt_pass_d (pass_data_ipa_comdats, ctxt,
- NULL, /* generate_summary */
- NULL, /* write_summary */
- NULL, /* read_summary */
- NULL, /* write_optimization_summary */
- NULL, /* read_optimization_summary */
- NULL, /* stmt_fixup */
- 0, /* function_transform_todo_flags_start */
- NULL, /* function_transform */
- NULL) /* variable_transform */
- {}
- /* opt_pass methods: */
- virtual bool gate (function *);
- virtual unsigned int execute (function *) { return ipa_comdats (); }
- }; // class pass_ipa_comdats
- bool
- pass_ipa_comdats::gate (function *)
- {
- return optimize;
- }
- } // anon namespace
- ipa_opt_pass_d *
- make_pass_ipa_comdats (gcc::context *ctxt)
- {
- return new pass_ipa_comdats (ctxt);
- }
|