| /* Symbol table. |
| Copyright (C) 2012-2022 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 "backend.h" |
| #include "target.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "gimple.h" |
| #include "timevar.h" |
| #include "cgraph.h" |
| #include "lto-streamer.h" |
| #include "print-tree.h" |
| #include "varasm.h" |
| #include "langhooks.h" |
| #include "output.h" |
| #include "ipa-utils.h" |
| #include "calls.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "builtins.h" |
| #include "fold-const.h" |
| |
| static const char *ipa_ref_use_name[] = {"read","write","addr","alias"}; |
| |
| 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" |
| }; |
| |
| /* Follow the IDENTIFIER_TRANSPARENT_ALIAS chain starting at ALIAS |
| until we find an identifier that is not itself a transparent alias. */ |
| |
| static inline tree |
| ultimate_transparent_alias_target (tree alias) |
| { |
| tree target = alias; |
| |
| while (IDENTIFIER_TRANSPARENT_ALIAS (target)) |
| { |
| gcc_checking_assert (TREE_CHAIN (target)); |
| target = TREE_CHAIN (target); |
| } |
| gcc_checking_assert (! IDENTIFIER_TRANSPARENT_ALIAS (target) |
| && ! TREE_CHAIN (target)); |
| |
| return target; |
| } |
| |
| |
| /* Hash asmnames ignoring the user specified marks. */ |
| |
| hashval_t |
| symbol_table::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)); |
| } |
| |
| /* Return true if assembler names NAME1 and NAME2 leads to the same symbol |
| name. */ |
| |
| bool |
| symbol_table::assembler_names_equal_p (const char *name1, const char *name2) |
| { |
| if (name1 != name2) |
| { |
| if (name1[0] == '*') |
| { |
| size_t ulp_len = strlen (user_label_prefix); |
| |
| name1 ++; |
| |
| if (ulp_len == 0) |
| ; |
| else if (strncmp (name1, user_label_prefix, ulp_len) == 0) |
| name1 += ulp_len; |
| else |
| return false; |
| } |
| if (name2[0] == '*') |
| { |
| size_t ulp_len = strlen (user_label_prefix); |
| |
| name2 ++; |
| |
| if (ulp_len == 0) |
| ; |
| else if (strncmp (name2, user_label_prefix, ulp_len) == 0) |
| name2 += ulp_len; |
| else |
| return false; |
| } |
| return !strcmp (name1, name2); |
| } |
| return true; |
| } |
| |
| /* Compare ASMNAME with the DECL_ASSEMBLER_NAME of DECL. */ |
| |
| bool |
| symbol_table::decl_assembler_name_equal (tree decl, const_tree asmname) |
| { |
| tree decl_asmname = DECL_ASSEMBLER_NAME (decl); |
| const char *decl_str; |
| const char *asmname_str; |
| |
| if (decl_asmname == asmname) |
| return true; |
| |
| decl_str = IDENTIFIER_POINTER (decl_asmname); |
| asmname_str = IDENTIFIER_POINTER (asmname); |
| return assembler_names_equal_p (decl_str, asmname_str); |
| } |
| |
| |
| /* Returns nonzero if P1 and P2 are equal. */ |
| |
| /* Insert NODE to assembler name hash. */ |
| |
| void |
| symbol_table::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) |
| { |
| symtab_node **aslot; |
| cgraph_node *cnode; |
| tree decl = node->decl; |
| |
| tree name = DECL_ASSEMBLER_NAME (node->decl); |
| |
| /* C++ FE can produce decls without associated assembler name and insert |
| them to symtab to hold section or TLS information. */ |
| if (!name) |
| return; |
| |
| hashval_t hash = decl_assembler_name_hash (name); |
| aslot = assembler_name_hash->find_slot_with_hash (name, hash, INSERT); |
| gcc_assert (*aslot != node); |
| node->next_sharing_asm_name = (symtab_node *)*aslot; |
| if (*aslot != NULL) |
| (*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. */ |
| |
| void |
| symbol_table::unlink_from_assembler_name_hash (symtab_node *node, |
| bool with_clones) |
| { |
| if (assembler_name_hash) |
| { |
| 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); |
| symtab_node **slot; |
| |
| if (!name) |
| return; |
| |
| hashval_t hash = decl_assembler_name_hash (name); |
| slot = assembler_name_hash->find_slot_with_hash (name, hash, |
| NO_INSERT); |
| gcc_assert (*slot == node); |
| if (!node->next_sharing_asm_name) |
| assembler_name_hash->clear_slot (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 |
| symbol_table::symtab_prevail_in_asm_name_hash (symtab_node *node) |
| { |
| unlink_from_assembler_name_hash (node, false); |
| insert_to_assembler_name_hash (node, false); |
| } |
| |
| /* Initialize asm name hash unless. */ |
| |
| void |
| symbol_table::symtab_initialize_asm_name_hash (void) |
| { |
| symtab_node *node; |
| if (!assembler_name_hash) |
| { |
| assembler_name_hash = hash_table<asmname_hasher>::create_ggc (10); |
| FOR_EACH_SYMBOL (node) |
| insert_to_assembler_name_hash (node, false); |
| } |
| } |
| |
| /* Set the DECL_ASSEMBLER_NAME and update symtab hashtables. */ |
| |
| void |
| symbol_table::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 ((VAR_P (decl) && (TREE_STATIC (decl) || DECL_EXTERNAL (decl))) |
| || TREE_CODE (decl) == FUNCTION_DECL) |
| node = symtab_node::get (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); |
| |
| const char *old_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); |
| if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)) |
| && DECL_RTL_SET_P (decl)) |
| warning (0, "%qD 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 we change assembler name, also all transparent aliases must |
| be updated. There are three kinds - those having same assembler name, |
| those being renamed in varasm.cc and weakref being renamed by the |
| assembler. */ |
| if (node) |
| { |
| insert_to_assembler_name_hash (node, true); |
| ipa_ref *ref; |
| for (unsigned i = 0; node->iterate_direct_aliases (i, ref); i++) |
| { |
| struct symtab_node *alias = ref->referring; |
| if (alias->transparent_alias && !alias->weakref |
| && symbol_table::assembler_names_equal_p |
| (old_name, IDENTIFIER_POINTER ( |
| DECL_ASSEMBLER_NAME (alias->decl)))) |
| change_decl_assembler_name (alias->decl, name); |
| else if (alias->transparent_alias |
| && IDENTIFIER_TRANSPARENT_ALIAS (alias->decl)) |
| { |
| gcc_assert (TREE_CHAIN (DECL_ASSEMBLER_NAME (alias->decl)) |
| && IDENTIFIER_TRANSPARENT_ALIAS |
| (DECL_ASSEMBLER_NAME (alias->decl))); |
| |
| TREE_CHAIN (DECL_ASSEMBLER_NAME (alias->decl)) = |
| ultimate_transparent_alias_target |
| (DECL_ASSEMBLER_NAME (node->decl)); |
| } |
| #ifdef ASM_OUTPUT_WEAKREF |
| else gcc_assert (!alias->transparent_alias || alias->weakref); |
| #else |
| else gcc_assert (!alias->transparent_alias); |
| #endif |
| } |
| gcc_assert (!node->transparent_alias || !node->definition |
| || node->weakref |
| || TREE_CHAIN (DECL_ASSEMBLER_NAME (decl)) |
| || symbol_table::assembler_names_equal_p |
| (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), |
| IDENTIFIER_POINTER |
| (DECL_ASSEMBLER_NAME |
| (node->get_alias_target ()->decl)))); |
| } |
| } |
| } |
| |
| /* Hash sections by their names. */ |
| |
| hashval_t |
| section_name_hasher::hash (section_hash_entry *n) |
| { |
| return htab_hash_string (n->name); |
| } |
| |
| /* Return true if section P1 name equals to P2. */ |
| |
| bool |
| section_name_hasher::equal (section_hash_entry *n1, const char *name) |
| { |
| return n1->name == name || !strcmp (n1->name, name); |
| } |
| |
| /* Bump the reference count on ENTRY so that it is retained. */ |
| |
| static section_hash_entry * |
| retain_section_hash_entry (section_hash_entry *entry) |
| { |
| entry->ref_count++; |
| return entry; |
| } |
| |
| /* Drop the reference count on ENTRY and remove it if the reference |
| count drops to zero. */ |
| |
| static void |
| release_section_hash_entry (section_hash_entry *entry) |
| { |
| if (entry) |
| { |
| entry->ref_count--; |
| if (!entry->ref_count) |
| { |
| hashval_t hash = htab_hash_string (entry->name); |
| section_hash_entry **slot |
| = symtab->section_hash->find_slot_with_hash (entry->name, |
| hash, INSERT); |
| ggc_free (entry); |
| symtab->section_hash->clear_slot (slot); |
| } |
| } |
| } |
| |
| /* Add node into symbol table. This function is not used directly, but via |
| cgraph/varpool node creation routines. */ |
| |
| void |
| symtab_node::register_symbol (void) |
| { |
| symtab->register_symbol (this); |
| |
| if (!decl->decl_with_vis.symtab_node) |
| decl->decl_with_vis.symtab_node = this; |
| |
| ref_list.clear (); |
| |
| /* Be sure to do this last; C++ FE might create new nodes via |
| DECL_ASSEMBLER_NAME langhook! */ |
| symtab->insert_to_assembler_name_hash (this, false); |
| } |
| |
| /* Remove NODE from same comdat group. */ |
| |
| void |
| symtab_node::remove_from_same_comdat_group (void) |
| { |
| if (same_comdat_group) |
| { |
| symtab_node *prev; |
| for (prev = same_comdat_group; |
| prev->same_comdat_group != this; |
| prev = prev->same_comdat_group) |
| ; |
| if (same_comdat_group == prev) |
| prev->same_comdat_group = NULL; |
| else |
| prev->same_comdat_group = same_comdat_group; |
| same_comdat_group = NULL; |
| set_comdat_group (NULL); |
| } |
| } |
| |
| /* Remove node from symbol table. This function is not used directly, but via |
| cgraph/varpool node removal routines. |
| INFO is a clone info to attach to new root of clone tree (if any). */ |
| |
| void |
| symtab_node::unregister (clone_info *info) |
| { |
| remove_all_references (); |
| remove_all_referring (); |
| |
| /* Remove reference to section. */ |
| set_section_for_node (NULL); |
| |
| remove_from_same_comdat_group (); |
| |
| symtab->unregister (this); |
| |
| /* During LTO symtab merging we temporarily corrupt decl to symtab node |
| hash. */ |
| gcc_assert (decl->decl_with_vis.symtab_node || in_lto_p); |
| if (decl->decl_with_vis.symtab_node == this) |
| { |
| symtab_node *replacement_node = NULL; |
| if (cgraph_node *cnode = dyn_cast <cgraph_node *> (this)) |
| replacement_node = cnode->find_replacement (info); |
| decl->decl_with_vis.symtab_node = replacement_node; |
| } |
| if (!is_a <varpool_node *> (this) || !DECL_HARD_REGISTER (decl)) |
| symtab->unlink_from_assembler_name_hash (this, false); |
| if (in_init_priority_hash) |
| symtab->init_priority_hash->remove (this); |
| } |
| |
| |
| /* Remove symbol from symbol table. */ |
| |
| void |
| symtab_node::remove (void) |
| { |
| if (cgraph_node *cnode = dyn_cast <cgraph_node *> (this)) |
| cnode->remove (); |
| else if (varpool_node *vnode = dyn_cast <varpool_node *> (this)) |
| vnode->remove (); |
| } |
| |
| /* Add NEW_ to the same comdat group that OLD is in. */ |
| |
| void |
| symtab_node::add_to_same_comdat_group (symtab_node *old_node) |
| { |
| gcc_assert (old_node->get_comdat_group ()); |
| gcc_assert (!same_comdat_group); |
| gcc_assert (this != old_node); |
| |
| set_comdat_group (old_node->get_comdat_group ()); |
| same_comdat_group = old_node; |
| if (!old_node->same_comdat_group) |
| old_node->same_comdat_group = this; |
| 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 = this; |
| } |
| |
| cgraph_node *n; |
| if (comdat_local_p () |
| && (n = dyn_cast <cgraph_node *> (this)) != NULL) |
| { |
| for (cgraph_edge *e = n->callers; e; e = e->next_caller) |
| if (e->caller->inlined_to) |
| e->caller->inlined_to->calls_comdat_local = true; |
| else |
| e->caller->calls_comdat_local = true; |
| } |
| } |
| |
| /* Dissolve the same_comdat_group list in which NODE resides. */ |
| |
| void |
| symtab_node::dissolve_same_comdat_group_list (void) |
| { |
| symtab_node *n = this; |
| symtab_node *next; |
| |
| if (!same_comdat_group) |
| return; |
| do |
| { |
| next = n->same_comdat_group; |
| n->same_comdat_group = NULL; |
| if (dyn_cast <cgraph_node *> (n)) |
| dyn_cast <cgraph_node *> (n)->calls_comdat_local = false; |
| /* Clear comdat_group for comdat locals, since |
| make_decl_local doesn't. */ |
| if (!TREE_PUBLIC (n->decl)) |
| n->set_comdat_group (NULL); |
| n = next; |
| } |
| while (n != this); |
| } |
| |
| /* 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 name (); |
| return IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); |
| } |
| |
| /* Return printable identifier name. */ |
| |
| const char * |
| symtab_node::name () const |
| { |
| if (!DECL_NAME (decl)) |
| { |
| if (DECL_ASSEMBLER_NAME_SET_P (decl)) |
| return asm_name (); |
| else |
| return "<unnamed>"; |
| } |
| return lang_hooks.decl_printable_name (decl, 2); |
| } |
| |
| const char * |
| symtab_node::get_dump_name (bool asm_name_p) const |
| { |
| #define EXTRA 16 |
| const char *fname = asm_name_p ? asm_name () : name (); |
| unsigned l = strlen (fname); |
| |
| char *s = (char *)ggc_internal_cleared_alloc (l + EXTRA); |
| snprintf (s, l + EXTRA, "%s/%d", fname, order); |
| |
| return s; |
| } |
| |
| const char * |
| symtab_node::dump_name () const |
| { |
| return get_dump_name (false); |
| } |
| |
| const char * |
| symtab_node::dump_asm_name () const |
| { |
| return get_dump_name (true); |
| } |
| |
| /* Return ipa reference from this symtab_node to |
| REFERRED_NODE or REFERRED_VARPOOL_NODE. USE_TYPE specify type |
| of the use. */ |
| |
| ipa_ref * |
| symtab_node::create_reference (symtab_node *referred_node, |
| enum ipa_ref_use use_type) |
| { |
| return create_reference (referred_node, use_type, NULL); |
| } |
| |
| |
| /* Return ipa reference from this symtab_node to |
| REFERRED_NODE or REFERRED_VARPOOL_NODE. USE_TYPE specify type |
| of the use and STMT the statement (if it exists). */ |
| |
| ipa_ref * |
| symtab_node::create_reference (symtab_node *referred_node, |
| enum ipa_ref_use use_type, gimple *stmt) |
| { |
| ipa_ref *ref = NULL, *ref2 = NULL; |
| ipa_ref_list *list, *list2; |
| ipa_ref_t *old_references; |
| |
| gcc_checking_assert (!stmt || is_a <cgraph_node *> (this)); |
| gcc_checking_assert (use_type != IPA_REF_ALIAS || !stmt); |
| |
| list = &ref_list; |
| old_references = list->references.address (); |
| list->references.safe_grow (list->references.length () + 1, false); |
| ref = &list->references.last (); |
| |
| list2 = &referred_node->ref_list; |
| |
| /* IPA_REF_ALIAS is always inserted at the beginning of the list. */ |
| if(use_type == IPA_REF_ALIAS) |
| { |
| list2->referring.safe_insert (0, ref); |
| ref->referred_index = 0; |
| |
| for (unsigned int i = 1; i < list2->referring.length (); i++) |
| list2->referring[i]->referred_index = i; |
| } |
| else |
| { |
| list2->referring.safe_push (ref); |
| ref->referred_index = list2->referring.length () - 1; |
| } |
| |
| ref->referring = this; |
| ref->referred = referred_node; |
| ref->stmt = stmt; |
| ref->lto_stmt_uid = 0; |
| ref->speculative_id = 0; |
| ref->use = use_type; |
| ref->speculative = 0; |
| |
| /* If vector was moved in memory, update pointers. */ |
| if (old_references != list->references.address ()) |
| { |
| int i; |
| for (i = 0; iterate_reference(i, ref2); i++) |
| ref2->referred_ref_list ()->referring[ref2->referred_index] = ref2; |
| } |
| return ref; |
| } |
| |
| ipa_ref * |
| symtab_node::maybe_create_reference (tree val, gimple *stmt) |
| { |
| STRIP_NOPS (val); |
| ipa_ref_use use_type; |
| |
| switch (TREE_CODE (val)) |
| { |
| case VAR_DECL: |
| use_type = IPA_REF_LOAD; |
| break; |
| case ADDR_EXPR: |
| use_type = IPA_REF_ADDR; |
| break; |
| default: |
| gcc_assert (!handled_component_p (val)); |
| return NULL; |
| } |
| |
| val = get_base_var (val); |
| if (val && VAR_OR_FUNCTION_DECL_P (val)) |
| { |
| symtab_node *referred = symtab_node::get (val); |
| gcc_checking_assert (referred); |
| return create_reference (referred, use_type, stmt); |
| } |
| return NULL; |
| } |
| |
| /* Clone all references from symtab NODE to this symtab_node. */ |
| |
| void |
| symtab_node::clone_references (symtab_node *node) |
| { |
| ipa_ref *ref = NULL, *ref2 = NULL; |
| int i; |
| for (i = 0; node->iterate_reference (i, ref); i++) |
| { |
| bool speculative = ref->speculative; |
| unsigned int stmt_uid = ref->lto_stmt_uid; |
| unsigned int spec_id = ref->speculative_id; |
| |
| ref2 = create_reference (ref->referred, ref->use, ref->stmt); |
| ref2->speculative = speculative; |
| ref2->lto_stmt_uid = stmt_uid; |
| ref2->speculative_id = spec_id; |
| } |
| } |
| |
| /* Clone all referring from symtab NODE to this symtab_node. */ |
| |
| void |
| symtab_node::clone_referring (symtab_node *node) |
| { |
| ipa_ref *ref = NULL, *ref2 = NULL; |
| int i; |
| for (i = 0; node->iterate_referring(i, ref); i++) |
| { |
| bool speculative = ref->speculative; |
| unsigned int stmt_uid = ref->lto_stmt_uid; |
| unsigned int spec_id = ref->speculative_id; |
| |
| ref2 = ref->referring->create_reference (this, ref->use, ref->stmt); |
| ref2->speculative = speculative; |
| ref2->lto_stmt_uid = stmt_uid; |
| ref2->speculative_id = spec_id; |
| } |
| } |
| |
| /* Clone reference REF to this symtab_node and set its stmt to STMT. */ |
| |
| ipa_ref * |
| symtab_node::clone_reference (ipa_ref *ref, gimple *stmt) |
| { |
| bool speculative = ref->speculative; |
| unsigned int stmt_uid = ref->lto_stmt_uid; |
| unsigned int spec_id = ref->speculative_id; |
| ipa_ref *ref2; |
| |
| ref2 = create_reference (ref->referred, ref->use, stmt); |
| ref2->speculative = speculative; |
| ref2->lto_stmt_uid = stmt_uid; |
| ref2->speculative_id = spec_id; |
| return ref2; |
| } |
| |
| /* Find the structure describing a reference to REFERRED_NODE |
| and associated with statement STMT. */ |
| |
| ipa_ref * |
| symtab_node::find_reference (symtab_node *referred_node, |
| gimple *stmt, unsigned int lto_stmt_uid) |
| { |
| ipa_ref *r = NULL; |
| int i; |
| |
| for (i = 0; iterate_reference (i, r); i++) |
| if (r->referred == referred_node |
| && !r->speculative |
| && ((stmt && r->stmt == stmt) |
| || (lto_stmt_uid && r->lto_stmt_uid == lto_stmt_uid) |
| || (!stmt && !lto_stmt_uid && !r->stmt && !r->lto_stmt_uid))) |
| return r; |
| return NULL; |
| } |
| |
| /* Remove all references that are associated with statement STMT. */ |
| |
| void |
| symtab_node::remove_stmt_references (gimple *stmt) |
| { |
| ipa_ref *r = NULL; |
| int i = 0; |
| |
| while (iterate_reference (i, r)) |
| if (r->stmt == stmt) |
| r->remove_reference (); |
| else |
| i++; |
| } |
| |
| /* Remove all stmt references in non-speculative references in THIS |
| and all clones. |
| Those are not maintained during inlining & cloning. |
| The exception are speculative references that are updated along |
| with callgraph edges associated with them. */ |
| |
| void |
| symtab_node::clear_stmts_in_references (void) |
| { |
| ipa_ref *r = NULL; |
| int i; |
| |
| for (i = 0; iterate_reference (i, r); i++) |
| if (!r->speculative) |
| { |
| r->stmt = NULL; |
| r->lto_stmt_uid = 0; |
| r->speculative_id = 0; |
| } |
| cgraph_node *cnode = dyn_cast <cgraph_node *> (this); |
| if (cnode) |
| { |
| if (cnode->clones) |
| for (cnode = cnode->clones; cnode; cnode = cnode->next_sibling_clone) |
| cnode->clear_stmts_in_references (); |
| } |
| } |
| |
| /* Remove all references in ref list. */ |
| |
| void |
| symtab_node::remove_all_references (void) |
| { |
| while (ref_list.references.length ()) |
| ref_list.references.last ().remove_reference (); |
| ref_list.references.release (); |
| } |
| |
| /* Remove all referring items in ref list. */ |
| |
| void |
| symtab_node::remove_all_referring (void) |
| { |
| while (ref_list.referring.length ()) |
| ref_list.referring.last ()->remove_reference (); |
| ref_list.referring.release (); |
| } |
| |
| /* Dump references in ref list to FILE. */ |
| |
| void |
| symtab_node::dump_references (FILE *file) |
| { |
| ipa_ref *ref = NULL; |
| int i; |
| for (i = 0; iterate_reference (i, ref); i++) |
| { |
| fprintf (file, "%s (%s) ", ref->referred->dump_asm_name (), |
| ipa_ref_use_name[ref->use]); |
| if (ref->speculative) |
| fprintf (file, "(speculative) "); |
| } |
| fprintf (file, "\n"); |
| } |
| |
| /* Dump referring in list to FILE. */ |
| |
| void |
| symtab_node::dump_referring (FILE *file) |
| { |
| ipa_ref *ref = NULL; |
| int i; |
| for (i = 0; iterate_referring(i, ref); i++) |
| { |
| fprintf (file, "%s (%s) ", ref->referring->dump_asm_name (), |
| ipa_ref_use_name[ref->use]); |
| if (ref->speculative) |
| fprintf (file, "(speculative) "); |
| } |
| fprintf (file, "\n"); |
| } |
| |
| static const char * const symtab_type_names[] = {"symbol", "function", "variable"}; |
| |
| /* Dump the visibility of the symbol. */ |
| |
| const char * |
| symtab_node::get_visibility_string () const |
| { |
| static const char * const visibility_types[] |
| = { "default", "protected", "hidden", "internal" }; |
| return visibility_types[DECL_VISIBILITY (decl)]; |
| } |
| |
| /* Dump the type_name of the symbol. */ |
| const char * |
| symtab_node::get_symtab_type_string () const |
| { |
| return symtab_type_names[type]; |
| } |
| |
| /* Dump base fields of symtab nodes to F. Not to be used directly. */ |
| |
| void |
| symtab_node::dump_base (FILE *f) |
| { |
| static const char * const visibility_types[] = { |
| "default", "protected", "hidden", "internal" |
| }; |
| |
| fprintf (f, "%s (%s)", dump_asm_name (), name ()); |
| dump_addr (f, " @", (void *)this); |
| fprintf (f, "\n Type: %s", symtab_type_names[type]); |
| |
| if (definition) |
| fprintf (f, " definition"); |
| if (analyzed) |
| fprintf (f, " analyzed"); |
| if (alias) |
| fprintf (f, " alias"); |
| if (transparent_alias) |
| fprintf (f, " transparent_alias"); |
| if (weakref) |
| fprintf (f, " weakref"); |
| if (symver) |
| fprintf (f, " symver"); |
| if (cpp_implicit_alias) |
| fprintf (f, " cpp_implicit_alias"); |
| if (alias_target) |
| fprintf (f, " target:%s", |
| DECL_P (alias_target) |
| ? IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME |
| (alias_target)) |
| : IDENTIFIER_POINTER (alias_target)); |
| if (body_removed) |
| fprintf (f, "\n Body removed by symtab_remove_unreachable_nodes"); |
| fprintf (f, "\n Visibility:"); |
| if (in_other_partition) |
| fprintf (f, " in_other_partition"); |
| if (used_from_other_partition) |
| fprintf (f, " used_from_other_partition"); |
| if (force_output) |
| fprintf (f, " force_output"); |
| if (forced_by_abi) |
| fprintf (f, " forced_by_abi"); |
| if (externally_visible) |
| fprintf (f, " externally_visible"); |
| if (semantic_interposition) |
| fprintf (f, " semantic_interposition"); |
| if (no_reorder) |
| fprintf (f, " no_reorder"); |
| if (resolution != LDPR_UNKNOWN) |
| fprintf (f, " %s", |
| ld_plugin_symbol_resolution_names[(int)resolution]); |
| if (TREE_ASM_WRITTEN (decl)) |
| fprintf (f, " asm_written"); |
| if (DECL_EXTERNAL (decl)) |
| fprintf (f, " external"); |
| if (TREE_PUBLIC (decl)) |
| fprintf (f, " public"); |
| if (DECL_COMMON (decl)) |
| fprintf (f, " common"); |
| if (DECL_WEAK (decl)) |
| fprintf (f, " weak"); |
| if (DECL_DLLIMPORT_P (decl)) |
| fprintf (f, " dll_import"); |
| if (DECL_COMDAT (decl)) |
| fprintf (f, " comdat"); |
| if (get_comdat_group ()) |
| fprintf (f, " comdat_group:%s", |
| IDENTIFIER_POINTER (get_comdat_group_id ())); |
| if (DECL_ONE_ONLY (decl)) |
| fprintf (f, " one_only"); |
| if (get_section ()) |
| fprintf (f, " section:%s", |
| get_section ()); |
| if (implicit_section) |
| fprintf (f," (implicit_section)"); |
| if (DECL_VISIBILITY_SPECIFIED (decl)) |
| fprintf (f, " visibility_specified"); |
| if (DECL_VISIBILITY (decl)) |
| fprintf (f, " visibility:%s", |
| visibility_types [DECL_VISIBILITY (decl)]); |
| if (DECL_VIRTUAL_P (decl)) |
| fprintf (f, " virtual"); |
| if (DECL_ARTIFICIAL (decl)) |
| fprintf (f, " artificial"); |
| if (TREE_CODE (decl) == FUNCTION_DECL) |
| { |
| if (DECL_STATIC_CONSTRUCTOR (decl)) |
| fprintf (f, " constructor"); |
| if (DECL_STATIC_DESTRUCTOR (decl)) |
| fprintf (f, " destructor"); |
| } |
| if (ifunc_resolver) |
| fprintf (f, " ifunc_resolver"); |
| fprintf (f, "\n"); |
| |
| if (same_comdat_group) |
| fprintf (f, " Same comdat group as: %s\n", |
| same_comdat_group->dump_asm_name ()); |
| if (next_sharing_asm_name) |
| fprintf (f, " next sharing asm name: %i\n", |
| next_sharing_asm_name->order); |
| if (previous_sharing_asm_name) |
| fprintf (f, " previous sharing asm name: %i\n", |
| previous_sharing_asm_name->order); |
| |
| if (address_taken) |
| fprintf (f, " Address is taken.\n"); |
| if (aux) |
| { |
| fprintf (f, " Aux:"); |
| dump_addr (f, " @", (void *)aux); |
| fprintf (f, "\n"); |
| } |
| |
| fprintf (f, " References: "); |
| dump_references (f); |
| fprintf (f, " Referring: "); |
| dump_referring (f); |
| if (lto_file_data) |
| fprintf (f, " Read from file: %s\n", |
| lto_file_data->file_name); |
| } |
| |
| /* Dump symtab node to F. */ |
| |
| void |
| symtab_node::dump (FILE *f) |
| { |
| if (cgraph_node *cnode = dyn_cast <cgraph_node *> (this)) |
| cnode->dump (f); |
| else if (varpool_node *vnode = dyn_cast <varpool_node *> (this)) |
| vnode->dump (f); |
| } |
| |
| void |
| symtab_node::dump_graphviz (FILE *f) |
| { |
| if (cgraph_node *cnode = dyn_cast <cgraph_node *> (this)) |
| cnode->dump_graphviz (f); |
| } |
| |
| void |
| symbol_table::dump (FILE *f) |
| { |
| symtab_node *node; |
| fprintf (f, "Symbol table:\n\n"); |
| FOR_EACH_SYMBOL (node) |
| node->dump (f); |
| } |
| |
| void |
| symbol_table::dump_graphviz (FILE *f) |
| { |
| symtab_node *node; |
| fprintf (f, "digraph symtab {\n"); |
| FOR_EACH_SYMBOL (node) |
| node->dump_graphviz (f); |
| fprintf (f, "}\n"); |
| } |
| |
| DEBUG_FUNCTION void |
| symbol_table::debug (void) |
| { |
| dump (stderr); |
| } |
| |
| /* Return the cgraph node that has ASMNAME for its DECL_ASSEMBLER_NAME. |
| Return NULL if there's no such node. */ |
| |
| symtab_node * |
| symtab_node::get_for_asmname (const_tree asmname) |
| { |
| symtab_node *node; |
| |
| symtab->symtab_initialize_asm_name_hash (); |
| hashval_t hash = symtab->decl_assembler_name_hash (asmname); |
| symtab_node **slot |
| = symtab->assembler_name_hash->find_slot_with_hash (asmname, hash, |
| NO_INSERT); |
| |
| if (slot) |
| { |
| node = *slot; |
| return node; |
| } |
| return NULL; |
| } |
| |
| /* Dump symtab node NODE to stderr. */ |
| |
| DEBUG_FUNCTION void |
| symtab_node::debug (void) |
| { |
| dump (stderr); |
| } |
| |
| /* Verify common part of symtab nodes. */ |
| |
| #if __GNUC__ >= 10 |
| /* Disable warnings about missing quoting in GCC diagnostics for |
| the verification errors. Their format strings don't follow GCC |
| diagnostic conventions and the calls are ultimately followed by |
| one to internal_error. */ |
| # pragma GCC diagnostic push |
| # pragma GCC diagnostic ignored "-Wformat-diag" |
| #endif |
| |
| DEBUG_FUNCTION bool |
| symtab_node::verify_base (void) |
| { |
| bool error_found = false; |
| symtab_node *hashed_node; |
| |
| if (is_a <cgraph_node *> (this)) |
| { |
| if (TREE_CODE (decl) != FUNCTION_DECL) |
| { |
| error ("function symbol is not function"); |
| error_found = true; |
| } |
| else if ((lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl)) |
| != NULL) |
| != dyn_cast <cgraph_node *> (this)->ifunc_resolver) |
| { |
| error ("inconsistent %<ifunc%> attribute"); |
| error_found = true; |
| } |
| } |
| else if (is_a <varpool_node *> (this)) |
| { |
| if (!VAR_P (decl)) |
| { |
| error ("variable symbol is not variable"); |
| error_found = true; |
| } |
| } |
| else |
| { |
| error ("node has unknown type"); |
| error_found = true; |
| } |
| if (order < 0 || order >= symtab->order) |
| { |
| error ("node has invalid order %i", order); |
| error_found = true; |
| } |
| |
| if (symtab->state != LTO_STREAMING) |
| { |
| hashed_node = symtab_node::get (decl); |
| if (!hashed_node) |
| { |
| error ("node not found node->decl->decl_with_vis.symtab_node"); |
| error_found = true; |
| } |
| if (hashed_node != this |
| && (!is_a <cgraph_node *> (this) |
| || !dyn_cast <cgraph_node *> (this)->clone_of |
| || dyn_cast <cgraph_node *> (this)->clone_of->decl != decl)) |
| { |
| error ("node differs from node->decl->decl_with_vis.symtab_node"); |
| error_found = true; |
| } |
| } |
| if (symtab->assembler_name_hash) |
| { |
| hashed_node = symtab_node::get_for_asmname (DECL_ASSEMBLER_NAME (decl)); |
| if (hashed_node) |
| { |
| if (hashed_node->previous_sharing_asm_name) |
| { |
| error ("assembler name hash list corrupted"); |
| error_found = true; |
| } |
| else if (previous_sharing_asm_name == NULL) |
| { |
| if (hashed_node != this) |
| { |
| error ("assembler name hash list corrupted"); |
| error_found = true; |
| } |
| } |
| else if (!(is_a <varpool_node *> (this) && DECL_HARD_REGISTER (decl))) |
| { |
| if (!asmname_hasher::equal (previous_sharing_asm_name, |
| DECL_ASSEMBLER_NAME (decl))) |
| { |
| error ("node not found in symtab assembler name hash"); |
| error_found = true; |
| } |
| } |
| } |
| } |
| if (previous_sharing_asm_name |
| && previous_sharing_asm_name->next_sharing_asm_name != this) |
| { |
| error ("double linked list of assembler names corrupted"); |
| error_found = true; |
| } |
| if (body_removed && definition) |
| { |
| error ("node has body_removed but is definition"); |
| error_found = true; |
| } |
| if (analyzed && !definition) |
| { |
| error ("node is analyzed but it is not a definition"); |
| error_found = true; |
| } |
| if (cpp_implicit_alias && !alias) |
| { |
| error ("node is alias but not implicit alias"); |
| error_found = true; |
| } |
| if (alias && !definition && !weakref) |
| { |
| error ("node is alias but not definition"); |
| error_found = true; |
| } |
| if (weakref && !transparent_alias) |
| { |
| error ("node is weakref but not an transparent_alias"); |
| error_found = true; |
| } |
| if (transparent_alias && !alias) |
| { |
| error ("node is transparent_alias but not an alias"); |
| error_found = true; |
| } |
| if (symver && !alias) |
| { |
| error ("node is symver but not alias"); |
| error_found = true; |
| } |
| /* Limitation of gas requires us to output targets of symver aliases as |
| global symbols. This is binutils PR 25295. */ |
| if (symver |
| && (!TREE_PUBLIC (get_alias_target ()->decl) |
| || DECL_VISIBILITY (get_alias_target ()->decl) != VISIBILITY_DEFAULT)) |
| { |
| error ("symver target is not exported with default visibility"); |
| error_found = true; |
| } |
| if (symver |
| && (!TREE_PUBLIC (decl) |
| || DECL_VISIBILITY (decl) != VISIBILITY_DEFAULT)) |
| { |
| error ("symver is not exported with default visibility"); |
| error_found = true; |
| } |
| if (same_comdat_group) |
| { |
| symtab_node *n = same_comdat_group; |
| |
| if (!n->get_comdat_group ()) |
| { |
| error ("node is in same_comdat_group list but has no comdat_group"); |
| error_found = true; |
| } |
| if (n->get_comdat_group () != get_comdat_group ()) |
| { |
| error ("same_comdat_group list across different groups"); |
| error_found = true; |
| } |
| if (n->type != type) |
| { |
| error ("mixing different types of symbol in same comdat groups is not supported"); |
| error_found = true; |
| } |
| if (n == this) |
| { |
| 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 != this); |
| if (comdat_local_p ()) |
| { |
| ipa_ref *ref = NULL; |
| |
| for (int i = 0; iterate_referring (i, ref); ++i) |
| { |
| if (!in_same_comdat_group_p (ref->referring)) |
| { |
| error ("comdat-local symbol referred to by %s outside its " |
| "comdat", |
| identifier_to_locale (ref->referring->name())); |
| error_found = true; |
| } |
| } |
| } |
| } |
| if (implicit_section && !get_section ()) |
| { |
| error ("implicit_section flag is set but section isn%'t"); |
| error_found = true; |
| } |
| if (get_section () && get_comdat_group () |
| && !implicit_section |
| && !lookup_attribute ("section", DECL_ATTRIBUTES (decl))) |
| { |
| error ("Both section and comdat group is set"); |
| error_found = true; |
| } |
| /* TODO: Add string table for sections, so we do not keep holding duplicated |
| strings. */ |
| if (alias && definition |
| && get_section () != get_alias_target ()->get_section () |
| && (!get_section() |
| || !get_alias_target ()->get_section () |
| || strcmp (get_section(), |
| get_alias_target ()->get_section ()))) |
| { |
| error ("Alias and target%'s section differs"); |
| get_alias_target ()->dump (stderr); |
| error_found = true; |
| } |
| if (alias && definition |
| && get_comdat_group () != get_alias_target ()->get_comdat_group ()) |
| { |
| error ("Alias and target%'s comdat groups differs"); |
| get_alias_target ()->dump (stderr); |
| error_found = true; |
| } |
| if (transparent_alias && definition && !weakref) |
| { |
| symtab_node *to = get_alias_target (); |
| const char *name1 |
| = IDENTIFIER_POINTER ( |
| ultimate_transparent_alias_target (DECL_ASSEMBLER_NAME (decl))); |
| const char *name2 |
| = IDENTIFIER_POINTER ( |
| ultimate_transparent_alias_target (DECL_ASSEMBLER_NAME (to->decl))); |
| if (!symbol_table::assembler_names_equal_p (name1, name2)) |
| { |
| error ("Transparent alias and target%'s assembler names differs"); |
| get_alias_target ()->dump (stderr); |
| error_found = true; |
| } |
| } |
| if (transparent_alias && definition |
| && get_alias_target()->transparent_alias && get_alias_target()->analyzed) |
| { |
| error ("Chained transparent aliases"); |
| get_alias_target ()->dump (stderr); |
| error_found = true; |
| } |
| |
| return error_found; |
| } |
| |
| /* Verify consistency of NODE. */ |
| |
| DEBUG_FUNCTION void |
| symtab_node::verify (void) |
| { |
| if (seen_error ()) |
| return; |
| |
| timevar_push (TV_CGRAPH_VERIFY); |
| if (cgraph_node *node = dyn_cast <cgraph_node *> (this)) |
| node->verify_node (); |
| else |
| if (verify_base ()) |
| { |
| debug (); |
| internal_error ("symtab_node::verify failed"); |
| } |
| timevar_pop (TV_CGRAPH_VERIFY); |
| } |
| |
| /* Verify symbol table for internal consistency. */ |
| |
| DEBUG_FUNCTION void |
| symtab_node::verify_symtab_nodes (void) |
| { |
| symtab_node *node; |
| hash_map<tree, symtab_node *> comdat_head_map (251); |
| asm_node *anode; |
| |
| for (anode = symtab->first_asm_symbol (); anode; anode = anode->next) |
| if (anode->order < 0 || anode->order >= symtab->order) |
| { |
| error ("invalid order in asm node %i", anode->order); |
| internal_error ("symtab_node::verify failed"); |
| } |
| |
| FOR_EACH_SYMBOL (node) |
| { |
| node->verify (); |
| if (node->get_comdat_group ()) |
| { |
| symtab_node **entry, *s; |
| bool existed; |
| |
| entry = &comdat_head_map.get_or_insert (node->get_comdat_group (), |
| &existed); |
| if (!existed) |
| *entry = node; |
| else if (!DECL_EXTERNAL (node->decl)) |
| { |
| for (s = (*entry)->same_comdat_group; |
| s != NULL && s != node && s != *entry; |
| s = s->same_comdat_group) |
| ; |
| if (!s || s == *entry) |
| { |
| error ("Two symbols with same comdat_group are not linked by " |
| "the same_comdat_group list."); |
| (*entry)->debug (); |
| node->debug (); |
| internal_error ("symtab_node::verify failed"); |
| } |
| } |
| } |
| } |
| } |
| |
| #if __GNUC__ >= 10 |
| # pragma GCC diagnostic pop |
| #endif |
| |
| /* 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_node::make_decl_local (void) |
| { |
| rtx rtl, symbol; |
| |
| if (weakref) |
| { |
| weakref = false; |
| IDENTIFIER_TRANSPARENT_ALIAS (DECL_ASSEMBLER_NAME (decl)) = 0; |
| TREE_CHAIN (DECL_ASSEMBLER_NAME (decl)) = NULL_TREE; |
| symtab->change_decl_assembler_name |
| (decl, DECL_ASSEMBLER_NAME (get_alias_target ()->decl)); |
| DECL_ATTRIBUTES (decl) = remove_attribute ("weakref", |
| DECL_ATTRIBUTES (decl)); |
| } |
| /* Avoid clearing comdat_groups on comdat-local decls. */ |
| else if (TREE_PUBLIC (decl) == 0) |
| return; |
| |
| /* Localizing a symbol also make all its transparent aliases local. */ |
| ipa_ref *ref; |
| for (unsigned i = 0; iterate_direct_aliases (i, ref); i++) |
| { |
| struct symtab_node *alias = ref->referring; |
| if (alias->transparent_alias) |
| alias->make_decl_local (); |
| } |
| |
| if (VAR_P (decl)) |
| { |
| DECL_COMMON (decl) = 0; |
| /* ADDRESSABLE flag is not defined for public symbols. */ |
| TREE_ADDRESSABLE (decl) = 1; |
| TREE_STATIC (decl) = 1; |
| } |
| else |
| gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); |
| |
| DECL_COMDAT (decl) = 0; |
| DECL_WEAK (decl) = 0; |
| DECL_EXTERNAL (decl) = 0; |
| DECL_VISIBILITY_SPECIFIED (decl) = 0; |
| DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT; |
| TREE_PUBLIC (decl) = 0; |
| DECL_DLLIMPORT_P (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); |
| } |
| |
| /* Copy visibility from N. |
| This is useful when THIS becomes a transparent alias of N. */ |
| |
| void |
| symtab_node::copy_visibility_from (symtab_node *n) |
| { |
| gcc_checking_assert (n->weakref == weakref); |
| |
| ipa_ref *ref; |
| for (unsigned i = 0; iterate_direct_aliases (i, ref); i++) |
| { |
| struct symtab_node *alias = ref->referring; |
| if (alias->transparent_alias) |
| alias->copy_visibility_from (n); |
| } |
| |
| if (VAR_P (decl)) |
| { |
| DECL_COMMON (decl) = DECL_COMMON (n->decl); |
| /* ADDRESSABLE flag is not defined for public symbols. */ |
| if (TREE_PUBLIC (decl) && !TREE_PUBLIC (n->decl)) |
| TREE_ADDRESSABLE (decl) = 1; |
| TREE_STATIC (decl) = TREE_STATIC (n->decl); |
| } |
| else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); |
| |
| DECL_COMDAT (decl) = DECL_COMDAT (n->decl); |
| DECL_WEAK (decl) = DECL_WEAK (n->decl); |
| DECL_EXTERNAL (decl) = DECL_EXTERNAL (n->decl); |
| DECL_VISIBILITY_SPECIFIED (decl) = DECL_VISIBILITY_SPECIFIED (n->decl); |
| DECL_VISIBILITY (decl) = DECL_VISIBILITY (n->decl); |
| TREE_PUBLIC (decl) = TREE_PUBLIC (n->decl); |
| DECL_DLLIMPORT_P (decl) = DECL_DLLIMPORT_P (n->decl); |
| resolution = n->resolution; |
| set_comdat_group (n->get_comdat_group ()); |
| set_section (*n); |
| externally_visible = n->externally_visible; |
| if (!DECL_RTL_SET_P (decl)) |
| return; |
| |
| /* Update rtl flags. */ |
| make_decl_rtl (decl); |
| |
| rtx rtl = DECL_RTL (decl); |
| if (!MEM_P (rtl)) |
| return; |
| |
| rtx symbol = XEXP (rtl, 0); |
| if (GET_CODE (symbol) != SYMBOL_REF) |
| return; |
| |
| SYMBOL_REF_WEAK (symbol) = DECL_WEAK (decl); |
| } |
| |
| /* Walk the alias chain to return the symbol NODE is alias of. |
| If NODE is not an alias, return NODE. |
| Assumes NODE is known to be alias. */ |
| |
| symtab_node * |
| symtab_node::ultimate_alias_target_1 (enum availability *availability, |
| symtab_node *ref) |
| { |
| bool transparent_p = false; |
| |
| /* 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. |
| |
| Transparent alias is just alternative name of a given symbol used within |
| one compilation unit and is translated prior hitting the object file. It |
| inherits the visibility of its target. |
| Weakref is a different animal (and noweak definition is weak). |
| |
| If we ever get into supporting targets with different semantics, a target |
| hook will be needed here. */ |
| |
| if (availability) |
| { |
| transparent_p = transparent_alias; |
| if (!transparent_p) |
| *availability = get_availability (ref); |
| else |
| *availability = AVAIL_NOT_AVAILABLE; |
| } |
| |
| symtab_node *node = this; |
| while (node) |
| { |
| if (node->alias && node->analyzed) |
| node = node->get_alias_target (); |
| else |
| { |
| if (!availability || (!transparent_p && node->analyzed)) |
| ; |
| else if (node->analyzed && !node->transparent_alias) |
| *availability = node->get_availability (ref); |
| else |
| *availability = AVAIL_NOT_AVAILABLE; |
| return node; |
| } |
| if (node && availability && transparent_p |
| && node->transparent_alias) |
| { |
| *availability = node->get_availability (ref); |
| transparent_p = false; |
| } |
| } |
| 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 |
| symtab_node::fixup_same_cpp_alias_visibility (symtab_node *target) |
| { |
| if (is_a <cgraph_node *> (this)) |
| { |
| DECL_DECLARED_INLINE_P (decl) |
| = DECL_DECLARED_INLINE_P (target->decl); |
| DECL_DISREGARD_INLINE_LIMITS (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 (decl) = DECL_WEAK (target->decl); |
| DECL_EXTERNAL (decl) = DECL_EXTERNAL (target->decl); |
| DECL_VISIBILITY (decl) = DECL_VISIBILITY (target->decl); |
| } |
| if (TREE_PUBLIC (decl)) |
| { |
| tree group; |
| |
| DECL_EXTERNAL (decl) = DECL_EXTERNAL (target->decl); |
| DECL_COMDAT (decl) = DECL_COMDAT (target->decl); |
| group = target->get_comdat_group (); |
| set_comdat_group (group); |
| if (group && !same_comdat_group) |
| add_to_same_comdat_group (target); |
| } |
| externally_visible = target->externally_visible; |
| } |
| |
| /* Set section, do not recurse into aliases. |
| When one wants to change section of a symbol and its aliases, |
| use set_section. */ |
| |
| void |
| symtab_node::set_section_for_node (const char *section) |
| { |
| const char *current = get_section (); |
| |
| if (current == section |
| || (current && section |
| && !strcmp (current, section))) |
| return; |
| |
| release_section_hash_entry (x_section); |
| if (!section) |
| { |
| x_section = NULL; |
| implicit_section = false; |
| return; |
| } |
| if (!symtab->section_hash) |
| symtab->section_hash = hash_table<section_name_hasher>::create_ggc (10); |
| section_hash_entry **slot = symtab->section_hash->find_slot_with_hash |
| (section, htab_hash_string (section), INSERT); |
| if (*slot) |
| x_section = retain_section_hash_entry (*slot); |
| else |
| { |
| int len = strlen (section); |
| *slot = x_section = ggc_cleared_alloc<section_hash_entry> (); |
| x_section->ref_count = 1; |
| x_section->name = ggc_vec_alloc<char> (len + 1); |
| memcpy (x_section->name, section, len + 1); |
| } |
| } |
| |
| /* Set the section of node THIS to be the same as the section |
| of node OTHER. Keep reference counts of the sections |
| up-to-date as needed. */ |
| |
| void |
| symtab_node::set_section_for_node (const symtab_node &other) |
| { |
| if (x_section == other.x_section) |
| return; |
| if (get_section () && other.get_section ()) |
| gcc_checking_assert (strcmp (get_section (), other.get_section ()) != 0); |
| release_section_hash_entry (x_section); |
| if (other.x_section) |
| x_section = retain_section_hash_entry (other.x_section); |
| else |
| { |
| x_section = NULL; |
| implicit_section = false; |
| } |
| } |
| |
| /* Workers for set_section. */ |
| |
| bool |
| symtab_node::set_section_from_string (symtab_node *n, void *s) |
| { |
| n->set_section_for_node ((char *)s); |
| return false; |
| } |
| |
| /* Set the section of node N to be the same as the section |
| of node O. */ |
| |
| bool |
| symtab_node::set_section_from_node (symtab_node *n, void *o) |
| { |
| const symtab_node &other = *static_cast<const symtab_node *> (o); |
| n->set_section_for_node (other); |
| return false; |
| } |
| |
| /* Set section of symbol and its aliases. */ |
| |
| void |
| symtab_node::set_section (const char *section) |
| { |
| gcc_assert (!this->alias || !this->analyzed); |
| call_for_symbol_and_aliases |
| (symtab_node::set_section_from_string, const_cast<char *>(section), true); |
| } |
| |
| void |
| symtab_node::set_section (const symtab_node &other) |
| { |
| call_for_symbol_and_aliases |
| (symtab_node::set_section_from_node, const_cast<symtab_node *>(&other), true); |
| } |
| |
| /* Return the initialization priority. */ |
| |
| priority_type |
| symtab_node::get_init_priority () |
| { |
| if (!this->in_init_priority_hash) |
| return DEFAULT_INIT_PRIORITY; |
| |
| symbol_priority_map *h = symtab->init_priority_hash->get (this); |
| return h ? h->init : DEFAULT_INIT_PRIORITY; |
| } |
| |
| /* Return the finalization priority. */ |
| |
| priority_type |
| cgraph_node::get_fini_priority () |
| { |
| if (!this->in_init_priority_hash) |
| return DEFAULT_INIT_PRIORITY; |
| symbol_priority_map *h = symtab->init_priority_hash->get (this); |
| return h ? h->fini : DEFAULT_INIT_PRIORITY; |
| } |
| |
| /* Return the initialization and finalization priority information for |
| DECL. If there is no previous priority information, a freshly |
| allocated structure is returned. */ |
| |
| symbol_priority_map * |
| symtab_node::priority_info (void) |
| { |
| if (!symtab->init_priority_hash) |
| symtab->init_priority_hash = hash_map<symtab_node *, symbol_priority_map>::create_ggc (13); |
| |
| bool existed; |
| symbol_priority_map *h |
| = &symtab->init_priority_hash->get_or_insert (this, &existed); |
| if (!existed) |
| { |
| h->init = DEFAULT_INIT_PRIORITY; |
| h->fini = DEFAULT_INIT_PRIORITY; |
| in_init_priority_hash = true; |
| } |
| |
| return h; |
| } |
| |
| /* Set initialization priority to PRIORITY. */ |
| |
| void |
| symtab_node::set_init_priority (priority_type priority) |
| { |
| symbol_priority_map *h; |
| |
| if (is_a <cgraph_node *> (this)) |
| gcc_assert (DECL_STATIC_CONSTRUCTOR (this->decl)); |
| |
| if (priority == DEFAULT_INIT_PRIORITY) |
| { |
| gcc_assert (get_init_priority() == priority); |
| return; |
| } |
| h = priority_info (); |
| h->init = priority; |
| } |
| |
| /* Set finalization priority to PRIORITY. */ |
| |
| void |
| cgraph_node::set_fini_priority (priority_type priority) |
| { |
| symbol_priority_map *h; |
| |
| gcc_assert (DECL_STATIC_DESTRUCTOR (this->decl)); |
| |
| if (priority == DEFAULT_INIT_PRIORITY) |
| { |
| gcc_assert (get_fini_priority() == priority); |
| return; |
| } |
| h = priority_info (); |
| h->fini = priority; |
| } |
| |
| /* Worker for symtab_resolve_alias. */ |
| |
| bool |
| symtab_node::set_implicit_section (symtab_node *n, |
| void *data ATTRIBUTE_UNUSED) |
| { |
| n->implicit_section = true; |
| return false; |
| } |
| |
| /* Add reference recording that symtab node is alias of TARGET. |
| The function can fail in the case of aliasing cycles; in this case |
| it returns false. */ |
| |
| bool |
| symtab_node::resolve_alias (symtab_node *target, bool transparent) |
| { |
| symtab_node *n; |
| |
| gcc_assert (!analyzed && !ref_list.references.length ()); |
| |
| /* 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 ? n->get_alias_target () : NULL) |
| if (n == this) |
| { |
| if (is_a <cgraph_node *> (this)) |
| error ("function %q+D part of alias cycle", decl); |
| else if (is_a <varpool_node *> (this)) |
| error ("variable %q+D part of alias cycle", decl); |
| else |
| gcc_unreachable (); |
| alias = false; |
| return false; |
| } |
| |
| /* "analyze" the node - i.e. mark the reference. */ |
| definition = true; |
| alias = true; |
| analyzed = true; |
| transparent |= transparent_alias; |
| transparent_alias = transparent; |
| if (transparent) |
| while (target->transparent_alias && target->analyzed) |
| target = target->get_alias_target (); |
| create_reference (target, IPA_REF_ALIAS, NULL); |
| |
| /* Add alias into the comdat group of its target unless it is already there. */ |
| if (same_comdat_group) |
| remove_from_same_comdat_group (); |
| set_comdat_group (NULL); |
| if (target->get_comdat_group ()) |
| add_to_same_comdat_group (target); |
| |
| if ((get_section () != target->get_section () |
| || target->get_comdat_group ()) && get_section () && !implicit_section) |
| { |
| error ("section of alias %q+D must match section of its target", decl); |
| } |
| set_section (*target); |
| if (target->implicit_section) |
| call_for_symbol_and_aliases (set_implicit_section, NULL, true); |
| |
| /* Alias targets become redundant 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. */ |
| alias_target = NULL; |
| |
| if (!transparent && cpp_implicit_alias && symtab->state >= CONSTRUCTION) |
| fixup_same_cpp_alias_visibility (target); |
| |
| /* If alias has address taken, so does the target. */ |
| if (address_taken) |
| target->ultimate_alias_target ()->address_taken = true; |
| |
| /* All non-transparent aliases of THIS are now in fact aliases of TARGET. |
| If alias is transparent, also all transparent aliases of THIS are now |
| aliases of TARGET. |
| Also merge same comdat group lists. */ |
| ipa_ref *ref; |
| for (unsigned i = 0; iterate_direct_aliases (i, ref);) |
| { |
| struct symtab_node *alias_alias = ref->referring; |
| if (alias_alias->get_comdat_group ()) |
| { |
| alias_alias->remove_from_same_comdat_group (); |
| alias_alias->set_comdat_group (NULL); |
| if (target->get_comdat_group ()) |
| alias_alias->add_to_same_comdat_group (target); |
| } |
| if ((!alias_alias->transparent_alias |
| && !alias_alias->symver) |
| || transparent) |
| { |
| alias_alias->remove_all_references (); |
| alias_alias->create_reference (target, IPA_REF_ALIAS, NULL); |
| } |
| else i++; |
| } |
| return true; |
| } |
| |
| /* Worker searching noninterposable alias. */ |
| |
| bool |
| symtab_node::noninterposable_alias (symtab_node *node, void *data) |
| { |
| if (!node->transparent_alias && decl_binds_to_current_def_p (node->decl)) |
| { |
| symtab_node *fn = node->ultimate_alias_target (); |
| |
| /* Ensure that the alias is well formed this may not be the case |
| of user defined aliases and currently it is not always the case |
| of C++ same body aliases (that is a bug). */ |
| if (TREE_TYPE (node->decl) != TREE_TYPE (fn->decl) |
| || DECL_CONTEXT (node->decl) != DECL_CONTEXT (fn->decl) |
| || (TREE_CODE (node->decl) == FUNCTION_DECL |
| && flags_from_decl_or_type (node->decl) |
| != flags_from_decl_or_type (fn->decl)) |
| || DECL_ATTRIBUTES (node->decl) != DECL_ATTRIBUTES (fn->decl)) |
| return false; |
| *(symtab_node **)data = node; |
| return true; |
| } |
| return false; |
| } |
| |
| /* If node cannot 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_node::noninterposable_alias (void) |
| { |
| tree new_decl; |
| symtab_node *new_node = NULL; |
| |
| /* First try to look up existing alias or base object |
| (if that is already non-overwritable). */ |
| symtab_node *node = ultimate_alias_target (); |
| gcc_assert (!node->alias && !node->weakref); |
| node->call_for_symbol_and_aliases (symtab_node::noninterposable_alias, |
| (void *)&new_node, true); |
| if (new_node) |
| return new_node; |
| |
| /* If aliases aren't supported by the assembler, fail. */ |
| if (!TARGET_SUPPORTS_ALIASES) |
| return NULL; |
| else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (node->decl))) |
| return NULL; |
| |
| /* Otherwise create a new one. */ |
| new_decl = copy_node (node->decl); |
| DECL_DLLIMPORT_P (new_decl) = 0; |
| tree name = clone_function_name (node->decl, "localalias"); |
| if (!flag_wpa) |
| { |
| unsigned long num = 0; |
| /* In the rare case we already have a localalias, but the above |
| node->call_for_symbol_and_aliases call didn't find any suitable, |
| iterate until we find one not used yet. */ |
| while (symtab_node::get_for_asmname (name)) |
| name = clone_function_name (node->decl, "localalias", num++); |
| } |
| DECL_NAME (new_decl) = name; |
| 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; |
| TREE_PUBLIC (new_decl) = 0; |
| DECL_COMDAT (new_decl) = 0; |
| DECL_WEAK (new_decl) = 0; |
| |
| /* Since the aliases can be added to vtables, keep DECL_VIRTUAL flag. */ |
| DECL_VIRTUAL_P (new_decl) = DECL_VIRTUAL_P (node->decl); |
| if (TREE_CODE (new_decl) == FUNCTION_DECL) |
| { |
| DECL_STATIC_CONSTRUCTOR (new_decl) = 0; |
| DECL_STATIC_DESTRUCTOR (new_decl) = 0; |
| new_node = cgraph_node::create_alias (new_decl, node->decl); |
| |
| cgraph_node *new_cnode = dyn_cast <cgraph_node *> (new_node), |
| *cnode = dyn_cast <cgraph_node *> (node); |
| |
| new_cnode->unit_id = cnode->unit_id; |
| new_cnode->merged_comdat = cnode->merged_comdat; |
| new_cnode->merged_extern_inline = cnode->merged_extern_inline; |
| } |
| else |
| { |
| TREE_READONLY (new_decl) = TREE_READONLY (node->decl); |
| DECL_INITIAL (new_decl) = error_mark_node; |
| new_node = varpool_node::create_alias (new_decl, node->decl); |
| } |
| new_node->resolve_alias (node); |
| gcc_assert (decl_binds_to_current_def_p (new_decl) |
| && targetm.binds_local_p (new_decl)); |
| return new_node; |
| } |
| |
| /* Return true if symtab node and TARGET represents |
| semantically equivalent symbols. */ |
| |
| bool |
| symtab_node::semantically_equivalent_p (symtab_node *target) |
| { |
| enum availability avail; |
| symtab_node *ba; |
| symtab_node *bb; |
| |
| /* Equivalent functions are equivalent. */ |
| if (decl == target->decl) |
| return true; |
| |
| /* If symbol is not overwritable by different implementation, |
| walk to the base object it defines. */ |
| ba = ultimate_alias_target (&avail); |
| if (avail >= AVAIL_AVAILABLE) |
| { |
| if (target == ba) |
| return true; |
| } |
| else |
| ba = this; |
| bb = target->ultimate_alias_target (&avail); |
| if (avail >= AVAIL_AVAILABLE) |
| { |
| if (this == bb) |
| return true; |
| } |
| else |
| bb = target; |
| return bb == ba; |
| } |
| |
| /* Classify symbol symtab node for partitioning. */ |
| |
| enum symbol_partitioning_class |
| symtab_node::get_partitioning_class (void) |
| { |
| /* Inline clones are always duplicated. |
| This include external declarations. */ |
| cgraph_node *cnode = dyn_cast <cgraph_node *> (this); |
| |
| if (DECL_ABSTRACT_P (decl)) |
| return SYMBOL_EXTERNAL; |
| |
| if (cnode && (cnode->inlined_to || cnode->declare_variant_alt)) |
| return SYMBOL_DUPLICATE; |
| |
| /* Transparent aliases are always duplicated. */ |
| if (transparent_alias) |
| return definition ? SYMBOL_DUPLICATE : SYMBOL_EXTERNAL; |
| |
| /* External declarations are external. */ |
| if (DECL_EXTERNAL (decl)) |
| return SYMBOL_EXTERNAL; |
| |
| /* Even static aliases of external functions as external. Those can happen |
| when COMDAT got resolved to non-IL implementation. */ |
| if (alias && DECL_EXTERNAL (ultimate_alias_target ()->decl)) |
| return SYMBOL_EXTERNAL; |
| |
| if (varpool_node *vnode = dyn_cast <varpool_node *> (this)) |
| { |
| if (alias && definition && !ultimate_alias_target ()->definition) |
| return SYMBOL_EXTERNAL; |
| /* Constant pool references use local symbol names that cannot |
| be promoted global. We should never put into a constant pool |
| objects that cannot be duplicated across partitions. */ |
| if (DECL_IN_CONSTANT_POOL (decl)) |
| return SYMBOL_DUPLICATE; |
| if (DECL_HARD_REGISTER (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 (!dyn_cast <cgraph_node *> (this)->function_symbol ()->definition) |
| return SYMBOL_EXTERNAL; |
| |
| /* Linker discardable symbols are duplicated to every use unless they are |
| keyed. */ |
| if (DECL_ONE_ONLY (decl) |
| && !force_output |
| && !forced_by_abi |
| && !used_from_object_file_p ()) |
| return SYMBOL_DUPLICATE; |
| |
| return SYMBOL_PARTITION; |
| } |
| |
| /* Return true when symbol is known to be non-zero. */ |
| |
| bool |
| symtab_node::nonzero_address () |
| { |
| /* Weakrefs may be NULL when their target is not defined. */ |
| if (alias && weakref) |
| { |
| if (analyzed) |
| { |
| symtab_node *target = ultimate_alias_target (); |
| |
| if (target->alias && target->weakref) |
| return false; |
| /* We cannot recurse to target::nonzero. It is possible that the |
| target is used only via the alias. |
| We may walk references and look for strong use, but we do not know |
| if this strong use will survive to final binary, so be |
| conservative here. |
| ??? Maybe we could do the lookup during late optimization that |
| could be useful to eliminate the NULL pointer checks in LTO |
| programs. */ |
| if (target->definition && !DECL_EXTERNAL (target->decl)) |
| return true; |
| if (target->resolution != LDPR_UNKNOWN |
| && target->resolution != LDPR_UNDEF |
| && !target->can_be_discarded_p () |
| && flag_delete_null_pointer_checks) |
| return true; |
| return false; |
| } |
| else |
| return false; |
| } |
| |
| /* With !flag_delete_null_pointer_checks we assume that symbols may |
| bind to NULL. This is on by default on embedded targets only. |
| |
| Otherwise all non-WEAK symbols must be defined and thus non-NULL or |
| linking fails. Important case of WEAK we want to do well are comdats, |
| which also must be defined somewhere. |
| |
| When parsing, beware the cases when WEAK attribute is added later. */ |
| if ((!DECL_WEAK (decl) || DECL_COMDAT (decl)) |
| && flag_delete_null_pointer_checks) |
| { |
| refuse_visibility_changes = true; |
| return true; |
| } |
| |
| /* If target is defined and not extern, we know it will be |
| output and thus it will bind to non-NULL. |
| Play safe for flag_delete_null_pointer_checks where weak definition may |
| be re-defined by NULL. */ |
| if (definition && !DECL_EXTERNAL (decl) |
| && (flag_delete_null_pointer_checks || !DECL_WEAK (decl))) |
| { |
| if (!DECL_WEAK (decl)) |
| refuse_visibility_changes = true; |
| return true; |
| } |
| |
| /* As the last resort, check the resolution info. */ |
| if (resolution != LDPR_UNKNOWN |
| && resolution != LDPR_UNDEF |
| && !can_be_discarded_p () |
| && flag_delete_null_pointer_checks) |
| return true; |
| return false; |
| } |
| |
| /* Return 0 if symbol is known to have different address than S2, |
| Return 1 if symbol is known to have same address as S2, |
| return -1 otherwise. |
| |
| If MEMORY_ACCESSED is true, assume that both memory pointer to THIS |
| and S2 is going to be accessed. This eliminates the situations when |
| either THIS or S2 is NULL and is useful for comparing bases when deciding |
| about memory aliasing. */ |
| int |
| symtab_node::equal_address_to (symtab_node *s2, bool memory_accessed) |
| { |
| enum availability avail1, avail2; |
| |
| /* A Shortcut: equivalent symbols are always equivalent. */ |
| if (this == s2) |
| return 1; |
| |
| /* Unwind transparent aliases first; those are always equal to their |
| target. */ |
| if (this->transparent_alias && this->analyzed) |
| return this->get_alias_target ()->equal_address_to (s2); |
| while (s2->transparent_alias && s2->analyzed) |
| s2 = s2->get_alias_target(); |
| |
| if (this == s2) |
| return 1; |
| |
| /* For non-interposable aliases, lookup and compare their actual definitions. |
| Also check if the symbol needs to bind to given definition. */ |
| symtab_node *rs1 = ultimate_alias_target (&avail1); |
| symtab_node *rs2 = s2->ultimate_alias_target (&avail2); |
| bool binds_local1 = rs1->analyzed && decl_binds_to_current_def_p (this->decl); |
| bool binds_local2 = rs2->analyzed && decl_binds_to_current_def_p (s2->decl); |
| bool really_binds_local1 = binds_local1; |
| bool really_binds_local2 = binds_local2; |
| |
| /* Addresses of vtables and virtual functions cannot be used by user |
| code and are used only within speculation. In this case we may make |
| symbol equivalent to its alias even if interposition may break this |
| rule. Doing so will allow us to turn speculative inlining into |
| non-speculative more aggressively. */ |
| if (DECL_VIRTUAL_P (this->decl) && avail1 >= AVAIL_AVAILABLE) |
| binds_local1 = true; |
| if (DECL_VIRTUAL_P (s2->decl) && avail2 >= AVAIL_AVAILABLE) |
| binds_local2 = true; |
| |
| /* If both definitions are available we know that even if they are bound |
| to other unit they must be defined same way and therefore we can use |
| equivalence test. */ |
| if (rs1 != rs2 && avail1 >= AVAIL_AVAILABLE && avail2 >= AVAIL_AVAILABLE) |
| binds_local1 = binds_local2 = true; |
| |
| if (binds_local1 && binds_local2 && rs1 == rs2) |
| { |
| /* We made use of the fact that alias is not weak. */ |
| if (rs1 != this) |
| refuse_visibility_changes = true; |
| if (rs2 != s2) |
| s2->refuse_visibility_changes = true; |
| return 1; |
| } |
| |
| /* If both symbols may resolve to NULL, we cannot really prove them |
| different. */ |
| if (!memory_accessed && !nonzero_address () && !s2->nonzero_address ()) |
| return -1; |
| |
| /* Except for NULL, functions and variables never overlap. */ |
| if (TREE_CODE (decl) != TREE_CODE (s2->decl)) |
| return 0; |
| |
| /* If one of the symbols is unresolved alias, punt. */ |
| if (rs1->alias || rs2->alias) |
| return -1; |
| |
| /* If we have a non-interposable definition of at least one of the symbols |
| and the other symbol is different, we know other unit cannot interpose |
| it to the first symbol; all aliases of the definition needs to be |
| present in the current unit. */ |
| if (((really_binds_local1 || really_binds_local2) |
| /* If we have both definitions and they are different, we know they |
| will be different even in units they binds to. */ |
| || (binds_local1 && binds_local2)) |
| && rs1 != rs2) |
| { |
| /* We make use of the fact that one symbol is not alias of the other |
| and that the definition is non-interposable. */ |
| refuse_visibility_changes = true; |
| s2->refuse_visibility_changes = true; |
| rs1->refuse_visibility_changes = true; |
| rs2->refuse_visibility_changes = true; |
| return 0; |
| } |
| |
| if (rs1 == rs2) |
| return -1; |
| |
| /* If the FE tells us at least one of the decls will never be aliased nor |
| overlapping with other vars in some other way, return 0. */ |
| if (VAR_P (decl) |
| && (lookup_attribute ("non overlapping", DECL_ATTRIBUTES (decl)) |
| || lookup_attribute ("non overlapping", DECL_ATTRIBUTES (s2->decl)))) |
| return 0; |
| |
| /* TODO: Alias oracle basically assume that addresses of global variables |
| are different unless they are declared as alias of one to another while |
| the code folding comparisons doesn't. |
| We probably should be consistent and use this fact here, too, but for |
| the moment return false only when we are called from the alias oracle. |
| Return 0 in C constant initializers and C++ manifestly constant |
| expressions, the likelyhood that different vars will be aliases is |
| small and returning -1 lets us reject too many initializers. */ |
| if (memory_accessed || folding_initializer) |
| return 0; |
| |
| return -1; |
| } |
| |
| /* Worker for call_for_symbol_and_aliases. */ |
| |
| bool |
| symtab_node::call_for_symbol_and_aliases_1 (bool (*callback) (symtab_node *, |
| void *), |
| void *data, |
| bool include_overwritable) |
| { |
| ipa_ref *ref; |
| FOR_EACH_ALIAS (this, ref) |
| { |
| symtab_node *alias = ref->referring; |
| if (include_overwritable |
| || alias->get_availability () > AVAIL_INTERPOSABLE) |
| if (alias->call_for_symbol_and_aliases (callback, data, |
| include_overwritable)) |
| return true; |
| } |
| return false; |
| } |
| |
| /* Return true if address of N is possibly compared. */ |
| |
| static bool |
| address_matters_1 (symtab_node *n, void *) |
| { |
| struct ipa_ref *ref; |
| |
| if (!n->address_can_be_compared_p ()) |
| return false; |
| if (n->externally_visible || n->force_output) |
| return true; |
| |
| for (unsigned int i = 0; n->iterate_referring (i, ref); i++) |
| if (ref->address_matters_p ()) |
| return true; |
| return false; |
| } |
| |
| /* Return true if symbol's address may possibly be compared to other |
| symbol's address. */ |
| |
| bool |
| symtab_node::address_matters_p () |
| { |
| gcc_assert (!alias); |
| return call_for_symbol_and_aliases (address_matters_1, NULL, true); |
| } |
| |
| /* Return true if symbol's alignment may be increased. */ |
| |
| bool |
| symtab_node::can_increase_alignment_p (void) |
| { |
| symtab_node *target = ultimate_alias_target (); |
| |
| /* For now support only variables. */ |
| if (!VAR_P (decl)) |
| return false; |
| |
| /* With -fno-toplevel-reorder we may have already output the constant. */ |
| if (TREE_ASM_WRITTEN (target->decl)) |
| return false; |
| |
| /* If target is already placed in an anchor, we cannot touch its |
| alignment. */ |
| if (DECL_RTL_SET_P (target->decl) |
| && MEM_P (DECL_RTL (target->decl)) |
| && SYMBOL_REF_HAS_BLOCK_INFO_P (XEXP (DECL_RTL (target->decl), 0))) |
| return false; |
| |
| /* Constant pool entries may be shared. */ |
| if (DECL_IN_CONSTANT_POOL (target->decl)) |
| return false; |
| |
| /* We cannot change alignment of symbols that may bind to symbols |
| in other translation unit that may contain a definition with lower |
| alignment. */ |
| if (!decl_binds_to_current_def_p (decl)) |
| return false; |
| |
| /* When compiling partition, be sure the symbol is not output by other |
| partition. */ |
| if (flag_ltrans |
| && (target->in_other_partition |
| || target->get_partitioning_class () == SYMBOL_DUPLICATE)) |
| return false; |
| |
| /* Do not override the alignment as specified by the ABI when the used |
| attribute is set. */ |
| if (DECL_PRESERVE_P (decl) || DECL_PRESERVE_P (target->decl)) |
| return false; |
| |
| /* Do not override explicit alignment set by the user when an explicit |
| section name is also used. This is a common idiom used by many |
| software projects. */ |
| if (DECL_SECTION_NAME (target->decl) != NULL && !target->implicit_section) |
| return false; |
| |
| return true; |
| } |
| |
| /* Worker for symtab_node::increase_alignment. */ |
| |
| static bool |
| increase_alignment_1 (symtab_node *n, void *v) |
| { |
| unsigned int align = (size_t)v; |
| if (DECL_ALIGN (n->decl) < align |
| && n->can_increase_alignment_p ()) |
| { |
| SET_DECL_ALIGN (n->decl, align); |
| DECL_USER_ALIGN (n->decl) = 1; |
| } |
| return false; |
| } |
| |
| /* Increase alignment of THIS to ALIGN. */ |
| |
| void |
| symtab_node::increase_alignment (unsigned int align) |
| { |
| gcc_assert (can_increase_alignment_p () && align <= MAX_OFILE_ALIGNMENT); |
| ultimate_alias_target()->call_for_symbol_and_aliases (increase_alignment_1, |
| (void *)(size_t) align, |
| true); |
| gcc_assert (DECL_ALIGN (decl) >= align); |
| } |
| |
| /* Helper for symtab_node::definition_alignment. */ |
| |
| static bool |
| get_alignment_1 (symtab_node *n, void *v) |
| { |
| *((unsigned int *)v) = MAX (*((unsigned int *)v), DECL_ALIGN (n->decl)); |
| return false; |
| } |
| |
| /* Return desired alignment of the definition. This is NOT alignment useful |
| to access THIS, because THIS may be interposable and DECL_ALIGN should |
| be used instead. It however must be guaranteed when output definition |
| of THIS. */ |
| |
| unsigned int |
| symtab_node::definition_alignment () |
| { |
| unsigned int align = 0; |
| gcc_assert (!alias); |
| call_for_symbol_and_aliases (get_alignment_1, &align, true); |
| return align; |
| } |
| |
| /* Return symbol used to separate symbol name from suffix. */ |
| |
| char |
| symbol_table::symbol_suffix_separator () |
| { |
| #ifndef NO_DOT_IN_LABEL |
| return '.'; |
| #elif !defined NO_DOLLAR_IN_LABEL |
| return '$'; |
| #else |
| return '_'; |
| #endif |
| } |
| |
| /* Return true when references to this symbol from REF must bind to current |
| definition in final executable. */ |
| |
| bool |
| symtab_node::binds_to_current_def_p (symtab_node *ref) |
| { |
| if (!definition && !in_other_partition) |
| return false; |
| if (transparent_alias) |
| return definition |
| && get_alias_target()->binds_to_current_def_p (ref); |
| cgraph_node *cnode = dyn_cast <cgraph_node *> (this); |
| if (cnode && cnode->ifunc_resolver) |
| return false; |
| if (decl_binds_to_current_def_p (decl)) |
| return true; |
| |
| /* Inline clones always binds locally. */ |
| if (cnode && cnode->inlined_to) |
| return true; |
| |
| if (DECL_EXTERNAL (decl)) |
| return false; |
| |
| gcc_assert (externally_visible); |
| |
| if (ref) |
| { |
| cgraph_node *cref = dyn_cast <cgraph_node *> (ref); |
| if (cref) |
| ref = cref->inlined_to; |
| } |
| |
| /* If this is a reference from symbol itself and there are no aliases, we |
| may be sure that the symbol was not interposed by something else because |
| the symbol itself would be unreachable otherwise. This is important |
| to optimize recursive functions well. |
| |
| This assumption may be broken by inlining: if symbol is interposable |
| but the body is available (i.e. declared inline), inliner may make |
| the body reachable even with interposition. */ |
| if (this == ref && !has_aliases_p () |
| && (!cnode |
| || symtab->state >= IPA_SSA_AFTER_INLINING |
| || get_availability () >= AVAIL_INTERPOSABLE)) |
| return true; |
| |
| |
| /* References within one comdat group are always bound in a group. */ |
| if (ref |
| && symtab->state >= IPA_SSA_AFTER_INLINING |
| && get_comdat_group () |
| && get_comdat_group () == ref->get_comdat_group ()) |
| return true; |
| |
| return false; |
| } |
| |
| /* Return true if symbol should be output to the symbol table. */ |
| |
| bool |
| symtab_node::output_to_lto_symbol_table_p (void) |
| { |
| /* Only externally visible symbols matter. */ |
| if (!TREE_PUBLIC (decl)) |
| return false; |
| if (!real_symbol_p ()) |
| return false; |
| /* FIXME: variables probably should not be considered as real symbols at |
| first place. */ |
| if (VAR_P (decl) && DECL_HARD_REGISTER (decl)) |
| return false; |
| if (TREE_CODE (decl) == FUNCTION_DECL && !definition |
| && fndecl_built_in_p (decl)) |
| { |
| /* Builtins like those for most math functions have actual implementations |
| in libraries so make sure to output references into the symbol table to |
| make those libraries referenced. Note this is incomplete handling for |
| now and only covers math functions. */ |
| return builtin_with_linkage_p (decl); |
| } |
| |
| /* We have real symbol that should be in symbol table. However try to trim |
| down the references to libraries bit more because linker will otherwise |
| bring unnecessary object files into the final link. |
| FIXME: The following checks can easily be confused i.e. by self recursive |
| function or self-referring variable. */ |
| |
| /* We keep external functions in symtab for sake of inlining |
| and devirtualization. We do not want to see them in symbol table as |
| references unless they are really used. */ |
| cgraph_node *cnode = dyn_cast <cgraph_node *> (this); |
| if (cnode && (!definition || DECL_EXTERNAL (decl)) |
| && cnode->callers) |
| return true; |
| |
| /* Ignore all references from external vars initializers - they are not really |
| part of the compilation unit until they are used by folding. Some symbols, |
| like references to external construction vtables cannot be referred to at |
| all. We decide this at can_refer_decl_in_current_unit_p. */ |
| if (!definition || DECL_EXTERNAL (decl)) |
| { |
| int i; |
| struct ipa_ref *ref; |
| for (i = 0; iterate_referring (i, ref); i++) |
| { |
| if (ref->use == IPA_REF_ALIAS) |
| continue; |
| if (is_a <cgraph_node *> (ref->referring)) |
| return true; |
| if (!DECL_EXTERNAL (ref->referring->decl)) |
| return true; |
| } |
| return false; |
| } |
| return true; |
| } |