OSDN Git Service

* gcc.dg/lto/ipacp_0.c: New test.
[pf3gnuchains/gcc-fork.git] / gcc / varpool.c
index 4149c1d..c713a77 100644 (file)
@@ -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 "tree-gimple.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 unit-at-a-time compilation we construct the queue of needed variables
+
+   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;
@@ -214,33 +310,24 @@ varpool_reset_queue (void)
 
 /* Determine if variable DECL is needed.  That is, visible to something
    either outside this translation unit, something magic in the system
-   configury, or (if not doing unit-at-a-time) to something we haven't
-   seen yet.  */
+   configury */
 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 (!flag_unit_at_a_time
-      && lookup_attribute ("used", DECL_ATTRIBUTES (decl)))
+  if (node->used_from_other_partition)
     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)))
-    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;
 
@@ -257,7 +344,7 @@ decide_is_variable_needed (struct varpool_node *node, tree decl)
 
   /* When not reordering top level variables, we have to assume that
      we are going to keep everything.  */
-  if (flag_unit_at_a_time && flag_toplevel_reorder)
+  if (flag_toplevel_reorder)
     return false;
 
   /* We want to emit COMDAT variables only when absolutely necessary.  */
@@ -280,13 +367,15 @@ varpool_finalize_decl (tree decl)
      if this function has already run.  */
   if (node->finalized)
     {
-      if (cgraph_global_info_ready || (!flag_unit_at_a_time && !flag_openmp))
+      if (cgraph_global_info_ready)
        varpool_assemble_pending_decls ();
       return;
     }
   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);
@@ -295,7 +384,7 @@ varpool_finalize_decl (tree decl)
      there.  */
   else if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
     varpool_mark_needed_node (node);
-  if (cgraph_global_info_ready || (!flag_unit_at_a_time && !flag_openmp))
+  if (cgraph_global_info_ready)
     varpool_assemble_pending_decls ();
 }
 
@@ -323,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;
 }
 
@@ -353,11 +449,34 @@ 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);
-      return TREE_ASM_WRITTEN (decl);
+      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;
+       }
     }
 
   return false;
@@ -412,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.  */
@@ -423,42 +543,39 @@ varpool_assemble_pending_decls (void)
 
       varpool_nodes_queue = varpool_nodes_queue->next_needed;
       if (varpool_assemble_decl (node))
+       changed = true;
+      else
        {
-         changed = true;
-         node->next_needed = varpool_assembled_nodes_queue;
-         varpool_assembled_nodes_queue = node;
-         node->finalized = 1;
+         node->prev_needed = NULL;
+          node->next_needed = NULL;
        }
-      else
-        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;
 }
 
-/* Output all variables enqueued to be assembled.  */
+/* Remove all elements from the queue so we can re-use it for debug output.  */
 void
-varpool_output_debug_info (void)
+varpool_empty_needed_queue (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);
+  /* 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.  */
+  varpool_analyze_pending_decls ();
+
+  while (varpool_nodes_queue)
+    {
+      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;
 }
 
 /* Create a new global variable of type TYPE.  */
@@ -485,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"