OSDN Git Service

* ipa-reference.c: Do not include c-common.h, include splay-tree.h.
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index 674d6da..7b87d38 100644 (file)
@@ -1,5 +1,5 @@
 /* Callgraph based interprocedural optimizations.
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009
    Free Software Foundation, Inc.
    Contributed by Jan Hubicka
 
@@ -126,13 +126,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "timevar.h"
 #include "params.h"
 #include "fibheap.h"
-#include "c-common.h"
 #include "intl.h"
 #include "function.h"
 #include "ipa-prop.h"
-#include "tree-gimple.h"
+#include "gimple.h"
+#include "tree-iterator.h"
 #include "tree-pass.h"
 #include "output.h"
+#include "coverage.h"
 
 static void cgraph_expand_all_functions (void);
 static void cgraph_mark_functions_to_output (void);
@@ -172,7 +173,6 @@ record_cdtor_fn (tree fndecl)
       VEC_safe_push (tree, gc, static_dtors, fndecl);
       DECL_STATIC_DESTRUCTOR (fndecl) = 0;
     }
-  DECL_INLINE (fndecl) = 1;
   node = cgraph_node (fndecl);
   node->local.disregard_inline_limits = 1;
   cgraph_mark_reachable_node (node);
@@ -405,7 +405,7 @@ cgraph_process_new_functions (void)
             transformations that has been already performed on the whole
             cgraph but not on this function.  */
 
-         tree_register_cfg_hooks ();
+         gimple_register_cfg_hooks ();
          if (!node->analyzed)
            cgraph_analyze_function (node);
          push_cfun (DECL_STRUCT_FUNCTION (fndecl));
@@ -426,7 +426,7 @@ cgraph_process_new_functions (void)
        case CGRAPH_STATE_EXPANSION:
          /* Functions created during expansion shall be compiled
             directly.  */
-         node->output = 0;
+         node->process = 0;
          cgraph_expand_function (node);
          break;
 
@@ -434,6 +434,7 @@ cgraph_process_new_functions (void)
          gcc_unreachable ();
          break;
        }
+      cgraph_call_function_insertion_hooks (node);
     }
   return output;
 }
@@ -451,12 +452,12 @@ cgraph_process_new_functions (void)
 static void
 cgraph_reset_node (struct cgraph_node *node)
 {
-  /* If node->output is set, then we have already begun whole-unit analysis.
+  /* If node->process is set, then 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);
+  gcc_assert (!node->process);
 
   /* Reset our data structures so we can analyze the function again.  */
   memset (&node->local, 0, sizeof (node->local));
@@ -556,7 +557,7 @@ verify_cgraph_node (struct cgraph_node *node)
   struct function *this_cfun = DECL_STRUCT_FUNCTION (node->decl);
   struct function *saved_cfun = cfun;
   basic_block this_block;
-  block_stmt_iterator bsi;
+  gimple_stmt_iterator gsi;
   bool error_found = false;
 
   if (errorcount || sorrycount)
@@ -638,7 +639,7 @@ verify_cgraph_node (struct cgraph_node *node)
     }
 
   if (node->analyzed
-      && DECL_SAVED_TREE (node->decl) && !TREE_ASM_WRITTEN (node->decl)
+      && !TREE_ASM_WRITTEN (node->decl)
       && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to))
     {
       if (this_cfun->cfg)
@@ -649,12 +650,13 @@ verify_cgraph_node (struct cgraph_node *node)
          /* Reach the trees by walking over the CFG, and note the
             enclosing basic-blocks in the call edges.  */
          FOR_EACH_BB_FN (this_block, this_cfun)
-           for (bsi = bsi_start (this_block); !bsi_end_p (bsi); bsi_next (&bsi))
+           for (gsi = gsi_start_bb (this_block);
+                 !gsi_end_p (gsi);
+                 gsi_next (&gsi))
              {
-               tree stmt = bsi_stmt (bsi);
-               tree call = get_call_expr_in (stmt);
+               gimple stmt = gsi_stmt (gsi);
                tree decl;
-               if (call && (decl = get_callee_fndecl (call)))
+               if (is_gimple_call (stmt) && (decl = gimple_call_fndecl (stmt)))
                  {
                    struct cgraph_edge *e = cgraph_edge (node, stmt);
                    if (e)
@@ -662,7 +664,7 @@ verify_cgraph_node (struct cgraph_node *node)
                        if (e->aux)
                          {
                            error ("shared call_stmt:");
-                           debug_generic_stmt (stmt);
+                           debug_gimple_stmt (stmt);
                            error_found = true;
                          }
                        if (e->callee->decl != cgraph_node (decl)->decl
@@ -678,7 +680,7 @@ verify_cgraph_node (struct cgraph_node *node)
                    else
                      {
                        error ("missing callgraph edge for call stmt:");
-                       debug_generic_stmt (stmt);
+                       debug_gimple_stmt (stmt);
                        error_found = true;
                      }
                  }
@@ -696,7 +698,7 @@ verify_cgraph_node (struct cgraph_node *node)
              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);
+             debug_gimple_stmt (e->call_stmt);
              error_found = true;
            }
          e->aux = 0;
@@ -857,7 +859,7 @@ cgraph_analyze_functions (void)
     {
       fprintf (cgraph_dump_file, "Initial entry points:");
       for (node = cgraph_nodes; node != first_analyzed; node = node->next)
-       if (node->needed && DECL_SAVED_TREE (node->decl))
+       if (node->needed)
          fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
       fprintf (cgraph_dump_file, "\n");
     }
@@ -879,14 +881,14 @@ 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_SAVED_TREE (decl))
+      if (!DECL_STRUCT_FUNCTION (decl))
        {
          cgraph_reset_node (node);
          continue;
        }
 
       gcc_assert (!node->analyzed && node->reachable);
-      gcc_assert (DECL_SAVED_TREE (decl));
+      gcc_assert (gimple_body (decl));
 
       cgraph_analyze_function (node);
 
@@ -894,6 +896,15 @@ cgraph_analyze_functions (void)
        if (!edge->callee->reachable)
          cgraph_mark_reachable_node (edge->callee);
 
+      /* 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.  */
+      if (DECL_ABSTRACT_ORIGIN (decl))
+       {
+         struct cgraph_node *origin_node = cgraph_node (DECL_ABSTRACT_ORIGIN (decl));
+         origin_node->abstract_and_needed = true;
+       }
+
       /* We finalize local static variables during constructing callgraph
          edges.  Process their attributes too.  */
       process_function_and_variable_attributes (first_processed,
@@ -909,7 +920,7 @@ cgraph_analyze_functions (void)
     {
       fprintf (cgraph_dump_file, "Unit entry points:");
       for (node = cgraph_nodes; node != first_analyzed; node = node->next)
-       if (node->needed && DECL_SAVED_TREE (node->decl))
+       if (node->needed)
          fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
       fprintf (cgraph_dump_file, "\n\nInitial ");
       dump_cgraph (cgraph_dump_file);
@@ -923,10 +934,10 @@ cgraph_analyze_functions (void)
       tree decl = node->decl;
       next = node->next;
 
-      if (node->local.finalized && !DECL_SAVED_TREE (decl))
+      if (node->local.finalized && !gimple_has_body_p (decl))
        cgraph_reset_node (node);
 
-      if (!node->reachable && DECL_SAVED_TREE (decl))
+      if (!node->reachable && gimple_has_body_p (decl))
        {
          if (cgraph_dump_file)
            fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
@@ -935,7 +946,7 @@ cgraph_analyze_functions (void)
        }
       else
        node->next_needed = NULL;
-      gcc_assert (!node->local.finalized || DECL_SAVED_TREE (decl));
+      gcc_assert (!node->local.finalized || gimple_has_body_p (decl));
       gcc_assert (node->analyzed == node->local.finalized);
     }
   if (cgraph_dump_file)
@@ -967,6 +978,8 @@ cgraph_finalize_compilation_unit (void)
   cgraph_analyze_functions ();
   timevar_pop (TV_CGRAPH);
 }
+
+
 /* Figure out what functions we want to assemble.  */
 
 static void
@@ -979,7 +992,7 @@ cgraph_mark_functions_to_output (void)
       tree decl = node->decl;
       struct cgraph_edge *e;
 
-      gcc_assert (!node->output);
+      gcc_assert (!node->process);
 
       for (e = node->callers; e; e = e->next_caller)
        if (e->inline_failed)
@@ -988,25 +1001,27 @@ cgraph_mark_functions_to_output (void)
       /* We need to output all local functions that are used and not
         always inlined, as well as those that are reachable from
         outside the current compilation unit.  */
-      if (DECL_SAVED_TREE (decl)
+      if (node->analyzed
          && !node->global.inlined_to
          && (node->needed
              || (e && node->reachable))
          && !TREE_ASM_WRITTEN (decl)
          && !DECL_EXTERNAL (decl))
-       node->output = 1;
+       node->process = 1;
       else
        {
          /* We should've reclaimed all functions that are not needed.  */
 #ifdef ENABLE_CHECKING
-         if (!node->global.inlined_to && DECL_SAVED_TREE (decl)
+         if (!node->global.inlined_to
+             && gimple_has_body_p (decl)
              && !DECL_EXTERNAL (decl))
            {
              dump_cgraph_node (stderr, node);
              internal_error ("failed to reclaim unneeded function");
            }
 #endif
-         gcc_assert (node->global.inlined_to || !DECL_SAVED_TREE (decl)
+         gcc_assert (node->global.inlined_to
+                     || !gimple_has_body_p (decl)
                      || DECL_EXTERNAL (decl));
 
        }
@@ -1025,6 +1040,7 @@ cgraph_expand_function (struct cgraph_node *node)
   gcc_assert (!node->global.inlined_to);
 
   announce_function (decl);
+  node->process = 0;
 
   gcc_assert (node->lowered);
 
@@ -1034,17 +1050,13 @@ cgraph_expand_function (struct cgraph_node *node)
   tree_rest_of_compilation (decl);
 
   /* Make sure that BE didn't give up on compiling.  */
-  /* ??? Can happen with nested function of extern inline.  */
   gcc_assert (TREE_ASM_WRITTEN (decl));
-
   current_function_decl = NULL;
-  if (!cgraph_preserve_function_body_p (decl))
-    {
-      cgraph_release_function_body (node);
-      /* Eliminate all call edges.  This is important so the call_expr no longer
-        points to the dead function body.  */
-      cgraph_node_remove_callees (node);
-    }
+  gcc_assert (!cgraph_preserve_function_body_p (decl));
+  cgraph_release_function_body (node);
+  /* Eliminate all call edges.  This is important so the GIMPLE_CALL no longer
+     points to the dead function body.  */
+  cgraph_node_remove_callees (node);
 
   cgraph_function_flags_ready = true;
 }
@@ -1052,7 +1064,7 @@ cgraph_expand_function (struct cgraph_node *node)
 /* Return true when CALLER_DECL should be inlined into CALLEE_DECL.  */
 
 bool
-cgraph_inline_p (struct cgraph_edge *e, const char **reason)
+cgraph_inline_p (struct cgraph_edge *e, cgraph_inline_failed_t *reason)
 {
   *reason = e->inline_failed;
   return !e->inline_failed;
@@ -1084,16 +1096,16 @@ cgraph_expand_all_functions (void)
   /* Garbage collector may remove inline clones we eliminate during
      optimization.  So we must be sure to not reference them.  */
   for (i = 0; i < order_pos; i++)
-    if (order[i]->output)
+    if (order[i]->process)
       order[new_order_pos++] = order[i];
 
   for (i = new_order_pos - 1; i >= 0; i--)
     {
       node = order[i];
-      if (node->output)
+      if (node->process)
        {
          gcc_assert (node->reachable);
-         node->output = 0;
+         node->process = 0;
          cgraph_expand_function (node);
        }
     }
@@ -1142,7 +1154,7 @@ cgraph_output_in_order (void)
 
   for (pf = cgraph_nodes; pf; pf = pf->next)
     {
-      if (pf->output)
+      if (pf->process)
        {
          i = pf->order;
          gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
@@ -1182,7 +1194,7 @@ cgraph_output_in_order (void)
       switch (nodes[i].kind)
        {
        case ORDER_FUNCTION:
-         nodes[i].u.f->output = 0;
+         nodes[i].u.f->process = 0;
          cgraph_expand_function (nodes[i].u.f);
          break;
 
@@ -1211,10 +1223,8 @@ bool
 cgraph_preserve_function_body_p (tree decl)
 {
   struct cgraph_node *node;
-  if (!cgraph_global_info_ready)
-    return (flag_really_no_inline
-           ? DECL_DISREGARD_INLINE_LIMITS (decl)
-           : DECL_INLINE (decl));
+
+  gcc_assert (cgraph_global_info_ready);
   /* Look if there is any clone around.  */
   for (node = cgraph_node (decl); node; node = node->next_clone)
     if (node->global.inlined_to)
@@ -1227,9 +1237,18 @@ ipa_passes (void)
 {
   set_cfun (NULL);
   current_function_decl = NULL;
-  tree_register_cfg_hooks ();
+  gimple_register_cfg_hooks ();
   bitmap_obstack_initialize (NULL);
   execute_ipa_pass_list (all_ipa_passes);
+
+  /* Generate coverage variables and constructors.  */
+  coverage_finish ();
+
+  /* Process new functions added.  */
+  set_cfun (NULL);
+  current_function_decl = NULL;
+  cgraph_process_new_functions ();
+
   bitmap_obstack_release (NULL);
 }
 
@@ -1306,7 +1325,6 @@ cgraph_optimize (void)
 
       varpool_assemble_pending_decls ();
     }
-  varpool_output_debug_info ();
   cgraph_process_new_functions ();
   cgraph_state = CGRAPH_STATE_FINISHED;
 
@@ -1327,7 +1345,7 @@ cgraph_optimize (void)
       for (node = cgraph_nodes; node; node = node->next)
        if (node->analyzed
            && (node->global.inlined_to
-               || DECL_SAVED_TREE (node->decl)))
+               || gimple_has_body_p (node->decl)))
          {
            error_found = true;
            dump_cgraph_node (stderr, node);
@@ -1362,6 +1380,7 @@ cgraph_build_static_cdtor (char which, tree body, int priority)
   resdecl = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
   DECL_ARTIFICIAL (resdecl) = 1;
   DECL_RESULT (decl) = resdecl;
+  DECL_CONTEXT (resdecl) = decl;
 
   allocate_struct_function (decl, false);
 
@@ -1416,10 +1435,17 @@ update_call_expr (struct cgraph_node *new_version)
   struct cgraph_edge *e;
 
   gcc_assert (new_version);
+
+  /* Update the call expr on the edges to call the 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 (CALL_EXPR_FN (get_call_expr_in (e->call_stmt)), 0) = new_version->decl;
+    {
+      struct function *inner_function = DECL_STRUCT_FUNCTION (e->caller->decl);
+      gimple_call_set_fndecl (e->call_stmt, new_version->decl);
+      /* Update EH information too, just in case.  */
+      if (!stmt_could_throw_p (e->call_stmt)
+          && lookup_stmt_eh_region_fn (inner_function, e->call_stmt))
+        remove_stmt_from_eh_region_fn (inner_function, e->call_stmt);
+    }
 }
 
 
@@ -1494,12 +1520,15 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_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.  */
+    It returns the new version's cgraph node. 
+    ARGS_TO_SKIP lists arguments to be omitted from functions
+    */
 
 struct cgraph_node *
 cgraph_function_versioning (struct cgraph_node *old_version_node,
                            VEC(cgraph_edge_p,heap) *redirect_callers,
-                           varray_type tree_map)
+                           varray_type tree_map,
+                           bitmap args_to_skip)
 {
   tree old_decl = old_version_node->decl;
   struct cgraph_node *new_version_node = NULL;
@@ -1510,7 +1539,10 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
 
   /* Make a new FUNCTION_DECL tree node for the
      new version. */
-  new_decl = copy_node (old_decl);
+  if (!args_to_skip)
+    new_decl = copy_node (old_decl);
+  else
+    new_decl = build_function_decl_skip_args (old_decl, args_to_skip);
 
   /* Create the new version's call-graph node.
      and update the edges of the new node. */
@@ -1519,21 +1551,27 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
                                     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);
+  tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip);
 
   /* Update the new version's properties.
-     Make The new version visible only within this translation unit.
+     Make The new version visible only within this translation unit.  Make sure
+     that is not weak also.
      ??? 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;
+  DECL_WEAK (new_version_node->decl) = 0;
+  DECL_VIRTUAL_P (new_version_node->decl) = 0;
   new_version_node->local.externally_visible = 0;
   new_version_node->local.local = 1;
   new_version_node->lowered = true;
+
+  /* Update the call_expr on the edges to call the new version node. */
+  update_call_expr (new_version_node);
+  
+  cgraph_call_function_insertion_hooks (new_version_node);
   return new_version_node;
 }
 
@@ -1557,7 +1595,7 @@ save_inline_function_body (struct cgraph_node *node)
   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);
+  tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL);
 
   DECL_EXTERNAL (first_clone->decl) = 0;
   DECL_ONE_ONLY (first_clone->decl) = 0;