OSDN Git Service

/c-family
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index 15660a7..834acb1 100644 (file)
@@ -138,6 +138,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "output.h"
 #include "coverage.h"
 #include "plugin.h"
+#include "ipa-inline.h"
+#include "ipa-utils.h"
 
 static void cgraph_expand_all_functions (void);
 static void cgraph_mark_functions_to_output (void);
@@ -172,7 +174,7 @@ cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
   if (flag_keep_inline_functions
       && DECL_DECLARED_INLINE_P (decl)
       && !DECL_EXTERNAL (decl)
-      && !lookup_attribute ("always_inline", DECL_ATTRIBUTES (decl)))
+      && !DECL_DISREGARD_INLINE_LIMITS (decl))
      return true;
 
   /* If we decided it was needed before, but at the time we didn't have
@@ -191,7 +193,7 @@ cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
      to change the behavior here.  */
   if (((TREE_PUBLIC (decl)
        || (!optimize
-           && !node->local.disregard_inline_limits
+           && !DECL_DISREGARD_INLINE_LIMITS (decl)
            && !DECL_DECLARED_INLINE_P (decl)
            && !(DECL_CONTEXT (decl)
                 && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)))
@@ -232,6 +234,7 @@ cgraph_process_new_functions (void)
          cgraph_finalize_function (fndecl, false);
          cgraph_mark_reachable_node (node);
          output = true;
+          cgraph_call_function_insertion_hooks (node);
          break;
 
        case CGRAPH_STATE_IPA:
@@ -252,17 +255,19 @@ cgraph_process_new_functions (void)
              || !optimize)
            execute_pass_list (pass_early_local_passes.pass.sub);
          else
-           compute_inline_parameters (node);
+           compute_inline_parameters (node, true);
          free_dominance_info (CDI_POST_DOMINATORS);
          free_dominance_info (CDI_DOMINATORS);
          pop_cfun ();
          current_function_decl = NULL;
+          cgraph_call_function_insertion_hooks (node);
          break;
 
        case CGRAPH_STATE_EXPANSION:
          /* Functions created during expansion shall be compiled
             directly.  */
          node->process = 0;
+          cgraph_call_function_insertion_hooks (node);
          cgraph_expand_function (node);
          break;
 
@@ -270,7 +275,6 @@ cgraph_process_new_functions (void)
          gcc_unreachable ();
          break;
        }
-      cgraph_call_function_insertion_hooks (node);
       varpool_analyze_pending_decls ();
     }
   return output;
@@ -343,16 +347,14 @@ cgraph_lower_function (struct cgraph_node *node)
 void
 cgraph_finalize_function (tree decl, bool nested)
 {
-  struct cgraph_node *node = cgraph_node (decl);
+  struct cgraph_node *node = cgraph_get_create_node (decl);
 
   if (node->local.finalized)
     cgraph_reset_node (node);
 
-  node->pid = cgraph_max_pid ++;
   notice_global_symbol (decl);
   node->local.finalized = true;
   node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL;
-  node->finalized_by_frontend = true;
 
   if (cgraph_decide_is_function_needed (node, decl))
     cgraph_mark_needed_node (node);
@@ -368,7 +370,8 @@ cgraph_finalize_function (tree decl, bool nested)
         to those so we need to analyze them.
         FIXME: We should introduce may edges for this purpose and update
         their handling in unreachable function removal and inliner too.  */
-      || (DECL_VIRTUAL_P (decl) && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
+      || (DECL_VIRTUAL_P (decl)
+         && optimize && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
     cgraph_mark_reachable_node (node);
 
   /* If we've not yet emitted decl, tell the debug info about it.  */
@@ -390,7 +393,7 @@ cgraph_finalize_function (tree decl, bool nested)
 void
 cgraph_mark_if_needed (tree decl)
 {
-  struct cgraph_node *node = cgraph_node (decl);
+  struct cgraph_node *node = cgraph_get_node (decl);
   if (node->local.finalized && cgraph_decide_is_function_needed (node, decl))
     cgraph_mark_needed_node (node);
 }
@@ -427,6 +430,11 @@ verify_edge_count_and_frequency (struct cgraph_edge *e)
     }
   if (gimple_has_body_p (e->caller->decl)
       && !e->caller->global.inlined_to
+      /* FIXME: Inline-analysis sets frequency to 0 when edge is optimized out.
+        Remove this once edges are actualy removed from the function at that time.  */
+      && (e->frequency
+         || (inline_edge_summary_vec
+             && !inline_edge_summary (e)->predicate))
       && (e->frequency
          != compute_call_stmt_bb_frequency (e->caller->decl,
                                             gimple_bb (e->call_stmt))))
@@ -622,10 +630,28 @@ verify_cgraph_node (struct cgraph_node *node)
       while (n != node);
     }
 
-  if (node->analyzed && gimple_has_body_p (node->decl)
-      && !TREE_ASM_WRITTEN (node->decl)
-      && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to)
-      && !flag_wpa)
+  if (node->analyzed && node->thunk.thunk_p)
+    {
+      if (!node->callees)
+       {
+         error ("No edge out of thunk node");
+          error_found = true;
+       }
+      else if (node->callees->next_callee)
+       {
+         error ("More than one edge out of thunk node");
+          error_found = true;
+       }
+      if (gimple_has_body_p (node->decl))
+        {
+         error ("Thunk is not supposed to have body");
+          error_found = true;
+        }
+    }
+  else if (node->analyzed && gimple_has_body_p (node->decl)
+           && !TREE_ASM_WRITTEN (node->decl)
+           && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to)
+           && !flag_wpa)
     {
       if (this_cfun->cfg)
        {
@@ -654,8 +680,6 @@ verify_cgraph_node (struct cgraph_node *node)
                          }
                        if (!e->indirect_unknown_callee)
                          {
-                           struct cgraph_node *n;
-
                            if (e->callee->same_body_alias)
                              {
                                error ("edge points to same body alias:");
@@ -667,7 +691,7 @@ verify_cgraph_node (struct cgraph_node *node)
                                     && cgraph_get_node (decl)
                                     && (e->callee->former_clone_of
                                         != cgraph_get_node (decl)->decl)
-                                    && !clone_of_p (cgraph_node (decl),
+                                    && !clone_of_p (cgraph_get_node (decl),
                                                     e->callee))
                              {
                                error ("edge points to wrong declaration:");
@@ -676,16 +700,6 @@ verify_cgraph_node (struct cgraph_node *node)
                                debug_tree (decl);
                                error_found = true;
                              }
-                           else if (decl
-                                    && (n = cgraph_get_node_or_alias (decl))
-                                    && (n->same_body_alias
-                                        && n->thunk.thunk_p))
-                             {
-                               error ("a call to thunk improperly represented "
-                                      "in the call graph:");
-                               cgraph_debug_gimple_stmt (this_cfun, stmt);
-                               error_found = true;
-                             }
                          }
                        else if (decl)
                          {
@@ -778,28 +792,31 @@ cgraph_analyze_function (struct cgraph_node *node)
   tree save = current_function_decl;
   tree decl = node->decl;
 
-  current_function_decl = decl;
-  push_cfun (DECL_STRUCT_FUNCTION (decl));
-
-  assign_assembler_name_if_neeeded (node->decl);
+  if (node->thunk.thunk_p)
+    {
+      cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
+                         NULL, 0, CGRAPH_FREQ_BASE);
+    }
+  else
+    {
+      current_function_decl = decl;
+      push_cfun (DECL_STRUCT_FUNCTION (decl));
 
-  /* disregard_inline_limits affects topological order of the early optimization,
-     so we need to compute it ahead of rest of inline parameters.  */
-  node->local.disregard_inline_limits
-    = DECL_DISREGARD_INLINE_LIMITS (node->decl);
+      assign_assembler_name_if_neeeded (node->decl);
 
-  /* Make sure to gimplify bodies only once.  During analyzing a
-     function we lower it, which will require gimplified nested
-     functions, so we can end up here with an already gimplified
-     body.  */
-  if (!gimple_body (decl))
-    gimplify_function_tree (decl);
-  dump_function (TDI_generic, decl);
+      /* Make sure to gimplify bodies only once.  During analyzing a
+        function we lower it, which will require gimplified nested
+        functions, so we can end up here with an already gimplified
+        body.  */
+      if (!gimple_body (decl))
+       gimplify_function_tree (decl);
+      dump_function (TDI_generic, decl);
 
-  cgraph_lower_function (node);
+      cgraph_lower_function (node);
+      pop_cfun ();
+    }
   node->analyzed = true;
 
-  pop_cfun ();
   current_function_decl = save;
 }
 
@@ -972,7 +989,8 @@ cgraph_analyze_functions (void)
       /* ??? It is possible to create extern inline function and later using
         weak alias attribute to kill its body. See
         gcc.c-torture/compile/20011119-1.c  */
-      if (!DECL_STRUCT_FUNCTION (decl))
+      if (!DECL_STRUCT_FUNCTION (decl)
+         && !node->thunk.thunk_p)
        {
          cgraph_reset_node (node);
          continue;
@@ -984,6 +1002,9 @@ cgraph_analyze_functions (void)
       for (edge = node->callees; edge; edge = edge->next_callee)
        if (!edge->callee->reachable)
          cgraph_mark_reachable_node (edge->callee);
+      for (edge = node->callers; edge; edge = edge->next_caller)
+       if (!edge->caller->reachable && edge->caller->thunk.thunk_p)
+         cgraph_mark_reachable_node (edge->caller);
 
       if (node->same_comdat_group)
        {
@@ -995,10 +1016,12 @@ cgraph_analyze_functions (void)
 
       /* If decl is a clone of an abstract function, mark that abstract
         function so that we don't release its body. The DECL_INITIAL() of that
-         abstract function declaration will be later needed to output debug info.  */
+        abstract function declaration will be later needed to output debug
+        info.  */
       if (DECL_ABSTRACT_ORIGIN (decl))
        {
-         struct cgraph_node *origin_node = cgraph_node (DECL_ABSTRACT_ORIGIN (decl));
+         struct cgraph_node *origin_node;
+         origin_node = cgraph_get_node (DECL_ABSTRACT_ORIGIN (decl));
          origin_node->abstract_and_needed = true;
        }
 
@@ -1032,10 +1055,12 @@ cgraph_analyze_functions (void)
       tree decl = node->decl;
       next = node->next;
 
-      if (node->local.finalized && !gimple_has_body_p (decl))
+      if (node->local.finalized && !gimple_has_body_p (decl)
+         && !node->thunk.thunk_p)
        cgraph_reset_node (node);
 
-      if (!node->reachable && gimple_has_body_p (decl))
+      if (!node->reachable
+         && (gimple_has_body_p (decl) || node->thunk.thunk_p))
        {
          if (cgraph_dump_file)
            fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
@@ -1044,7 +1069,8 @@ cgraph_analyze_functions (void)
        }
       else
        node->next_needed = NULL;
-      gcc_assert (!node->local.finalized || gimple_has_body_p (decl));
+      gcc_assert (!node->local.finalized || node->thunk.thunk_p
+                 || gimple_has_body_p (decl));
       gcc_assert (node->analyzed == node->local.finalized);
     }
   if (cgraph_dump_file)
@@ -1133,6 +1159,7 @@ cgraph_mark_functions_to_output (void)
         always inlined, as well as those that are reachable from
         outside the current compilation unit.  */
       if (node->analyzed
+         && !node->thunk.thunk_p
          && !node->global.inlined_to
          && (!cgraph_only_called_directly_p (node)
              || (e && node->reachable))
@@ -1146,7 +1173,8 @@ cgraph_mark_functions_to_output (void)
              for (next = node->same_comdat_group;
                   next != node;
                   next = next->same_comdat_group)
-               next->process = 1;
+               if (!next->thunk.thunk_p)
+                 next->process = 1;
            }
        }
       else if (node->same_comdat_group)
@@ -1407,6 +1435,8 @@ assemble_thunk (struct cgraph_node *node)
       free_after_compilation (cfun);
       set_cfun (NULL);
       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+      node->thunk.thunk_p = false;
+      node->analyzed = false;
     }
   else
     {
@@ -1531,15 +1561,36 @@ assemble_thunk (struct cgraph_node *node)
       delete_unreachable_blocks ();
       update_ssa (TODO_update_ssa);
 
-      cgraph_remove_same_body_alias (node);
       /* Since we want to emit the thunk, we explicitly mark its name as
         referenced.  */
+      node->thunk.thunk_p = false;
+      cgraph_node_remove_callees (node);
       cgraph_add_new_function (thunk_fndecl, true);
       bitmap_obstack_release (NULL);
     }
   current_function_decl = NULL;
 }
 
+
+/* Assemble thunks asociated to NODE.  */
+
+static void
+assemble_thunks (struct cgraph_node *node)
+{
+  struct cgraph_edge *e;
+  for (e = node->callers; e;)
+    if (e->caller->thunk.thunk_p)
+      {
+       struct cgraph_node *thunk = e->caller;
+
+       e = e->next_caller;
+       assemble_thunks (thunk);
+        assemble_thunk (thunk);
+      }
+    else
+      e = e->next_caller;
+}
+
 /* Expand function specified by NODE.  */
 
 static void
@@ -1567,13 +1618,12 @@ cgraph_expand_function (struct cgraph_node *node)
          if (!alias->thunk.thunk_p)
            assemble_alias (alias->decl,
                            DECL_ASSEMBLER_NAME (alias->thunk.alias));
-         else
-           assemble_thunk (alias);
        }
       node->alias = saved_alias;
       cgraph_process_new_functions ();
     }
 
+  assemble_thunks (node);
   gcc_assert (node->lowered);
 
   /* Generate RTL for the body of DECL.  */
@@ -1582,7 +1632,7 @@ cgraph_expand_function (struct cgraph_node *node)
   /* Make sure that BE didn't give up on compiling.  */
   gcc_assert (TREE_ASM_WRITTEN (decl));
   current_function_decl = NULL;
-  gcc_assert (!cgraph_preserve_function_body_p (decl));
+  gcc_assert (!cgraph_preserve_function_body_p (node));
   cgraph_release_function_body (node);
   /* Eliminate all call edges.  This is important so the GIMPLE_CALL no longer
      points to the dead function body.  */
@@ -1620,7 +1670,7 @@ cgraph_expand_all_functions (void)
   int order_pos, new_order_pos = 0;
   int i;
 
-  order_pos = cgraph_postorder (order);
+  order_pos = ipa_reverse_postorder (order);
   gcc_assert (order_pos == cgraph_n_nodes);
 
   /* Garbage collector may remove inline clones we eliminate during
@@ -1689,7 +1739,7 @@ cgraph_output_in_order (void)
 
   for (pf = cgraph_nodes; pf; pf = pf->next)
     {
-      if (pf->process)
+      if (pf->process && !pf->thunk.thunk_p)
        {
          i = pf->order;
          gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
@@ -1760,13 +1810,12 @@ cgraph_output_in_order (void)
 /* Return true when function body of DECL still needs to be kept around
    for later re-use.  */
 bool
-cgraph_preserve_function_body_p (tree decl)
+cgraph_preserve_function_body_p (struct cgraph_node *node)
 {
-  struct cgraph_node *node;
-
   gcc_assert (cgraph_global_info_ready);
+  gcc_assert (!node->same_body_alias);
+
   /* Look if there is any clone around.  */
-  node = cgraph_node (decl);
   if (node->clones)
     return true;
   return false;
@@ -1988,13 +2037,12 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
 
    gcc_assert (old_version);
 
-   new_version = cgraph_node (new_decl);
+   new_version = cgraph_create_node (new_decl);
 
    new_version->analyzed = true;
    new_version->local = old_version->local;
    new_version->local.externally_visible = false;
    new_version->local.local = true;
-   new_version->local.vtable_method = false;
    new_version->global = old_version->global;
    new_version->rtl = old_version->rtl;
    new_version->reachable = true;
@@ -2006,14 +2054,14 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
        cgraph_clone_edge (e, new_version, e->call_stmt,
                          e->lto_stmt_uid, REG_BR_PROB_BASE,
                          CGRAPH_FREQ_BASE,
-                         e->loop_nest, true);
+                         true);
    for (e = old_version->indirect_calls; e; e=e->next_callee)
      if (!bbs_to_copy
         || bitmap_bit_p (bbs_to_copy, gimple_bb (e->call_stmt)->index))
        cgraph_clone_edge (e, new_version, e->call_stmt,
                          e->lto_stmt_uid, REG_BR_PROB_BASE,
                          CGRAPH_FREQ_BASE,
-                         e->loop_nest, true);
+                         true);
    FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e)
      {
        /* Redirect calls to the old version node to point to its new
@@ -2099,74 +2147,6 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
   return new_version_node;
 }
 
-/* Produce separate function body for inline clones so the offline copy can be
-   modified without affecting them.  */
-struct cgraph_node *
-save_inline_function_body (struct cgraph_node *node)
-{
-  struct cgraph_node *first_clone, *n;
-
-  gcc_assert (node == cgraph_node (node->decl));
-
-  cgraph_lower_function (node);
-
-  first_clone = node->clones;
-
-  first_clone->decl = copy_node (node->decl);
-  cgraph_insert_node_to_hashtable (first_clone);
-  gcc_assert (first_clone == cgraph_node (first_clone->decl));
-  if (first_clone->next_sibling_clone)
-    {
-      for (n = first_clone->next_sibling_clone; n->next_sibling_clone; n = n->next_sibling_clone)
-        n->clone_of = first_clone;
-      n->clone_of = first_clone;
-      n->next_sibling_clone = first_clone->clones;
-      if (first_clone->clones)
-        first_clone->clones->prev_sibling_clone = n;
-      first_clone->clones = first_clone->next_sibling_clone;
-      first_clone->next_sibling_clone->prev_sibling_clone = NULL;
-      first_clone->next_sibling_clone = NULL;
-      gcc_assert (!first_clone->prev_sibling_clone);
-    }
-  first_clone->clone_of = NULL;
-  node->clones = NULL;
-
-  if (first_clone->clones)
-    for (n = first_clone->clones; n != first_clone;)
-      {
-        gcc_assert (n->decl == node->decl);
-       n->decl = first_clone->decl;
-       if (n->clones)
-         n = n->clones;
-       else if (n->next_sibling_clone)
-         n = n->next_sibling_clone;
-       else
-         {
-           while (n != first_clone && !n->next_sibling_clone)
-             n = n->clone_of;
-           if (n != first_clone)
-             n = n->next_sibling_clone;
-         }
-      }
-
-  /* Copy the OLD_VERSION_NODE function tree to the new version.  */
-  tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL,
-                           NULL, NULL);
-
-  DECL_EXTERNAL (first_clone->decl) = 0;
-  DECL_COMDAT_GROUP (first_clone->decl) = NULL_TREE;
-  TREE_PUBLIC (first_clone->decl) = 0;
-  DECL_COMDAT (first_clone->decl) = 0;
-  VEC_free (ipa_opt_pass, heap,
-            first_clone->ipa_transforms_to_apply);
-  first_clone->ipa_transforms_to_apply = NULL;
-
-#ifdef ENABLE_CHECKING
-  verify_cgraph_node (first_clone);
-#endif
-  return first_clone;
-}
-
 /* Given virtual clone, turn it into actual clone.  */
 static void
 cgraph_materialize_clone (struct cgraph_node *node)