| /* IPA visibility pass |
| Copyright (C) 2003-2021 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 file implements two related passes: |
| |
| - pass_data_ipa_function_and_variable_visibility run just after |
| symbol table, references and callgraph are built |
| |
| - pass_data_ipa_function_and_variable_visibility run as first |
| proper IPA pass (that is after early optimization, or, (with LTO) |
| as a first pass done at link-time. |
| |
| Purpose of both passes is to set correctly visibility properties |
| of all symbols. This includes: |
| |
| - Symbol privatization: |
| |
| Some symbols that are declared public by frontend may be |
| turned local (either by -fwhole-program flag, by linker plugin feedback |
| or by other reasons) |
| |
| - Discovery of local functions: |
| |
| A local function is one whose calls can occur only in the current |
| compilation unit and all its calls are explicit, so we can change |
| its calling convention. We simply mark all static functions whose |
| address is not taken as local. |
| |
| externally_visible flag is set for symbols that cannot be privatized. |
| For privatized symbols we clear TREE_PUBLIC flag and dismantle comdat |
| group. |
| |
| - Dismantling of comdat groups: |
| |
| Comdat group represent a section that may be replaced by linker by |
| a different copy of the same section from other unit. |
| If we have resolution information (from linker plugin) and we know that |
| a given comdat gorup is prevailing, we can dismantle it and turn symbols |
| into normal symbols. If the resolution information says that the |
| section was previaled by copy from non-LTO code, we can also dismantle |
| it and turn all symbols into external. |
| |
| - Local aliases: |
| |
| Some symbols can be interposed by dynamic linker. Refering to these |
| symbols is expensive, since it needs to be overwritable by the dynamic |
| linker. In some cases we know that the interposition does not change |
| semantic and we can always refer to a local copy (as in the case of |
| inline function). In this case we produce a local alias and redirect |
| calls to it. |
| |
| TODO: This should be done for references, too. |
| |
| - Removal of static ocnstructors and destructors that have no side effects. |
| |
| - Regularization of several oddities introduced by frontends that may |
| be impractical later in the optimization queue. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "function.h" |
| #include "tree.h" |
| #include "gimple-expr.h" |
| #include "tree-pass.h" |
| #include "cgraph.h" |
| #include "calls.h" |
| #include "varasm.h" |
| #include "ipa-utils.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| |
| /* Return true when NODE cannot be local. Worker for cgraph_local_node_p. */ |
| |
| static bool |
| non_local_p (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED) |
| { |
| return !(node->only_called_directly_or_aliased_p () |
| /* i386 would need update to output thunk with local calling |
| conventions. */ |
| && !node->thunk |
| && node->definition |
| && !DECL_EXTERNAL (node->decl) |
| && !lookup_attribute ("noipa", DECL_ATTRIBUTES (node->decl)) |
| && !node->externally_visible |
| && !node->used_from_other_partition |
| && !node->in_other_partition |
| && node->get_availability () >= AVAIL_AVAILABLE); |
| } |
| |
| /* Return true when function can be marked local. */ |
| |
| bool |
| cgraph_node::local_p (void) |
| { |
| cgraph_node *n = ultimate_alias_target (); |
| |
| if (n->thunk) |
| return n->callees->callee->local_p (); |
| return !n->call_for_symbol_thunks_and_aliases (non_local_p, |
| NULL, true); |
| |
| } |
| |
| /* A helper for comdat_can_be_unshared_p. */ |
| |
| static bool |
| comdat_can_be_unshared_p_1 (symtab_node *node) |
| { |
| if (!node->externally_visible) |
| return true; |
| if (node->address_can_be_compared_p ()) |
| { |
| struct ipa_ref *ref; |
| |
| for (unsigned int i = 0; node->iterate_referring (i, ref); i++) |
| if (ref->address_matters_p ()) |
| return false; |
| } |
| |
| /* If the symbol is used in some weird way, better to not touch it. */ |
| if (node->force_output) |
| return false; |
| |
| /* Explicit instantiations needs to be output when possibly |
| used externally. */ |
| if (node->forced_by_abi |
| && TREE_PUBLIC (node->decl) |
| && (node->resolution != LDPR_PREVAILING_DEF_IRONLY |
| && !flag_whole_program)) |
| return false; |
| |
| /* Non-readonly and volatile variables cannot be duplicated. */ |
| if (is_a <varpool_node *> (node) |
| && (!TREE_READONLY (node->decl) |
| || TREE_THIS_VOLATILE (node->decl))) |
| return false; |
| return true; |
| } |
| |
| /* COMDAT functions must be shared only if they have address taken, |
| otherwise we can produce our own private implementation with |
| -fwhole-program. |
| Return true when turning COMDAT function static cannot lead to wrong |
| code when the resulting object links with a library defining same COMDAT. |
| |
| Virtual functions do have their addresses taken from the vtables, |
| but in C++ there is no way to compare their addresses for equality. */ |
| |
| static bool |
| comdat_can_be_unshared_p (symtab_node *node) |
| { |
| if (!comdat_can_be_unshared_p_1 (node)) |
| return false; |
| if (node->same_comdat_group) |
| { |
| symtab_node *next; |
| |
| /* If more than one function is in the same COMDAT group, it must |
| be shared even if just one function in the comdat group has |
| address taken. */ |
| for (next = node->same_comdat_group; |
| next != node; next = next->same_comdat_group) |
| if (!comdat_can_be_unshared_p_1 (next)) |
| return false; |
| } |
| return true; |
| } |
| |
| /* Return true when function NODE should be considered externally visible. */ |
| |
| static bool |
| cgraph_externally_visible_p (struct cgraph_node *node, |
| bool whole_program) |
| { |
| while (node->transparent_alias && node->definition) |
| node = node->get_alias_target (); |
| if (!node->definition) |
| return false; |
| if (!TREE_PUBLIC (node->decl) |
| || DECL_EXTERNAL (node->decl)) |
| return false; |
| |
| /* Do not try to localize built-in functions yet. One of problems is that we |
| end up mangling their asm for WHOPR that makes it impossible to call them |
| using the implicit built-in declarations anymore. Similarly this enables |
| us to remove them as unreachable before actual calls may appear during |
| expansion or folding. */ |
| if (fndecl_built_in_p (node->decl)) |
| return true; |
| |
| /* If linker counts on us, we must preserve the function. */ |
| if (node->used_from_object_file_p ()) |
| return true; |
| if (DECL_PRESERVE_P (node->decl)) |
| return true; |
| if (lookup_attribute ("externally_visible", |
| DECL_ATTRIBUTES (node->decl))) |
| return true; |
| if (lookup_attribute ("noipa", DECL_ATTRIBUTES (node->decl))) |
| return true; |
| if (TARGET_DLLIMPORT_DECL_ATTRIBUTES |
| && lookup_attribute ("dllexport", |
| DECL_ATTRIBUTES (node->decl))) |
| return true; |
| |
| /* Limitation of gas requires us to output targets of symver aliases as |
| global symbols. This is binutils PR 25295. */ |
| ipa_ref *ref; |
| FOR_EACH_ALIAS (node, ref) |
| if (ref->referring->symver) |
| return true; |
| |
| if (node->resolution == LDPR_PREVAILING_DEF_IRONLY) |
| return false; |
| /* When doing LTO or whole program, we can bring COMDAT functions static. |
| This improves code quality and we know we will duplicate them at most twice |
| (in the case that we are not using plugin and link with object file |
| implementing same COMDAT) */ |
| if (((in_lto_p || whole_program) && !flag_incremental_link) |
| && DECL_COMDAT (node->decl) |
| && comdat_can_be_unshared_p (node)) |
| return false; |
| |
| /* When doing link time optimizations, hidden symbols become local. */ |
| if ((in_lto_p && !flag_incremental_link) |
| && (DECL_VISIBILITY (node->decl) == VISIBILITY_HIDDEN |
| || DECL_VISIBILITY (node->decl) == VISIBILITY_INTERNAL) |
| /* Be sure that node is defined in IR file, not in other object |
| file. In that case we don't set used_from_other_object_file. */ |
| && node->definition) |
| ; |
| else if (!whole_program) |
| return true; |
| |
| if (MAIN_NAME_P (DECL_NAME (node->decl))) |
| return true; |
| |
| return false; |
| } |
| |
| /* Return true when variable should be considered externally visible. */ |
| |
| bool |
| varpool_node::externally_visible_p (void) |
| { |
| while (transparent_alias && definition) |
| return get_alias_target ()->externally_visible_p (); |
| if (DECL_EXTERNAL (decl)) |
| return true; |
| |
| if (!TREE_PUBLIC (decl)) |
| return false; |
| |
| /* If linker counts on us, we must preserve the function. */ |
| if (used_from_object_file_p ()) |
| return true; |
| |
| /* Bringing TLS variables local may cause dynamic linker failures |
| on limits of static TLS vars. */ |
| if (DECL_THREAD_LOCAL_P (decl) |
| && (DECL_TLS_MODEL (decl) != TLS_MODEL_EMULATED |
| && DECL_TLS_MODEL (decl) != TLS_MODEL_INITIAL_EXEC)) |
| return true; |
| |
| if (DECL_HARD_REGISTER (decl)) |
| return true; |
| if (DECL_PRESERVE_P (decl)) |
| return true; |
| if (lookup_attribute ("externally_visible", |
| DECL_ATTRIBUTES (decl))) |
| return true; |
| if (TARGET_DLLIMPORT_DECL_ATTRIBUTES |
| && lookup_attribute ("dllexport", |
| DECL_ATTRIBUTES (decl))) |
| return true; |
| |
| /* Limitation of gas requires us to output targets of symver aliases as |
| global symbols. This is binutils PR 25295. */ |
| ipa_ref *ref; |
| FOR_EACH_ALIAS (this, ref) |
| if (ref->referring->symver) |
| return true; |
| |
| if (resolution == LDPR_PREVAILING_DEF_IRONLY) |
| return false; |
| |
| /* As a special case, the COMDAT virtual tables can be unshared. |
| In LTO mode turn vtables into static variables. The variable is readonly, |
| so this does not enable more optimization, but referring static var |
| is faster for dynamic linking. Also this match logic hidding vtables |
| from LTO symbol tables. */ |
| if (((in_lto_p || flag_whole_program) && !flag_incremental_link) |
| && DECL_COMDAT (decl) |
| && comdat_can_be_unshared_p (this)) |
| return false; |
| |
| /* When doing link time optimizations, hidden symbols become local. */ |
| if (in_lto_p && !flag_incremental_link |
| && (DECL_VISIBILITY (decl) == VISIBILITY_HIDDEN |
| || DECL_VISIBILITY (decl) == VISIBILITY_INTERNAL) |
| /* Be sure that node is defined in IR file, not in other object |
| file. In that case we don't set used_from_other_object_file. */ |
| && definition) |
| ; |
| else if (!flag_whole_program) |
| return true; |
| |
| /* Do not attempt to privatize COMDATS by default. |
| This would break linking with C++ libraries sharing |
| inline definitions. |
| |
| FIXME: We can do so for readonly vars with no address taken and |
| possibly also for vtables since no direct pointer comparsion is done. |
| It might be interesting to do so to reduce linking overhead. */ |
| if (DECL_COMDAT (decl) || DECL_WEAK (decl)) |
| return true; |
| return false; |
| } |
| |
| /* Return true if reference to NODE can be replaced by a local alias. |
| Local aliases save dynamic linking overhead and enable more optimizations. |
| */ |
| |
| static bool |
| can_replace_by_local_alias (symtab_node *node) |
| { |
| /* If aliases aren't supported, we can't do replacement. */ |
| if (!TARGET_SUPPORTS_ALIASES) |
| return false; |
| |
| /* Weakrefs have a reason to be non-local. Be sure we do not replace |
| them. */ |
| while (node->transparent_alias && node->definition && !node->weakref) |
| node = node->get_alias_target (); |
| if (node->weakref) |
| return false; |
| |
| return (node->get_availability () > AVAIL_INTERPOSABLE |
| && !decl_binds_to_current_def_p (node->decl) |
| && !node->can_be_discarded_p ()); |
| } |
| |
| /* Return true if we can replace reference to NODE by local alias |
| within a virtual table. Generally we can replace function pointers |
| and virtual table pointers. */ |
| |
| static bool |
| can_replace_by_local_alias_in_vtable (symtab_node *node) |
| { |
| if (is_a <varpool_node *> (node) |
| && !DECL_VIRTUAL_P (node->decl)) |
| return false; |
| return can_replace_by_local_alias (node); |
| } |
| |
| /* walk_tree callback that rewrites initializer references. */ |
| |
| static tree |
| update_vtable_references (tree *tp, int *walk_subtrees, |
| void *data ATTRIBUTE_UNUSED) |
| { |
| if (VAR_OR_FUNCTION_DECL_P (*tp)) |
| { |
| if (can_replace_by_local_alias_in_vtable (symtab_node::get (*tp))) |
| *tp = symtab_node::get (*tp)->noninterposable_alias ()->decl; |
| *walk_subtrees = 0; |
| } |
| else if (IS_TYPE_OR_DECL_P (*tp)) |
| *walk_subtrees = 0; |
| return NULL; |
| } |
| |
| /* In LTO we can remove COMDAT groups and weak symbols. |
| Either turn them into normal symbols or external symbol depending on |
| resolution info. */ |
| |
| static void |
| update_visibility_by_resolution_info (symtab_node * node) |
| { |
| bool define; |
| |
| if (!node->externally_visible |
| || (!DECL_WEAK (node->decl) && !DECL_ONE_ONLY (node->decl)) |
| || node->resolution == LDPR_UNKNOWN) |
| return; |
| |
| define = (node->resolution == LDPR_PREVAILING_DEF_IRONLY |
| || node->resolution == LDPR_PREVAILING_DEF |
| || node->resolution == LDPR_UNDEF |
| || node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP); |
| |
| /* The linker decisions ought to agree in the whole group. */ |
| if (node->same_comdat_group) |
| for (symtab_node *next = node->same_comdat_group; |
| next != node; next = next->same_comdat_group) |
| { |
| if (!next->externally_visible || next->transparent_alias) |
| continue; |
| |
| bool same_def |
| = define == (next->resolution == LDPR_PREVAILING_DEF_IRONLY |
| || next->resolution == LDPR_PREVAILING_DEF |
| || next->resolution == LDPR_UNDEF |
| || next->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP); |
| gcc_assert (in_lto_p || same_def); |
| if (!same_def) |
| return; |
| } |
| |
| if (node->same_comdat_group) |
| for (symtab_node *next = node->same_comdat_group; |
| next != node; next = next->same_comdat_group) |
| { |
| /* During incremental linking we need to keep symbol weak for future |
| linking. We can still drop definition if we know non-LTO world |
| prevails. */ |
| if (!flag_incremental_link) |
| { |
| DECL_WEAK (next->decl) = false; |
| next->set_comdat_group (NULL); |
| } |
| if (!define) |
| { |
| if (next->externally_visible) |
| DECL_EXTERNAL (next->decl) = true; |
| next->set_comdat_group (NULL); |
| } |
| } |
| |
| /* During incremental linking we need to keep symbol weak for future |
| linking. We can still drop definition if we know non-LTO world prevails. */ |
| if (!flag_incremental_link) |
| { |
| DECL_WEAK (node->decl) = false; |
| node->set_comdat_group (NULL); |
| node->dissolve_same_comdat_group_list (); |
| } |
| if (!define) |
| { |
| DECL_EXTERNAL (node->decl) = true; |
| node->set_comdat_group (NULL); |
| node->dissolve_same_comdat_group_list (); |
| } |
| } |
| |
| /* Try to get rid of weakref. */ |
| |
| static void |
| optimize_weakref (symtab_node *node) |
| { |
| bool strip_weakref = false; |
| bool static_alias = false; |
| |
| gcc_assert (node->weakref); |
| |
| /* Weakrefs with no target defined cannot be optimized. */ |
| if (!node->analyzed) |
| return; |
| symtab_node *target = node->get_alias_target (); |
| |
| /* Weakrefs to weakrefs can be optimized only if target can be. */ |
| if (target->weakref) |
| optimize_weakref (target); |
| if (target->weakref) |
| return; |
| |
| /* If we have definition of weakref's target and we know it binds locally, |
| we can turn weakref to static alias. */ |
| if (TARGET_SUPPORTS_ALIASES |
| && target->definition && decl_binds_to_current_def_p (target->decl)) |
| strip_weakref = static_alias = true; |
| /* Otherwise we can turn weakref into transparent alias. This transformation |
| may break asm statements which directly refers to symbol name and expect |
| GNU as to translate it via .weakref directive. So do not optimize when |
| DECL_PRESERVED is set and .weakref is supported. */ |
| else if ((!DECL_PRESERVE_P (target->decl) |
| || IDENTIFIER_TRANSPARENT_ALIAS (DECL_ASSEMBLER_NAME (node->decl))) |
| && !DECL_WEAK (target->decl) |
| && !DECL_EXTERNAL (target->decl) |
| && ((target->definition && !target->can_be_discarded_p ()) |
| || target->resolution != LDPR_UNDEF)) |
| strip_weakref = true; |
| if (!strip_weakref) |
| return; |
| node->weakref = false; |
| IDENTIFIER_TRANSPARENT_ALIAS (DECL_ASSEMBLER_NAME (node->decl)) = 0; |
| TREE_CHAIN (DECL_ASSEMBLER_NAME (node->decl)) = NULL_TREE; |
| DECL_ATTRIBUTES (node->decl) = remove_attribute ("weakref", |
| DECL_ATTRIBUTES |
| (node->decl)); |
| |
| if (dump_file) |
| fprintf (dump_file, "Optimizing weakref %s %s\n", |
| node->dump_name (), |
| static_alias ? "as static alias" : "as transparent alias"); |
| |
| if (static_alias) |
| { |
| /* make_decl_local will shortcircuit if it doesn't see TREE_PUBLIC. |
| be sure it really clears the WEAK flag. */ |
| TREE_PUBLIC (node->decl) = true; |
| node->make_decl_local (); |
| node->forced_by_abi = false; |
| node->resolution = LDPR_PREVAILING_DEF_IRONLY; |
| node->externally_visible = false; |
| gcc_assert (!DECL_WEAK (node->decl)); |
| node->transparent_alias = false; |
| } |
| else |
| { |
| symtab->change_decl_assembler_name |
| (node->decl, DECL_ASSEMBLER_NAME (node->get_alias_target ()->decl)); |
| node->transparent_alias = true; |
| node->copy_visibility_from (target); |
| } |
| gcc_assert (node->alias); |
| } |
| |
| /* NODE is an externally visible definition, which we've discovered is |
| not needed externally. Make it local to this compilation. */ |
| |
| static void |
| localize_node (bool whole_program, symtab_node *node) |
| { |
| gcc_assert (whole_program || in_lto_p || !TREE_PUBLIC (node->decl)); |
| |
| /* It is possible that one comdat group contains both hidden and non-hidden |
| symbols. In this case we can privatize all hidden symbol but we need |
| to keep non-hidden exported. */ |
| if (node->same_comdat_group |
| && (node->resolution == LDPR_PREVAILING_DEF_IRONLY |
| || node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)) |
| { |
| symtab_node *next; |
| for (next = node->same_comdat_group; |
| next != node; next = next->same_comdat_group) |
| if (next->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP |
| || next->resolution == LDPR_PREVAILING_DEF) |
| break; |
| if (node != next) |
| { |
| if (!node->transparent_alias) |
| { |
| node->resolution = LDPR_PREVAILING_DEF_IRONLY; |
| node->make_decl_local (); |
| if (!flag_incremental_link) |
| node->unique_name |= true; |
| return; |
| } |
| } |
| } |
| /* For similar reason do not privatize whole comdat when seeing comdat |
| local. Wait for non-comdat symbol to be privatized first. */ |
| if (node->comdat_local_p ()) |
| return; |
| |
| if (node->same_comdat_group && TREE_PUBLIC (node->decl)) |
| { |
| for (symtab_node *next = node->same_comdat_group; |
| next != node; next = next->same_comdat_group) |
| { |
| next->set_comdat_group (NULL); |
| if (!next->alias) |
| next->set_section (NULL); |
| if (!next->transparent_alias) |
| next->make_decl_local (); |
| next->unique_name |
| |= ((next->resolution == LDPR_PREVAILING_DEF_IRONLY |
| || next->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP) |
| && TREE_PUBLIC (next->decl) |
| && !flag_incremental_link); |
| } |
| |
| /* Now everything's localized, the grouping has no meaning, and |
| will cause crashes if we keep it around. */ |
| node->dissolve_same_comdat_group_list (); |
| } |
| |
| node->unique_name |
| |= ((node->resolution == LDPR_PREVAILING_DEF_IRONLY |
| || node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP) |
| && TREE_PUBLIC (node->decl) |
| && !flag_incremental_link); |
| |
| if (TREE_PUBLIC (node->decl)) |
| node->set_comdat_group (NULL); |
| if (DECL_COMDAT (node->decl) && !node->alias) |
| node->set_section (NULL); |
| if (!node->transparent_alias) |
| { |
| node->resolution = LDPR_PREVAILING_DEF_IRONLY; |
| node->make_decl_local (); |
| } |
| } |
| |
| /* Decide on visibility of all symbols. */ |
| |
| static unsigned int |
| function_and_variable_visibility (bool whole_program) |
| { |
| struct cgraph_node *node; |
| varpool_node *vnode; |
| |
| /* All aliases should be processed at this point. */ |
| gcc_checking_assert (!alias_pairs || !alias_pairs->length ()); |
| |
| #ifdef ASM_OUTPUT_DEF |
| FOR_EACH_DEFINED_FUNCTION (node) |
| { |
| if (node->get_availability () != AVAIL_INTERPOSABLE |
| || DECL_EXTERNAL (node->decl) |
| || node->has_aliases_p () |
| || lookup_attribute ("noipa", DECL_ATTRIBUTES (node->decl))) |
| continue; |
| |
| cgraph_node *alias = 0; |
| cgraph_edge *next_edge; |
| for (cgraph_edge *e = node->callees; e; e = next_edge) |
| { |
| next_edge = e->next_callee; |
| /* Recursive function calls usually can't be interposed. */ |
| |
| if (!e->recursive_p ()) |
| continue; |
| |
| if (!alias) |
| { |
| alias = dyn_cast<cgraph_node *> (node->noninterposable_alias ()); |
| gcc_assert (alias && alias != node); |
| } |
| |
| e->redirect_callee (alias); |
| if (gimple_has_body_p (e->caller->decl)) |
| { |
| push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl)); |
| cgraph_edge::redirect_call_stmt_to_callee (e); |
| pop_cfun (); |
| } |
| } |
| } |
| #endif |
| |
| FOR_EACH_FUNCTION (node) |
| { |
| int flags = flags_from_decl_or_type (node->decl); |
| |
| /* Optimize away PURE and CONST constructors and destructors. */ |
| if (node->analyzed |
| && (DECL_STATIC_CONSTRUCTOR (node->decl) |
| || DECL_STATIC_DESTRUCTOR (node->decl)) |
| && (flags & (ECF_CONST | ECF_PURE)) |
| && !(flags & ECF_LOOPING_CONST_OR_PURE) |
| && opt_for_fn (node->decl, optimize)) |
| { |
| DECL_STATIC_CONSTRUCTOR (node->decl) = 0; |
| DECL_STATIC_DESTRUCTOR (node->decl) = 0; |
| } |
| |
| /* Frontends and alias code marks nodes as needed before parsing |
| is finished. We may end up marking as node external nodes |
| where this flag is meaningless strip it. */ |
| if (DECL_EXTERNAL (node->decl) || !node->definition) |
| { |
| node->force_output = 0; |
| node->forced_by_abi = 0; |
| } |
| |
| /* C++ FE on lack of COMDAT support create local COMDAT functions |
| (that ought to be shared but cannot due to object format |
| limitations). It is necessary to keep the flag to make rest of C++ FE |
| happy. Clear the flag here to avoid confusion in middle-end. */ |
| if (DECL_COMDAT (node->decl) && !TREE_PUBLIC (node->decl)) |
| DECL_COMDAT (node->decl) = 0; |
| |
| /* For external decls stop tracking same_comdat_group. It doesn't matter |
| what comdat group they are in when they won't be emitted in this TU. |
| |
| An exception is LTO where we may end up with both external |
| and non-external declarations in the same comdat group in |
| the case declarations was not merged. */ |
| if (node->same_comdat_group && DECL_EXTERNAL (node->decl) && !in_lto_p) |
| { |
| if (flag_checking) |
| { |
| for (symtab_node *n = node->same_comdat_group; |
| n != node; |
| n = n->same_comdat_group) |
| /* If at least one of same comdat group functions is external, |
| all of them have to be, otherwise it is a front-end bug. */ |
| gcc_assert (DECL_EXTERNAL (n->decl)); |
| } |
| node->dissolve_same_comdat_group_list (); |
| } |
| gcc_assert ((!DECL_WEAK (node->decl) |
| && !DECL_COMDAT (node->decl)) |
| || TREE_PUBLIC (node->decl) |
| || node->weakref |
| || DECL_EXTERNAL (node->decl)); |
| if (cgraph_externally_visible_p (node, whole_program)) |
| { |
| gcc_assert (!node->inlined_to); |
| node->externally_visible = true; |
| } |
| else |
| { |
| node->externally_visible = false; |
| node->forced_by_abi = false; |
| } |
| if (!node->externally_visible |
| && node->definition && !node->weakref |
| && !DECL_EXTERNAL (node->decl)) |
| localize_node (whole_program, node); |
| |
| if (node->thunk |
| && TREE_PUBLIC (node->decl)) |
| { |
| struct cgraph_node *decl_node = node; |
| |
| decl_node = decl_node->callees->callee->function_symbol (); |
| |
| /* Thunks have the same visibility as function they are attached to. |
| Make sure the C++ front end set this up properly. */ |
| if (DECL_ONE_ONLY (decl_node->decl)) |
| { |
| gcc_checking_assert (DECL_COMDAT (node->decl) |
| == DECL_COMDAT (decl_node->decl)); |
| gcc_checking_assert (node->in_same_comdat_group_p (decl_node)); |
| gcc_checking_assert (node->same_comdat_group); |
| } |
| node->forced_by_abi = decl_node->forced_by_abi; |
| if (DECL_EXTERNAL (decl_node->decl)) |
| DECL_EXTERNAL (node->decl) = 1; |
| } |
| |
| update_visibility_by_resolution_info (node); |
| if (node->weakref) |
| optimize_weakref (node); |
| } |
| FOR_EACH_DEFINED_FUNCTION (node) |
| { |
| if (!node->local) |
| node->local |= node->local_p (); |
| |
| /* If we know that function cannot be overwritten by a |
| different semantics and moreover its section cannot be |
| discarded, replace all direct calls by calls to an |
| noninterposable alias. This make dynamic linking cheaper and |
| enable more optimization. |
| |
| TODO: We can also update virtual tables. */ |
| if (node->callers |
| && can_replace_by_local_alias (node)) |
| { |
| cgraph_node *alias = dyn_cast<cgraph_node *> |
| (node->noninterposable_alias ()); |
| |
| if (alias && alias != node) |
| { |
| while (node->callers) |
| { |
| struct cgraph_edge *e = node->callers; |
| |
| e->redirect_callee (alias); |
| if (gimple_has_body_p (e->caller->decl)) |
| { |
| push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl)); |
| cgraph_edge::redirect_call_stmt_to_callee (e); |
| pop_cfun (); |
| } |
| } |
| } |
| } |
| } |
| FOR_EACH_VARIABLE (vnode) |
| { |
| /* weak flag makes no sense on local variables. */ |
| gcc_assert (!DECL_WEAK (vnode->decl) |
| || vnode->weakref |
| || TREE_PUBLIC (vnode->decl) |
| || DECL_EXTERNAL (vnode->decl)); |
| /* In several cases declarations cannot be common: |
| |
| - when declaration has initializer |
| - when it is in weak |
| - when it has specific section |
| - when it resides in non-generic address space. |
| - if declaration is local, it will get into .local common section |
| so common flag is not needed. Frontends still produce these in |
| certain cases, such as for: |
| |
| static int a __attribute__ ((common)) |
| |
| Canonicalize things here and clear the redundant flag. */ |
| if (DECL_COMMON (vnode->decl) |
| && (!(TREE_PUBLIC (vnode->decl) |
| || DECL_EXTERNAL (vnode->decl)) |
| || (DECL_INITIAL (vnode->decl) |
| && DECL_INITIAL (vnode->decl) != error_mark_node) |
| || DECL_WEAK (vnode->decl) |
| || DECL_SECTION_NAME (vnode->decl) != NULL |
| || ! (ADDR_SPACE_GENERIC_P |
| (TYPE_ADDR_SPACE (TREE_TYPE (vnode->decl)))))) |
| DECL_COMMON (vnode->decl) = 0; |
| if (vnode->weakref) |
| optimize_weakref (vnode); |
| } |
| FOR_EACH_DEFINED_VARIABLE (vnode) |
| { |
| if (!vnode->definition) |
| continue; |
| if (vnode->externally_visible_p ()) |
| vnode->externally_visible = true; |
| else |
| { |
| vnode->externally_visible = false; |
| vnode->forced_by_abi = false; |
| } |
| if (lookup_attribute ("no_reorder", |
| DECL_ATTRIBUTES (vnode->decl))) |
| vnode->no_reorder = 1; |
| |
| if (!vnode->externally_visible |
| && !vnode->transparent_alias |
| && !DECL_EXTERNAL (vnode->decl)) |
| localize_node (whole_program, vnode); |
| |
| update_visibility_by_resolution_info (vnode); |
| |
| /* Update virtual tables to point to local aliases where possible. */ |
| if (DECL_VIRTUAL_P (vnode->decl) |
| && !DECL_EXTERNAL (vnode->decl)) |
| { |
| int i; |
| struct ipa_ref *ref; |
| bool found = false; |
| |
| /* See if there is something to update. */ |
| for (i = 0; vnode->iterate_reference (i, ref); i++) |
| if (ref->use == IPA_REF_ADDR |
| && can_replace_by_local_alias_in_vtable (ref->referred)) |
| { |
| found = true; |
| break; |
| } |
| if (found) |
| { |
| hash_set<tree> visited_nodes; |
| |
| vnode->get_constructor (); |
| walk_tree (&DECL_INITIAL (vnode->decl), |
| update_vtable_references, NULL, &visited_nodes); |
| vnode->remove_all_references (); |
| record_references_in_initializer (vnode->decl, false); |
| } |
| } |
| } |
| |
| if (dump_file) |
| { |
| fprintf (dump_file, "\nMarking local functions:"); |
| FOR_EACH_DEFINED_FUNCTION (node) |
| if (node->local) |
| fprintf (dump_file, " %s", node->dump_name ()); |
| fprintf (dump_file, "\n\n"); |
| fprintf (dump_file, "\nMarking externally visible functions:"); |
| FOR_EACH_DEFINED_FUNCTION (node) |
| if (node->externally_visible) |
| fprintf (dump_file, " %s", node->dump_name ()); |
| fprintf (dump_file, "\n\n"); |
| fprintf (dump_file, "\nMarking externally visible variables:"); |
| FOR_EACH_DEFINED_VARIABLE (vnode) |
| if (vnode->externally_visible) |
| fprintf (dump_file, " %s", vnode->dump_name ()); |
| fprintf (dump_file, "\n\n"); |
| } |
| symtab->function_flags_ready = true; |
| return 0; |
| } |
| |
| /* Local function pass handling visibilities. This happens before LTO streaming |
| so in particular -fwhole-program should be ignored at this level. */ |
| |
| namespace { |
| |
| const pass_data pass_data_ipa_function_and_variable_visibility = |
| { |
| SIMPLE_IPA_PASS, /* type */ |
| "visibility", /* name */ |
| OPTGROUP_NONE, /* optinfo_flags */ |
| TV_CGRAPHOPT, /* tv_id */ |
| 0, /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| ( TODO_remove_functions | TODO_dump_symtab ), /* todo_flags_finish */ |
| }; |
| |
| /* Bring functions local at LTO time with -fwhole-program. */ |
| |
| static unsigned int |
| whole_program_function_and_variable_visibility (void) |
| { |
| function_and_variable_visibility (flag_whole_program); |
| if (optimize || in_lto_p) |
| ipa_discover_variable_flags (); |
| return 0; |
| } |
| |
| } // anon namespace |
| |
| namespace { |
| |
| const pass_data pass_data_ipa_whole_program_visibility = |
| { |
| IPA_PASS, /* type */ |
| "whole-program", /* name */ |
| OPTGROUP_NONE, /* optinfo_flags */ |
| TV_CGRAPHOPT, /* tv_id */ |
| 0, /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| ( TODO_remove_functions | TODO_dump_symtab ), /* todo_flags_finish */ |
| }; |
| |
| class pass_ipa_whole_program_visibility : public ipa_opt_pass_d |
| { |
| public: |
| pass_ipa_whole_program_visibility (gcc::context *ctxt) |
| : ipa_opt_pass_d (pass_data_ipa_whole_program_visibility, 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 *) |
| { |
| /* Do not re-run on ltrans stage. */ |
| return !flag_ltrans; |
| } |
| virtual unsigned int execute (function *) |
| { |
| return whole_program_function_and_variable_visibility (); |
| } |
| |
| }; // class pass_ipa_whole_program_visibility |
| |
| } // anon namespace |
| |
| ipa_opt_pass_d * |
| make_pass_ipa_whole_program_visibility (gcc::context *ctxt) |
| { |
| return new pass_ipa_whole_program_visibility (ctxt); |
| } |
| |
| class pass_ipa_function_and_variable_visibility : public simple_ipa_opt_pass |
| { |
| public: |
| pass_ipa_function_and_variable_visibility (gcc::context *ctxt) |
| : simple_ipa_opt_pass (pass_data_ipa_function_and_variable_visibility, |
| ctxt) |
| {} |
| |
| /* opt_pass methods: */ |
| virtual unsigned int execute (function *) |
| { |
| return function_and_variable_visibility (flag_whole_program && !flag_lto); |
| } |
| |
| }; // class pass_ipa_function_and_variable_visibility |
| |
| simple_ipa_opt_pass * |
| make_pass_ipa_function_and_variable_visibility (gcc::context *ctxt) |
| { |
| return new pass_ipa_function_and_variable_visibility (ctxt); |
| } |