X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fvarpool.c;h=c713a7705f6eb7bfa0083824d30f902d5ae9bdaf;hb=be526aa0465b5a3a31111e756ea2e2e479fb435e;hp=1d1cc9ed630b40420b524de3e67e0f1ca442c54d;hpb=75a70cf95f65fe9204b15ad9aba31c571381d224;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/varpool.c b/gcc/varpool.c index 1d1cc9ed630..c713a7705f6 100644 --- a/gcc/varpool.c +++ b/gcc/varpool.c @@ -1,5 +1,6 @@ /* Callgraph handling code. - Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2010 + Free Software Foundation, Inc. Contributed by Jan Hubicka This file is part of GCC. @@ -29,11 +30,12 @@ along with GCC; see the file COPYING3. If not see #include "hashtab.h" #include "ggc.h" #include "timevar.h" -#include "debug.h" +#include "debug.h" #include "target.h" #include "output.h" #include "gimple.h" #include "tree-flow.h" +#include "flags.h" /* This file contains basic routines manipulating variable pool. @@ -55,21 +57,21 @@ struct varpool_node *varpool_nodes; /* Queue of cgraph nodes scheduled to be lowered and output. The queue is maintained via mark_needed_node, linked via node->next_needed - pointer. + pointer. LAST_NEEDED_NODE points to the end of queue, so it can be maintained in forward order. GTY is needed to make it friendly to PCH. - + During compilation we construct the queue of needed variables twice: first time it is during cgraph construction, second time it is at the end of compilation in VARPOOL_REMOVE_UNREFERENCED_DECLS so we can avoid optimized out variables being output. - - Each variable is thus first analyzed and then later possibly output. + + Each variable is thus first analyzed and then later possibly output. FIRST_UNANALYZED_NODE points to first node in queue that was not analyzed yet and is moved via VARPOOL_ANALYZE_PENDING_DECLS. */ - + struct varpool_node *varpool_nodes_queue; static GTY(()) struct varpool_node *varpool_last_needed_node; static GTY(()) struct varpool_node *varpool_first_unanalyzed_node; @@ -78,7 +80,7 @@ static GTY(()) struct varpool_node *varpool_first_unanalyzed_node; static GTY(()) struct varpool_node *varpool_assembled_nodes_queue; /* Return name of the node used in debug output. */ -static const char * +const char * varpool_node_name (struct varpool_node *node) { return lang_hooks.decl_printable_name (node->decl, 2); @@ -103,6 +105,22 @@ eq_varpool_node (const void *p1, const void *p2) return DECL_UID (n1->decl) == DECL_UID (n2->decl); } +/* Return varpool node assigned to DECL without creating new one. */ +struct varpool_node * +varpool_get_node (tree decl) +{ + struct varpool_node key, **slot; + + gcc_assert (DECL_P (decl) && TREE_CODE (decl) != FUNCTION_DECL); + + if (!varpool_hash) + return NULL; + key.decl = decl; + slot = (struct varpool_node **) + htab_find_slot (varpool_hash, &key, INSERT); + return *slot; +} + /* Return varpool node assigned to DECL. Create new one when needed. */ struct varpool_node * varpool_node (tree decl) @@ -123,11 +141,64 @@ varpool_node (tree decl) node->decl = decl; node->order = cgraph_order++; node->next = varpool_nodes; + ipa_empty_ref_list (&node->ref_list); + if (varpool_nodes) + varpool_nodes->prev = node; varpool_nodes = node; *slot = node; return node; } +/* Remove node from the varpool. */ +void +varpool_remove_node (struct varpool_node *node) +{ + void **slot; + slot = htab_find_slot (varpool_hash, node, NO_INSERT); + gcc_assert (*slot == node); + htab_clear_slot (varpool_hash, slot); + gcc_assert (!varpool_assembled_nodes_queue); + if (!node->alias) + while (node->extra_name) + varpool_remove_node (node->extra_name); + if (node->next) + node->next->prev = node->prev; + if (node->prev) + node->prev->next = node->next; + else + { + if (node->alias) + { + gcc_assert (node->extra_name->extra_name == node); + node->extra_name->extra_name = node->next; + } + else + { + gcc_assert (varpool_nodes == node); + varpool_nodes = node->next; + } + } + if (varpool_first_unanalyzed_node == node) + varpool_first_unanalyzed_node = node->next_needed; + if (node->next_needed) + node->next_needed->prev_needed = node->prev_needed; + else if (node->prev_needed) + { + gcc_assert (varpool_last_needed_node); + varpool_last_needed_node = node->prev_needed; + } + if (node->prev_needed) + node->prev_needed->next_needed = node->next_needed; + else if (node->next_needed) + { + gcc_assert (varpool_nodes_queue == node); + varpool_nodes_queue = node->next_needed; + } + ipa_remove_all_references (&node->ref_list); + ipa_remove_all_refering (&node->ref_list); + ggc_free (node); +} + /* Dump given cgraph node. */ void dump_varpool_node (FILE *f, struct varpool_node *node) @@ -137,8 +208,12 @@ dump_varpool_node (FILE *f, struct varpool_node *node) cgraph_function_flags_ready ? cgraph_availability_names[cgraph_variable_initializer_availability (node)] : "not-ready"); + if (DECL_ASSEMBLER_NAME_SET_P (node->decl)) + fprintf (f, " (asm: %s)", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->decl))); if (DECL_INITIAL (node->decl)) fprintf (f, " initialized"); + if (TREE_ASM_WRITTEN (node->decl)) + fprintf (f, " (asm written)"); if (node->needed) fprintf (f, " needed"); if (node->analyzed) @@ -149,7 +224,15 @@ dump_varpool_node (FILE *f, struct varpool_node *node) fprintf (f, " output"); if (node->externally_visible) fprintf (f, " externally_visible"); + if (node->in_other_partition) + fprintf (f, " in_other_partition"); + else if (node->used_from_other_partition) + fprintf (f, " used_from_other_partition"); fprintf (f, "\n"); + fprintf (f, " References: "); + ipa_dump_references (f, &node->ref_list); + fprintf (f, " Refering this var: "); + ipa_dump_refering (f, &node->ref_list); } /* Dump the variable pool. */ @@ -163,6 +246,14 @@ dump_varpool (FILE *f) dump_varpool_node (f, node); } +/* Dump the variable pool to stderr. */ + +void +debug_varpool (void) +{ + dump_varpool (stderr); +} + /* Given an assembler name, lookup node. */ struct varpool_node * varpool_node_for_asm (tree asmname) @@ -182,7 +273,10 @@ static void varpool_enqueue_needed_node (struct varpool_node *node) { if (varpool_last_needed_node) - varpool_last_needed_node->next_needed = node; + { + varpool_last_needed_node->next_needed = node; + node->prev_needed = varpool_last_needed_node; + } varpool_last_needed_node = node; node->next_needed = NULL; if (!varpool_nodes_queue) @@ -197,6 +291,8 @@ varpool_enqueue_needed_node (struct varpool_node *node) void varpool_mark_needed_node (struct varpool_node *node) { + if (node->alias && node->extra_name) + node = node->extra_name; if (!node->needed && node->finalized && !TREE_ASM_WRITTEN (node->decl)) varpool_enqueue_needed_node (node); @@ -204,7 +300,7 @@ varpool_mark_needed_node (struct varpool_node *node) } /* Reset the queue of needed nodes. */ -static void +void varpool_reset_queue (void) { varpool_last_needed_node = NULL; @@ -218,25 +314,20 @@ varpool_reset_queue (void) bool decide_is_variable_needed (struct varpool_node *node, tree decl) { - /* If the user told us it is used, then it must be so. */ - if (node->externally_visible || node->force_output) - return true; - - /* ??? If the assembler name is set by hand, it is possible to assemble - the name later after finalizing the function and the fact is noticed - in assemble_name then. This is arguably a bug. */ - if (DECL_ASSEMBLER_NAME_SET_P (decl) - && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))) + if (node->used_from_other_partition) return true; - - /* If we decided it was needed before, but at the time we didn't have - the definition available, then it's still needed. */ - if (node->needed) + /* If the user told us it is used, then it must be so. */ + if ((node->externally_visible && !DECL_COMDAT (decl)) + || node->force_output) return true; /* Externally visible variables must be output. The exception is COMDAT variables that must be output only when they are needed. */ - if (TREE_PUBLIC (decl) && !flag_whole_program && !DECL_COMDAT (decl) + if (TREE_PUBLIC (decl) + && !flag_whole_program + && !flag_lto + && !flag_whopr + && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl)) return true; @@ -283,6 +374,8 @@ varpool_finalize_decl (tree decl) if (node->needed) varpool_enqueue_needed_node (node); node->finalized = true; + if (TREE_THIS_VOLATILE (decl) || DECL_PRESERVE_P (decl)) + node->force_output = true; if (decide_is_variable_needed (node, decl)) varpool_mark_needed_node (node); @@ -319,25 +412,32 @@ bool varpool_analyze_pending_decls (void) { bool changed = false; - timevar_push (TV_CGRAPH); + timevar_push (TV_VARPOOL); while (varpool_first_unanalyzed_node) { tree decl = varpool_first_unanalyzed_node->decl; + bool analyzed = varpool_first_unanalyzed_node->analyzed; varpool_first_unanalyzed_node->analyzed = true; varpool_first_unanalyzed_node = varpool_first_unanalyzed_node->next_needed; - /* Compute the alignment early so function body expanders are - already informed about increased alignment. */ - align_variable (decl, 0); - + /* 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 || cgraph_function_flags_ready); + /* Compute the alignment early so function body expanders are + already informed about increased alignment. */ + align_variable (decl, 0); + } if (DECL_INITIAL (decl)) - record_references_in_initializer (decl); + record_references_in_initializer (decl, analyzed); changed = true; } - timevar_pop (TV_CGRAPH); + timevar_pop (TV_VARPOOL); return changed; } @@ -349,15 +449,32 @@ varpool_assemble_decl (struct varpool_node *node) if (!TREE_ASM_WRITTEN (decl) && !node->alias + && !node->in_other_partition && !DECL_EXTERNAL (decl) && (TREE_CODE (decl) != VAR_DECL || !DECL_HAS_VALUE_EXPR_P (decl))) { assemble_variable (decl, 0, 1, 0); if (TREE_ASM_WRITTEN (decl)) { + struct varpool_node *alias; + node->next_needed = varpool_assembled_nodes_queue; + node->prev_needed = NULL; + if (varpool_assembled_nodes_queue) + varpool_assembled_nodes_queue->prev_needed = node; varpool_assembled_nodes_queue = node; node->finalized = 1; + + /* Also emit any extra name aliases. */ + for (alias = node->extra_name; alias; alias = alias->next) + { + /* Update linkage fields in case they've changed. */ + DECL_WEAK (alias->decl) = DECL_WEAK (decl); + TREE_PUBLIC (alias->decl) = TREE_PUBLIC (decl); + DECL_VISIBILITY (alias->decl) = DECL_VISIBILITY (decl); + assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (decl)); + } + return true; } } @@ -414,6 +531,7 @@ varpool_assemble_pending_decls (void) if (errorcount || sorrycount) return false; + timevar_push (TV_VAROUT); /* EH might mark decls as needed during expansion. This should be safe since we don't create references to new function, but it should not be used elsewhere. */ @@ -427,11 +545,15 @@ varpool_assemble_pending_decls (void) if (varpool_assemble_decl (node)) changed = true; else - node->next_needed = NULL; + { + node->prev_needed = NULL; + node->next_needed = NULL; + } } /* varpool_nodes_queue is now empty, clear the pointer to the last element in the queue. */ varpool_last_needed_node = NULL; + timevar_pop (TV_VAROUT); return changed; } @@ -449,35 +571,13 @@ varpool_empty_needed_queue (void) struct varpool_node *node = varpool_nodes_queue; varpool_nodes_queue = varpool_nodes_queue->next_needed; node->next_needed = NULL; + node->prev_needed = NULL; } /* varpool_nodes_queue is now empty, clear the pointer to the last element in the queue. */ varpool_last_needed_node = NULL; } -/* Output all variables enqueued to be assembled. */ -void -varpool_output_debug_info (void) -{ - timevar_push (TV_SYMOUT); - if (errorcount == 0 && sorrycount == 0) - while (varpool_assembled_nodes_queue) - { - struct varpool_node *node = varpool_assembled_nodes_queue; - - /* Local static variables are never seen by check_global_declarations - so we need to output debug info by hand. */ - if (DECL_CONTEXT (node->decl) - && (TREE_CODE (DECL_CONTEXT (node->decl)) == BLOCK - || TREE_CODE (DECL_CONTEXT (node->decl)) == FUNCTION_DECL) - && errorcount == 0 && sorrycount == 0) - (*debug_hooks->global_decl) (node->decl); - varpool_assembled_nodes_queue = node->next_needed; - node->next_needed = 0; - } - timevar_pop (TV_SYMOUT); -} - /* Create a new global variable of type TYPE. */ tree add_new_static_var (tree type) @@ -502,4 +602,43 @@ add_new_static_var (tree type) return new_node->decl; } +/* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful. + Extra name aliases are output whenever DECL is output. */ + +bool +varpool_extra_name_alias (tree alias, tree decl) +{ + struct varpool_node key, *alias_node, *decl_node, **slot; + +#ifndef ASM_OUTPUT_DEF + /* If aliases aren't supported by the assembler, fail. */ + return false; +#endif + + gcc_assert (TREE_CODE (decl) == VAR_DECL); + gcc_assert (TREE_CODE (alias) == VAR_DECL); + /* Make sure the hash table has been created. */ + decl_node = varpool_node (decl); + + key.decl = alias; + + slot = (struct varpool_node **) htab_find_slot (varpool_hash, &key, INSERT); + + /* If the varpool_node has been already created, fail. */ + if (*slot) + return false; + + alias_node = GGC_CNEW (struct varpool_node); + alias_node->decl = alias; + alias_node->alias = 1; + alias_node->extra_name = decl_node; + alias_node->next = decl_node->extra_name; + ipa_empty_ref_list (&alias_node->ref_list); + if (decl_node->extra_name) + decl_node->extra_name->prev = alias_node; + decl_node->extra_name = alias_node; + *slot = alias_node; + return true; +} + #include "gt-varpool.h"