aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/gcc/symtab.c
diff options
context:
space:
mode:
authorBen Cheng <bccheng@google.com>2014-03-25 22:37:19 -0700
committerBen Cheng <bccheng@google.com>2014-03-25 22:37:19 -0700
commit1bc5aee63eb72b341f506ad058502cd0361f0d10 (patch)
treec607e8252f3405424ff15bc2d00aa38dadbb2518 /gcc-4.9/gcc/symtab.c
parent283a0bf58fcf333c58a2a92c3ebbc41fb9eb1fdb (diff)
downloadtoolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.tar.gz
toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.tar.bz2
toolchain_gcc-1bc5aee63eb72b341f506ad058502cd0361f0d10.zip
Initial checkin of GCC 4.9.0 from trunk (r208799).
Change-Id: I48a3c08bb98542aa215912a75f03c0890e497dba
Diffstat (limited to 'gcc-4.9/gcc/symtab.c')
-rw-r--r--gcc-4.9/gcc/symtab.c1323
1 files changed, 1323 insertions, 0 deletions
diff --git a/gcc-4.9/gcc/symtab.c b/gcc-4.9/gcc/symtab.c
new file mode 100644
index 000000000..4db4870fa
--- /dev/null
+++ b/gcc-4.9/gcc/symtab.c
@@ -0,0 +1,1323 @@
+/* Symbol table.
+ Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ Contributed by Jan Hubicka
+
+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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "tree.h"
+#include "print-tree.h"
+#include "varasm.h"
+#include "function.h"
+#include "emit-rtl.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "tree-inline.h"
+#include "langhooks.h"
+#include "hashtab.h"
+#include "cgraph.h"
+#include "diagnostic.h"
+#include "timevar.h"
+#include "lto-streamer.h"
+#include "output.h"
+
+const char * const ld_plugin_symbol_resolution_names[]=
+{
+ "",
+ "undef",
+ "prevailing_def",
+ "prevailing_def_ironly",
+ "preempted_reg",
+ "preempted_ir",
+ "resolved_ir",
+ "resolved_exec",
+ "resolved_dyn",
+ "prevailing_def_ironly_exp"
+};
+
+/* Hash table used to convert declarations into nodes. */
+static GTY((param_is (symtab_node))) htab_t symtab_hash;
+/* Hash table used to convert assembler names into nodes. */
+static GTY((param_is (symtab_node))) htab_t assembler_name_hash;
+
+/* Linked list of symbol table nodes. */
+symtab_node *symtab_nodes;
+
+/* The order index of the next symtab node to be created. This is
+ used so that we can sort the cgraph nodes in order by when we saw
+ them, to support -fno-toplevel-reorder. */
+int symtab_order;
+
+/* Returns a hash code for P. */
+
+static hashval_t
+hash_node (const void *p)
+{
+ const symtab_node *n = (const symtab_node *) p;
+ return (hashval_t) DECL_UID (n->decl);
+}
+
+
+/* Returns nonzero if P1 and P2 are equal. */
+
+static int
+eq_node (const void *p1, const void *p2)
+{
+ const symtab_node *n1 = (const symtab_node *) p1;
+ const symtab_node *n2 = (const symtab_node *) p2;
+ return DECL_UID (n1->decl) == DECL_UID (n2->decl);
+}
+
+/* Hash asmnames ignoring the user specified marks. */
+
+static hashval_t
+decl_assembler_name_hash (const_tree asmname)
+{
+ if (IDENTIFIER_POINTER (asmname)[0] == '*')
+ {
+ const char *decl_str = IDENTIFIER_POINTER (asmname) + 1;
+ size_t ulp_len = strlen (user_label_prefix);
+
+ if (ulp_len == 0)
+ ;
+ else if (strncmp (decl_str, user_label_prefix, ulp_len) == 0)
+ decl_str += ulp_len;
+
+ return htab_hash_string (decl_str);
+ }
+
+ return htab_hash_string (IDENTIFIER_POINTER (asmname));
+}
+
+
+/* Returns a hash code for P. */
+
+static hashval_t
+hash_node_by_assembler_name (const void *p)
+{
+ const symtab_node *n = (const symtab_node *) p;
+ return (hashval_t) decl_assembler_name_hash (DECL_ASSEMBLER_NAME (n->decl));
+}
+
+/* Compare ASMNAME with the DECL_ASSEMBLER_NAME of DECL. */
+
+static bool
+decl_assembler_name_equal (tree decl, const_tree asmname)
+{
+ tree decl_asmname = DECL_ASSEMBLER_NAME (decl);
+ const char *decl_str;
+ const char *asmname_str;
+ bool test = false;
+
+ if (decl_asmname == asmname)
+ return true;
+
+ decl_str = IDENTIFIER_POINTER (decl_asmname);
+ asmname_str = IDENTIFIER_POINTER (asmname);
+
+
+ /* If the target assembler name was set by the user, things are trickier.
+ We have a leading '*' to begin with. After that, it's arguable what
+ is the correct thing to do with -fleading-underscore. Arguably, we've
+ historically been doing the wrong thing in assemble_alias by always
+ printing the leading underscore. Since we're not changing that, make
+ sure user_label_prefix follows the '*' before matching. */
+ if (decl_str[0] == '*')
+ {
+ size_t ulp_len = strlen (user_label_prefix);
+
+ decl_str ++;
+
+ if (ulp_len == 0)
+ test = true;
+ else if (strncmp (decl_str, user_label_prefix, ulp_len) == 0)
+ decl_str += ulp_len, test=true;
+ else
+ decl_str --;
+ }
+ if (asmname_str[0] == '*')
+ {
+ size_t ulp_len = strlen (user_label_prefix);
+
+ asmname_str ++;
+
+ if (ulp_len == 0)
+ test = true;
+ else if (strncmp (asmname_str, user_label_prefix, ulp_len) == 0)
+ asmname_str += ulp_len, test=true;
+ else
+ asmname_str --;
+ }
+
+ if (!test)
+ return false;
+ return strcmp (decl_str, asmname_str) == 0;
+}
+
+
+/* Returns nonzero if P1 and P2 are equal. */
+
+static int
+eq_assembler_name (const void *p1, const void *p2)
+{
+ const symtab_node *n1 = (const symtab_node *) p1;
+ const_tree name = (const_tree)p2;
+ return (decl_assembler_name_equal (n1->decl, name));
+}
+
+/* Insert NODE to assembler name hash. */
+
+static void
+insert_to_assembler_name_hash (symtab_node *node, bool with_clones)
+{
+ if (is_a <varpool_node> (node) && DECL_HARD_REGISTER (node->decl))
+ return;
+ gcc_checking_assert (!node->previous_sharing_asm_name
+ && !node->next_sharing_asm_name);
+ if (assembler_name_hash)
+ {
+ void **aslot;
+ struct cgraph_node *cnode;
+ tree decl = node->decl;
+
+ tree name = DECL_ASSEMBLER_NAME (node->decl);
+
+ aslot = htab_find_slot_with_hash (assembler_name_hash, name,
+ decl_assembler_name_hash (name),
+ INSERT);
+ gcc_assert (*aslot != node);
+ node->next_sharing_asm_name = (symtab_node *)*aslot;
+ if (*aslot != NULL)
+ ((symtab_node *)*aslot)->previous_sharing_asm_name = node;
+ *aslot = node;
+
+ /* Update also possible inline clones sharing a decl. */
+ cnode = dyn_cast <cgraph_node> (node);
+ if (cnode && cnode->clones && with_clones)
+ for (cnode = cnode->clones; cnode; cnode = cnode->next_sibling_clone)
+ if (cnode->decl == decl)
+ insert_to_assembler_name_hash (cnode, true);
+ }
+
+}
+
+/* Remove NODE from assembler name hash. */
+
+static void
+unlink_from_assembler_name_hash (symtab_node *node, bool with_clones)
+{
+ if (assembler_name_hash)
+ {
+ struct cgraph_node *cnode;
+ tree decl = node->decl;
+
+ if (node->next_sharing_asm_name)
+ node->next_sharing_asm_name->previous_sharing_asm_name
+ = node->previous_sharing_asm_name;
+ if (node->previous_sharing_asm_name)
+ {
+ node->previous_sharing_asm_name->next_sharing_asm_name
+ = node->next_sharing_asm_name;
+ }
+ else
+ {
+ tree name = DECL_ASSEMBLER_NAME (node->decl);
+ void **slot;
+ slot = htab_find_slot_with_hash (assembler_name_hash, name,
+ decl_assembler_name_hash (name),
+ NO_INSERT);
+ gcc_assert (*slot == node);
+ if (!node->next_sharing_asm_name)
+ htab_clear_slot (assembler_name_hash, slot);
+ else
+ *slot = node->next_sharing_asm_name;
+ }
+ node->next_sharing_asm_name = NULL;
+ node->previous_sharing_asm_name = NULL;
+
+ /* Update also possible inline clones sharing a decl. */
+ cnode = dyn_cast <cgraph_node> (node);
+ if (cnode && cnode->clones && with_clones)
+ for (cnode = cnode->clones; cnode; cnode = cnode->next_sibling_clone)
+ if (cnode->decl == decl)
+ unlink_from_assembler_name_hash (cnode, true);
+ }
+}
+
+/* Arrange node to be first in its entry of assembler_name_hash. */
+
+void
+symtab_prevail_in_asm_name_hash (symtab_node *node)
+{
+ unlink_from_assembler_name_hash (node, false);
+ insert_to_assembler_name_hash (node, false);
+}
+
+
+/* Add node into symbol table. This function is not used directly, but via
+ cgraph/varpool node creation routines. */
+
+void
+symtab_register_node (symtab_node *node)
+{
+ struct symtab_node key;
+ symtab_node **slot;
+
+ node->next = symtab_nodes;
+ node->previous = NULL;
+ if (symtab_nodes)
+ symtab_nodes->previous = node;
+ symtab_nodes = node;
+
+ if (!symtab_hash)
+ symtab_hash = htab_create_ggc (10, hash_node, eq_node, NULL);
+ key.decl = node->decl;
+ slot = (symtab_node **) htab_find_slot (symtab_hash, &key, INSERT);
+ if (*slot == NULL)
+ *slot = node;
+
+ ipa_empty_ref_list (&node->ref_list);
+
+ node->order = symtab_order++;
+
+ /* Be sure to do this last; C++ FE might create new nodes via
+ DECL_ASSEMBLER_NAME langhook! */
+ insert_to_assembler_name_hash (node, false);
+}
+
+/* Make NODE to be the one symtab hash is pointing to. Used when reshaping tree
+ of inline clones. */
+
+void
+symtab_insert_node_to_hashtable (symtab_node *node)
+{
+ struct symtab_node key;
+ symtab_node **slot;
+
+ if (!symtab_hash)
+ symtab_hash = htab_create_ggc (10, hash_node, eq_node, NULL);
+ key.decl = node->decl;
+ slot = (symtab_node **) htab_find_slot (symtab_hash, &key, INSERT);
+ *slot = node;
+}
+
+/* Remove node from symbol table. This function is not used directly, but via
+ cgraph/varpool node removal routines. */
+
+void
+symtab_unregister_node (symtab_node *node)
+{
+ void **slot;
+ ipa_remove_all_references (&node->ref_list);
+ ipa_remove_all_referring (&node->ref_list);
+
+ if (node->same_comdat_group)
+ {
+ symtab_node *prev;
+ for (prev = node->same_comdat_group;
+ prev->same_comdat_group != node;
+ prev = prev->same_comdat_group)
+ ;
+ if (node->same_comdat_group == prev)
+ prev->same_comdat_group = NULL;
+ else
+ prev->same_comdat_group = node->same_comdat_group;
+ node->same_comdat_group = NULL;
+ }
+
+ if (node->previous)
+ node->previous->next = node->next;
+ else
+ symtab_nodes = node->next;
+ if (node->next)
+ node->next->previous = node->previous;
+ node->next = NULL;
+ node->previous = NULL;
+
+ slot = htab_find_slot (symtab_hash, node, NO_INSERT);
+
+ /* During LTO symtab merging we temporarily corrupt decl to symtab node
+ hash. */
+ gcc_assert ((slot && *slot) || in_lto_p);
+ if (slot && *slot && *slot == node)
+ {
+ symtab_node *replacement_node = NULL;
+ if (cgraph_node *cnode = dyn_cast <cgraph_node> (node))
+ replacement_node = cgraph_find_replacement_node (cnode);
+ if (!replacement_node)
+ htab_clear_slot (symtab_hash, slot);
+ else
+ *slot = replacement_node;
+ }
+ if (!is_a <varpool_node> (node) || !DECL_HARD_REGISTER (node->decl))
+ unlink_from_assembler_name_hash (node, false);
+}
+
+/* Return symbol table node associated with DECL, if any,
+ and NULL otherwise. */
+
+symtab_node *
+symtab_get_node (const_tree decl)
+{
+ symtab_node **slot;
+ struct symtab_node key;
+
+#ifdef ENABLE_CHECKING
+ /* Check that we are called for sane type of object - functions
+ and static or external variables. */
+ gcc_checking_assert (TREE_CODE (decl) == FUNCTION_DECL
+ || (TREE_CODE (decl) == VAR_DECL
+ && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)
+ || in_lto_p)));
+#endif
+
+ if (!symtab_hash)
+ return NULL;
+
+ key.decl = CONST_CAST2 (tree, const_tree, decl);
+
+ slot = (symtab_node **) htab_find_slot (symtab_hash, &key,
+ NO_INSERT);
+
+ if (slot)
+ return *slot;
+ return NULL;
+}
+
+/* Remove symtab NODE from the symbol table. */
+
+void
+symtab_remove_node (symtab_node *node)
+{
+ if (cgraph_node *cnode = dyn_cast <cgraph_node> (node))
+ cgraph_remove_node (cnode);
+ else if (varpool_node *vnode = dyn_cast <varpool_node> (node))
+ varpool_remove_node (vnode);
+}
+
+/* Initalize asm name hash unless. */
+
+void
+symtab_initialize_asm_name_hash (void)
+{
+ symtab_node *node;
+ if (!assembler_name_hash)
+ {
+ assembler_name_hash =
+ htab_create_ggc (10, hash_node_by_assembler_name, eq_assembler_name,
+ NULL);
+ FOR_EACH_SYMBOL (node)
+ insert_to_assembler_name_hash (node, false);
+ }
+}
+
+/* Return the cgraph node that has ASMNAME for its DECL_ASSEMBLER_NAME.
+ Return NULL if there's no such node. */
+
+symtab_node *
+symtab_node_for_asm (const_tree asmname)
+{
+ symtab_node *node;
+ void **slot;
+
+ symtab_initialize_asm_name_hash ();
+ slot = htab_find_slot_with_hash (assembler_name_hash, asmname,
+ decl_assembler_name_hash (asmname),
+ NO_INSERT);
+
+ if (slot)
+ {
+ node = (symtab_node *) *slot;
+ return node;
+ }
+ return NULL;
+}
+
+/* Set the DECL_ASSEMBLER_NAME and update symtab hashtables. */
+
+void
+change_decl_assembler_name (tree decl, tree name)
+{
+ symtab_node *node = NULL;
+
+ /* We can have user ASM names on things, like global register variables, that
+ are not in the symbol table. */
+ if ((TREE_CODE (decl) == VAR_DECL
+ && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
+ || TREE_CODE (decl) == FUNCTION_DECL)
+ node = symtab_get_node (decl);
+ if (!DECL_ASSEMBLER_NAME_SET_P (decl))
+ {
+ SET_DECL_ASSEMBLER_NAME (decl, name);
+ if (node)
+ insert_to_assembler_name_hash (node, true);
+ }
+ else
+ {
+ if (name == DECL_ASSEMBLER_NAME (decl))
+ return;
+
+ tree alias = (IDENTIFIER_TRANSPARENT_ALIAS (DECL_ASSEMBLER_NAME (decl))
+ ? TREE_CHAIN (DECL_ASSEMBLER_NAME (decl))
+ : NULL);
+ if (node)
+ unlink_from_assembler_name_hash (node, true);
+ if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
+ && DECL_RTL_SET_P (decl))
+ warning (0, "%D renamed after being referenced in assembly", decl);
+
+ SET_DECL_ASSEMBLER_NAME (decl, name);
+ if (alias)
+ {
+ IDENTIFIER_TRANSPARENT_ALIAS (name) = 1;
+ TREE_CHAIN (name) = alias;
+ }
+ if (node)
+ insert_to_assembler_name_hash (node, true);
+ }
+}
+
+/* Add NEW_ to the same comdat group that OLD is in. */
+
+void
+symtab_add_to_same_comdat_group (symtab_node *new_node,
+ symtab_node *old_node)
+{
+ gcc_assert (DECL_ONE_ONLY (old_node->decl));
+ gcc_assert (!new_node->same_comdat_group);
+ gcc_assert (new_node != old_node);
+
+ DECL_COMDAT_GROUP (new_node->decl) = DECL_COMDAT_GROUP (old_node->decl);
+ new_node->same_comdat_group = old_node;
+ if (!old_node->same_comdat_group)
+ old_node->same_comdat_group = new_node;
+ else
+ {
+ symtab_node *n;
+ for (n = old_node->same_comdat_group;
+ n->same_comdat_group != old_node;
+ n = n->same_comdat_group)
+ ;
+ n->same_comdat_group = new_node;
+ }
+}
+
+/* Dissolve the same_comdat_group list in which NODE resides. */
+
+void
+symtab_dissolve_same_comdat_group_list (symtab_node *node)
+{
+ symtab_node *n = node;
+ symtab_node *next;
+
+ if (!node->same_comdat_group)
+ return;
+ do
+ {
+ next = n->same_comdat_group;
+ n->same_comdat_group = NULL;
+ /* Clear DECL_COMDAT_GROUP for comdat locals, since
+ make_decl_local doesn't. */
+ if (!TREE_PUBLIC (n->decl))
+ DECL_COMDAT_GROUP (n->decl) = NULL_TREE;
+ n = next;
+ }
+ while (n != node);
+}
+
+/* Return printable assembler name of NODE.
+ This function is used only for debugging. When assembler name
+ is unknown go with identifier name. */
+
+const char *
+symtab_node::asm_name () const
+{
+ if (!DECL_ASSEMBLER_NAME_SET_P (decl))
+ return lang_hooks.decl_printable_name (decl, 2);
+ return IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+}
+
+/* Return printable identifier name. */
+
+const char *
+symtab_node::name () const
+{
+ return lang_hooks.decl_printable_name (decl, 2);
+}
+
+static const char * const symtab_type_names[] = {"symbol", "function", "variable"};
+
+/* Dump base fields of symtab nodes. Not to be used directly. */
+
+void
+dump_symtab_base (FILE *f, symtab_node *node)
+{
+ static const char * const visibility_types[] = {
+ "default", "protected", "hidden", "internal"
+ };
+
+ fprintf (f, "%s/%i (%s)",
+ node->asm_name (),
+ node->order,
+ node->name ());
+ dump_addr (f, " @", (void *)node);
+ fprintf (f, "\n Type: %s", symtab_type_names[node->type]);
+
+ if (node->definition)
+ fprintf (f, " definition");
+ if (node->analyzed)
+ fprintf (f, " analyzed");
+ if (node->alias)
+ fprintf (f, " alias");
+ if (node->weakref)
+ fprintf (f, " weakref");
+ if (node->cpp_implicit_alias)
+ fprintf (f, " cpp_implicit_alias");
+ if (node->alias_target)
+ fprintf (f, " target:%s",
+ DECL_P (node->alias_target)
+ ? IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME
+ (node->alias_target))
+ : IDENTIFIER_POINTER (node->alias_target));
+ if (node->body_removed)
+ fprintf (f, "\n Body removed by symtab_remove_unreachable_nodes");
+ fprintf (f, "\n Visibility:");
+ if (node->in_other_partition)
+ fprintf (f, " in_other_partition");
+ if (node->used_from_other_partition)
+ fprintf (f, " used_from_other_partition");
+ if (node->force_output)
+ fprintf (f, " force_output");
+ if (node->forced_by_abi)
+ fprintf (f, " forced_by_abi");
+ 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 (TREE_ASM_WRITTEN (node->decl))
+ fprintf (f, " asm_written");
+ if (DECL_EXTERNAL (node->decl))
+ fprintf (f, " external");
+ if (TREE_PUBLIC (node->decl))
+ fprintf (f, " public");
+ if (DECL_COMMON (node->decl))
+ fprintf (f, " common");
+ if (DECL_WEAK (node->decl))
+ fprintf (f, " weak");
+ if (DECL_DLLIMPORT_P (node->decl))
+ fprintf (f, " dll_import");
+ if (DECL_COMDAT (node->decl))
+ fprintf (f, " comdat");
+ if (DECL_COMDAT_GROUP (node->decl))
+ fprintf (f, " comdat_group:%s",
+ IDENTIFIER_POINTER (DECL_COMDAT_GROUP (node->decl)));
+ if (DECL_ONE_ONLY (node->decl))
+ fprintf (f, " one_only");
+ if (DECL_SECTION_NAME (node->decl))
+ fprintf (f, " section_name:%s",
+ TREE_STRING_POINTER (DECL_SECTION_NAME (node->decl)));
+ if (DECL_VISIBILITY_SPECIFIED (node->decl))
+ fprintf (f, " visibility_specified");
+ if (DECL_VISIBILITY (node->decl))
+ fprintf (f, " visibility:%s",
+ visibility_types [DECL_VISIBILITY (node->decl)]);
+ if (DECL_VIRTUAL_P (node->decl))
+ fprintf (f, " virtual");
+ if (DECL_ARTIFICIAL (node->decl))
+ fprintf (f, " artificial");
+ if (TREE_CODE (node->decl) == FUNCTION_DECL)
+ {
+ if (DECL_STATIC_CONSTRUCTOR (node->decl))
+ fprintf (f, " constructor");
+ if (DECL_STATIC_DESTRUCTOR (node->decl))
+ fprintf (f, " destructor");
+ }
+ fprintf (f, "\n");
+
+ if (node->same_comdat_group)
+ fprintf (f, " Same comdat group as: %s/%i\n",
+ node->same_comdat_group->asm_name (),
+ node->same_comdat_group->order);
+ if (node->next_sharing_asm_name)
+ fprintf (f, " next sharing asm name: %i\n",
+ node->next_sharing_asm_name->order);
+ if (node->previous_sharing_asm_name)
+ fprintf (f, " previous sharing asm name: %i\n",
+ node->previous_sharing_asm_name->order);
+
+ if (node->address_taken)
+ fprintf (f, " Address is taken.\n");
+ if (node->aux)
+ {
+ fprintf (f, " Aux:");
+ dump_addr (f, " @", (void *)node->aux);
+ }
+
+ fprintf (f, " References: ");
+ ipa_dump_references (f, &node->ref_list);
+ fprintf (f, " Referring: ");
+ ipa_dump_referring (f, &node->ref_list);
+ if (node->lto_file_data)
+ fprintf (f, " Read from file: %s\n",
+ node->lto_file_data->file_name);
+}
+
+/* Dump symtab node. */
+
+void
+dump_symtab_node (FILE *f, symtab_node *node)
+{
+ if (cgraph_node *cnode = dyn_cast <cgraph_node> (node))
+ dump_cgraph_node (f, cnode);
+ else if (varpool_node *vnode = dyn_cast <varpool_node> (node))
+ dump_varpool_node (f, vnode);
+}
+
+/* Dump symbol table. */
+
+void
+dump_symtab (FILE *f)
+{
+ symtab_node *node;
+ fprintf (f, "Symbol table:\n\n");
+ FOR_EACH_SYMBOL (node)
+ dump_symtab_node (f, node);
+}
+
+/* Dump symtab node NODE to stderr. */
+
+DEBUG_FUNCTION void
+debug_symtab_node (symtab_node *node)
+{
+ dump_symtab_node (stderr, node);
+}
+
+/* Dump symbol table to stderr. */
+
+DEBUG_FUNCTION void
+debug_symtab (void)
+{
+ dump_symtab (stderr);
+}
+
+/* Verify common part of symtab nodes. */
+
+DEBUG_FUNCTION bool
+verify_symtab_base (symtab_node *node)
+{
+ bool error_found = false;
+ symtab_node *hashed_node;
+
+ if (is_a <cgraph_node> (node))
+ {
+ if (TREE_CODE (node->decl) != FUNCTION_DECL)
+ {
+ error ("function symbol is not function");
+ error_found = true;
+ }
+ }
+ else if (is_a <varpool_node> (node))
+ {
+ if (TREE_CODE (node->decl) != VAR_DECL)
+ {
+ error ("variable symbol is not variable");
+ error_found = true;
+ }
+ }
+ else
+ {
+ error ("node has unknown type");
+ error_found = true;
+ }
+
+ if (cgraph_state != CGRAPH_LTO_STREAMING)
+ {
+ hashed_node = symtab_get_node (node->decl);
+ if (!hashed_node)
+ {
+ error ("node not found in symtab decl hashtable");
+ error_found = true;
+ }
+ if (hashed_node != node
+ && (!is_a <cgraph_node> (node)
+ || !dyn_cast <cgraph_node> (node)->clone_of
+ || dyn_cast <cgraph_node> (node)->clone_of->decl
+ != node->decl))
+ {
+ error ("node differs from symtab decl hashtable");
+ error_found = true;
+ }
+ }
+ if (assembler_name_hash)
+ {
+ hashed_node = symtab_node_for_asm (DECL_ASSEMBLER_NAME (node->decl));
+ if (hashed_node && hashed_node->previous_sharing_asm_name)
+ {
+ error ("assembler name hash list corrupted");
+ error_found = true;
+ }
+ while (hashed_node)
+ {
+ if (hashed_node == node)
+ break;
+ hashed_node = hashed_node->next_sharing_asm_name;
+ }
+ if (!hashed_node
+ && !(is_a <varpool_node> (node)
+ || DECL_HARD_REGISTER (node->decl)))
+ {
+ error ("node not found in symtab assembler name hash");
+ error_found = true;
+ }
+ }
+ if (node->previous_sharing_asm_name
+ && node->previous_sharing_asm_name->next_sharing_asm_name != node)
+ {
+ error ("double linked list of assembler names corrupted");
+ error_found = true;
+ }
+ if (node->analyzed && !node->definition)
+ {
+ error ("node is analyzed byt it is not a definition");
+ error_found = true;
+ }
+ if (node->cpp_implicit_alias && !node->alias)
+ {
+ error ("node is alias but not implicit alias");
+ error_found = true;
+ }
+ if (node->alias && !node->definition
+ && !node->weakref)
+ {
+ error ("node is alias but not definition");
+ error_found = true;
+ }
+ if (node->weakref && !node->alias)
+ {
+ error ("node is weakref but not an alias");
+ error_found = true;
+ }
+ if (node->same_comdat_group)
+ {
+ symtab_node *n = node->same_comdat_group;
+
+ if (!DECL_ONE_ONLY (n->decl))
+ {
+ error ("non-DECL_ONE_ONLY node in a same_comdat_group list");
+ error_found = true;
+ }
+ if (n->type != node->type)
+ {
+ error ("mixing different types of symbol in same comdat groups is not supported");
+ error_found = true;
+ }
+ if (n == node)
+ {
+ error ("node is alone in a comdat group");
+ error_found = true;
+ }
+ do
+ {
+ if (!n->same_comdat_group)
+ {
+ error ("same_comdat_group is not a circular list");
+ error_found = true;
+ break;
+ }
+ n = n->same_comdat_group;
+ }
+ while (n != node);
+ if (symtab_comdat_local_p (node))
+ {
+ struct ipa_ref_list *refs = &node->ref_list;
+ struct ipa_ref *ref;
+ for (int i = 0; ipa_ref_list_referring_iterate (refs, i, ref); ++i)
+ {
+ if (!symtab_in_same_comdat_p (ref->referring, node))
+ {
+ error ("comdat-local symbol referred to by %s outside its "
+ "comdat",
+ identifier_to_locale (ref->referring->name()));
+ error_found = true;
+ }
+ }
+ }
+ }
+ return error_found;
+}
+
+/* Verify consistency of NODE. */
+
+DEBUG_FUNCTION void
+verify_symtab_node (symtab_node *node)
+{
+ if (seen_error ())
+ return;
+
+ timevar_push (TV_CGRAPH_VERIFY);
+ if (cgraph_node *cnode = dyn_cast <cgraph_node> (node))
+ verify_cgraph_node (cnode);
+ else
+ if (verify_symtab_base (node))
+ {
+ dump_symtab_node (stderr, node);
+ internal_error ("verify_symtab_node failed");
+ }
+ timevar_pop (TV_CGRAPH_VERIFY);
+}
+
+/* Verify symbol table for internal consistency. */
+
+DEBUG_FUNCTION void
+verify_symtab (void)
+{
+ symtab_node *node;
+ FOR_EACH_SYMBOL (node)
+ verify_symtab_node (node);
+}
+
+/* Return true when RESOLUTION indicate that linker will use
+ the symbol from non-LTO object files. */
+
+bool
+resolution_used_from_other_file_p (enum ld_plugin_symbol_resolution resolution)
+{
+ return (resolution == LDPR_PREVAILING_DEF
+ || resolution == LDPR_PREEMPTED_REG
+ || resolution == LDPR_RESOLVED_EXEC
+ || resolution == LDPR_RESOLVED_DYN);
+}
+
+/* Return true when NODE is known to be used from other (non-LTO) object file.
+ Known only when doing LTO via linker plugin. */
+
+bool
+symtab_used_from_object_file_p (symtab_node *node)
+{
+ if (!TREE_PUBLIC (node->decl) || DECL_EXTERNAL (node->decl))
+ return false;
+ if (resolution_used_from_other_file_p (node->resolution))
+ return true;
+ return false;
+}
+
+/* Make DECL local. FIXME: We shouldn't need to mess with rtl this early,
+ but other code such as notice_global_symbol generates rtl. */
+
+void
+symtab_make_decl_local (tree decl)
+{
+ rtx rtl, symbol;
+
+ /* Avoid clearing DECL_COMDAT_GROUP on comdat-local decls. */
+ if (TREE_PUBLIC (decl) == 0)
+ return;
+
+ if (TREE_CODE (decl) == VAR_DECL)
+ DECL_COMMON (decl) = 0;
+ else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+
+ if (DECL_ONE_ONLY (decl) || DECL_COMDAT (decl))
+ {
+ DECL_SECTION_NAME (decl) = 0;
+ DECL_COMDAT (decl) = 0;
+ }
+ DECL_COMDAT_GROUP (decl) = 0;
+ DECL_WEAK (decl) = 0;
+ DECL_EXTERNAL (decl) = 0;
+ DECL_VISIBILITY_SPECIFIED (decl) = 0;
+ DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT;
+ TREE_PUBLIC (decl) = 0;
+ if (!DECL_RTL_SET_P (decl))
+ return;
+
+ /* Update rtl flags. */
+ make_decl_rtl (decl);
+
+ rtl = DECL_RTL (decl);
+ if (!MEM_P (rtl))
+ return;
+
+ symbol = XEXP (rtl, 0);
+ if (GET_CODE (symbol) != SYMBOL_REF)
+ return;
+
+ SYMBOL_REF_WEAK (symbol) = DECL_WEAK (decl);
+}
+
+/* Return availability of NODE. */
+
+enum availability
+symtab_node_availability (symtab_node *node)
+{
+ if (is_a <cgraph_node> (node))
+ return cgraph_function_body_availability (cgraph (node));
+ else
+ return cgraph_variable_initializer_availability (varpool (node));
+}
+
+/* Given NODE, walk the alias chain to return the symbol NODE is alias of.
+ If NODE is not an alias, return NODE.
+ When AVAILABILITY is non-NULL, get minimal availability in the chain. */
+
+symtab_node *
+symtab_alias_ultimate_target (symtab_node *node, enum availability *availability)
+{
+ bool weakref_p = false;
+
+ if (!node->alias)
+ {
+ if (availability)
+ *availability = symtab_node_availability (node);
+ return node;
+ }
+
+ /* To determine visibility of the target, we follow ELF semantic of aliases.
+ Here alias is an alternative assembler name of a given definition. Its
+ availability prevails the availability of its target (i.e. static alias of
+ weak definition is available.
+
+ Weakref is a different animal (and not part of ELF per se). It is just
+ alternative name of a given symbol used within one complation unit
+ and is translated prior hitting the object file. It inherits the
+ visibility of its target (i.e. weakref of non-overwritable definition
+ is non-overwritable, while weakref of weak definition is weak).
+
+ If we ever get into supporting targets with different semantics, a target
+ hook will be needed here. */
+
+ if (availability)
+ {
+ weakref_p = node->weakref;
+ if (!weakref_p)
+ *availability = symtab_node_availability (node);
+ else
+ *availability = AVAIL_LOCAL;
+ }
+ while (node)
+ {
+ if (node->alias && node->analyzed)
+ node = symtab_alias_target (node);
+ else
+ {
+ if (!availability)
+ ;
+ else if (node->analyzed)
+ {
+ if (weakref_p)
+ {
+ enum availability a = symtab_node_availability (node);
+ if (a < *availability)
+ *availability = a;
+ }
+ }
+ else
+ *availability = AVAIL_NOT_AVAILABLE;
+ return node;
+ }
+ if (node && availability && weakref_p)
+ {
+ enum availability a = symtab_node_availability (node);
+ if (a < *availability)
+ *availability = a;
+ weakref_p = node->weakref;
+ }
+ }
+ if (availability)
+ *availability = AVAIL_NOT_AVAILABLE;
+ return NULL;
+}
+
+/* C++ FE sometimes change linkage flags after producing same body aliases.
+
+ FIXME: C++ produce implicit aliases for virtual functions and vtables that
+ are obviously equivalent. The way it is doing so is however somewhat
+ kludgy and interferes with the visibility code. As a result we need to
+ copy the visibility from the target to get things right. */
+
+void
+fixup_same_cpp_alias_visibility (symtab_node *node, symtab_node *target)
+{
+ if (is_a <cgraph_node> (node))
+ {
+ DECL_DECLARED_INLINE_P (node->decl)
+ = DECL_DECLARED_INLINE_P (target->decl);
+ DECL_DISREGARD_INLINE_LIMITS (node->decl)
+ = DECL_DISREGARD_INLINE_LIMITS (target->decl);
+ }
+ /* FIXME: It is not really clear why those flags should not be copied for
+ functions, too. */
+ else
+ {
+ DECL_WEAK (node->decl) = DECL_WEAK (target->decl);
+ DECL_EXTERNAL (node->decl) = DECL_EXTERNAL (target->decl);
+ DECL_VISIBILITY (node->decl) = DECL_VISIBILITY (target->decl);
+ }
+ DECL_VIRTUAL_P (node->decl) = DECL_VIRTUAL_P (target->decl);
+ if (TREE_PUBLIC (node->decl))
+ {
+ DECL_EXTERNAL (node->decl) = DECL_EXTERNAL (target->decl);
+ DECL_COMDAT (node->decl) = DECL_COMDAT (target->decl);
+ DECL_COMDAT_GROUP (node->decl)
+ = DECL_COMDAT_GROUP (target->decl);
+ if (DECL_ONE_ONLY (target->decl)
+ && !node->same_comdat_group)
+ symtab_add_to_same_comdat_group (node, target);
+ }
+ node->externally_visible = target->externally_visible;
+}
+
+/* Add reference recording that NODE is alias of TARGET.
+ The function can fail in the case of aliasing cycles; in this case
+ it returns false. */
+
+bool
+symtab_resolve_alias (symtab_node *node, symtab_node *target)
+{
+ symtab_node *n;
+
+ gcc_assert (!node->analyzed
+ && !vec_safe_length (node->ref_list.references));
+
+ /* Never let cycles to creep into the symbol table alias references;
+ those will make alias walkers to be infinite. */
+ for (n = target; n && n->alias;
+ n = n->analyzed ? symtab_alias_target (n) : NULL)
+ if (n == node)
+ {
+ if (is_a <cgraph_node> (node))
+ error ("function %q+D part of alias cycle", node->decl);
+ else if (is_a <varpool_node> (node))
+ error ("variable %q+D part of alias cycle", node->decl);
+ else
+ gcc_unreachable ();
+ node->alias = false;
+ return false;
+ }
+
+ /* "analyze" the node - i.e. mark the reference. */
+ node->definition = true;
+ node->alias = true;
+ node->analyzed = true;
+ ipa_record_reference (node, target, IPA_REF_ALIAS, NULL);
+
+ /* Alias targets become reudndant after alias is resolved into an reference.
+ We do not want to keep it around or we would have to mind updating them
+ when renaming symbols. */
+ node->alias_target = NULL;
+
+ if (node->cpp_implicit_alias && cgraph_state >= CGRAPH_STATE_CONSTRUCTION)
+ fixup_same_cpp_alias_visibility (node, target);
+
+ /* If alias has address taken, so does the target. */
+ if (node->address_taken)
+ symtab_alias_ultimate_target (target, NULL)->address_taken = true;
+ return true;
+}
+
+/* Call calback on NODE and aliases associated to NODE.
+ When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
+ skipped. */
+
+bool
+symtab_for_node_and_aliases (symtab_node *node,
+ bool (*callback) (symtab_node *, void *),
+ void *data,
+ bool include_overwritable)
+{
+ int i;
+ struct ipa_ref *ref;
+
+ if (callback (node, data))
+ return true;
+ for (i = 0; ipa_ref_list_referring_iterate (&node->ref_list, i, ref); i++)
+ if (ref->use == IPA_REF_ALIAS)
+ {
+ symtab_node *alias = ref->referring;
+ if (include_overwritable
+ || symtab_node_availability (alias) > AVAIL_OVERWRITABLE)
+ if (symtab_for_node_and_aliases (alias, callback, data,
+ include_overwritable))
+ return true;
+ }
+ return false;
+}
+
+/* Worker searching nonoverwritable alias. */
+
+static bool
+symtab_nonoverwritable_alias_1 (symtab_node *node, void *data)
+{
+ if (decl_binds_to_current_def_p (node->decl))
+ {
+ *(symtab_node **)data = node;
+ return true;
+ }
+ return false;
+}
+
+/* If NODE can not be overwriten by static or dynamic linker to point to different
+ definition, return NODE. Otherwise look for alias with such property and if
+ none exists, introduce new one. */
+
+symtab_node *
+symtab_nonoverwritable_alias (symtab_node *node)
+{
+ tree new_decl;
+ symtab_node *new_node = NULL;
+
+ /* First try to look up existing alias or base object
+ (if that is already non-overwritable). */
+ node = symtab_alias_ultimate_target (node, NULL);
+ gcc_assert (!node->alias && !node->weakref);
+ symtab_for_node_and_aliases (node, symtab_nonoverwritable_alias_1,
+ (void *)&new_node, true);
+ if (new_node)
+ return new_node;
+#ifndef ASM_OUTPUT_DEF
+ /* If aliases aren't supported by the assembler, fail. */
+ return NULL;
+#endif
+
+ /* Otherwise create a new one. */
+ new_decl = copy_node (node->decl);
+ DECL_NAME (new_decl) = clone_function_name (node->decl, "localalias");
+ if (TREE_CODE (new_decl) == FUNCTION_DECL)
+ DECL_STRUCT_FUNCTION (new_decl) = NULL;
+ DECL_INITIAL (new_decl) = NULL;
+ SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
+ SET_DECL_RTL (new_decl, NULL);
+
+ /* Update the properties. */
+ DECL_EXTERNAL (new_decl) = 0;
+ if (DECL_ONE_ONLY (node->decl))
+ DECL_SECTION_NAME (new_decl) = NULL;
+ DECL_COMDAT_GROUP (new_decl) = 0;
+ TREE_PUBLIC (new_decl) = 0;
+ DECL_COMDAT (new_decl) = 0;
+ DECL_WEAK (new_decl) = 0;
+ DECL_VIRTUAL_P (new_decl) = 0;
+ if (TREE_CODE (new_decl) == FUNCTION_DECL)
+ {
+ DECL_STATIC_CONSTRUCTOR (new_decl) = 0;
+ DECL_STATIC_DESTRUCTOR (new_decl) = 0;
+ new_node = cgraph_create_function_alias
+ (new_decl, node->decl);
+ }
+ else
+ new_node = varpool_create_variable_alias (new_decl,
+ node->decl);
+ symtab_resolve_alias (new_node, node);
+ gcc_assert (decl_binds_to_current_def_p (new_decl));
+ return new_node;
+}
+
+/* Return true if A and B represents semantically equivalent symbols. */
+
+bool
+symtab_semantically_equivalent_p (symtab_node *a,
+ symtab_node *b)
+{
+ enum availability avail;
+ symtab_node *ba;
+ symtab_node *bb;
+
+ /* Equivalent functions are equivalent. */
+ if (a->decl == b->decl)
+ return true;
+
+ /* If symbol is not overwritable by different implementation,
+ walk to the base object it defines. */
+ ba = symtab_alias_ultimate_target (a, &avail);
+ if (avail >= AVAIL_AVAILABLE)
+ {
+ if (ba == b)
+ return true;
+ }
+ else
+ ba = a;
+ bb = symtab_alias_ultimate_target (b, &avail);
+ if (avail >= AVAIL_AVAILABLE)
+ {
+ if (a == bb)
+ return true;
+ }
+ else
+ bb = b;
+ return bb == ba;
+}
+
+/* Classify symbol NODE for partitioning. */
+
+enum symbol_partitioning_class
+symtab_get_symbol_partitioning_class (symtab_node *node)
+{
+ /* Inline clones are always duplicated.
+ This include external delcarations. */
+ cgraph_node *cnode = dyn_cast <cgraph_node> (node);
+
+ if (DECL_ABSTRACT (node->decl))
+ return SYMBOL_EXTERNAL;
+
+ if (cnode && cnode->global.inlined_to)
+ return SYMBOL_DUPLICATE;
+
+ /* Weakref aliases are always duplicated. */
+ if (node->weakref)
+ return SYMBOL_DUPLICATE;
+
+ /* External declarations are external. */
+ if (DECL_EXTERNAL (node->decl))
+ return SYMBOL_EXTERNAL;
+
+ if (varpool_node *vnode = dyn_cast <varpool_node> (node))
+ {
+ /* Constant pool references use local symbol names that can not
+ be promoted global. We should never put into a constant pool
+ objects that can not be duplicated across partitions. */
+ if (DECL_IN_CONSTANT_POOL (node->decl))
+ return SYMBOL_DUPLICATE;
+ gcc_checking_assert (vnode->definition);
+ }
+ /* Functions that are cloned may stay in callgraph even if they are unused.
+ Handle them as external; compute_ltrans_boundary take care to make
+ proper things to happen (i.e. to make them appear in the boundary but
+ with body streamed, so clone can me materialized). */
+ else if (!cgraph (node)->definition)
+ return SYMBOL_EXTERNAL;
+
+ /* Linker discardable symbols are duplicated to every use unless they are
+ keyed.
+ Keyed symbols or those. */
+ if (DECL_ONE_ONLY (node->decl)
+ && !node->force_output
+ && !node->forced_by_abi
+ && !symtab_used_from_object_file_p (node))
+ return SYMBOL_DUPLICATE;
+
+ return SYMBOL_PARTITION;
+}
+#include "gt-symtab.h"