OSDN Git Service

Revert accidental commit.
[pf3gnuchains/gcc-fork.git] / gcc / ipa-inline.c
index 601695a..e8c78f9 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;
 }
@@ -248,7 +250,17 @@ cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
       /* We may eliminate the need for out-of-line copy to be output.
         In that case just go ahead and re-use it.  */
       if (!e->callee->callers->next_caller
+         /* FIXME: When address is taken of DECL_EXTERNAL function we still can remove its
+            offline copy, but we would need to keep unanalyzed node in the callgraph so
+            references can point to it.  */
+         && !e->callee->address_taken
          && cgraph_can_remove_if_no_direct_calls_p (e->callee)
+         /* Inlining might enable more devirtualizing, so we want to remove
+            those only after all devirtualizable virtual calls are processed.
+            Lacking may edges in callgraph we just preserve them post
+            inlining.  */
+         && (!DECL_VIRTUAL_P (e->callee->decl)
+             || (!DECL_COMDAT (e->callee->decl) && !DECL_EXTERNAL (e->callee->decl)))
          /* Don't reuse if more than one function shares a comdat group.
             If the other function(s) are needed, we need to emit even
             this function out of line.  */
@@ -256,7 +268,7 @@ cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
          && !cgraph_new_nodes)
        {
          gcc_assert (!e->callee->global.inlined_to);
-         if (e->callee->analyzed)
+         if (e->callee->analyzed && !DECL_EXTERNAL (e->callee->decl))
            {
              overall_size -= e->callee->global.size;
              nfunctions_inlined++;
@@ -268,7 +280,8 @@ cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
       else
        {
          struct cgraph_node *n;
-         n = cgraph_clone_node (e->callee, e->count, e->frequency, e->loop_nest,
+         n = cgraph_clone_node (e->callee, e->callee->decl,
+                                e->count, e->frequency, e->loop_nest,
                                 update_original, NULL);
          cgraph_redirect_edge_callee (e, n);
        }
@@ -285,6 +298,7 @@ cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
       + inline_summary (e->callee)->estimated_self_stack_size;
   if (e->callee->global.inlined_to->global.estimated_stack_size < peak)
     e->callee->global.inlined_to->global.estimated_stack_size = peak;
+  cgraph_propagate_frequency (e->callee);
 
   /* Recursively clone all bodies.  */
   for (e = e->callee->callees; e; e = e->next_callee)
@@ -309,10 +323,7 @@ cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
 
   gcc_assert (e->inline_failed);
   e->inline_failed = CIF_OK;
-
-  if (!e->callee->global.inlined)
-    DECL_POSSIBLY_INLINED (e->callee->decl) = true;
-  e->callee->global.inlined = true;
+  DECL_POSSIBLY_INLINED (e->callee->decl) = true;
 
   cgraph_clone_inlined_nodes (e, true, update_original);
 
@@ -333,7 +344,9 @@ cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
     overall_size += new_size - old_size;
   ncalls_inlined++;
 
-  if (flag_indirect_inlining)
+  /* FIXME: We should remove the optimize check after we ensure we never run
+     IPA passes when not optimizng.  */
+  if (flag_indirect_inlining && optimize)
     return ipa_propagate_indirect_call_infos (curr, new_edges);
   else
     return false;
@@ -388,9 +401,15 @@ cgraph_estimate_growth (struct cgraph_node *node)
      we decide to not inline for different reasons, but it is not big deal
      as in that case we will keep the body around, but we will also avoid
      some inlining.  */
-  if (cgraph_only_called_directly_p (node)
+  if (cgraph_will_be_removed_from_program_if_no_direct_calls (node)
       && !DECL_EXTERNAL (node->decl) && !self_recursive)
     growth -= node->global.size;
+  /* COMDAT functions are very often not shared across multiple units since they
+     come from various template instantiations.  Take this into account.  */
+  else  if (DECL_COMDAT (node->decl) && !self_recursive
+           && cgraph_can_remove_if_no_direct_calls_p (node))
+    growth -= (node->global.size
+              * (100 - PARAM_VALUE (PARAM_COMDAT_SHARING_PROBABILITY)) + 50) / 100;
 
   node->global.estimated_growth = growth;
   return growth;
@@ -477,13 +496,19 @@ cgraph_default_inline_p (struct cgraph_node *n, cgraph_inline_failed_t *reason)
        *reason = CIF_FUNCTION_NOT_INLINE_CANDIDATE;
       return false;
     }
-
   if (!n->analyzed)
     {
       if (reason)
        *reason = CIF_BODY_NOT_AVAILABLE;
       return false;
     }
+  if (cgraph_function_body_availability (n) <= AVAIL_OVERWRITABLE)
+    {
+      if (reason)
+       *reason = CIF_OVERWRITABLE;
+      return false;
+    }
+
 
   if (DECL_DECLARED_INLINE_P (decl))
     {
@@ -511,7 +536,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)
@@ -543,6 +568,9 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
     (cgraph_estimate_size_after_inlining (1, edge->caller, edge->callee)
      - edge->caller->global.size);
 
+  if (edge->callee->local.disregard_inline_limits)
+    return INT_MIN;
+
   if (dump)
     {
       fprintf (dump_file, "    Badness calculcation for %s -> %s\n",
@@ -657,6 +685,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
@@ -666,20 +718,24 @@ update_caller_keys (fibheap_t heap, struct cgraph_node *node,
   struct cgraph_edge *edge;
   cgraph_inline_failed_t failed_reason;
 
-  if (!node->local.inlinable || node->local.disregard_inline_limits
+  if (!node->local.inlinable
+      || cgraph_function_body_availability (node) <= AVAIL_OVERWRITABLE
       || node->global.inlined_to)
     return;
-  if (bitmap_bit_p (updated_nodes, node->uid))
+  if (!bitmap_set_bit (updated_nodes, node->uid))
     return;
-  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);
@@ -690,44 +746,92 @@ 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
+           && cgraph_function_body_availability (e->callee) >= AVAIL_AVAILABLE
+           && !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
@@ -807,7 +911,8 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node,
             cgraph_node_name (node));
 
   /* We need original clone to copy around.  */
-  master_clone = cgraph_clone_node (node, node->count, CGRAPH_FREQ_BASE, 1,
+  master_clone = cgraph_clone_node (node, node->decl,
+                                   node->count, CGRAPH_FREQ_BASE, 1,
                                    false, NULL);
   master_clone->needed = true;
   for (e = master_clone->callees; e; e = e->next_callee)
@@ -935,7 +1040,10 @@ 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
+         && edge->inline_failed
+         && cgraph_default_inline_p (edge->callee, &edge->inline_failed))
+        edge->aux = fibheap_insert (heap, cgraph_edge_badness (edge, false), edge);
     }
 }
 
@@ -997,6 +1105,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;
 
@@ -1005,9 +1114,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)
@@ -1024,8 +1142,9 @@ cgraph_decide_inlining_of_small_functions (void)
                   " Estimated growth after inlined into all callees is %+i insns.\n"
                   " Estimated badness is %i, frequency %.2f.\n",
                   cgraph_node_name (edge->caller),
-                  gimple_filename ((const_gimple) edge->call_stmt),
-                  gimple_lineno ((const_gimple) edge->call_stmt),
+                  flag_wpa ? "unknown"
+                  : gimple_filename ((const_gimple) edge->call_stmt),
+                  flag_wpa ? -1 : gimple_lineno ((const_gimple) edge->call_stmt),
                   cgraph_estimate_growth (edge->callee),
                   badness,
                   edge->frequency / (double)CGRAPH_FREQ_BASE);
@@ -1069,12 +1188,14 @@ cgraph_decide_inlining_of_small_functions (void)
            }
        }
 
-      if (!cgraph_maybe_hot_edge_p (edge))
+      if (edge->callee->local.disregard_inline_limits)
+       ;
+      else if (!cgraph_maybe_hot_edge_p (edge))
        not_good = CIF_UNLIKELY_CALL;
-      if (!flag_inline_functions
+      else if (!flag_inline_functions
          && !DECL_DECLARED_INLINE_P (edge->callee->decl))
        not_good = CIF_NOT_DECLARED_INLINED;
-      if (optimize_function_for_size_p (DECL_STRUCT_FUNCTION(edge->caller->decl)))
+      else if (optimize_function_for_size_p (DECL_STRUCT_FUNCTION(edge->caller->decl)))
        not_good = CIF_OPTIMIZING_FOR_SIZE;
       if (not_good && growth > 0 && cgraph_estimate_growth (edge->callee) > 0)
        {
@@ -1118,7 +1239,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
        {
@@ -1134,11 +1255,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)
@@ -1187,7 +1315,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)
        {
@@ -1200,8 +1328,9 @@ cgraph_decide_inlining_of_small_functions (void)
                   " Estimated growth after inlined into all callees is %+i insns.\n"
                   " Estimated badness is %i, frequency %.2f.\n",
                   cgraph_node_name (edge->caller),
-                  gimple_filename ((const_gimple) edge->call_stmt),
-                  gimple_lineno ((const_gimple) edge->call_stmt),
+                  flag_wpa ? "unknown"
+                  : gimple_filename ((const_gimple) edge->call_stmt),
+                  flag_wpa ? -1 : gimple_lineno ((const_gimple) edge->call_stmt),
                   cgraph_estimate_growth (edge->callee),
                   badness,
                   edge->frequency / (double)CGRAPH_FREQ_BASE);
@@ -1284,6 +1413,14 @@ cgraph_flatten (struct cgraph_node *node)
          continue;
        }
 
+      if (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
+         != gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
+       {
+         if (dump_file)
+           fprintf (dump_file, "Not inlining: SSA form does not match.\n");
+         continue;
+       }
+
       /* Inline the edge and flatten the inline clone.  Avoid
          recursing through the original node if the node was cloned.  */
       if (dump_file)
@@ -1319,6 +1456,8 @@ cgraph_decide_inlining (void)
   cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
   if (in_lto_p && flag_indirect_inlining)
     ipa_update_after_lto_read ();
+  if (flag_indirect_inlining)
+    ipa_create_all_structures_for_iinln ();
 
   max_count = 0;
   max_benefit = 0;
@@ -1328,7 +1467,8 @@ cgraph_decide_inlining (void)
        struct cgraph_edge *e;
 
        gcc_assert (inline_summary (node)->self_size == node->global.size);
-       initial_size += node->global.size;
+       if (!DECL_EXTERNAL (node->decl))
+         initial_size += node->global.size;
        for (e = node->callees; e; e = e->next_callee)
          if (max_count < e->count)
            max_count = e->count;
@@ -1391,14 +1531,15 @@ cgraph_decide_inlining (void)
 
          if (node->callers
              && !node->callers->next_caller
-             && cgraph_only_called_directly_p (node)
+             && !node->global.inlined_to
+             && cgraph_will_be_removed_from_program_if_no_direct_calls (node)
              && node->local.inlinable
+             && cgraph_function_body_availability (node) >= AVAIL_AVAILABLE
              && node->callers->inline_failed
              && node->callers->caller != node
              && node->callers->caller->global.inlined_to != node
              && !node->callers->call_stmt_cannot_inline_p
-             && !DECL_EXTERNAL (node->decl)
-             && !DECL_COMDAT (node->decl))
+             && !DECL_EXTERNAL (node->decl))
            {
              cgraph_inline_failed_t reason;
              old_size = overall_size;
@@ -1416,13 +1557,14 @@ cgraph_decide_inlining (void)
              if (cgraph_check_inline_limits (node->callers->caller, node,
                                              &reason, false))
                {
+                 struct cgraph_node *caller = node->callers->caller;
                  cgraph_mark_inline (node->callers);
                  if (dump_file)
                    fprintf (dump_file,
                             " Inlined into %s which now has %i size"
                             " for a net change of %+i size.\n",
-                            cgraph_node_name (node->callers->caller),
-                            node->callers->caller->global.size,
+                            cgraph_node_name (caller),
+                            caller->global.size,
                             overall_size - old_size);
                }
              else
@@ -1438,7 +1580,7 @@ cgraph_decide_inlining (void)
 
   /* Free ipa-prop structures if they are no longer needed.  */
   if (flag_indirect_inlining)
-    free_all_ipa_structures_after_iinln ();
+    ipa_free_all_structures_after_iinln ();
 
   if (dump_file)
     fprintf (dump_file,
@@ -1450,16 +1592,15 @@ cgraph_decide_inlining (void)
   return 0;
 }
 
-/* Return true when N is leaf function.  Accept cheap (pure&const) builtins
+/* Return true when N is leaf function.  Accept cheap builtins
    in leaf functions.  */
+
 static bool
 leaf_node_p (struct cgraph_node *n)
 {
   struct cgraph_edge *e;
   for (e = n->callees; e; e = e->next_callee)
-    if (!DECL_BUILT_IN (e->callee->decl)
-       || (!TREE_READONLY (e->callee->decl)
-           || DECL_PURE_P (e->callee->decl)))
+    if (!is_inexpensive_builtin (e->callee->decl))
       return false;
   return true;
 }
@@ -1651,7 +1792,7 @@ cgraph_early_inlining (void)
   unsigned int todo = 0;
   int iterations = 0;
 
-  if (sorrycount || errorcount)
+  if (seen_error ())
     return 0;
 
   if (!optimize
@@ -1667,6 +1808,17 @@ cgraph_early_inlining (void)
     }
   else
     {
+      if (lookup_attribute ("flatten",
+                           DECL_ATTRIBUTES (node->decl)) != NULL)
+       {
+         if (dump_file)
+           fprintf (dump_file,
+                    "Flattening %s\n", cgraph_node_name (node));
+         cgraph_flatten (node);
+         timevar_push (TV_INTEGRATION);
+         todo |= optimize_inline_calls (current_function_decl);
+         timevar_pop (TV_INTEGRATION);
+       }
       /* We iterate incremental inlining to get trivial cases of indirect
         inlining.  */
       while (iterations < PARAM_VALUE (PARAM_EARLY_INLINER_MAX_ITERATIONS)
@@ -1708,55 +1860,30 @@ struct gimple_opt_pass pass_early_inline =
  }
 };
 
-/* When inlining shall be performed.  */
-static bool
-cgraph_gate_ipa_early_inlining (void)
-{
-  return (flag_early_inlining
-         && !in_lto_p
-         && (flag_branch_probabilities || flag_test_coverage
-             || profile_arc_flag));
-}
-
-/* IPA pass wrapper for early inlining pass.  We need to run early inlining
-   before tree profiling so we have stand alone IPA pass for doing so.  */
-struct simple_ipa_opt_pass pass_ipa_early_inline =
-{
- {
-  SIMPLE_IPA_PASS,
-  "einline_ipa",                       /* name */
-  cgraph_gate_ipa_early_inlining,      /* gate */
-  NULL,                                        /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  TV_INLINE_HEURISTICS,                        /* tv_id */
-  0,                                   /* properties_required */
-  0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  TODO_dump_cgraph                     /* todo_flags_finish */
- }
-};
 
-/* See if statement might disappear after inlining.  We are not terribly
-   sophisficated, basically looking for simple abstraction penalty wrappers.  */
+/* See if statement might disappear after inlining.
+   0 - means not eliminated
+   1 - half of statements goes away
+   2 - for sure it is eliminated.
+   We are not terribly sophisficated, basically looking for simple abstraction
+   penalty wrappers.  */
 
-static bool
-likely_eliminated_by_inlining_p (gimple stmt)
+static int
+eliminated_by_inlining_prob (gimple stmt)
 {
   enum gimple_code code = gimple_code (stmt);
   switch (code)
     {
       case GIMPLE_RETURN:
-        return true;
+        return 2;
       case GIMPLE_ASSIGN:
        if (gimple_num_ops (stmt) != 2)
-         return false;
+         return 0;
 
        /* Casts of parameters, loads from parameters passed by reference
-          and stores to return value or parameters are probably free after
-          inlining.  */
+          and stores to return value or parameters are often free after
+          inlining dua to SRA and further combining.
+          Assume that half of statements goes away.  */
        if (gimple_assign_rhs_code (stmt) == CONVERT_EXPR
            || gimple_assign_rhs_code (stmt) == NOP_EXPR
            || gimple_assign_rhs_code (stmt) == VIEW_CONVERT_EXPR
@@ -1769,10 +1896,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);
 
 
@@ -1792,14 +1921,15 @@ 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;
+             return 1;
          }
-       return false;
+       return 0;
       default:
-       return false;
+       return 0;
     }
 }
 
@@ -1810,8 +1940,11 @@ estimate_function_body_sizes (struct cgraph_node *node)
 {
   gcov_type time = 0;
   gcov_type time_inlining_benefit = 0;
-  int size = 0;
-  int size_inlining_benefit = 0;
+  /* Estimate static overhead for function prologue/epilogue and alignment. */
+  int size = 2;
+  /* Benefits are scaled by probability of elimination that is in range
+     <0,2>.  */
+  int size_inlining_benefit = 2 * 2;
   basic_block bb;
   gimple_stmt_iterator bsi;
   struct function *my_function = DECL_STRUCT_FUNCTION (node->decl);
@@ -1819,14 +1952,6 @@ estimate_function_body_sizes (struct cgraph_node *node)
   int freq;
   tree funtype = TREE_TYPE (node->decl);
 
-  if (node->local.disregard_inline_limits)
-    {
-      inline_summary (node)->self_time = 0;
-      inline_summary (node)->self_size = 0;
-      inline_summary (node)->time_inlining_benefit = 0;
-      inline_summary (node)->size_inlining_benefit = 0;
-    }
-
   if (dump_file)
     fprintf (dump_file, "Analyzing function body size: %s\n",
             cgraph_node_name (node));
@@ -1840,6 +1965,7 @@ estimate_function_body_sizes (struct cgraph_node *node)
          gimple stmt = gsi_stmt (bsi);
          int this_size = estimate_num_insns (stmt, &eni_size_weights);
          int this_time = estimate_num_insns (stmt, &eni_time_weights);
+         int prob;
 
          if (dump_file && (dump_flags & TDF_DETAILS))
            {
@@ -1850,20 +1976,21 @@ estimate_function_body_sizes (struct cgraph_node *node)
          this_time *= freq;
          time += this_time;
          size += this_size;
-         if (likely_eliminated_by_inlining_p (stmt))
-           {
-             size_inlining_benefit += this_size;
-             time_inlining_benefit += this_time;
-             if (dump_file && (dump_flags & TDF_DETAILS))
-               fprintf (dump_file, "    Likely eliminated\n");
-           }
+         prob = eliminated_by_inlining_prob (stmt);
+         if (prob == 1 && dump_file && (dump_flags & TDF_DETAILS))
+           fprintf (dump_file, "    50%% will be eliminated by inlining\n");
+         if (prob == 2 && dump_file && (dump_flags & TDF_DETAILS))
+           fprintf (dump_file, "    will eliminated by inlining\n");
+         size_inlining_benefit += this_size * prob;
+         time_inlining_benefit += this_time * prob;
          gcc_assert (time >= 0);
          gcc_assert (size >= 0);
        }
     }
   time = (time + CGRAPH_FREQ_BASE / 2) / CGRAPH_FREQ_BASE;
-  time_inlining_benefit = ((time_inlining_benefit + CGRAPH_FREQ_BASE / 2)
-                          / CGRAPH_FREQ_BASE);
+  time_inlining_benefit = ((time_inlining_benefit + CGRAPH_FREQ_BASE)
+                          / (CGRAPH_FREQ_BASE * 2));
+  size_inlining_benefit = (size_inlining_benefit + 1) / 2;
   if (dump_file)
     fprintf (dump_file, "Overall function body time: %i-%i size: %i-%i\n",
             (int)time, (int)time_inlining_benefit,
@@ -1876,7 +2003,7 @@ estimate_function_body_sizes (struct cgraph_node *node)
       time_inlining_benefit += cost;
       size_inlining_benefit += cost;
     }
-  for (arg = DECL_ARGUMENTS (node->decl); arg; arg = TREE_CHAIN (arg))
+  for (arg = DECL_ARGUMENTS (node->decl); arg; arg = DECL_CHAIN (arg))
     if (!VOID_TYPE_P (TREE_TYPE (arg)))
       {
         int cost = estimate_move_cost (TREE_TYPE (arg));
@@ -1907,7 +2034,7 @@ compute_inline_parameters (struct cgraph_node *node)
 
   /* Estimate the stack size for the function.  But not at -O0
      because estimated_stack_frame_size is a quadratic problem.  */
-  self_stack_size = optimize ? estimated_stack_frame_size () : 0;
+  self_stack_size = optimize ? estimated_stack_frame_size (node->decl) : 0;
   inline_summary (node)->estimated_self_stack_size = self_stack_size;
   node->global.estimated_stack_size = self_stack_size;
   node->global.stack_frame_offset = 0;
@@ -1958,23 +2085,8 @@ struct gimple_opt_pass pass_inline_parameters =
 static void
 inline_indirect_intraprocedural_analysis (struct cgraph_node *node)
 {
-  struct cgraph_edge *cs;
-
-  if (!flag_ipa_cp)
-    {
-      ipa_initialize_node_params (node);
-      ipa_detect_param_modifications (node);
-    }
-  ipa_analyze_params_uses (node);
-
-  if (!flag_ipa_cp)
-    for (cs = node->callees; cs; cs = cs->next_callee)
-      {
-       ipa_count_arguments (cs);
-       ipa_compute_jump_functions (cs);
-      }
-
-  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);
@@ -1989,7 +2101,9 @@ analyze_function (struct cgraph_node *node)
   current_function_decl = node->decl;
 
   compute_inline_parameters (node);
-  if (flag_indirect_inlining)
+  /* FIXME: We should remove the optimize check after we ensure we never run
+     IPA passes when not optimizng.  */
+  if (flag_indirect_inlining && optimize)
     inline_indirect_intraprocedural_analysis (node);
 
   current_function_decl = NULL;
@@ -2032,6 +2146,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.  */
@@ -2044,10 +2159,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);
@@ -2080,7 +2198,8 @@ inline_read_summary (void)
    active, we don't need to write them twice.  */
 
 static void
-inline_write_summary (cgraph_node_set set)
+inline_write_summary (cgraph_node_set set,
+                     varpool_node_set vset ATTRIBUTE_UNUSED)
 {
   if (flag_indirect_inlining && !flag_ipa_cp)
     ipa_prop_write_jump_functions (set);
@@ -2116,13 +2235,14 @@ struct ipa_opt_pass_d pass_ipa_inline =
   0,                                   /* properties_destroyed */
   TODO_remove_functions,               /* todo_flags_finish */
   TODO_dump_cgraph | TODO_dump_func
-  | TODO_remove_functions              /* todo_flags_finish */
+  | TODO_remove_functions | TODO_ggc_collect   /* todo_flags_finish */
  },
  inline_generate_summary,              /* generate_summary */
  inline_write_summary,                 /* write_summary */
  inline_read_summary,                  /* read_summary */
- NULL,                                 /* function_read_summary */
- lto_ipa_fixup_call_notes,             /* stmt_fixup */
+ NULL,                                 /* write_optimization_summary */
+ NULL,                                 /* read_optimization_summary */
+ NULL,                                 /* stmt_fixup */
  0,                                    /* TODOs */
  inline_transform,                     /* function_transform */
  NULL,                                 /* variable_transform */