OSDN Git Service

2005-06-12 Thomas Koenig <Thomas.Koenig@online.de>
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index 81436aa..2f993f3 100644 (file)
@@ -170,7 +170,6 @@ static void cgraph_expand_all_functions (void);
 static void cgraph_mark_functions_to_output (void);
 static void cgraph_expand_function (struct cgraph_node *);
 static tree record_reference (tree *, int *, void *);
-static void cgraph_mark_local_functions (void);
 static void cgraph_analyze_function (struct cgraph_node *node);
 
 /* Records tree nodes seen in record_reference.  Simply using
@@ -191,6 +190,17 @@ decide_is_function_needed (struct cgraph_node *node, tree decl)
 {
   tree origin;
 
+  /* 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 body of the function available, then it's still needed.  We have
      to go back and re-check its dependencies now.  */
@@ -207,17 +217,6 @@ decide_is_function_needed (struct cgraph_node *node, tree decl)
   if (DECL_STATIC_CONSTRUCTOR (decl) || DECL_STATIC_DESTRUCTOR (decl))
     return true;
 
-  /* 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 (flag_unit_at_a_time)
     return false;
 
@@ -340,6 +339,59 @@ cgraph_assemble_pending_functions (void)
 
   return output;
 }
+/* As an GCC extension we allow redefinition of the function.  The
+   semantics when both copies of bodies differ is not well defined.
+   We replace the old body with new body so in unit at a time mode
+   we always use new body, while in normal mode we may end up with
+   old body inlined into some functions and new body expanded and
+   inlined in others.
+
+   ??? It may make more sense to use one body for inlining and other
+   body for expanding the function but this is difficult to do.  */
+
+static void
+cgraph_reset_node (struct cgraph_node *node)
+{
+  /* If node->output is set, then this is a unit-at-a-time compilation
+     and we have already begun whole-unit analysis.  This is *not*
+     testing for whether we've already emitted the function.  That
+     case can be sort-of legitimately seen with real function 
+     redefinition errors.  I would argue that the front end should
+     never present us with such a case, but don't enforce that for now.  */
+  gcc_assert (!node->output);
+
+  /* Reset our data structures so we can analyze the function again.  */
+  memset (&node->local, 0, sizeof (node->local));
+  memset (&node->global, 0, sizeof (node->global));
+  memset (&node->rtl, 0, sizeof (node->rtl));
+  node->analyzed = false;
+  node->local.redefined_extern_inline = true;
+  node->local.finalized = false;
+
+  if (!flag_unit_at_a_time)
+    {
+      struct cgraph_node *n;
+
+      for (n = cgraph_nodes; n; n = n->next)
+       if (n->global.inlined_to == node)
+         cgraph_remove_node (n);
+    }
+
+  cgraph_node_remove_callees (node);
+
+  /* We may need to re-queue the node for assembling in case
+     we already proceeded it and ignored as not needed.  */
+  if (node->reachable && !flag_unit_at_a_time)
+    {
+      struct cgraph_node *n;
+
+      for (n = cgraph_nodes_queue; n; n = n->next_needed)
+       if (n == node)
+         break;
+      if (!n)
+       node->reachable = 0;
+    }
+}
 
 /* DECL has been parsed.  Take it, queue it, compile it at the whim of the
    logic in effect.  If NESTED is true, then our caller cannot stand to have
@@ -352,56 +404,7 @@ cgraph_finalize_function (tree decl, bool nested)
   struct cgraph_node *node = cgraph_node (decl);
 
   if (node->local.finalized)
-    {
-      /* As an GCC extension we allow redefinition of the function.  The
-        semantics when both copies of bodies differ is not well defined.
-        We replace the old body with new body so in unit at a time mode
-        we always use new body, while in normal mode we may end up with
-        old body inlined into some functions and new body expanded and
-        inlined in others.
-        
-        ??? It may make more sense to use one body for inlining and other
-        body for expanding the function but this is difficult to do.  */
-
-      /* If node->output is set, then this is a unit-at-a-time compilation
-        and we have already begun whole-unit analysis.  This is *not*
-        testing for whether we've already emitted the function.  That
-        case can be sort-of legitimately seen with real function 
-        redefinition errors.  I would argue that the front end should
-        never present us with such a case, but don't enforce that for now.  */
-      gcc_assert (!node->output);
-
-      /* Reset our data structures so we can analyze the function again.  */
-      memset (&node->local, 0, sizeof (node->local));
-      memset (&node->global, 0, sizeof (node->global));
-      memset (&node->rtl, 0, sizeof (node->rtl));
-      node->analyzed = false;
-      node->local.redefined_extern_inline = true;
-
-      if (!flag_unit_at_a_time)
-       {
-         struct cgraph_node *n;
-
-         for (n = cgraph_nodes; n; n = n->next)
-           if (n->global.inlined_to == node)
-             cgraph_remove_node (n);
-       }
-
-      cgraph_node_remove_callees (node);
-
-      /* We may need to re-queue the node for assembling in case
-         we already proceeded it and ignored as not needed.  */
-      if (node->reachable && !flag_unit_at_a_time)
-       {
-         struct cgraph_node *n;
-
-         for (n = cgraph_nodes_queue; n; n = n->next_needed)
-           if (n == node)
-             break;
-         if (!n)
-           node->reachable = 0;
-       }
-    }
+    cgraph_reset_node (node);
 
   notice_global_symbol (decl);
   node->decl = decl;
@@ -422,6 +425,12 @@ cgraph_finalize_function (tree decl, bool nested)
   if (decide_is_function_needed (node, decl))
     cgraph_mark_needed_node (node);
 
+  /* Since we reclaim unreachable nodes at the end of every language
+     level unit, we need to be conservative about possible entry points
+     there.  */
+  if (TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
+    cgraph_mark_reachable_node (node);
+
   /* If not unit at a time, go ahead and emit everything we've found
      to be reachable at this time.  */
   if (!nested)
@@ -534,26 +543,19 @@ cgraph_create_edges (struct cgraph_node *node, tree body)
          walk_tree (bsi_stmt_ptr (bsi), record_reference, node, visited_nodes);
       }
 
-  /* Walk over any private statics that may take addresses of functions.  */
-  if (TREE_CODE (DECL_INITIAL (body)) == BLOCK)
+  /* Look for initializers of constant variables and private statics.  */
+  for (step = DECL_STRUCT_FUNCTION (body)->unexpanded_var_list;
+       step;
+       step = TREE_CHAIN (step))
     {
-      for (step = BLOCK_VARS (DECL_INITIAL (body));
-          step;
-          step = TREE_CHAIN (step))
-       if (DECL_INITIAL (step))
-         walk_tree (&DECL_INITIAL (step), record_reference, node, visited_nodes);
+      tree decl = TREE_VALUE (step);
+      if (TREE_CODE (decl) == VAR_DECL
+         && (TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
+         && flag_unit_at_a_time)
+       cgraph_varpool_finalize_decl (decl);
+      else if (TREE_CODE (decl) == VAR_DECL && DECL_INITIAL (decl))
+       walk_tree (&DECL_INITIAL (decl), record_reference, node, visited_nodes);
     }
-
-  /* Also look here for private statics.  */
-  if (DECL_STRUCT_FUNCTION (body))
-    for (step = DECL_STRUCT_FUNCTION (body)->unexpanded_var_list;
-        step;
-        step = TREE_CHAIN (step))
-      {
-       tree decl = TREE_VALUE (step);
-       if (DECL_INITIAL (decl) && TREE_STATIC (decl))
-         walk_tree (&DECL_INITIAL (decl), record_reference, node, visited_nodes);
-      }
     
   pointer_set_destroy (visited_nodes);
   visited_nodes = NULL;
@@ -610,7 +612,7 @@ verify_cgraph_node (struct cgraph_node *node)
     }
   if (node->global.inlined_to == node)
     {
-      error ("Inlined_to pointer reffers to itself");
+      error ("Inlined_to pointer refers to itself");
       error_found = true;
     }
 
@@ -734,6 +736,14 @@ cgraph_varpool_assemble_pending_decls (void)
       if (!TREE_ASM_WRITTEN (decl) && !node->alias && !DECL_EXTERNAL (decl))
        {
          assemble_variable (decl, 0, 1, 0);
+         /* Local static variables are never seen by check_global_declarations
+            so we need to output debug info by hand.  */
+         if (decl_function_context (decl) && errorcount == 0 && sorrycount == 0)
+           {
+             timevar_push (TV_SYMOUT);
+             (*debug_hooks->global_decl) (decl);
+             timevar_pop (TV_SYMOUT);
+           }
          changed = true;
        }
       node->next_needed = NULL;
@@ -832,7 +842,10 @@ cgraph_finalize_compilation_unit (void)
         weak alias attribute to kill its body. See
         gcc.c-torture/compile/20011119-1.c  */
       if (!DECL_SAVED_TREE (decl))
-       continue;
+       {
+         cgraph_reset_node (node);
+         continue;
+       }
 
       gcc_assert (!node->analyzed && node->reachable);
       gcc_assert (DECL_SAVED_TREE (decl));
@@ -865,14 +878,20 @@ cgraph_finalize_compilation_unit (void)
     {
       tree decl = node->decl;
 
+      if (node->local.finalized && !DECL_SAVED_TREE (decl))
+        cgraph_reset_node (node);
+
       if (!node->reachable && DECL_SAVED_TREE (decl))
        {
          if (cgraph_dump_file)
            fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
          cgraph_remove_node (node);
+         continue;
        }
       else
        node->next_needed = NULL;
+      gcc_assert (!node->local.finalized || DECL_SAVED_TREE (decl));
+      gcc_assert (node->analyzed == node->local.finalized);
     }
   if (cgraph_dump_file)
     {
@@ -962,6 +981,8 @@ cgraph_expand_function (struct cgraph_node *node)
         points to the dead function body.  */
       cgraph_node_remove_callees (node);
     }
+
+  cgraph_function_flags_ready = true;
 }
 
 /* Return true when CALLER_DECL should be inlined into CALLEE_DECL.  */
@@ -1016,26 +1037,49 @@ cgraph_expand_all_functions (void)
   free (order);
 }
 
-/* Mark all local functions.
+/* Mark visibility of all functions.
    
    A local function is one whose calls can occur only in the current
    compilation unit and all its calls are explicit, so we can change
    its calling convention.  We simply mark all static functions whose
-   address is not taken as local.  */
+   address is not taken as local.
+
+   We also change the TREE_PUBLIC flag of all declarations that are public
+   in language point of view but we want to overwrite this default
+   via visibilities for the backend point of view.  */
 
 static void
-cgraph_mark_local_functions (void)
+cgraph_function_and_variable_visibility (void)
 {
   struct cgraph_node *node;
+  struct cgraph_varpool_node *vnode;
 
-  /* Figure out functions we want to assemble.  */
   for (node = cgraph_nodes; node; node = node->next)
     {
+      if (node->reachable
+         && (DECL_COMDAT (node->decl)
+             || (TREE_PUBLIC (node->decl) && !DECL_EXTERNAL (node->decl))))
+       node->local.externally_visible = 1;
       node->local.local = (!node->needed
-                          && DECL_SAVED_TREE (node->decl)
-                          && !TREE_PUBLIC (node->decl));
+                          && node->analyzed
+                          && !DECL_EXTERNAL (node->decl)
+                          && !node->local.externally_visible);
+    }
+  for (vnode = cgraph_varpool_nodes_queue; vnode; vnode = vnode->next_needed)
+    {
+      if (vnode->needed
+         && (DECL_COMDAT (vnode->decl) || TREE_PUBLIC (vnode->decl)))
+       vnode->externally_visible = 1;
+     gcc_assert (TREE_STATIC (vnode->decl));
     }
 
+  /* Because we have to be conservative on the boundaries of source
+     level units, it is possible that we marked some functions in
+     reachable just because they might be used later via external
+     linkage, but after making them local they are really unreachable
+     now.  */
+  cgraph_remove_unreachable_nodes (true, cgraph_dump_file);
+
   if (cgraph_dump_file)
     {
       fprintf (cgraph_dump_file, "\nMarking local functions:");
@@ -1043,7 +1087,13 @@ cgraph_mark_local_functions (void)
        if (node->local.local)
          fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
       fprintf (cgraph_dump_file, "\n\n");
+      fprintf (cgraph_dump_file, "\nMarking externally visible functions:");
+      for (node = cgraph_nodes; node; node = node->next)
+       if (node->local.externally_visible)
+         fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
+      fprintf (cgraph_dump_file, "\n\n");
     }
+  cgraph_function_flags_ready = true;
 }
 
 /* Return true when function body of DECL still needs to be kept around
@@ -1088,13 +1138,16 @@ cgraph_optimize (void)
   if (!quiet_flag)
     fprintf (stderr, "Performing intraprocedural optimizations\n");
 
-  cgraph_mark_local_functions ();
+  cgraph_function_and_variable_visibility ();
   if (cgraph_dump_file)
     {
       fprintf (cgraph_dump_file, "Marked ");
       dump_cgraph (cgraph_dump_file);
     }
   ipa_passes ();
+  /* This pass remove bodies of extern inline functions we never inlined.
+     Do this later so other IPA passes see what is really going on.  */
+  cgraph_remove_unreachable_nodes (false, dump_file);
   cgraph_global_info_ready = true;
   if (cgraph_dump_file)
     {