Mercurial > hg > CbC > CbC_gcc
diff gcc/ipa-visibility.c @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | |
children | 84e7813d76e9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcc/ipa-visibility.c Fri Oct 27 22:46:09 2017 +0900 @@ -0,0 +1,996 @@ +/* IPA visibility pass + Copyright (C) 2003-2017 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 can not 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 can not 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.thunk_p + && 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.thunk_p) + 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 can not 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 can not 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 (DECL_BUILT_IN (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; + if (node->resolution == LDPR_PREVAILING_DEF_IRONLY) + return false; + /* When doing LTO or whole program, we can bring COMDAT functoins 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; + + if (node->instrumentation_clone + && MAIN_NAME_P (DECL_NAME (node->orig_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; + + /* See if we have linker information about symbol not being used or + if we need to make guess based on the declaration. + + Even if the linker clams the symbol is unused, never bring internal + symbols that are declared by user as used or externally visible. + This is needed for i.e. references from asm statements. */ + if (used_from_object_file_p ()) + 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 can not 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->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) + { + 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 ()) + continue; + + cgraph_node *alias = 0; + for (cgraph_edge *e = node->callees; e; e = 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)); + e->redirect_call_stmt_to_callee (); + 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 can not 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->global.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.thunk_p + && !node->thunk.add_pointer_bounds_args + && 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.local) + node->local.local |= node->local_p (); + + /* If we know that function can not be overwritten by a + different semantics and moreover its section can not 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)); + e->redirect_call_stmt_to_callee (); + 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 can not 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.local) + fprintf (dump_file, " %s", node->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->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->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_readonly_nonaddressable_vars (); + 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); +}