/* Callgraph handling code.
- Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2010
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2010, 2011
Free Software Foundation, Inc.
Contributed by Jan Hubicka
/* Return varpool node assigned to DECL without creating new one. */
struct varpool_node *
-varpool_get_node (tree decl)
+varpool_get_node (const_tree decl)
{
struct varpool_node key, **slot;
- gcc_assert (DECL_P (decl) && TREE_CODE (decl) != FUNCTION_DECL);
+ gcc_assert (TREE_CODE (decl) == VAR_DECL
+ && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)));
if (!varpool_hash)
return NULL;
- key.decl = decl;
+ key.decl = CONST_CAST2 (tree, const_tree, decl);
slot = (struct varpool_node **)
htab_find_slot (varpool_hash, &key, NO_INSERT);
if (!slot)
{
struct varpool_node key, *node, **slot;
- gcc_assert (DECL_P (decl) && TREE_CODE (decl) != FUNCTION_DECL);
+ gcc_assert (TREE_CODE (decl) == VAR_DECL
+ && (TREE_STATIC (decl) || DECL_EXTERNAL (decl) || in_lto_p));
if (!varpool_hash)
varpool_hash = htab_create_ggc (10, hash_varpool_node,
htab_find_slot (varpool_hash, &key, INSERT);
if (*slot)
return *slot;
- node = GGC_CNEW (struct varpool_node);
+ node = ggc_alloc_cleared_varpool_node ();
node->decl = decl;
node->order = cgraph_order++;
node->next = varpool_nodes;
gcc_assert (*slot == node);
htab_clear_slot (varpool_hash, slot);
gcc_assert (!varpool_assembled_nodes_queue);
- if (!node->alias)
- while (node->extra_name)
- varpool_remove_node (node->extra_name);
if (node->next)
node->next->prev = node->prev;
if (node->prev)
node->prev->next = node->next;
else
{
- if (node->alias && node->extra_name)
- {
- gcc_assert (node->extra_name->extra_name == node);
- node->extra_name->extra_name = node->next;
- }
- else
- {
- gcc_assert (varpool_nodes == node);
- varpool_nodes = node->next;
- }
+ gcc_assert (varpool_nodes == node);
+ varpool_nodes = node->next;
}
if (varpool_first_unanalyzed_node == node)
varpool_first_unanalyzed_node = node->next_needed;
fprintf (f, " output");
if (node->externally_visible)
fprintf (f, " externally_visible");
+ if (node->resolution != LDPR_UNKNOWN)
+ fprintf (f, " %s",
+ ld_plugin_symbol_resolution_names[(int)node->resolution]);
if (node->in_other_partition)
fprintf (f, " in_other_partition");
else if (node->used_from_other_partition)
void
varpool_mark_needed_node (struct varpool_node *node)
{
- if (node->alias && node->extra_name)
- node = node->extra_name;
if (!node->needed && node->finalized
&& !TREE_ASM_WRITTEN (node->decl))
varpool_enqueue_needed_node (node);
bool
decide_is_variable_needed (struct varpool_node *node, tree decl)
{
- if (node->used_from_other_partition)
- return true;
/* If the user told us it is used, then it must be so. */
- if ((node->externally_visible && !DECL_COMDAT (decl))
- || node->force_output)
+ if (node->force_output)
return true;
+ gcc_assert (!DECL_EXTERNAL (decl));
+
/* Externally visible variables must be output. The exception is
COMDAT variables that must be output only when they are needed. */
if (TREE_PUBLIC (decl)
- && !flag_whole_program
- && !flag_lto
- && !flag_whopr
&& !DECL_COMDAT (decl)
&& !DECL_EXTERNAL (decl))
return true;
- /* When emulating tls, we actually see references to the control
- variable, rather than the user-level variable. */
- if (!targetm.have_tls
- && TREE_CODE (decl) == VAR_DECL
- && DECL_THREAD_LOCAL_P (decl))
- {
- tree control = emutls_decl (decl);
- if (decide_is_variable_needed (varpool_node (control), control))
- return true;
- }
-
/* When not reordering top level variables, we have to assume that
we are going to keep everything. */
- if (flag_toplevel_reorder)
+ if (!flag_toplevel_reorder)
+ return true;
+ return false;
+}
+
+/* Return if DECL is constant and its initial value is known (so we can do
+ constant folding using DECL_INITIAL (decl)). */
+
+bool
+const_value_known_p (tree decl)
+{
+ if (TREE_CODE (decl) != VAR_DECL
+ &&TREE_CODE (decl) != CONST_DECL)
+ return false;
+
+ if (TREE_CODE (decl) == CONST_DECL
+ || DECL_IN_CONSTANT_POOL (decl))
+ return true;
+
+ gcc_assert (TREE_CODE (decl) == VAR_DECL);
+
+ if (!TREE_READONLY (decl) || TREE_THIS_VOLATILE (decl))
return false;
- /* We want to emit COMDAT variables only when absolutely necessary. */
- if (DECL_COMDAT (decl))
+ /* Gimplifier takes away constructors of local vars */
+ if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
+ return DECL_INITIAL (decl) != NULL;
+
+ gcc_assert (TREE_STATIC (decl) || DECL_EXTERNAL (decl));
+
+ /* Variables declared 'const' without an initializer
+ have zero as the initializer if they may not be
+ overridden at link or run time. */
+ if (!DECL_INITIAL (decl)
+ && (DECL_EXTERNAL (decl)
+ || decl_replaceable_p (decl)))
return false;
+
+ /* Variables declared `const' with an initializer are considered
+ to not be overwritable with different initializer by default.
+
+ ??? Previously we behaved so for scalar variables but not for array
+ accesses. */
return true;
}
{
struct varpool_node *node = varpool_node (decl);
+ gcc_assert (TREE_STATIC (decl));
+
/* The first declaration of a variable that comes through this function
decides whether it is global (in C, has external linkage)
or local (in C, has internal linkage). So do nothing more
if (decide_is_variable_needed (node, decl))
varpool_mark_needed_node (node);
- /* Since we reclaim unreachable nodes at the end of every language
- level unit, we need to be conservative about possible entry points
- there. */
- else if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
- varpool_mark_needed_node (node);
if (cgraph_global_info_ready)
varpool_assemble_pending_decls ();
}
/* If the variable can be overwritten, return OVERWRITABLE. Takes
care of at least two notable extensions - the COMDAT variables
used to share template instantiations in C++. */
- if (!(*targetm.binds_local_p) (node->decl) && !DECL_COMDAT (node->decl))
+ if (!decl_replaceable_p (node->decl))
return AVAIL_OVERWRITABLE;
return AVAIL_AVAILABLE;
}
already informed about increased alignment. */
align_variable (decl, 0);
}
- if (DECL_INITIAL (decl))
+ if (node->alias && node->alias_of)
+ {
+ struct varpool_node *tgt = varpool_node (node->alias_of);
+ if (!VEC_length (ipa_ref_t, node->ref_list.references))
+ ipa_record_reference (NULL, node, NULL, tgt, IPA_REF_ALIAS, NULL);
+ /* C++ FE sometimes change linkage flags after producing same body aliases. */
+ if (node->extra_name_alias)
+ {
+ DECL_WEAK (node->decl) = DECL_WEAK (node->alias_of);
+ TREE_PUBLIC (node->decl) = TREE_PUBLIC (node->alias_of);
+ DECL_VISIBILITY (node->decl) = DECL_VISIBILITY (node->alias_of);
+ if (TREE_PUBLIC (node->decl))
+ {
+ DECL_COMDAT (node->decl) = DECL_COMDAT (node->alias_of);
+ DECL_COMDAT_GROUP (node->decl) = DECL_COMDAT_GROUP (node->alias_of);
+ if (DECL_ONE_ONLY (node->alias_of) && !node->same_comdat_group)
+ {
+ node->same_comdat_group = tgt;
+ if (!tgt->same_comdat_group)
+ tgt->same_comdat_group = node;
+ else
+ {
+ struct varpool_node *n;
+ for (n = tgt->same_comdat_group;
+ n->same_comdat_group != tgt;
+ n = n->same_comdat_group)
+ ;
+ n->same_comdat_group = node;
+ }
+ }
+ }
+ }
+ }
+ else if (DECL_INITIAL (decl))
record_references_in_initializer (decl, analyzed);
if (node->same_comdat_group)
{
return changed;
}
+/* Assemble thunks and aliases asociated to NODE. */
+
+static void
+assemble_aliases (struct varpool_node *node)
+{
+ int i;
+ struct ipa_ref *ref;
+ for (i = 0; ipa_ref_list_refering_iterate (&node->ref_list, i, ref); i++)
+ if (ref->use == IPA_REF_ALIAS)
+ {
+ struct varpool_node *alias = ipa_ref_refering_varpool_node (ref);
+ assemble_alias (alias->decl,
+ DECL_ASSEMBLER_NAME (alias->alias_of));
+ assemble_aliases (alias);
+ }
+}
+
/* Output one variable, if necessary. Return whether we output it. */
bool
varpool_assemble_decl (struct varpool_node *node)
assemble_variable (decl, 0, 1, 0);
if (TREE_ASM_WRITTEN (decl))
{
- struct varpool_node *alias;
-
node->next_needed = varpool_assembled_nodes_queue;
node->prev_needed = NULL;
if (varpool_assembled_nodes_queue)
varpool_assembled_nodes_queue->prev_needed = node;
varpool_assembled_nodes_queue = node;
node->finalized = 1;
-
- /* Also emit any extra name aliases. */
- for (alias = node->extra_name; alias; alias = alias->next)
- {
- /* Update linkage fields in case they've changed. */
- DECL_WEAK (alias->decl) = DECL_WEAK (decl);
- TREE_PUBLIC (alias->decl) = TREE_PUBLIC (decl);
- DECL_VISIBILITY (alias->decl) = DECL_VISIBILITY (decl);
- assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (decl));
- }
-
+ assemble_aliases (node);
return true;
}
}
while (node)
{
- tree decl = node->decl;
next = node->next_needed;
node->needed = 0;
- if (node->finalized
- && (decide_is_variable_needed (node, decl)
- /* ??? Cgraph does not yet rule the world with an iron hand,
- and does not control the emission of debug information.
- After a variable has its DECL_RTL set, we must assume that
- it may be referenced by the debug information, and we can
- no longer elide it. */
- || DECL_RTL_SET_P (decl)))
+ if (node->analyzed
+ && (!varpool_can_remove_if_no_refs (node)
+ /* We just expanded all function bodies. See if any of
+ them needed the variable. */
+ || DECL_RTL_SET_P (node->decl)))
varpool_mark_needed_node (node);
node = next;
varpool_analyze_pending_decls ();
}
+/* For variables in named sections make sure get_variable_section
+ is called before we switch to those sections. Then section
+ conflicts between read-only and read-only requiring relocations
+ sections can be resolved. */
+void
+varpool_finalize_named_section_flags (struct varpool_node *node)
+{
+ if (!TREE_ASM_WRITTEN (node->decl)
+ && !node->alias
+ && !node->in_other_partition
+ && !DECL_EXTERNAL (node->decl)
+ && TREE_CODE (node->decl) == VAR_DECL
+ && !DECL_HAS_VALUE_EXPR_P (node->decl)
+ && DECL_SECTION_NAME (node->decl))
+ get_variable_section (node->decl, false);
+}
+
/* Output all variables enqueued to be assembled. */
bool
varpool_assemble_pending_decls (void)
{
bool changed = false;
+ struct varpool_node *node;
if (seen_error ())
return false;
elsewhere. */
varpool_analyze_pending_decls ();
+ for (node = varpool_nodes_queue; node; node = node->next_needed)
+ varpool_finalize_named_section_flags (node);
+
while (varpool_nodes_queue)
{
struct varpool_node *node = varpool_nodes_queue;
/* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful.
Extra name aliases are output whenever DECL is output. */
-bool
+struct varpool_node *
+varpool_create_variable_alias (tree alias, tree decl)
+{
+ struct varpool_node *alias_node;
+
+ gcc_assert (TREE_CODE (decl) == VAR_DECL);
+ gcc_assert (TREE_CODE (alias) == VAR_DECL);
+ alias_node = varpool_node (alias);
+ alias_node->alias = 1;
+ if (!DECL_EXTERNAL (alias))
+ alias_node->finalized = 1;
+ alias_node->alias_of = decl;
+ if ((!DECL_EXTERNAL (alias)
+ && decide_is_variable_needed (alias_node, alias))
+ || alias_node->needed)
+ varpool_mark_needed_node (alias_node);
+ return alias_node;
+}
+
+/* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful.
+ Extra name aliases are output whenever DECL is output. */
+
+struct varpool_node *
varpool_extra_name_alias (tree alias, tree decl)
{
- struct varpool_node key, *alias_node, *decl_node, **slot;
+ struct varpool_node *alias_node;
#ifndef ASM_OUTPUT_DEF
/* If aliases aren't supported by the assembler, fail. */
- return false;
+ return NULL;
#endif
+ alias_node = varpool_create_variable_alias (alias, decl);
+ alias_node->extra_name_alias = true;
+ return alias_node;
+}
- gcc_assert (TREE_CODE (decl) == VAR_DECL);
- gcc_assert (TREE_CODE (alias) == VAR_DECL);
- /* Make sure the hash table has been created. */
- decl_node = varpool_node (decl);
+/* Return true when NODE is known to be used from other (non-LTO) object file.
+ Known only when doing LTO via linker plugin. */
- key.decl = alias;
+bool
+varpool_used_from_object_file_p (struct varpool_node *node)
+{
+ if (!TREE_PUBLIC (node->decl))
+ return false;
+ if (resolution_used_from_other_file_p (node->resolution))
+ return true;
+ return false;
+}
- slot = (struct varpool_node **) htab_find_slot (varpool_hash, &key, INSERT);
+/* Call calback on NODE and aliases asociated to NODE.
+ When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
+ skipped. */
- /* If the varpool_node has been already created, fail. */
- if (*slot)
- return false;
+bool
+varpool_for_node_and_aliases (struct varpool_node *node,
+ bool (*callback) (struct varpool_node *, void *),
+ void *data,
+ bool include_overwritable)
+{
+ int i;
+ struct ipa_ref *ref;
- alias_node = GGC_CNEW (struct varpool_node);
- alias_node->decl = alias;
- alias_node->alias = 1;
- alias_node->extra_name = decl_node;
- alias_node->next = decl_node->extra_name;
- ipa_empty_ref_list (&alias_node->ref_list);
- if (decl_node->extra_name)
- decl_node->extra_name->prev = alias_node;
- decl_node->extra_name = alias_node;
- *slot = alias_node;
- return true;
+ if (callback (node, data))
+ return true;
+ for (i = 0; ipa_ref_list_refering_iterate (&node->ref_list, i, ref); i++)
+ if (ref->use == IPA_REF_ALIAS)
+ {
+ struct varpool_node *alias = ipa_ref_refering_varpool_node (ref);
+ if (include_overwritable
+ || cgraph_variable_initializer_availability (alias) > AVAIL_OVERWRITABLE)
+ if (varpool_for_node_and_aliases (alias, callback, data,
+ include_overwritable))
+ return true;
+ }
+ return false;
}
-
#include "gt-varpool.h"