/* IPA visibility pass
- Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003-2015 Free Software Foundation, Inc.
This file is part of GCC.
#include "system.h"
#include "coretypes.h"
#include "tm.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "hard-reg-set.h"
+#include "function.h"
#include "cgraph.h"
#include "tree-pass.h"
-#include "pointer-set.h"
#include "calls.h"
#include "gimple-expr.h"
#include "varasm.h"
/* Return true when NODE can not be local. Worker for cgraph_local_node_p. */
static bool
-cgraph_non_local_node_p_1 (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED)
+non_local_p (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED)
{
- /* FIXME: Aliases can be local, but i386 gets thunks wrong then. */
- return !(cgraph_only_called_directly_or_aliased_p (node)
- && !ipa_ref_has_aliases_p (&node->ref_list)
- && node->definition
- && !DECL_EXTERNAL (node->decl)
- && !node->externally_visible
- && !node->used_from_other_partition
- && !node->in_other_partition);
+ return !(node->only_called_directly_or_aliased_p ()
+ /* i386 would need update to output thunk with locak calling
+ ocnvetions. */
+ && !node->thunk.thunk_p
+ && node->definition
+ && !DECL_EXTERNAL (node->decl)
+ && !node->externally_visible
+ && !node->used_from_other_partition
+ && !node->in_other_partition);
}
/* Return true when function can be marked local. */
bool
-cgraph_local_node_p (struct cgraph_node *node)
+cgraph_node::local_p (void)
{
- struct cgraph_node *n = cgraph_function_or_thunk_node (node, NULL);
+ cgraph_node *n = ultimate_alias_target ();
- /* FIXME: thunks can be considered local, but we need prevent i386
- from attempting to change calling convention of them. */
if (n->thunk.thunk_p)
- return false;
- return !cgraph_for_node_and_aliases (n,
- cgraph_non_local_node_p_1, NULL, true);
+ return n->callees->callee->local_p ();
+ return !n->call_for_symbol_thunks_and_aliases (non_local_p,
+ NULL, true);
}
-/* Return true when there is a reference to node and it is not vtable. */
-bool
-address_taken_from_non_vtable_p (symtab_node *node)
-{
- int i;
- struct ipa_ref *ref;
- for (i = 0; ipa_ref_list_referring_iterate (&node->ref_list,
- i, ref); i++)
- if (ref->use == IPA_REF_ADDR)
- {
- varpool_node *node;
- if (is_a <cgraph_node *> (ref->referring))
- return true;
- node = ipa_ref_referring_varpool_node (ref);
- if (!DECL_VIRTUAL_P (node->decl))
- return true;
- }
- return false;
-}
-
/* A helper for comdat_can_be_unshared_p. */
static bool
{
if (!node->externally_visible)
return true;
- /* When address is taken, we don't know if equality comparison won't
- break eventually. Exception are virutal functions, C++
- constructors/destructors and vtables, where this is not possible by
- language standard. */
- if (!DECL_VIRTUAL_P (node->decl)
- && (TREE_CODE (node->decl) != FUNCTION_DECL
- || (!DECL_CXX_CONSTRUCTOR_P (node->decl)
- && !DECL_CXX_DESTRUCTOR_P (node->decl)))
- && address_taken_from_non_vtable_p (node))
- return false;
+ 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 true;
/* If linker counts on us, we must preserve the function. */
- if (symtab_used_from_object_file_p (node))
+ if (node->used_from_object_file_p ())
return true;
if (DECL_PRESERVE_P (node->decl))
return true;
return true;
if (node->resolution == LDPR_PREVAILING_DEF_IRONLY)
return false;
- /* When doing LTO or whole program, we can bring COMDAT functions static.
+ /* 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 (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 VNODE should be considered externally visible. */
+/* Return true when variable should be considered externally visible. */
bool
-varpool_externally_visible_p (varpool_node *vnode)
+varpool_node::externally_visible_p (void)
{
- if (DECL_EXTERNAL (vnode->decl))
+ if (DECL_EXTERNAL (decl))
return true;
- if (!TREE_PUBLIC (vnode->decl))
+ if (!TREE_PUBLIC (decl))
return false;
/* If linker counts on us, we must preserve the function. */
- if (symtab_used_from_object_file_p (vnode))
+ 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 (vnode->decl))
+ if (DECL_HARD_REGISTER (decl))
return true;
- if (DECL_PRESERVE_P (vnode->decl))
+ if (DECL_PRESERVE_P (decl))
return true;
if (lookup_attribute ("externally_visible",
- DECL_ATTRIBUTES (vnode->decl)))
+ DECL_ATTRIBUTES (decl)))
return true;
if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
&& lookup_attribute ("dllexport",
- DECL_ATTRIBUTES (vnode->decl)))
+ DECL_ATTRIBUTES (decl)))
return true;
/* See if we have linker information about symbol not being used or
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 (vnode->resolution == LDPR_PREVAILING_DEF_IRONLY)
+ 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.
is faster for dynamic linking. Also this match logic hidding vtables
from LTO symbol tables. */
if ((in_lto_p || flag_whole_program)
- && DECL_COMDAT (vnode->decl)
- && comdat_can_be_unshared_p (vnode))
+ && DECL_COMDAT (decl)
+ && comdat_can_be_unshared_p (this))
return false;
/* When doing link time optimizations, hidden symbols become local. */
if (in_lto_p
- && (DECL_VISIBILITY (vnode->decl) == VISIBILITY_HIDDEN
- || DECL_VISIBILITY (vnode->decl) == VISIBILITY_INTERNAL)
+ && (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. */
- && vnode->definition)
+ && definition)
;
else if (!flag_whole_program)
return true;
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 (vnode->decl) || DECL_WEAK (vnode->decl))
+ if (DECL_COMDAT (decl) || DECL_WEAK (decl))
return true;
return false;
}
bool
can_replace_by_local_alias (symtab_node *node)
{
- return (symtab_node_availability (node) > AVAIL_OVERWRITABLE
+ return (node->get_availability () > AVAIL_INTERPOSABLE
&& !decl_binds_to_current_def_p (node->decl)
- && !symtab_can_be_discarded (node));
+ && !node->can_be_discarded_p ());
}
/* Return true if we can replace refernece to NODE by local alias
/* walk_tree callback that rewrites initializer references. */
static tree
-update_vtable_references (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
+update_vtable_references (tree *tp, int *walk_subtrees,
+ void *data ATTRIBUTE_UNUSED)
{
if (TREE_CODE (*tp) == VAR_DECL
|| TREE_CODE (*tp) == FUNCTION_DECL)
{
- if (can_replace_by_local_alias_in_vtable (symtab_get_node (*tp)))
- *tp = symtab_nonoverwritable_alias (symtab_get_node (*tp))->decl;
+ 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))
if (!node->externally_visible
|| (!DECL_WEAK (node->decl) && !DECL_ONE_ONLY (node->decl))
- || node->resolution == LDPR_UNKNOWN
- || node->resolution == LDPR_UNDEF)
+ || 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)
- gcc_assert (!next->externally_visible
- || define == (next->resolution == LDPR_PREVAILING_DEF_IRONLY
- || next->resolution == LDPR_PREVAILING_DEF
- || next->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP));
+ {
+ if (!next->externally_visible)
+ 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;
if (next->externally_visible
&& !define)
DECL_EXTERNAL (next->decl) = true;
- if (!next->alias)
- next->reset_section ();
}
node->set_comdat_group (NULL);
DECL_WEAK (node->decl) = false;
if (!define)
DECL_EXTERNAL (node->decl) = true;
- if (!node->alias)
- node->reset_section ();
- symtab_dissolve_same_comdat_group_list (node);
+ node->dissolve_same_comdat_group_list ();
}
/* Decide on visibility of all symbols. */
all of them have to be, otherwise it is a front-end bug. */
gcc_assert (DECL_EXTERNAL (n->decl));
#endif
- symtab_dissolve_same_comdat_group_list (node);
+ node->dissolve_same_comdat_group_list ();
}
gcc_assert ((!DECL_WEAK (node->decl)
- && !DECL_COMDAT (node->decl))
+ && !DECL_COMDAT (node->decl))
|| TREE_PUBLIC (node->decl)
|| node->weakref
|| DECL_EXTERNAL (node->decl));
&& node->definition && !node->weakref
&& !DECL_EXTERNAL (node->decl))
{
- bool reset = TREE_PUBLIC (node->decl);
gcc_assert (whole_program || in_lto_p
|| !TREE_PUBLIC (node->decl));
node->unique_name = ((node->resolution == LDPR_PREVAILING_DEF_IRONLY
next = next->same_comdat_group)
{
next->set_comdat_group (NULL);
- symtab_make_decl_local (next->decl);
- if (!node->alias)
- node->reset_section ();
+ if (!next->alias)
+ next->set_section (NULL);
+ next->make_decl_local ();
next->unique_name = ((next->resolution == LDPR_PREVAILING_DEF_IRONLY
|| next->unique_name
|| next->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
in the group and they will all be made local. We need to
dissolve the group at once so that the predicate does not
segfault though. */
- symtab_dissolve_same_comdat_group_list (node);
+ node->dissolve_same_comdat_group_list ();
}
if (TREE_PUBLIC (node->decl))
node->set_comdat_group (NULL);
- symtab_make_decl_local (node->decl);
- if (reset && !node->alias)
- node->reset_section ();
+ if (DECL_COMDAT (node->decl) && !node->alias)
+ node->set_section (NULL);
+ node->make_decl_local ();
}
if (node->thunk.thunk_p
+ && !node->thunk.add_pointer_bounds_args
&& TREE_PUBLIC (node->decl))
{
struct cgraph_node *decl_node = node;
- decl_node = cgraph_function_node (decl_node->callees->callee, NULL);
+ 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. */
{
gcc_checking_assert (DECL_COMDAT (node->decl)
== DECL_COMDAT (decl_node->decl));
- gcc_checking_assert (symtab_in_same_comdat_p (node, decl_node));
+ 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;
}
FOR_EACH_DEFINED_FUNCTION (node)
{
- node->local.local |= cgraph_local_node_p (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 nonoverwritable alias. This make dynamic linking
+ 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))
+ if (node->callers
+ && can_replace_by_local_alias (node))
{
- struct cgraph_node *alias = cgraph (symtab_nonoverwritable_alias (node));
+ cgraph_node *alias = dyn_cast<cgraph_node *>
+ (node->noninterposable_alias ());
if (alias && alias != node)
{
{
struct cgraph_edge *e = node->callers;
- cgraph_redirect_edge_callee (e, alias);
+ e->redirect_callee (alias);
if (gimple_has_body_p (e->caller->decl))
{
push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
- cgraph_redirect_edge_call_stmt_to_callee (e);
+ e->redirect_call_stmt_to_callee ();
pop_cfun ();
}
}
{
if (!vnode->definition)
continue;
- if (varpool_externally_visible_p (vnode))
+ 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->weakref)
{
- bool reset = TREE_PUBLIC (vnode->decl);
gcc_assert (in_lto_p || whole_program || !TREE_PUBLIC (vnode->decl));
vnode->unique_name = ((vnode->resolution == LDPR_PREVAILING_DEF_IRONLY
|| vnode->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
next = next->same_comdat_group)
{
next->set_comdat_group (NULL);
- symtab_make_decl_local (next->decl);
if (!next->alias)
- next->reset_section ();
+ next->set_section (NULL);
+ next->make_decl_local ();
next->unique_name = ((next->resolution == LDPR_PREVAILING_DEF_IRONLY
|| next->unique_name
|| next->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
&& TREE_PUBLIC (next->decl));
}
- symtab_dissolve_same_comdat_group_list (vnode);
+ vnode->dissolve_same_comdat_group_list ();
}
if (TREE_PUBLIC (vnode->decl))
vnode->set_comdat_group (NULL);
- symtab_make_decl_local (vnode->decl);
- if (reset && !vnode->alias)
- vnode->reset_section ();
+ if (DECL_COMDAT (vnode->decl) && !vnode->alias)
+ vnode->set_section (NULL);
+ vnode->make_decl_local ();
vnode->resolution = LDPR_PREVAILING_DEF_IRONLY;
}
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)
- /* FIXME: currently this optimization breaks on AIX. Disable it for targets
- without comdat support for now. */
- && SUPPORTS_ONE_ONLY)
+ && !DECL_EXTERNAL (vnode->decl))
{
int i;
struct ipa_ref *ref;
bool found = false;
/* See if there is something to update. */
- for (i = 0; ipa_ref_list_referring_iterate (&vnode->ref_list,
- i, ref); i++)
+ for (i = 0; vnode->iterate_referring (i, ref); i++)
if (ref->use == IPA_REF_ADDR
&& can_replace_by_local_alias_in_vtable (ref->referred))
{
}
if (found)
{
- struct pointer_set_t *visited_nodes = pointer_set_create ();
+ hash_set<tree> visited_nodes;
+
+ vnode->get_constructor ();
walk_tree (&DECL_INITIAL (vnode->decl),
- update_vtable_references, NULL, visited_nodes);
- pointer_set_destroy (visited_nodes);
- ipa_remove_all_references (&vnode->ref_list);
+ update_vtable_references, NULL, &visited_nodes);
+ vnode->remove_all_references ();
record_references_in_initializer (vnode->decl, false);
}
}
fprintf (dump_file, " %s", vnode->name ());
fprintf (dump_file, "\n\n");
}
- cgraph_function_flags_ready = true;
+ symtab->function_flags_ready = true;
return 0;
}
SIMPLE_IPA_PASS, /* type */
"visibility", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_execute */
TV_CGRAPHOPT, /* tv_id */
0, /* properties_required */
0, /* properties_provided */
IPA_PASS, /* type */
"whole-program", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_execute */
TV_CGRAPHOPT, /* tv_id */
0, /* properties_required */
0, /* properties_provided */