OSDN Git Service

PR target/25168
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index f3d718d..ae2dd51 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.
@@ -162,6 +162,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "c-common.h"
 #include "intl.h"
 #include "function.h"
+#include "ipa-prop.h"
 #include "tree-gimple.h"
 #include "tree-pass.h"
 #include "output.h"
@@ -248,7 +249,7 @@ decide_is_function_needed (struct cgraph_node *node, tree decl)
          /* When declared inline, defer even the uninlinable functions.
             This allows them to be eliminated when unused.  */
          && !DECL_DECLARED_INLINE_P (decl) 
-         && (!node->local.inlinable || !cgraph_default_inline_p (node))))
+         && (!node->local.inlinable || !cgraph_default_inline_p (node, NULL))))
     return true;
 
   return false;
@@ -310,11 +311,19 @@ cgraph_varpool_remove_unreferenced_decls (void)
          && ((DECL_ASSEMBLER_NAME_SET_P (decl)
               && TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
              || node->force_output
-             || decide_is_variable_needed (node, decl)))
+             || decide_is_variable_needed (node, decl)
+             /* ??? Cgraph does not yet rule the world with an iron hand, 
+                and does not control the emission of debug information.
+                After a variable has its DECL_RTL set, we must assume that
+                it may be referenced by the debug information, and we can
+                no longer elide it.  */
+             || DECL_RTL_SET_P (decl)))
        cgraph_varpool_mark_needed_node (node);
 
       node = next;
     }
+  /* Make sure we mark alias targets as used targets.  */
+  finish_aliases_1 ();
   cgraph_varpool_analyze_pending_decls ();
 }
 
@@ -427,7 +436,7 @@ 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))
@@ -569,6 +578,73 @@ cgraph_create_edges (struct cgraph_node *node, tree body)
   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
@@ -585,42 +661,52 @@ verify_cgraph_node (struct cgraph_node *node)
   for (e = node->callees; e; e = e->next_callee)
     if (e->aux)
       {
-       error ("Aux field set for edge %s->%s",
+       error ("aux field set for edge %s->%s",
               cgraph_node_name (e->caller), cgraph_node_name (e->callee));
        error_found = true;
       }
+  if (node->count < 0)
+    {
+      error ("Execution count is negative");
+      error_found = true;
+    }
   for (e = node->callers; e; e = e->next_caller)
     {
+      if (e->count < 0)
+       {
+         error ("caller edge count is negative");
+         error_found = true;
+       }
       if (!e->inline_failed)
        {
          if (node->global.inlined_to
              != (e->caller->global.inlined_to
                  ? e->caller->global.inlined_to : e->caller))
            {
-             error ("Inlined_to pointer is wrong");
+             error ("inlined_to pointer is wrong");
              error_found = true;
            }
          if (node->callers->next_caller)
            {
-             error ("Multiple inline callers");
+             error ("multiple inline callers");
              error_found = true;
            }
        }
       else
        if (node->global.inlined_to)
          {
-           error ("Inlined_to pointer set for noninline callers");
+           error ("inlined_to pointer set for noninline callers");
            error_found = true;
          }
     }
   if (!node->callers && node->global.inlined_to)
     {
-      error ("Inlined_to pointer is set but no predecesors found");
+      error ("inlined_to pointer is set but no predecesors found");
       error_found = true;
     }
   if (node->global.inlined_to == node)
     {
-      error ("Inlined_to pointer refers to itself");
+      error ("inlined_to pointer refers to itself");
       error_found = true;
     }
 
@@ -630,7 +716,7 @@ verify_cgraph_node (struct cgraph_node *node)
       break;
   if (!node)
     {
-      error ("Node not found in DECL_ASSEMBLER_NAME hash");
+      error ("node not found in DECL_ASSEMBLER_NAME hash");
       error_found = true;
     }
   
@@ -658,13 +744,14 @@ verify_cgraph_node (struct cgraph_node *node)
                      {
                        if (e->aux)
                          {
-                           error ("Shared call_stmt:");
+                           error ("shared call_stmt:");
                            debug_generic_stmt (stmt);
                            error_found = true;
                          }
-                       if (e->callee->decl != cgraph_node (decl)->decl)
+                       if (e->callee->decl != cgraph_node (decl)->decl
+                           && e->inline_failed)
                          {
-                           error ("Edge points to wrong declaration:");
+                           error ("edge points to wrong declaration:");
                            debug_tree (e->callee->decl);
                            fprintf (stderr," Instead of:");
                            debug_tree (decl);
@@ -673,7 +760,7 @@ verify_cgraph_node (struct cgraph_node *node)
                      }
                    else
                      {
-                       error ("Missing callgraph edge for call stmt:");
+                       error ("missing callgraph edge for call stmt:");
                        debug_generic_stmt (stmt);
                        error_found = true;
                      }
@@ -690,7 +777,7 @@ verify_cgraph_node (struct cgraph_node *node)
        {
          if (!e->aux)
            {
-             error ("Edge %s->%s has no corresponding call_stmt",
+             error ("edge %s->%s has no corresponding call_stmt",
                     cgraph_node_name (e->caller),
                     cgraph_node_name (e->callee));
              debug_generic_stmt (e->call_stmt);
@@ -702,7 +789,7 @@ verify_cgraph_node (struct cgraph_node *node)
   if (error_found)
     {
       dump_cgraph_node (stderr, node);
-      internal_error ("verify_cgraph_node failed.");
+      internal_error ("verify_cgraph_node failed");
     }
   timevar_pop (TV_CGRAPH_VERIFY);
 }
@@ -746,7 +833,10 @@ cgraph_varpool_assemble_pending_decls (void)
          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)
+         if (DECL_CONTEXT (decl) 
+             && (TREE_CODE (DECL_CONTEXT (decl)) == BLOCK
+                 || TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
+             && errorcount == 0 && sorrycount == 0)
            {
              timevar_push (TV_SYMOUT);
              (*debug_hooks->global_decl) (decl);
@@ -764,7 +854,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));
@@ -778,16 +867,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.  */
@@ -1066,7 +1146,8 @@ cgraph_function_and_variable_visibility (void)
     {
       if (node->reachable
          && (DECL_COMDAT (node->decl)
-             || (TREE_PUBLIC (node->decl) && !DECL_EXTERNAL (node->decl))))
+             || (!flag_whole_program
+                 && TREE_PUBLIC (node->decl) && !DECL_EXTERNAL (node->decl))))
        node->local.externally_visible = true;
       if (!node->local.externally_visible && node->analyzed
          && !DECL_EXTERNAL (node->decl))
@@ -1082,6 +1163,7 @@ cgraph_function_and_variable_visibility (void)
   for (vnode = cgraph_varpool_nodes_queue; vnode; vnode = vnode->next_needed)
     {
       if (vnode->needed
+         && !flag_whole_program
          && (DECL_COMDAT (vnode->decl) || TREE_PUBLIC (vnode->decl)))
        vnode->externally_visible = 1;
       if (!vnode->externally_visible)
@@ -1121,9 +1203,6 @@ bool
 cgraph_preserve_function_body_p (tree decl)
 {
   struct cgraph_node *node;
-  /* Keep the body; we're going to dump it.  */
-  if (dump_enabled_p (TDI_tree_all))
-    return true;
   if (!cgraph_global_info_ready)
     return (DECL_INLINE (decl) && !flag_really_no_inline);
   /* Look if there is any clone around.  */
@@ -1133,6 +1212,16 @@ cgraph_preserve_function_body_p (tree decl)
   return false;
 }
 
+static void
+ipa_passes (void)
+{
+  cfun = NULL;
+  tree_register_cfg_hooks ();
+  bitmap_obstack_initialize (NULL);
+  execute_ipa_pass_list (all_ipa_passes);
+  bitmap_obstack_release (NULL);
+}
+
 /* Perform simple optimizations based on callgraph.  */
 
 void
@@ -1214,7 +1303,7 @@ cgraph_optimize (void)
            dump_cgraph_node (stderr, node);
          }
       if (error_found)
-       internal_error ("Nodes with no released memory found.");
+       internal_error ("nodes with no released memory found");
     }
 #endif
 }
@@ -1299,3 +1388,188 @@ init_cgraph (void)
 {
   cgraph_dump_file = dump_begin (TDI_cgraph, NULL);
 }
+
+/* The edges representing the callers of the NEW_VERSION node were 
+   fixed by cgraph_function_versioning (), now the call_expr in their
+   respective tree code should be updated to call the NEW_VERSION.  */
+
+static void
+update_call_expr (struct cgraph_node *new_version)
+{
+  struct cgraph_edge *e;
+
+  gcc_assert (new_version);
+  for (e = new_version->callers; e; e = e->next_caller)
+    /* Update the call expr on the edges
+       to call the new version.  */
+    TREE_OPERAND (TREE_OPERAND (get_call_expr_in (e->call_stmt), 0), 0) = new_version->decl;
+}
+
+
+/* Create a new cgraph node which is the new version of
+   OLD_VERSION node.  REDIRECT_CALLERS holds the callers
+   edges which should be redirected to point to
+   NEW_VERSION.  ALL the callees edges of OLD_VERSION
+   are cloned to the new version node.  Return the new
+   version node.  */
+
+static struct cgraph_node *
+cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
+                                tree new_decl, varray_type redirect_callers)
+ {
+   struct cgraph_node *new_version;
+   struct cgraph_edge *e, *new_e;
+   struct cgraph_edge *next_callee;
+   unsigned i;
+
+   gcc_assert (old_version);
+   
+   new_version = cgraph_node (new_decl);
+
+   new_version->analyzed = true;
+   new_version->local = old_version->local;
+   new_version->global = old_version->global;
+   new_version->rtl = new_version->rtl;
+   new_version->reachable = true;
+   new_version->count = old_version->count;
+
+   /* Clone the old node callees.  Recursive calls are
+      also cloned.  */
+   for (e = old_version->callees;e; e=e->next_callee)
+     {
+       new_e = cgraph_clone_edge (e, new_version, e->call_stmt, 0, e->loop_nest, true);
+       new_e->count = e->count;
+     }
+   /* Fix recursive calls.
+      If OLD_VERSION has a recursive call after the
+      previous edge cloning, the new version will have an edge
+      pointing to the old version, which is wrong;
+      Redirect it to point to the new version. */
+   for (e = new_version->callees ; e; e = next_callee)
+     {
+       next_callee = e->next_callee;
+       if (e->callee == old_version)
+        cgraph_redirect_edge_callee (e, new_version);
+         
+       if (!next_callee)
+        break;
+     }
+   if (redirect_callers)
+     for (i = 0; i < VARRAY_ACTIVE_SIZE (redirect_callers); i++)
+       {
+         e = VARRAY_GENERIC_PTR (redirect_callers, i);
+        /* Redirect calls to the old version node
+           to point to it's new version.  */
+         cgraph_redirect_edge_callee (e, new_version);
+       }
+
+   return new_version;
+ }
+
+ /* Perform function versioning.
+    Function versioning includes copying of the tree and 
+    a callgraph update (creating a new cgraph node and updating
+    its callees and callers).
+
+    REDIRECT_CALLERS varray includes the edges to be redirected
+    to the new version.
+
+    TREE_MAP is a mapping of tree nodes we want to replace with
+    new ones (according to results of prior analysis).
+    OLD_VERSION_NODE is the node that is versioned.
+    It returns the new version's cgraph node.  */
+
+struct cgraph_node *
+cgraph_function_versioning (struct cgraph_node *old_version_node,
+                           varray_type redirect_callers,
+                           varray_type tree_map)
+{
+  tree old_decl = old_version_node->decl;
+  struct cgraph_node *new_version_node = NULL;
+  tree new_decl;
+
+  if (!tree_versionable_function_p (old_decl))
+    return NULL;
+
+  /* Make a new FUNCTION_DECL tree node for the
+     new version. */
+  new_decl = copy_node (old_decl);
+
+  /* Create the new version's call-graph node.
+     and update the edges of the new node. */
+  new_version_node =
+    cgraph_copy_node_for_versioning (old_version_node, new_decl,
+                                    redirect_callers);
+
+  /* Copy the OLD_VERSION_NODE function tree to the new version.  */
+  tree_function_versioning (old_decl, new_decl, tree_map, false);
+  /* Update the call_expr on the edges to call the new version node. */
+  update_call_expr (new_version_node);
+
+  /* Update the new version's properties.  
+     Make The new version visible only within this translation unit.
+     ??? We cannot use COMDAT linkage because there is no 
+     ABI support for this.  */
+  DECL_EXTERNAL (new_version_node->decl) = 0;
+  DECL_ONE_ONLY (new_version_node->decl) = 0;
+  TREE_PUBLIC (new_version_node->decl) = 0;
+  DECL_COMDAT (new_version_node->decl) = 0;
+  new_version_node->local.externally_visible = 0;
+  new_version_node->local.local = 1;
+  new_version_node->lowered = true;
+  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;
+
+  gcc_assert (node == cgraph_node (node->decl));
+
+  cgraph_lower_function (node);
+
+  /* In non-unit-at-a-time we construct full fledged clone we never output to
+     assembly file.  This clone is pointed out by inline_decl of orginal function
+     and inlining infrastructure knows how to deal with this.  */
+  if (!flag_unit_at_a_time)
+    {
+      struct cgraph_edge *e;
+
+      first_clone = cgraph_clone_node (node, node->count, 0, false);
+      first_clone->needed = 0;
+      first_clone->reachable = 1;
+      /* Recursively clone all bodies.  */
+      for (e = first_clone->callees; e; e = e->next_callee)
+       if (!e->inline_failed)
+         cgraph_clone_inlined_nodes (e, true, false);
+    }
+  else
+    first_clone = node->next_clone;
+
+  first_clone->decl = copy_node (node->decl);
+  node->next_clone = NULL;
+  if (!flag_unit_at_a_time)
+    node->inline_decl = first_clone->decl;
+  first_clone->prev_clone = NULL;
+  cgraph_insert_node_to_hashtable (first_clone);
+  gcc_assert (first_clone == cgraph_node (first_clone->decl));
+
+  /* Copy the OLD_VERSION_NODE function tree to the new version.  */
+  tree_function_versioning (node->decl, first_clone->decl, NULL, true);
+
+  DECL_EXTERNAL (first_clone->decl) = 0;
+  DECL_ONE_ONLY (first_clone->decl) = 0;
+  TREE_PUBLIC (first_clone->decl) = 0;
+  DECL_COMDAT (first_clone->decl) = 0;
+
+  for (node = first_clone->next_clone; node; node = node->next_clone)
+    node->decl = first_clone->decl;
+#ifdef ENABLE_CHECKING
+  verify_cgraph_node (first_clone);
+#endif
+  return first_clone;
+}
+