OSDN Git Service

Emit DW_AT_ranges for inlined subroutines that contain disjoint blocks.
[pf3gnuchains/gcc-fork.git] / gcc / cgraph.c
index 11953b4..5ed13bc 100644 (file)
@@ -84,6 +84,7 @@ The varpool data structure:
 #include "coretypes.h"
 #include "tm.h"
 #include "tree.h"
+#include "tree-inline.h"
 #include "langhooks.h"
 #include "hashtab.h"
 #include "toplev.h"
@@ -91,6 +92,7 @@ The varpool data structure:
 #include "ggc.h"
 #include "debug.h"
 #include "target.h"
+#include "basic-block.h"
 #include "cgraph.h"
 #include "varray.h"
 #include "output.h"
@@ -118,14 +120,21 @@ int cgraph_max_uid;
 /* Set when whole unit has been analyzed so we can access global info.  */
 bool cgraph_global_info_ready = false;
 
+/* Set when the cgraph is fully build and the basic flags are computed.  */
+bool cgraph_function_flags_ready = false;
+
 /* Hash table used to convert declarations into nodes.  */
 static GTY((param_is (struct cgraph_varpool_node))) htab_t cgraph_varpool_hash;
 
 /* Queue of cgraph nodes scheduled to be lowered and output.  */
-struct cgraph_varpool_node *cgraph_varpool_nodes_queue;
+struct cgraph_varpool_node *cgraph_varpool_nodes_queue, *cgraph_varpool_first_unanalyzed_node;
+
 
 /* The linked list of cgraph varpool nodes.  */
-static GTY(())  struct cgraph_varpool_node *cgraph_varpool_nodes;
+static GTY(()) struct cgraph_varpool_node *cgraph_varpool_nodes;
+
+/* End of the varpool queue.  Needs to be QTYed to work with PCH.  */
+static GTY(()) struct cgraph_varpool_node *cgraph_varpool_last_needed_node;
 
 static hashval_t hash_node (const void *);
 static int eq_node (const void *, const void *);
@@ -398,7 +407,7 @@ void
 cgraph_remove_node (struct cgraph_node *node)
 {
   void **slot;
-  bool check_dead = 1;
+  bool kill_body = false;
 
   cgraph_node_remove_callers (node);
   cgraph_node_remove_callees (node);
@@ -422,44 +431,40 @@ cgraph_remove_node (struct cgraph_node *node)
   if (*slot == node)
     {
       if (node->next_clone)
+      {
        *slot = node->next_clone;
+       node->next_clone->prev_clone = NULL;
+      }
       else
        {
           htab_clear_slot (cgraph_hash, slot);
-         if (!dump_enabled_p (TDI_tree_all))
-           {
-              DECL_SAVED_TREE (node->decl) = NULL;
-             DECL_STRUCT_FUNCTION (node->decl) = NULL;
-           }
-         check_dead = false;
+         kill_body = true;
        }
     }
   else
     {
-      struct cgraph_node *n;
+      node->prev_clone->next_clone = node->next_clone;
+      if (node->next_clone)
+        node->next_clone->prev_clone = node->prev_clone;
+    }
 
-      for (n = *slot; n->next_clone != node; n = n->next_clone)
-       continue;
-      n->next_clone = node->next_clone;
+  /* While all the clones are removed after being proceeded, the function 
+     itself is kept in the cgraph even after it is compiled.  Check whether
+     we are done with this body and reclaim it proactively if this is the case.
+     */
+  if (!kill_body && *slot)
+    {
+      struct cgraph_node *n = *slot;
+      if (!n->next_clone && !n->global.inlined_to
+         && (TREE_ASM_WRITTEN (n->decl) || DECL_EXTERNAL (n->decl)))
+       kill_body = true;
     }
 
-  /* Work out whether we still need a function body (either there is inline
-     clone or there is out of line function whose body is not written).  */
-  if (check_dead && flag_unit_at_a_time)
+  if (kill_body && !dump_enabled_p (TDI_tree_all) && flag_unit_at_a_time)
     {
-      struct cgraph_node *n;
-
-      for (n = *slot; n; n = n->next_clone)
-       if (n->global.inlined_to
-           || (!n->global.inlined_to
-               && !TREE_ASM_WRITTEN (n->decl) && !DECL_EXTERNAL (n->decl)))
-         break;
-      if (!n && !dump_enabled_p (TDI_tree_all))
-       {
-         DECL_SAVED_TREE (node->decl) = NULL;
-         DECL_STRUCT_FUNCTION (node->decl) = NULL;
-          DECL_INITIAL (node->decl) = error_mark_node;
-       }
+      DECL_SAVED_TREE (node->decl) = NULL;
+      DECL_STRUCT_FUNCTION (node->decl) = NULL;
+      DECL_INITIAL (node->decl) = error_mark_node;
     }
   cgraph_n_nodes--;
   /* Do not free the structure itself so the walk over chain can continue.  */
@@ -474,6 +479,7 @@ cgraph_mark_reachable_node (struct cgraph_node *node)
     {
       notice_global_symbol (node->decl);
       node->reachable = 1;
+      gcc_assert (!cgraph_global_info_ready);
 
       node->next_needed = cgraph_nodes_queue;
       cgraph_nodes_queue = node;
@@ -536,6 +542,13 @@ cgraph_node_name (struct cgraph_node *node)
   return lang_hooks.decl_printable_name (node->decl, 2);
 }
 
+/* Return name of the node used in debug output.  */
+static const char *
+cgraph_varpool_node_name (struct cgraph_varpool_node *node)
+{
+  return lang_hooks.decl_printable_name (node->decl, 2);
+}
+
 /* Dump given cgraph node.  */
 void
 dump_cgraph_node (FILE *f, struct cgraph_node *node)
@@ -601,6 +614,36 @@ dump_cgraph (FILE *f)
     dump_cgraph_node (f, node);
 }
 
+/* Dump given cgraph node.  */
+void
+dump_cgraph_varpool_node (FILE *f, struct cgraph_varpool_node *node)
+{
+  fprintf (f, "%s:", cgraph_varpool_node_name (node));
+  if (DECL_INITIAL (node->decl))
+    fprintf (f, " initialized");
+  if (node->needed)
+    fprintf (f, " needed");
+  if (node->analyzed)
+    fprintf (f, " analyzed");
+  if (node->finalized)
+    fprintf (f, " finalized");
+  if (node->output)
+    fprintf (f, " output");
+  fprintf (f, "\n");
+}
+
+/* Dump the callgraph.  */
+
+void
+dump_varpool (FILE *f)
+{
+  struct cgraph_varpool_node *node;
+
+  fprintf (f, "variable pool:\n\n");
+  for (node = cgraph_varpool_nodes; node; node = node->next_needed)
+    dump_cgraph_varpool_node (f, node);
+}
+
 /* Returns a hash code for P.  */
 
 static hashval_t
@@ -674,20 +717,82 @@ change_decl_assembler_name (tree decl, tree name)
   SET_DECL_ASSEMBLER_NAME (decl, name);
 }
 
+/* Helper function for finalization code - add node into lists so it will
+   be analyzed and compiled.  */
+void
+cgraph_varpool_enqueue_needed_node (struct cgraph_varpool_node *node)
+{
+  if (cgraph_varpool_last_needed_node)
+    cgraph_varpool_last_needed_node->next_needed = node;
+  cgraph_varpool_last_needed_node = node;
+  node->next_needed = NULL;
+  if (!cgraph_varpool_nodes_queue)
+    cgraph_varpool_nodes_queue = node;
+  if (!cgraph_varpool_first_unanalyzed_node)
+    cgraph_varpool_first_unanalyzed_node = node;
+  notice_global_symbol (node->decl);
+}
+
+/* Reset the queue of needed nodes.  */
+void
+cgraph_varpool_reset_queue (void)
+{
+  cgraph_varpool_last_needed_node = NULL;
+  cgraph_varpool_nodes_queue = NULL;
+  cgraph_varpool_first_unanalyzed_node = NULL;
+}
+
 /* Notify finalize_compilation_unit that given node is reachable
    or needed.  */
 void
 cgraph_varpool_mark_needed_node (struct cgraph_varpool_node *node)
 {
   if (!node->needed && node->finalized)
-    {
-      node->next_needed = cgraph_varpool_nodes_queue;
-      cgraph_varpool_nodes_queue = node;
-      notice_global_symbol (node->decl);
-    }
+    cgraph_varpool_enqueue_needed_node (node);
   node->needed = 1;
 }
 
+/* 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.  */
+
+bool
+decide_is_variable_needed (struct cgraph_varpool_node *node, tree decl)
+{
+  /* If the user told us it is used, then it must be so.  */
+  if (lookup_attribute ("used", DECL_ATTRIBUTES (decl)))
+    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)
+    return true;
+
+  /* Externally visible functions must be output.  The exception is
+     COMDAT functions that must be output only when they are needed.  */
+  if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
+    return true;
+
+  if (flag_unit_at_a_time)
+    return false;
+
+  /* If not doing unit at a time, then we'll only defer this function
+     if its marked for inlining.  Otherwise we want to emit it now.  */
+
+  /* We want to emit COMDAT variables only when absolutely necessary.  */
+  if (DECL_COMDAT (decl))
+    return false;
+  return true;
+}
+
 void
 cgraph_varpool_finalize_decl (tree decl)
 {
@@ -698,47 +803,19 @@ cgraph_varpool_finalize_decl (tree decl)
      or local (in C, has internal linkage).  So do nothing more
      if this function has already run.  */
   if (node->finalized)
-    return;
-  if (node->needed)
     {
-      node->next_needed = cgraph_varpool_nodes_queue;
-      cgraph_varpool_nodes_queue = node;
-      notice_global_symbol (decl);
+      if (cgraph_global_info_ready || !flag_unit_at_a_time)
+       cgraph_varpool_assemble_pending_decls ();
+      return;
     }
+  if (node->needed)
+    cgraph_varpool_enqueue_needed_node (node);
   node->finalized = true;
 
-  if (/* Externally visible variables must be output.  The exception are
-        COMDAT functions that must be output only when they are needed.  */
-      (TREE_PUBLIC (decl) && !DECL_COMDAT (decl))
-      /* Function whose name is output to the assembler file must be produced.
-        It is possible to assemble the name later after finalizing the function
-        and the fact is noticed in assemble_name then.  */
-      || (DECL_ASSEMBLER_NAME_SET_P (decl)
-         && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))))
-    {
-      cgraph_varpool_mark_needed_node (node);
-    }
-}
-
-bool
-cgraph_varpool_assemble_pending_decls (void)
-{
-  bool changed = false;
-
-  while (cgraph_varpool_nodes_queue)
-    {
-      tree decl = cgraph_varpool_nodes_queue->decl;
-      struct cgraph_varpool_node *node = cgraph_varpool_nodes_queue;
-
-      cgraph_varpool_nodes_queue = cgraph_varpool_nodes_queue->next_needed;
-      if (!TREE_ASM_WRITTEN (decl))
-       {
-         assemble_variable (decl, 0, 1, 0);
-         changed = true;
-       }
-      node->next_needed = NULL;
-    }
-  return changed;
+  if (decide_is_variable_needed (node, decl))
+    cgraph_varpool_mark_needed_node (node);
+  if (cgraph_global_info_ready || !flag_unit_at_a_time)
+    cgraph_varpool_assemble_pending_decls ();
 }
 
 /* Return true when the DECL can possibly be inlined.  */
@@ -783,7 +860,10 @@ cgraph_clone_node (struct cgraph_node *n)
     cgraph_clone_edge (e, new, e->call_expr);
 
   new->next_clone = n->next_clone;
+  new->prev_clone = n;
   n->next_clone = new;
+  if (new->next_clone)
+    new->next_clone->prev_clone = new;
 
   return new;
 }