| /* Callgraph handling code. |
| Copyright (C) 2003-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 "tree.h" |
| #include "gimple.h" |
| #include "timevar.h" |
| #include "cgraph.h" |
| #include "lto-streamer.h" |
| #include "varasm.h" |
| #include "debug.h" |
| #include "output.h" |
| #include "omp-offload.h" |
| #include "context.h" |
| #include "stringpool.h" |
| #include "attribs.h" |
| #include "tree-pass.h" |
| |
| const char * const tls_model_names[]={"none", "emulated", |
| "global-dynamic", "local-dynamic", |
| "initial-exec", "local-exec"}; |
| |
| /* List of hooks triggered on varpool_node events. */ |
| struct varpool_node_hook_list { |
| varpool_node_hook hook; |
| void *data; |
| struct varpool_node_hook_list *next; |
| }; |
| |
| /* Register HOOK to be called with DATA on each removed node. */ |
| varpool_node_hook_list * |
| symbol_table::add_varpool_removal_hook (varpool_node_hook hook, void *data) |
| { |
| varpool_node_hook_list *entry; |
| varpool_node_hook_list **ptr = &m_first_varpool_removal_hook; |
| |
| entry = (varpool_node_hook_list *) xmalloc (sizeof (*entry)); |
| entry->hook = hook; |
| entry->data = data; |
| entry->next = NULL; |
| while (*ptr) |
| ptr = &(*ptr)->next; |
| *ptr = entry; |
| return entry; |
| } |
| |
| /* Remove ENTRY from the list of hooks called on removing nodes. */ |
| void |
| symbol_table::remove_varpool_removal_hook (varpool_node_hook_list *entry) |
| { |
| varpool_node_hook_list **ptr = &m_first_varpool_removal_hook; |
| |
| while (*ptr != entry) |
| ptr = &(*ptr)->next; |
| *ptr = entry->next; |
| free (entry); |
| } |
| |
| /* Call all node removal hooks. */ |
| void |
| symbol_table::call_varpool_removal_hooks (varpool_node *node) |
| { |
| varpool_node_hook_list *entry = m_first_varpool_removal_hook; |
| while (entry) |
| { |
| entry->hook (node, entry->data); |
| entry = entry->next; |
| } |
| } |
| |
| /* Register HOOK to be called with DATA on each inserted node. */ |
| varpool_node_hook_list * |
| symbol_table::add_varpool_insertion_hook (varpool_node_hook hook, void *data) |
| { |
| varpool_node_hook_list *entry; |
| varpool_node_hook_list **ptr = &m_first_varpool_insertion_hook; |
| |
| entry = (varpool_node_hook_list *) xmalloc (sizeof (*entry)); |
| entry->hook = hook; |
| entry->data = data; |
| entry->next = NULL; |
| while (*ptr) |
| ptr = &(*ptr)->next; |
| *ptr = entry; |
| return entry; |
| } |
| |
| /* Remove ENTRY from the list of hooks called on inserted nodes. */ |
| void |
| symbol_table::remove_varpool_insertion_hook (varpool_node_hook_list *entry) |
| { |
| varpool_node_hook_list **ptr = &m_first_varpool_insertion_hook; |
| |
| while (*ptr != entry) |
| ptr = &(*ptr)->next; |
| *ptr = entry->next; |
| free (entry); |
| } |
| |
| /* Call all node insertion hooks. */ |
| void |
| symbol_table::call_varpool_insertion_hooks (varpool_node *node) |
| { |
| varpool_node_hook_list *entry = m_first_varpool_insertion_hook; |
| while (entry) |
| { |
| entry->hook (node, entry->data); |
| entry = entry->next; |
| } |
| } |
| |
| /* Allocate new callgraph node and insert it into basic data structures. */ |
| |
| varpool_node * |
| varpool_node::create_empty (void) |
| { |
| return new (ggc_alloc<varpool_node> ()) varpool_node (); |
| } |
| |
| /* Return varpool node assigned to DECL. Create new one when needed. */ |
| varpool_node * |
| varpool_node::get_create (tree decl) |
| { |
| varpool_node *node = varpool_node::get (decl); |
| gcc_checking_assert (VAR_P (decl)); |
| if (node) |
| return node; |
| |
| node = varpool_node::create_empty (); |
| node->decl = decl; |
| |
| if ((flag_openacc || flag_openmp) |
| && lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl))) |
| { |
| node->offloadable = 1; |
| if (ENABLE_OFFLOADING && !DECL_EXTERNAL (decl)) |
| { |
| g->have_offload = true; |
| if (!in_lto_p) |
| vec_safe_push (offload_vars, decl); |
| } |
| } |
| |
| node->register_symbol (); |
| return node; |
| } |
| |
| /* Remove variable from symbol table. */ |
| |
| void |
| varpool_node::remove (void) |
| { |
| symtab->call_varpool_removal_hooks (this); |
| if (lto_file_data) |
| { |
| lto_free_function_in_decl_state_for_node (this); |
| lto_file_data = NULL; |
| } |
| |
| /* When streaming we can have multiple nodes associated with decl. */ |
| if (symtab->state == LTO_STREAMING) |
| ; |
| /* Keep constructor when it may be used for folding. We remove |
| references to external variables before final compilation. */ |
| else if (DECL_INITIAL (decl) && DECL_INITIAL (decl) != error_mark_node |
| && !ctor_useable_for_folding_p ()) |
| remove_initializer (); |
| |
| unregister (NULL); |
| ggc_free (this); |
| } |
| |
| /* Remove node initializer when it is no longer needed. */ |
| void |
| varpool_node::remove_initializer (void) |
| { |
| if (DECL_INITIAL (decl) |
| && !DECL_IN_CONSTANT_POOL (decl) |
| /* Keep vtables for BINFO folding. */ |
| && !DECL_VIRTUAL_P (decl) |
| /* FIXME: http://gcc.gnu.org/PR55395 */ |
| && debug_info_level == DINFO_LEVEL_NONE |
| /* When doing declaration merging we have duplicate |
| entries for given decl. Do not attempt to remove |
| the bodies, or we will end up removing |
| wrong one. */ |
| && symtab->state != LTO_STREAMING) |
| DECL_INITIAL (decl) = error_mark_node; |
| } |
| |
| /* Dump given varpool node to F. */ |
| void |
| varpool_node::dump (FILE *f) |
| { |
| dump_base (f); |
| fprintf (f, " Availability: %s\n", |
| symtab->function_flags_ready |
| ? cgraph_availability_names[get_availability ()] |
| : "not-ready"); |
| fprintf (f, " Varpool flags:"); |
| if (DECL_INITIAL (decl)) |
| fprintf (f, " initialized"); |
| if (output) |
| fprintf (f, " output"); |
| if (used_by_single_function) |
| fprintf (f, " used-by-single-function"); |
| if (TREE_READONLY (decl)) |
| fprintf (f, " read-only"); |
| if (ctor_useable_for_folding_p ()) |
| fprintf (f, " const-value-known"); |
| if (writeonly) |
| fprintf (f, " write-only"); |
| if (tls_model) |
| fprintf (f, " tls-%s", tls_model_names [tls_model]); |
| fprintf (f, "\n"); |
| } |
| |
| |
| /* Dump given varpool node to stderr. */ |
| void varpool_node::debug (void) |
| { |
| varpool_node::dump (stderr); |
| } |
| |
| /* Dump the variable pool to F. */ |
| void |
| varpool_node::dump_varpool (FILE *f) |
| { |
| varpool_node *node; |
| |
| fprintf (f, "variable pool:\n\n"); |
| FOR_EACH_VARIABLE (node) |
| node->dump (f); |
| } |
| |
| /* Dump the variable pool to stderr. */ |
| |
| DEBUG_FUNCTION void |
| varpool_node::debug_varpool (void) |
| { |
| dump_varpool (stderr); |
| } |
| |
| /* Given an assembler name, lookup node. */ |
| varpool_node * |
| varpool_node::get_for_asmname (tree asmname) |
| { |
| if (symtab_node *node = symtab_node::get_for_asmname (asmname)) |
| return dyn_cast <varpool_node *> (node); |
| else |
| return NULL; |
| } |
| |
| /* When doing LTO, read variable's constructor from disk if |
| it is not already present. */ |
| |
| tree |
| varpool_node::get_constructor (void) |
| { |
| lto_file_decl_data *file_data; |
| const char *data, *name; |
| size_t len; |
| |
| if (DECL_INITIAL (decl) != error_mark_node |
| || !in_lto_p |
| || !lto_file_data) |
| return DECL_INITIAL (decl); |
| |
| timevar_push (TV_IPA_LTO_CTORS_IN); |
| |
| file_data = lto_file_data; |
| name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)); |
| |
| /* We may have renamed the declaration, e.g., a static function. */ |
| name = lto_get_decl_name_mapping (file_data, name); |
| struct lto_in_decl_state *decl_state |
| = lto_get_function_in_decl_state (file_data, decl); |
| |
| data = lto_get_section_data (file_data, LTO_section_function_body, |
| name, order - file_data->order_base, |
| &len, decl_state->compressed); |
| if (!data) |
| fatal_error (input_location, "%s: section %s.%d is missing", |
| file_data->file_name, |
| name, order - file_data->order_base); |
| |
| if (!quiet_flag) |
| fprintf (stderr, " in:%s", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); |
| lto_input_variable_constructor (file_data, this, data); |
| gcc_assert (DECL_INITIAL (decl) != error_mark_node); |
| lto_stats.num_function_bodies++; |
| lto_free_section_data (file_data, LTO_section_function_body, name, |
| data, len, decl_state->compressed); |
| lto_free_function_in_decl_state_for_node (this); |
| timevar_pop (TV_IPA_LTO_CTORS_IN); |
| return DECL_INITIAL (decl); |
| } |
| |
| /* Return true if variable has constructor that can be used for folding. */ |
| |
| bool |
| varpool_node::ctor_useable_for_folding_p (void) |
| { |
| varpool_node *real_node = this; |
| |
| if (real_node->alias && real_node->definition) |
| real_node = ultimate_alias_target (); |
| |
| if (TREE_CODE (decl) == CONST_DECL |
| || DECL_IN_CONSTANT_POOL (decl)) |
| return true; |
| if (TREE_THIS_VOLATILE (decl)) |
| return false; |
| |
| /* Avoid attempts to load constructors that was not streamed. */ |
| if (in_lto_p && DECL_INITIAL (real_node->decl) == error_mark_node |
| && real_node->body_removed) |
| return false; |
| |
| /* If we do not have a constructor, we can't use it. */ |
| if (DECL_INITIAL (real_node->decl) == error_mark_node |
| && !real_node->lto_file_data) |
| return false; |
| |
| /* Vtables are defined by their types and must match no matter of interposition |
| rules. */ |
| if (DECL_VIRTUAL_P (decl)) |
| { |
| /* The C++ front end creates VAR_DECLs for vtables of typeinfo |
| classes not defined in the current TU so that it can refer |
| to them from typeinfo objects. Avoid returning NULL_TREE. */ |
| return DECL_INITIAL (real_node->decl) != NULL; |
| } |
| |
| /* An alias of a read-only variable is also read-only, since the variable |
| is stored in read-only memory. We also accept read-only aliases of |
| non-read-only locations assuming that the user knows what he is asking |
| for. */ |
| if (!TREE_READONLY (decl) && !TREE_READONLY (real_node->decl)) |
| return false; |
| |
| /* Variables declared 'const' without an initializer |
| have zero as the initializer if they may not be |
| overridden at link or run time. |
| |
| It is actually requirement for C++ compiler to optimize const variables |
| consistently. As a GNU extension, do not enforce this rule for user defined |
| weak variables, so we support interposition on: |
| static const int dummy = 0; |
| extern const int foo __attribute__((__weak__, __alias__("dummy"))); |
| */ |
| if ((!DECL_INITIAL (real_node->decl) |
| || (DECL_WEAK (decl) && !DECL_COMDAT (decl))) |
| && ((DECL_EXTERNAL (decl) && !in_other_partition) |
| || decl_replaceable_p (decl, semantic_interposition))) |
| return false; |
| |
| /* Variables declared `const' with an initializer are considered |
| to not be overwritable with different initializer by default. |
| |
| ??? Previously we behaved so for scalar variables but not for array |
| accesses. */ |
| return true; |
| } |
| |
| /* If DECLARATION is constant variable and its initial value is known |
| (so we can do constant folding), return its constructor (DECL_INITIAL). |
| This may be an expression or NULL when DECL is initialized to 0. |
| Return ERROR_MARK_NODE otherwise. |
| |
| In LTO this may actually trigger reading the constructor from disk. |
| For this reason varpool_ctor_useable_for_folding_p should be used when |
| the actual constructor value is not needed. */ |
| |
| tree |
| ctor_for_folding (tree decl) |
| { |
| varpool_node *node, *real_node; |
| tree real_decl; |
| |
| if (!VAR_P (decl) && TREE_CODE (decl) != CONST_DECL) |
| return error_mark_node; |
| |
| if (TREE_CODE (decl) == CONST_DECL |
| || DECL_IN_CONSTANT_POOL (decl)) |
| return DECL_INITIAL (decl); |
| |
| if (TREE_THIS_VOLATILE (decl)) |
| return error_mark_node; |
| |
| /* Do not care about automatic variables. Those are never initialized |
| anyway, because gimplifier expands the code. */ |
| if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl)) |
| { |
| gcc_assert (!TREE_PUBLIC (decl)); |
| /* Unless this is called during FE folding. */ |
| if (cfun |
| && (cfun->curr_properties & (PROP_gimple | PROP_rtl)) == 0 |
| && TREE_READONLY (decl) |
| && !TREE_SIDE_EFFECTS (decl) |
| && DECL_INITIAL (decl)) |
| return DECL_INITIAL (decl); |
| return error_mark_node; |
| } |
| |
| gcc_assert (VAR_P (decl)); |
| |
| real_node = node = varpool_node::get (decl); |
| if (node) |
| { |
| real_node = node->ultimate_alias_target (); |
| real_decl = real_node->decl; |
| } |
| else |
| real_decl = decl; |
| |
| /* See if we are dealing with alias. |
| In most cases alias is just alternative symbol pointing to a given |
| constructor. This allows us to use interposition rules of DECL |
| constructor of REAL_NODE. However weakrefs are special by being just |
| alternative name of their target (if defined). */ |
| if (decl != real_decl) |
| { |
| gcc_assert (!DECL_INITIAL (decl) |
| || (node->alias && node->get_alias_target () == real_node) |
| || DECL_INITIAL (decl) == error_mark_node); |
| while (node->transparent_alias && node->analyzed) |
| { |
| node = node->get_alias_target (); |
| decl = node->decl; |
| } |
| } |
| |
| if ((!DECL_VIRTUAL_P (real_decl) |
| || DECL_INITIAL (real_decl) == error_mark_node |
| || !DECL_INITIAL (real_decl)) |
| && (!node || !node->ctor_useable_for_folding_p ())) |
| return error_mark_node; |
| |
| /* OK, we can return constructor. See if we need to fetch it from disk |
| in LTO mode. */ |
| if (DECL_INITIAL (real_decl) != error_mark_node |
| || !in_lto_p) |
| return DECL_INITIAL (real_decl); |
| return real_node->get_constructor (); |
| } |
| |
| /* Add the variable DECL to the varpool. |
| Unlike finalize_decl function is intended to be used |
| by middle end and allows insertion of new variable at arbitrary point |
| of compilation. */ |
| void |
| varpool_node::add (tree decl) |
| { |
| varpool_node *node; |
| varpool_node::finalize_decl (decl); |
| node = varpool_node::get_create (decl); |
| symtab->call_varpool_insertion_hooks (node); |
| if (node->externally_visible_p ()) |
| node->externally_visible = true; |
| if (lookup_attribute ("no_reorder", DECL_ATTRIBUTES (decl))) |
| node->no_reorder = 1; |
| } |
| |
| /* Return variable availability. See cgraph.h for description of individual |
| return values. */ |
| enum availability |
| varpool_node::get_availability (symtab_node *ref) |
| { |
| if (!definition && !in_other_partition) |
| return AVAIL_NOT_AVAILABLE; |
| if (!TREE_PUBLIC (decl)) |
| return AVAIL_AVAILABLE; |
| if (DECL_IN_CONSTANT_POOL (decl) |
| || DECL_VIRTUAL_P (decl)) |
| return AVAIL_AVAILABLE; |
| if (transparent_alias && definition) |
| { |
| enum availability avail; |
| |
| ultimate_alias_target (&avail, ref); |
| return avail; |
| } |
| /* 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. */ |
| if ((this == ref && !has_aliases_p ()) |
| || (ref && get_comdat_group () |
| && get_comdat_group () == ref->get_comdat_group ())) |
| return AVAIL_AVAILABLE; |
| /* If the variable can be overwritten, return OVERWRITABLE. Takes |
| care of at least one notable extension - the COMDAT variables |
| used to share template instantiations in C++. */ |
| if (decl_replaceable_p (decl, semantic_interposition) |
| || (DECL_EXTERNAL (decl) && !in_other_partition)) |
| return AVAIL_INTERPOSABLE; |
| return AVAIL_AVAILABLE; |
| } |
| |
| void |
| varpool_node::analyze (void) |
| { |
| /* When reading back varpool at LTO time, we re-construct the queue in order |
| to have "needed" list right by inserting all needed nodes into varpool. |
| We however don't want to re-analyze already analyzed nodes. */ |
| if (!analyzed) |
| { |
| gcc_assert (!in_lto_p || symtab->function_flags_ready); |
| /* Compute the alignment early so function body expanders are |
| already informed about increased alignment. */ |
| align_variable (decl, 0); |
| } |
| if (alias) |
| resolve_alias (varpool_node::get (alias_target)); |
| else if (DECL_INITIAL (decl)) |
| record_references_in_initializer (decl, analyzed); |
| analyzed = true; |
| } |
| |
| /* Assemble thunks and aliases associated to varpool node. */ |
| |
| void |
| varpool_node::assemble_aliases (void) |
| { |
| ipa_ref *ref; |
| |
| FOR_EACH_ALIAS (this, ref) |
| { |
| varpool_node *alias = dyn_cast <varpool_node *> (ref->referring); |
| if (alias->symver) |
| do_assemble_symver (alias->decl, |
| DECL_ASSEMBLER_NAME (decl)); |
| else if (!alias->transparent_alias) |
| do_assemble_alias (alias->decl, |
| DECL_ASSEMBLER_NAME (decl)); |
| alias->assemble_aliases (); |
| } |
| } |
| |
| /* Output one variable, if necessary. Return whether we output it. */ |
| |
| bool |
| varpool_node::assemble_decl (void) |
| { |
| /* Aliases are output when their target is produced or by |
| output_weakrefs. */ |
| if (alias) |
| return false; |
| |
| /* Constant pool is output from RTL land when the reference |
| survive till this level. */ |
| if (DECL_IN_CONSTANT_POOL (decl) && TREE_ASM_WRITTEN (decl)) |
| return false; |
| |
| /* Decls with VALUE_EXPR should not be in the varpool at all. They |
| are not real variables, but just info for debugging and codegen. |
| Unfortunately at the moment emutls is not updating varpool correctly |
| after turning real vars into value_expr vars. */ |
| if (DECL_HAS_VALUE_EXPR_P (decl) |
| && !targetm.have_tls) |
| return false; |
| |
| /* Hard register vars do not need to be output. */ |
| if (DECL_HARD_REGISTER (decl)) |
| return false; |
| |
| gcc_checking_assert (!TREE_ASM_WRITTEN (decl) |
| && VAR_P (decl) |
| && !DECL_HAS_VALUE_EXPR_P (decl)); |
| |
| if (!in_other_partition |
| && !DECL_EXTERNAL (decl)) |
| { |
| get_constructor (); |
| assemble_variable (decl, 0, 1, 0); |
| gcc_assert (TREE_ASM_WRITTEN (decl)); |
| gcc_assert (definition); |
| assemble_aliases (); |
| /* After the parser has generated debugging information, augment |
| this information with any new location/etc information that may |
| have become available after the compilation proper. */ |
| debug_hooks->late_global_decl (decl); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Add NODE to queue starting at FIRST. |
| The queue is linked via AUX pointers and terminated by pointer to 1. */ |
| |
| static void |
| enqueue_node (varpool_node *node, varpool_node **first) |
| { |
| if (node->aux) |
| return; |
| gcc_checking_assert (*first); |
| node->aux = *first; |
| *first = node; |
| } |
| |
| /* Optimization of function bodies might've rendered some variables as |
| unnecessary so we want to avoid these from being compiled. Re-do |
| reachability starting from variables that are either externally visible |
| or was referred from the asm output routines. */ |
| |
| void |
| symbol_table::remove_unreferenced_decls (void) |
| { |
| varpool_node *next, *node; |
| varpool_node *first = (varpool_node *)(void *)1; |
| int i; |
| ipa_ref *ref = NULL; |
| hash_set<varpool_node *> referenced; |
| |
| if (seen_error ()) |
| return; |
| |
| if (dump_file) |
| fprintf (dump_file, "Trivially needed variables:"); |
| FOR_EACH_DEFINED_VARIABLE (node) |
| { |
| if (node->analyzed |
| && (!node->can_remove_if_no_refs_p () |
| /* We just expanded all function bodies. See if any of |
| them needed the variable. */ |
| || DECL_RTL_SET_P (node->decl))) |
| { |
| enqueue_node (node, &first); |
| if (dump_file) |
| fprintf (dump_file, " %s", node->dump_asm_name ()); |
| } |
| } |
| while (first != (varpool_node *)(void *)1) |
| { |
| node = first; |
| first = (varpool_node *)first->aux; |
| |
| if (node->same_comdat_group) |
| { |
| symtab_node *next; |
| for (next = node->same_comdat_group; |
| next != node; |
| next = next->same_comdat_group) |
| { |
| varpool_node *vnext = dyn_cast <varpool_node *> (next); |
| if (vnext && vnext->analyzed && !next->comdat_local_p ()) |
| enqueue_node (vnext, &first); |
| } |
| } |
| for (i = 0; node->iterate_reference (i, ref); i++) |
| { |
| varpool_node *vnode = dyn_cast <varpool_node *> (ref->referred); |
| if (vnode |
| && !vnode->in_other_partition |
| && (!DECL_EXTERNAL (ref->referred->decl) |
| || vnode->alias) |
| && vnode->analyzed) |
| enqueue_node (vnode, &first); |
| else |
| { |
| referenced.add (vnode); |
| while (vnode && vnode->alias && vnode->definition) |
| { |
| vnode = vnode->get_alias_target (); |
| referenced.add (vnode); |
| } |
| } |
| } |
| } |
| if (dump_file) |
| fprintf (dump_file, "\nRemoving variables:"); |
| for (node = first_defined_variable (); node; node = next) |
| { |
| next = next_defined_variable (node); |
| if (!node->aux && !node->no_reorder) |
| { |
| if (dump_file) |
| fprintf (dump_file, " %s", node->dump_asm_name ()); |
| if (referenced.contains(node)) |
| node->remove_initializer (); |
| else |
| node->remove (); |
| } |
| } |
| |
| if (dump_file) |
| fprintf (dump_file, "\n"); |
| } |
| |
| /* For variables in named sections make sure get_variable_section |
| is called before we switch to those sections. Then section |
| conflicts between read-only and read-only requiring relocations |
| sections can be resolved. */ |
| void |
| varpool_node::finalize_named_section_flags (void) |
| { |
| if (!TREE_ASM_WRITTEN (decl) |
| && !alias |
| && !in_other_partition |
| && !DECL_EXTERNAL (decl) |
| && VAR_P (decl) |
| && !DECL_HAS_VALUE_EXPR_P (decl) |
| && get_section ()) |
| get_variable_section (decl, false); |
| } |
| |
| /* Output all variables enqueued to be assembled. */ |
| bool |
| symbol_table::output_variables (void) |
| { |
| bool changed = false; |
| varpool_node *node; |
| |
| if (seen_error ()) |
| return false; |
| |
| remove_unreferenced_decls (); |
| |
| timevar_push (TV_VAROUT); |
| |
| FOR_EACH_DEFINED_VARIABLE (node) |
| { |
| /* Handled in output_in_order. */ |
| if (node->no_reorder) |
| continue; |
| |
| node->finalize_named_section_flags (); |
| } |
| |
| /* There is a similar loop in output_in_order. Please keep them in sync. */ |
| FOR_EACH_VARIABLE (node) |
| { |
| /* Handled in output_in_order. */ |
| if (node->no_reorder) |
| continue; |
| if (DECL_HARD_REGISTER (node->decl) |
| || DECL_HAS_VALUE_EXPR_P (node->decl)) |
| continue; |
| if (node->definition) |
| changed |= node->assemble_decl (); |
| else |
| assemble_undefined_decl (node->decl); |
| } |
| timevar_pop (TV_VAROUT); |
| return changed; |
| } |
| |
| /* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful. |
| Extra name aliases are output whenever DECL is output. */ |
| |
| varpool_node * |
| varpool_node::create_alias (tree alias, tree decl) |
| { |
| varpool_node *alias_node; |
| |
| gcc_assert (VAR_P (decl)); |
| gcc_assert (VAR_P (alias)); |
| alias_node = varpool_node::get_create (alias); |
| alias_node->alias = true; |
| alias_node->definition = true; |
| alias_node->semantic_interposition = flag_semantic_interposition; |
| alias_node->alias_target = decl; |
| if (lookup_attribute ("weakref", DECL_ATTRIBUTES (alias)) != NULL) |
| alias_node->weakref = alias_node->transparent_alias = true; |
| return alias_node; |
| } |
| |
| /* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful. |
| Extra name aliases are output whenever DECL is output. */ |
| |
| varpool_node * |
| varpool_node::create_extra_name_alias (tree alias, tree decl) |
| { |
| varpool_node *alias_node; |
| |
| /* If aliases aren't supported by the assembler, fail. */ |
| if (!TARGET_SUPPORTS_ALIASES) |
| return NULL; |
| |
| alias_node = varpool_node::create_alias (alias, decl); |
| alias_node->cpp_implicit_alias = true; |
| |
| /* Extra name alias mechanism creates aliases really late |
| via DECL_ASSEMBLER_NAME mechanism. |
| This is unfortunate because they are not going through the |
| standard channels. Ensure they get output. */ |
| if (symtab->cpp_implicit_aliases_done) |
| alias_node->resolve_alias (varpool_node::get_create (decl)); |
| return alias_node; |
| } |
| |
| /* Worker for call_for_symbol_and_aliases. */ |
| |
| bool |
| varpool_node::call_for_symbol_and_aliases_1 (bool (*callback) (varpool_node *, |
| void *), |
| void *data, |
| bool include_overwritable) |
| { |
| ipa_ref *ref; |
| |
| FOR_EACH_ALIAS (this, ref) |
| { |
| varpool_node *alias = dyn_cast <varpool_node *> (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; |
| } |