OSDN Git Service

* varasm.c (assemble_variable): Fix format specifier thinko.
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index b10679d..a29dace 100644 (file)
@@ -16,8 +16,8 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 /* This module implements main driver of compilation process as well as
    few basic intraprocedural optimizers.
@@ -189,9 +189,16 @@ static bool
 decide_is_function_needed (struct cgraph_node *node, tree decl)
 {
   tree origin;
+  if (MAIN_NAME_P (DECL_NAME (decl))
+      && TREE_PUBLIC (decl))
+    {
+      node->local.externally_visible = true;
+      return true;
+    }
 
   /* If the user told us it is used, then it must be so.  */
-  if (lookup_attribute ("used", DECL_ATTRIBUTES (decl)))
+  if (node->local.externally_visible
+      || lookup_attribute ("used", DECL_ATTRIBUTES (decl)))
     return true;
 
   /* ??? If the assembler name is set by hand, it is possible to assemble
@@ -209,7 +216,8 @@ decide_is_function_needed (struct cgraph_node *node, tree decl)
 
   /* 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))
+  if ((TREE_PUBLIC (decl) && !flag_whole_program)
+      && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
     return true;
 
   /* Constructors and destructors are reachable from the runtime by
@@ -339,6 +347,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
@@ -351,56 +412,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;
@@ -415,16 +427,16 @@ cgraph_finalize_function (tree decl, bool nested)
   if (!flag_unit_at_a_time)
     {
       cgraph_analyze_function (node);
-      cgraph_decide_inlining_incrementally (node);
+      cgraph_decide_inlining_incrementally (node, false);
     }
 
   if (decide_is_function_needed (node, decl))
     cgraph_mark_needed_node (node);
 
-  /* Since we reclaim unrechable nodes at the end of every language
+  /* 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))
+  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
@@ -539,31 +551,91 @@ 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;
 }
 
+/* Give initial reasons why inlining would fail.  Those gets
+   either NULLified or usually overwritten by more precise reason
+   later.  */
+static void
+initialize_inline_failed (struct cgraph_node *node)
+{
+  struct cgraph_edge *e;
+
+  for (e = node->callers; e; e = e->next_caller)
+    {
+      gcc_assert (!e->callee->global.inlined_to);
+      gcc_assert (e->inline_failed);
+      if (node->local.redefined_extern_inline)
+       e->inline_failed = N_("redefined extern inline functions are not "
+                          "considered for inlining");
+      else if (!node->local.inlinable)
+       e->inline_failed = N_("function not inlinable");
+      else
+       e->inline_failed = N_("function not considered for inlining");
+    }
+}
+
+/* Rebuild call edges from current function after a passes not aware
+   of cgraph updating.  */
+static void
+rebuild_cgraph_edges (void)
+{
+  basic_block bb;
+  struct cgraph_node *node = cgraph_node (current_function_decl);
+  block_stmt_iterator bsi;
+
+  cgraph_node_remove_callees (node);
+
+  node->count = ENTRY_BLOCK_PTR->count;
+
+  FOR_EACH_BB (bb)
+    for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+      {
+       tree stmt = bsi_stmt (bsi);
+       tree call = get_call_expr_in (stmt);
+       tree decl;
+
+       if (call && (decl = get_callee_fndecl (call)))
+         cgraph_create_edge (node, cgraph_node (decl), stmt,
+                             bb->count,
+                             bb->loop_depth);
+      }
+  initialize_inline_failed (node);
+  gcc_assert (!node->global.inlined_to);
+}
+
+struct tree_opt_pass pass_rebuild_cgraph_edges =
+{
+  NULL,                                        /* name */
+  NULL,                                        /* gate */
+  rebuild_cgraph_edges,                        /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  0,                                   /* tv_id */
+  PROP_cfg,                            /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  0,                                   /* todo_flags_finish */
+  0                                    /* letter */
+};
 
 /* Verify cgraph nodes of given cgraph node.  */
 void
@@ -739,6 +811,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;
@@ -751,7 +831,6 @@ static void
 cgraph_analyze_function (struct cgraph_node *node)
 {
   tree decl = node->decl;
-  struct cgraph_edge *e;
 
   current_function_decl = decl;
   push_cfun (DECL_STRUCT_FUNCTION (decl));
@@ -765,16 +844,7 @@ cgraph_analyze_function (struct cgraph_node *node)
   if (node->local.inlinable)
     node->local.disregard_inline_limits
       = lang_hooks.tree_inlining.disregard_inline_limits (decl);
-  for (e = node->callers; e; e = e->next_caller)
-    {
-      if (node->local.redefined_extern_inline)
-       e->inline_failed = N_("redefined extern inline functions are not "
-                          "considered for inlining");
-      else if (!node->local.inlinable)
-       e->inline_failed = N_("function not inlinable");
-      else
-       e->inline_failed = N_("function not considered for inlining");
-    }
+  initialize_inline_failed (node);
   if (flag_really_no_inline && !node->local.disregard_inline_limits)
     node->local.inlinable = 0;
   /* Inlining characteristics are maintained by the cgraph_mark_inline.  */
@@ -837,7 +907,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));
@@ -870,14 +943,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)
     {
@@ -967,6 +1046,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.  */
@@ -1043,16 +1124,28 @@ cgraph_function_and_variable_visibility (void)
       if (node->reachable
          && (DECL_COMDAT (node->decl)
              || (TREE_PUBLIC (node->decl) && !DECL_EXTERNAL (node->decl))))
-       node->local.externally_visible = 1;
+       node->local.externally_visible = true;
+      if (!node->local.externally_visible && node->analyzed
+         && !DECL_EXTERNAL (node->decl))
+       {
+         gcc_assert (flag_whole_program || !TREE_PUBLIC (node->decl));
+         TREE_PUBLIC (node->decl) = 0;
+       }
       node->local.local = (!node->needed
                           && node->analyzed
-                          && node->local.externally_visible);
+                          && !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;
+      if (!vnode->externally_visible)
+       {
+         gcc_assert (flag_whole_program || !TREE_PUBLIC (vnode->decl));
+         TREE_PUBLIC (vnode->decl) = 0;
+       }
      gcc_assert (TREE_STATIC (vnode->decl));
     }
 
@@ -1128,6 +1221,9 @@ cgraph_optimize (void)
       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)
     {