OSDN Git Service

2010-07-05 Richard Guenther <rguenther@suse.de>
[pf3gnuchains/gcc-fork.git] / gcc / ipa-inline.c
index 12757f7..08f2ac3 100644 (file)
@@ -128,6 +128,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "cgraph.h"
 #include "diagnostic.h"
+#include "gimple-pretty-print.h"
 #include "timevar.h"
 #include "params.h"
 #include "fibheap.h"
@@ -200,11 +201,12 @@ cgraph_estimate_time_after_inlining (int frequency, struct cgraph_node *to,
 
 /* Estimate self time of the function after inlining WHAT into TO.  */
 
-static int
+static inline int
 cgraph_estimate_size_after_inlining (int times, struct cgraph_node *to,
                                     struct cgraph_node *what)
 {
-  int size = (what->global.size - inline_summary (what)->size_inlining_benefit) * times + to->global.size;
+  int size = ((what->global.size - inline_summary (what)->size_inlining_benefit)
+             * times + to->global.size);
   gcc_assert (size >= 0);
   return size;
 }
@@ -510,7 +512,7 @@ cgraph_default_inline_p (struct cgraph_node *n, cgraph_inline_failed_t *reason)
    We call recursive inlining all cases where same function appears more than
    once in the single recursion nest path in the inline graph.  */
 
-static bool
+static inline bool
 cgraph_recursive_inlining_p (struct cgraph_node *to,
                             struct cgraph_node *what,
                             cgraph_inline_failed_t *reason)
@@ -659,6 +661,30 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
     return badness;
 }
 
+/* Recompute badness of EDGE and update its key in HEAP if needed.  */
+static void
+update_edge_key (fibheap_t heap, struct cgraph_edge *edge)
+{
+  int badness = cgraph_edge_badness (edge, false);
+  if (edge->aux)
+    {
+      fibnode_t n = (fibnode_t) edge->aux;
+      gcc_checking_assert (n->data == edge);
+
+      /* fibheap_replace_key only decrease the keys.
+        When we increase the key we do not update heap
+        and instead re-insert the element once it becomes
+        a minium of heap.  */
+      if (badness < n->key)
+       {
+         fibheap_replace_key (heap, n, badness);
+         gcc_checking_assert (n->key == badness);
+       }
+    }
+  else
+    edge->aux = fibheap_insert (heap, badness, edge);
+}
+
 /* Recompute heap nodes for each of caller edge.  */
 
 static void
@@ -676,12 +702,16 @@ update_caller_keys (fibheap_t heap, struct cgraph_node *node,
   bitmap_set_bit (updated_nodes, node->uid);
   node->global.estimated_growth = INT_MIN;
 
-  if (!node->local.inlinable)
+  /* See if there is something to do.  */
+  for (edge = node->callers; edge; edge = edge->next_caller)
+    if (edge->inline_failed)
+      break;
+  if (!edge)
     return;
   /* Prune out edges we won't inline into anymore.  */
   if (!cgraph_default_inline_p (node, &failed_reason))
     {
-      for (edge = node->callers; edge; edge = edge->next_caller)
+      for (; edge; edge = edge->next_caller)
        if (edge->aux)
          {
            fibheap_delete_node (heap, (fibnode_t) edge->aux);
@@ -692,44 +722,91 @@ update_caller_keys (fibheap_t heap, struct cgraph_node *node,
       return;
     }
 
-  for (edge = node->callers; edge; edge = edge->next_caller)
+  for (; edge; edge = edge->next_caller)
     if (edge->inline_failed)
+      update_edge_key (heap, edge);
+}
+
+/* Recompute heap nodes for each uninlined call.
+   This is used when we know that edge badnesses are going only to increase
+   (we introduced new call site) and thus all we need is to insert newly
+   created edges into heap.  */
+
+static void
+update_callee_keys (fibheap_t heap, struct cgraph_node *node,
+                   bitmap updated_nodes)
+{
+  struct cgraph_edge *e = node->callees;
+  node->global.estimated_growth = INT_MIN;
+
+  if (!e)
+    return;
+  while (true)
+    if (!e->inline_failed && e->callee->callees)
+      e = e->callee->callees;
+    else
       {
-       int badness = cgraph_edge_badness (edge, false);
-       if (edge->aux)
+       if (e->inline_failed
+           && e->callee->local.inlinable
+           && !bitmap_bit_p (updated_nodes, e->callee->uid))
          {
-           fibnode_t n = (fibnode_t) edge->aux;
-           gcc_assert (n->data == edge);
-           if (n->key == badness)
-             continue;
-
-           /* fibheap_replace_key only increase the keys.  */
-           if (badness < n->key)
+           node->global.estimated_growth = INT_MIN;
+           /* If function becomes uninlinable, we need to remove it from the heap.  */
+           if (!cgraph_default_inline_p (e->callee, &e->inline_failed))
+             update_caller_keys (heap, e->callee, updated_nodes);
+           else
+           /* Otherwise update just edge E.  */
+             update_edge_key (heap, e);
+         }
+       if (e->next_callee)
+         e = e->next_callee;
+       else
+         {
+           do
              {
-               fibheap_replace_key (heap, n, badness);
-               gcc_assert (n->key == badness);
-               continue;
+               if (e->caller == node)
+                 return;
+               e = e->caller->callers;
              }
-           fibheap_delete_node (heap, (fibnode_t) edge->aux);
+           while (!e->next_callee);
+           e = e->next_callee;
          }
-       edge->aux = fibheap_insert (heap, badness, edge);
       }
 }
 
-/* Recompute heap nodes for each of caller edges of each of callees.  */
+/* Recompute heap nodes for each of caller edges of each of callees.
+   Walk recursively into all inline clones.  */
 
 static void
-update_callee_keys (fibheap_t heap, struct cgraph_node *node,
-                   bitmap updated_nodes)
+update_all_callee_keys (fibheap_t heap, struct cgraph_node *node,
+                       bitmap updated_nodes)
 {
-  struct cgraph_edge *e;
+  struct cgraph_edge *e = node->callees;
   node->global.estimated_growth = INT_MIN;
 
-  for (e = node->callees; e; e = e->next_callee)
-    if (e->inline_failed)
-      update_caller_keys (heap, e->callee, updated_nodes);
-    else if (!e->inline_failed)
-      update_callee_keys (heap, e->callee, updated_nodes);
+  if (!e)
+    return;
+  while (true)
+    if (!e->inline_failed && e->callee->callees)
+      e = e->callee->callees;
+    else
+      {
+       if (e->inline_failed)
+         update_caller_keys (heap, e->callee, updated_nodes);
+       if (e->next_callee)
+         e = e->next_callee;
+       else
+         {
+           do
+             {
+               if (e->caller == node)
+                 return;
+               e = e->caller->callers;
+             }
+           while (!e->next_callee);
+           e = e->next_callee;
+         }
+      }
 }
 
 /* Enqueue all recursive calls from NODE into priority queue depending on
@@ -938,7 +1015,9 @@ add_new_edges_to_heap (fibheap_t heap, VEC (cgraph_edge_p, heap) *new_edges)
       struct cgraph_edge *edge = VEC_pop (cgraph_edge_p, new_edges);
 
       gcc_assert (!edge->aux);
-      edge->aux = fibheap_insert (heap, cgraph_edge_badness (edge, false), edge);
+      if (edge->callee->local.inlinable
+         && cgraph_default_inline_p (edge->callee, &edge->inline_failed))
+        edge->aux = fibheap_insert (heap, cgraph_edge_badness (edge, false), edge);
     }
 }
 
@@ -1000,6 +1079,7 @@ cgraph_decide_inlining_of_small_functions (void)
       int old_size = overall_size;
       struct cgraph_node *where, *callee;
       int badness = fibheap_min_key (heap);
+      int current_badness;
       int growth;
       cgraph_inline_failed_t not_good = CIF_OK;
 
@@ -1008,9 +1088,18 @@ cgraph_decide_inlining_of_small_functions (void)
       edge->aux = NULL;
       if (!edge->inline_failed)
        continue;
-#ifdef ENABLE_CHECKING
-      gcc_assert (cgraph_edge_badness (edge, false) == badness);
-#endif
+
+      /* When updating the edge costs, we only decrease badness in the keys.
+        When the badness increase, we keep the heap as it is and re-insert
+        key now.  */
+      current_badness = cgraph_edge_badness (edge, false);
+      gcc_assert (current_badness >= badness);
+      if (current_badness != badness)
+       {
+         edge->aux = fibheap_insert (heap, current_badness, edge);
+         continue;
+       }
+      
       callee = edge->callee;
 
       growth = (cgraph_estimate_size_after_inlining (1, edge->caller, edge->callee)
@@ -1124,7 +1213,7 @@ cgraph_decide_inlining_of_small_functions (void)
            continue;
          if (flag_indirect_inlining)
            add_new_edges_to_heap (heap, new_indirect_edges);
-          update_callee_keys (heap, where, updated_nodes);
+          update_all_callee_keys (heap, where, updated_nodes);
        }
       else
        {
@@ -1140,11 +1229,18 @@ cgraph_decide_inlining_of_small_functions (void)
              continue;
            }
          callee = edge->callee;
+         gcc_checking_assert (!callee->global.inlined_to);
          cgraph_mark_inline_edge (edge, true, &new_indirect_edges);
          if (flag_indirect_inlining)
            add_new_edges_to_heap (heap, new_indirect_edges);
 
-         update_callee_keys (heap, callee, updated_nodes);
+         /* We inlined last offline copy to the body.  This might lead
+            to callees of function having fewer call sites and thus they
+            may need updating.  */
+         if (callee->global.inlined_to)
+           update_all_callee_keys (heap, callee, updated_nodes);
+         else
+           update_callee_keys (heap, edge->callee, updated_nodes);
        }
       where = edge->caller;
       if (where->global.inlined_to)
@@ -1193,7 +1289,7 @@ cgraph_decide_inlining_of_small_functions (void)
       if (!edge->inline_failed)
        continue;
 #ifdef ENABLE_CHECKING
-      gcc_assert (cgraph_edge_badness (edge, false) == badness);
+      gcc_assert (cgraph_edge_badness (edge, false) >= badness);
 #endif
       if (dump_file)
        {
@@ -1661,7 +1757,7 @@ cgraph_early_inlining (void)
   unsigned int todo = 0;
   int iterations = 0;
 
-  if (sorrycount || errorcount)
+  if (seen_error ())
     return 0;
 
   if (!optimize
@@ -1790,10 +1886,12 @@ likely_eliminated_by_inlining_p (gimple stmt)
            bool rhs_free = false;
            bool lhs_free = false;
 
-           while (handled_component_p (inner_lhs) || TREE_CODE (inner_lhs) == INDIRECT_REF)
+           while (handled_component_p (inner_lhs)
+                  || TREE_CODE (inner_lhs) == MEM_REF)
              inner_lhs = TREE_OPERAND (inner_lhs, 0);
            while (handled_component_p (inner_rhs)
-                  || TREE_CODE (inner_rhs) == ADDR_EXPR || TREE_CODE (inner_rhs) == INDIRECT_REF)
+                  || TREE_CODE (inner_rhs) == ADDR_EXPR
+                  || TREE_CODE (inner_rhs) == MEM_REF)
              inner_rhs = TREE_OPERAND (inner_rhs, 0);
 
 
@@ -1813,7 +1911,8 @@ likely_eliminated_by_inlining_p (gimple stmt)
                || (TREE_CODE (inner_lhs) == SSA_NAME
                    && TREE_CODE (SSA_NAME_VAR (inner_lhs)) == RESULT_DECL))
              lhs_free = true;
-           if (lhs_free && (is_gimple_reg (rhs) || is_gimple_min_invariant (rhs)))
+           if (lhs_free
+               && (is_gimple_reg (rhs) || is_gimple_min_invariant (rhs)))
              rhs_free = true;
            if (lhs_free && rhs_free)
              return true;
@@ -1971,12 +2070,8 @@ struct gimple_opt_pass pass_inline_parameters =
 static void
 inline_indirect_intraprocedural_analysis (struct cgraph_node *node)
 {
-  ipa_initialize_node_params (node);
-  ipa_detect_param_modifications (node);
-  ipa_analyze_params_uses (node);
-  ipa_compute_jump_functions (node);
-
-  if (dump_file)
+  ipa_analyze_node (node);
+  if (dump_file && (dump_flags & TDF_DETAILS))
     {
       ipa_print_node_params (dump_file, node);
       ipa_print_node_jump_functions (dump_file, node);
@@ -2034,6 +2129,7 @@ inline_transform (struct cgraph_node *node)
 {
   unsigned int todo = 0;
   struct cgraph_edge *e;
+  bool inline_p = false;
 
   /* FIXME: Currently the passmanager is adding inline transform more than once to some
      clones.  This needs revisiting after WPA cleanups.  */
@@ -2046,10 +2142,13 @@ inline_transform (struct cgraph_node *node)
     save_inline_function_body (node);
 
   for (e = node->callees; e; e = e->next_callee)
-    if (!e->inline_failed || warn_inline)
-      break;
+    {
+      cgraph_redirect_edge_call_stmt_to_callee (e);
+      if (!e->inline_failed || warn_inline)
+        inline_p = true;
+    }
 
-  if (e)
+  if (inline_p)
     {
       timevar_push (TV_INTEGRATION);
       todo = optimize_inline_calls (current_function_decl);