OSDN Git Service

Add dbg count support for ccp
[pf3gnuchains/gcc-fork.git] / gcc / ipa-inline.c
index 1aacce4..5da4b4c 100644 (file)
@@ -1,12 +1,12 @@
 /* Inlining decision heuristics.
-   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2007, 2008, 2009 Free Software Foundation, Inc.
    Contributed by Jan Hubicka
 
 This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -15,9 +15,8 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 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, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 /*  Inlining decision heuristics
 
@@ -61,7 +60,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 
       cgraph_decide_inlining implements heuristics taking whole callgraph
       into account, while cgraph_decide_inlining_incrementally considers
-      only one function at a time and is used in non-unit-at-a-time mode. 
+      only one function at a time and is used by early inliner.
 
    The inliner itself is split into several passes:
 
@@ -83,15 +82,13 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
      to do inlining expanding code size it might result in unbounded growth of
      whole unit.
 
-     This is the main inlining pass in non-unit-at-a-time.
-
-     With unit-at-a-time the pass is run during conversion into SSA form.
-     Only functions already converted into SSA form are inlined, so the
-     conversion must happen in topological order on the callgraph (that is
-     maintained by pass manager).  The functions after inlining are early
-     optimized so the early inliner sees unoptimized function itself, but
-     all considered callees are already optimized allowing it to unfold
-     abstraction penalty on C++ effectively and cheaply.
+     The pass is run during conversion into SSA form.  Only functions already
+     converted into SSA form are inlined, so the conversion must happen in
+     topological order on the callgraph (that is maintained by pass manager).
+     The functions after inlining are early optimized so the early inliner sees
+     unoptimized function itself, but all considered callees are already
+     optimized allowing it to unfold abstraction penalty on C++ effectively and
+     cheaply.
 
    pass_ipa_early_inlining
 
@@ -140,6 +137,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "ggc.h"
 #include "tree-flow.h"
 #include "rtl.h"
+#include "ipa-prop.h"
 
 /* Mode incremental inliner operate on:
 
@@ -150,16 +148,11 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
    In SIZE mode, only functions that reduce function body size after inlining
    are inlined, this is used during early inlining.
 
-   In SPEED mode, all small functions are inlined.  This might result in
-   unbounded growth of compilation unit and is used only in non-unit-at-a-time
-   mode.
-
    in ALL mode, everything is inlined.  This is used during flattening.  */
 enum inlining_mode {
   INLINE_NONE = 0,
   INLINE_ALWAYS_INLINE,
   INLINE_SIZE,
-  INLINE_SPEED,
   INLINE_ALL
 };
 static bool
@@ -173,6 +166,15 @@ static int nfunctions_inlined;
 static int overall_insns;
 static gcov_type max_count;
 
+/* Holders of ipa cgraph hooks: */
+static struct cgraph_node_hook_list *function_insertion_hook_holder;
+
+static inline struct inline_summary *
+inline_summary (struct cgraph_node *node)
+{
+  return &node->local.inline_summary;
+}
+
 /* Estimate size of the function after inlining WHAT into TO.  */
 
 static int
@@ -196,20 +198,21 @@ cgraph_estimate_size_after_inlining (int times, struct cgraph_node *to,
    clones or re-using node originally representing out-of-line function call.
    */
 void
-cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, bool update_original)
+cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
+                           bool update_original)
 {
   HOST_WIDE_INT peak;
+
   if (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
          && !e->callee->needed
-         && !cgraph_new_nodes
-         && flag_unit_at_a_time)
+         && !cgraph_new_nodes)
        {
          gcc_assert (!e->callee->global.inlined_to);
-         if (DECL_SAVED_TREE (e->callee->decl))
+         if (e->callee->analyzed)
            overall_insns -= e->callee->global.insns, nfunctions_inlined++;
          duplicate = false;
        }
@@ -227,8 +230,10 @@ cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, bool update_o
   else
     e->callee->global.inlined_to = e->caller;
   e->callee->global.stack_frame_offset
-    = e->caller->global.stack_frame_offset + e->caller->local.estimated_self_stack_size;
-  peak = e->callee->global.stack_frame_offset + e->callee->local.estimated_self_stack_size;
+    = e->caller->global.stack_frame_offset
+      + inline_summary (e->caller)->estimated_self_stack_size;
+  peak = e->callee->global.stack_frame_offset
+      + 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;
 
@@ -238,23 +243,27 @@ cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, bool update_o
       cgraph_clone_inlined_nodes (e, duplicate, update_original);
 }
 
-/* Mark edge E as inlined and update callgraph accordingly. 
-   UPDATE_ORIGINAL specify whether profile of original function should be
-   updated. */
+/* Mark edge E as inlined and update callgraph accordingly.  UPDATE_ORIGINAL
+   specify whether profile of original function should be updated.  If any new
+   indirect edges are discovered in the process, add them to NEW_EDGES, unless
+   it is NULL.  Return true iff any new callgraph edges were discovered as a
+   result of inlining.  */
 
-void
-cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original)
+static bool
+cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
+                        VEC (cgraph_edge_p, heap) **new_edges)
 {
   int old_insns = 0, new_insns = 0;
   struct cgraph_node *to = NULL, *what;
+  struct cgraph_edge *curr = e;
 
   if (e->callee->inline_decl)
     cgraph_redirect_edge_callee (e, cgraph_node (e->callee->inline_decl));
 
   gcc_assert (e->inline_failed);
-  e->inline_failed = NULL;
+  e->inline_failed = CIF_OK;
 
-  if (!e->callee->global.inlined && flag_unit_at_a_time)
+  if (!e->callee->global.inlined)
     DECL_POSSIBLY_INLINED (e->callee->decl) = true;
   e->callee->global.inlined = true;
 
@@ -276,6 +285,11 @@ cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original)
   if (new_insns > old_insns)
     overall_insns += new_insns - old_insns;
   ncalls_inlined++;
+
+  if (flag_indirect_inlining)
+    return ipa_propagate_indirect_call_infos (curr, new_edges);
+  else
+    return false;
 }
 
 /* Mark all calls of EDGE->CALLEE inlined into EDGE->CALLER.
@@ -289,7 +303,7 @@ cgraph_mark_inline (struct cgraph_edge *edge)
   struct cgraph_node *what = edge->callee;
   struct cgraph_edge *e, *next;
 
-  gcc_assert (!CALL_CANNOT_INLINE_P (edge->call_stmt));
+  gcc_assert (!gimple_call_cannot_inline_p (edge->call_stmt));
   /* Look for all calls, mark them inline and clone recursively
      all inlined functions.  */
   for (e = what->callers; e; e = next)
@@ -297,7 +311,7 @@ cgraph_mark_inline (struct cgraph_edge *edge)
       next = e->next_caller;
       if (e->caller == to && e->inline_failed)
        {
-          cgraph_mark_inline_edge (e, true);
+          cgraph_mark_inline_edge (e, true, NULL);
          if (e == edge)
            edge = next;
        }
@@ -313,18 +327,25 @@ cgraph_estimate_growth (struct cgraph_node *node)
 {
   int growth = 0;
   struct cgraph_edge *e;
+  bool self_recursive = false;
+
   if (node->global.estimated_growth != INT_MIN)
     return node->global.estimated_growth;
 
   for (e = node->callers; e; e = e->next_caller)
-    if (e->inline_failed)
-      growth += (cgraph_estimate_size_after_inlining (1, e->caller, node)
-                - e->caller->global.insns);
+    {
+      if (e->caller == node)
+        self_recursive = true;
+      if (e->inline_failed)
+       growth += (cgraph_estimate_size_after_inlining (1, e->caller, node)
+                  - e->caller->global.insns);
+    }
 
-  /* ??? Wrong for self recursive functions or cases where 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 (!node->needed && !DECL_EXTERNAL (node->decl))
+  /* ??? Wrong for non-trivially self recursive functions or cases where
+     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 (!node->needed && !DECL_EXTERNAL (node->decl) && !self_recursive)
     growth -= node->global.insns;
 
   node->global.estimated_growth = growth;
@@ -340,7 +361,7 @@ cgraph_estimate_growth (struct cgraph_node *node)
 
 static bool
 cgraph_check_inline_limits (struct cgraph_node *to, struct cgraph_node *what,
-                           const char **reason, bool one_only)
+                           cgraph_inline_failed_t *reason, bool one_only)
 {
   int times = 0;
   struct cgraph_edge *e;
@@ -360,10 +381,10 @@ cgraph_check_inline_limits (struct cgraph_node *to, struct cgraph_node *what,
 
   /* When inlining large function body called once into small function,
      take the inlined function as base for limiting the growth.  */
-  if (to->local.self_insns > what->local.self_insns)
-    limit = to->local.self_insns;
+  if (inline_summary (to)->self_insns > inline_summary(what)->self_insns)
+    limit = inline_summary (to)->self_insns;
   else
-    limit = what->local.self_insns;
+    limit = inline_summary (what)->self_insns;
 
   limit += limit * PARAM_VALUE (PARAM_LARGE_FUNCTION_GROWTH) / 100;
 
@@ -375,22 +396,22 @@ cgraph_check_inline_limits (struct cgraph_node *to, struct cgraph_node *what,
       && newsize > limit)
     {
       if (reason)
-        *reason = N_("--param large-function-growth limit reached");
+        *reason = CIF_LARGE_FUNCTION_GROWTH_LIMIT;
       return false;
     }
 
-  stack_size_limit = to->local.estimated_self_stack_size;
+  stack_size_limit = inline_summary (to)->estimated_self_stack_size;
 
   stack_size_limit += stack_size_limit * PARAM_VALUE (PARAM_STACK_FRAME_GROWTH) / 100;
 
   inlined_stack = (to->global.stack_frame_offset
-                  + to->local.estimated_self_stack_size
+                  + inline_summary (to)->estimated_self_stack_size
                   + what->global.estimated_stack_size);
   if (inlined_stack  > stack_size_limit
       && inlined_stack > PARAM_VALUE (PARAM_LARGE_STACK_FRAME))
     {
       if (reason)
-        *reason = N_("--param large-stack-frame-growth limit reached");
+        *reason = CIF_LARGE_STACK_FRAME_GROWTH_LIMIT;
       return false;
     }
   return true;
@@ -398,24 +419,24 @@ cgraph_check_inline_limits (struct cgraph_node *to, struct cgraph_node *what,
 
 /* Return true when function N is small enough to be inlined.  */
 
-bool
-cgraph_default_inline_p (struct cgraph_node *n, const char **reason)
+static bool
+cgraph_default_inline_p (struct cgraph_node *n, cgraph_inline_failed_t *reason)
 {
   tree decl = n->decl;
 
   if (n->inline_decl)
     decl = n->inline_decl;
-  if (!DECL_INLINE (decl))
+  if (!flag_inline_small_functions && !DECL_DECLARED_INLINE_P (decl))
     {
       if (reason)
-       *reason = N_("function not inlinable");
+       *reason = CIF_FUNCTION_NOT_INLINE_CANDIDATE;
       return false;
     }
 
   if (!DECL_STRUCT_FUNCTION (decl)->cfg)
     {
       if (reason)
-       *reason = N_("function body not available");
+       *reason = CIF_BODY_NOT_AVAILABLE;
       return false;
     }
 
@@ -424,7 +445,7 @@ cgraph_default_inline_p (struct cgraph_node *n, const char **reason)
       if (n->global.insns >= MAX_INLINE_INSNS_SINGLE)
        {
          if (reason)
-           *reason = N_("--param max-inline-insns-single limit reached");
+           *reason = CIF_MAX_INLINE_INSNS_SINGLE_LIMIT;
          return false;
        }
     }
@@ -433,7 +454,7 @@ cgraph_default_inline_p (struct cgraph_node *n, const char **reason)
       if (n->global.insns >= MAX_INLINE_INSNS_AUTO)
        {
          if (reason)
-           *reason = N_("--param max-inline-insns-auto limit reached");
+           *reason = CIF_MAX_INLINE_INSNS_AUTO_LIMIT;
          return false;
        }
     }
@@ -448,7 +469,7 @@ cgraph_default_inline_p (struct cgraph_node *n, const char **reason)
 static bool
 cgraph_recursive_inlining_p (struct cgraph_node *to,
                             struct cgraph_node *what,
-                            const char **reason)
+                            cgraph_inline_failed_t *reason)
 {
   bool recursive;
   if (to->global.inlined_to)
@@ -459,30 +480,10 @@ cgraph_recursive_inlining_p (struct cgraph_node *to,
      not warn on it.  */
   if (recursive && reason)
     *reason = (what->local.disregard_inline_limits
-              ? N_("recursive inlining") : "");
+              ? CIF_RECURSIVE_INLINING : CIF_UNSPECIFIED);
   return recursive;
 }
 
-/* Return true if the call can be hot.  */
-static bool
-cgraph_maybe_hot_edge_p (struct cgraph_edge *edge)
-{
-  if (profile_info && flag_branch_probabilities
-      && (edge->count
-         <= profile_info->sum_max / PARAM_VALUE (HOT_BB_COUNT_FRACTION)))
-    return false;
-  if (lookup_attribute ("cold", DECL_ATTRIBUTES (edge->callee->decl))
-      || lookup_attribute ("cold", DECL_ATTRIBUTES (edge->caller->decl)))
-    return false;
-  if (lookup_attribute ("hot", DECL_ATTRIBUTES (edge->caller->decl)))
-    return true;
-  if (flag_guess_branch_prob
-      && edge->frequency < (CGRAPH_FREQ_MAX
-                           / PARAM_VALUE (HOT_BB_FREQUENCY_FRACTION)))
-    return false;
-  return true;
-}
-
 /* A cost model driving the inlining heuristics in a way so the edges with
    smallest badness are inlined first.  After each inlining is performed
    the costs of all caller edges of nodes affected are recomputed so the
@@ -513,7 +514,7 @@ cgraph_edge_badness (struct cgraph_edge *edge)
      within function, the function itself is infrequent.
 
      Other objective to optimize for is number of different calls inlined.
-     We add the estimated growth after inlining all functions to biass the
+     We add the estimated growth after inlining all functions to bias the
      priorities slightly in this direction (so fewer times called functions
      of the same size gets priority).  */
   else if (flag_guess_branch_prob)
@@ -565,7 +566,7 @@ update_caller_keys (fibheap_t heap, struct cgraph_node *node,
                    bitmap updated_nodes)
 {
   struct cgraph_edge *edge;
-  const char *failed_reason;
+  cgraph_inline_failed_t failed_reason;
 
   if (!node->local.inlinable || node->local.disregard_inline_limits
       || node->global.inlined_to)
@@ -583,7 +584,7 @@ update_caller_keys (fibheap_t heap, struct cgraph_node *node,
       for (edge = node->callers; edge; edge = edge->next_caller)
        if (edge->aux)
          {
-           fibheap_delete_node (heap, edge->aux);
+           fibheap_delete_node (heap, (fibnode_t) edge->aux);
            edge->aux = NULL;
            if (edge->inline_failed)
              edge->inline_failed = failed_reason;
@@ -597,7 +598,7 @@ update_caller_keys (fibheap_t heap, struct cgraph_node *node,
        int badness = cgraph_edge_badness (edge);
        if (edge->aux)
          {
-           fibnode_t n = edge->aux;
+           fibnode_t n = (fibnode_t) edge->aux;
            gcc_assert (n->data == edge);
            if (n->key == badness)
              continue;
@@ -605,7 +606,7 @@ update_caller_keys (fibheap_t heap, struct cgraph_node *node,
            /* fibheap_replace_key only increase the keys.  */
            if (fibheap_replace_key (heap, n, badness))
              continue;
-           fibheap_delete_node (heap, edge->aux);
+           fibheap_delete_node (heap, (fibnode_t) edge->aux);
          }
        edge->aux = fibheap_insert (heap, badness, edge);
       }
@@ -653,10 +654,13 @@ lookup_recursive_calls (struct cgraph_node *node, struct cgraph_node *where,
 }
 
 /* Decide on recursive inlining: in the case function has recursive calls,
-   inline until body size reaches given argument.  */
+   inline until body size reaches given argument.  If any new indirect edges
+   are discovered in the process, add them to *NEW_EDGES, unless NEW_EDGES
+   is NULL.  */
 
 static bool
-cgraph_decide_recursive_inlining (struct cgraph_node *node)
+cgraph_decide_recursive_inlining (struct cgraph_node *node,
+                                 VEC (cgraph_edge_p, heap) **new_edges)
 {
   int limit = PARAM_VALUE (PARAM_MAX_INLINE_INSNS_RECURSIVE_AUTO);
   int max_depth = PARAM_VALUE (PARAM_MAX_INLINE_RECURSIVE_DEPTH_AUTO);
@@ -667,7 +671,8 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
   int depth = 0;
   int n = 0;
 
-  if (optimize_size)
+  if (optimize_function_for_size_p (DECL_STRUCT_FUNCTION (node->decl))
+      || (!flag_inline_functions && !DECL_DECLARED_INLINE_P (node->decl)))
     return false;
 
   if (DECL_DECLARED_INLINE_P (node->decl))
@@ -705,7 +710,8 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
         && (cgraph_estimate_size_after_inlining (1, node, master_clone)
             <= limit))
     {
-      struct cgraph_edge *curr = fibheap_extract_min (heap);
+      struct cgraph_edge *curr
+       = (struct cgraph_edge *) fibheap_extract_min (heap);
       struct cgraph_node *cnode;
 
       depth = 1;
@@ -750,7 +756,7 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
          fprintf (dump_file, "\n");
        }
       cgraph_redirect_edge_callee (curr, master_clone);
-      cgraph_mark_inline_edge (curr, false);
+      cgraph_mark_inline_edge (curr, false, new_edges);
       lookup_recursive_calls (node, curr->callee, heap);
       n++;
     }
@@ -784,12 +790,14 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
 /* Set inline_failed for all callers of given function to REASON.  */
 
 static void
-cgraph_set_inline_failed (struct cgraph_node *node, const char *reason)
+cgraph_set_inline_failed (struct cgraph_node *node,
+                         cgraph_inline_failed_t reason)
 {
   struct cgraph_edge *e;
 
   if (dump_file)
-    fprintf (dump_file, "Inlining failed: %s\n", reason);
+    fprintf (dump_file, "Inlining failed: %s\n",
+            cgraph_inline_failed_string (reason));
   for (e = node->callers; e; e = e->next_caller)
     if (e->inline_failed)
       e->inline_failed = reason;
@@ -808,6 +816,20 @@ compute_max_insns (int insns)
          * (100 + PARAM_VALUE (PARAM_INLINE_UNIT_GROWTH)) / 100);
 }
 
+/* Compute badness of all edges in NEW_EDGES and add them to the HEAP.  */
+static void
+add_new_edges_to_heap (fibheap_t heap, VEC (cgraph_edge_p, heap) *new_edges)
+{
+  while (VEC_length (cgraph_edge_p, new_edges) > 0)
+    {
+      struct cgraph_edge *edge = VEC_pop (cgraph_edge_p, new_edges);
+
+      gcc_assert (!edge->aux);
+      edge->aux = fibheap_insert (heap, cgraph_edge_badness (edge), edge);
+    }
+}
+
+
 /* We use greedy algorithm for inlining of small functions:
    All inline candidates are put into prioritized heap based on estimated
    growth of the overall number of instructions and then update the estimates.
@@ -820,10 +842,14 @@ cgraph_decide_inlining_of_small_functions (void)
 {
   struct cgraph_node *node;
   struct cgraph_edge *edge;
-  const char *failed_reason;
+  cgraph_inline_failed_t failed_reason;
   fibheap_t heap = fibheap_new ();
   bitmap updated_nodes = BITMAP_ALLOC (NULL);
   int min_insns, max_insns;
+  VEC (cgraph_edge_p, heap) *new_indirect_edges = NULL;
+
+  if (flag_indirect_inlining)
+    new_indirect_edges = VEC_alloc (cgraph_edge_p, heap, 8);
 
   if (dump_file)
     fprintf (dump_file, "\nDeciding on smaller functions:\n");
@@ -856,12 +882,14 @@ cgraph_decide_inlining_of_small_functions (void)
   max_insns = compute_max_insns (overall_insns);
   min_insns = overall_insns;
 
-  while (overall_insns <= max_insns && (edge = fibheap_extract_min (heap)))
+  while (overall_insns <= max_insns
+        && (edge = (struct cgraph_edge *) fibheap_extract_min (heap)))
     {
       int old_insns = overall_insns;
       struct cgraph_node *where;
       int growth =
        cgraph_estimate_size_after_inlining (1, edge->caller, edge->callee);
+      cgraph_inline_failed_t not_good = CIF_OK;
 
       growth -= edge->caller->global.insns;
 
@@ -895,8 +923,13 @@ cgraph_decide_inlining_of_small_functions (void)
         is not good idea so prohibit the recursive inlining.
 
         ??? When the frequencies are taken into account we might not need this
-        restriction.   */
-      if (!max_count)
+        restriction.
+
+        We need to be cureful here, in some testcases, e.g. directivec.c in
+        libcpp, we can estimate self recursive function to have negative growth
+        for inlining completely.
+        */
+      if (!edge->count)
        {
          where = edge->caller;
          while (where->global.inlined_to)
@@ -908,22 +941,30 @@ cgraph_decide_inlining_of_small_functions (void)
          if (where->global.inlined_to)
            {
              edge->inline_failed
-               = (edge->callee->local.disregard_inline_limits ? N_("recursive inlining") : "");
+               = (edge->callee->local.disregard_inline_limits
+                  ? CIF_RECURSIVE_INLINING : CIF_UNSPECIFIED);
              if (dump_file)
                fprintf (dump_file, " inline_failed:Recursive inlining performed only for function itself.\n");
              continue;
            }
        }
 
-      if ((!cgraph_maybe_hot_edge_p (edge) || optimize_size) && growth > 0)
+      if (!cgraph_maybe_hot_edge_p (edge))
+       not_good = CIF_UNLIKELY_CALL;
+      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)))
+       not_good = CIF_OPTIMIZING_FOR_SIZE;
+      if (not_good && growth > 0 && cgraph_estimate_growth (edge->callee) > 0)
        {
           if (!cgraph_recursive_inlining_p (edge->caller, edge->callee,
                                            &edge->inline_failed))
            {
-             edge->inline_failed = 
-               N_("call is unlikely");
+             edge->inline_failed = not_good;
              if (dump_file)
-               fprintf (dump_file, " inline_failed:%s.\n", edge->inline_failed);
+               fprintf (dump_file, " inline_failed:%s.\n",
+                        cgraph_inline_failed_string (edge->inline_failed));
            }
          continue;
        }
@@ -933,34 +974,52 @@ cgraph_decide_inlining_of_small_functions (void)
                                            &edge->inline_failed))
            {
              if (dump_file)
-               fprintf (dump_file, " inline_failed:%s.\n", edge->inline_failed);
+               fprintf (dump_file, " inline_failed:%s.\n",
+                        cgraph_inline_failed_string (edge->inline_failed));
            }
          continue;
        }
+      if (!tree_can_inline_p (edge->caller->decl, edge->callee->decl))
+       {
+         gimple_call_set_cannot_inline (edge->call_stmt, true);
+         edge->inline_failed = CIF_TARGET_OPTION_MISMATCH;
+         if (dump_file)
+           fprintf (dump_file, " inline_failed:%s.\n",
+                    cgraph_inline_failed_string (edge->inline_failed));
+         continue;
+       }
       if (cgraph_recursive_inlining_p (edge->caller, edge->callee,
                                       &edge->inline_failed))
        {
          where = edge->caller;
          if (where->global.inlined_to)
            where = where->global.inlined_to;
-         if (!cgraph_decide_recursive_inlining (where))
+         if (!cgraph_decide_recursive_inlining (where,
+                                                flag_indirect_inlining
+                                                ? &new_indirect_edges : NULL))
            continue;
+         if (flag_indirect_inlining)
+           add_new_edges_to_heap (heap, new_indirect_edges);
           update_callee_keys (heap, where, updated_nodes);
        }
       else
        {
          struct cgraph_node *callee;
-         if (CALL_CANNOT_INLINE_P (edge->call_stmt)
+         if (gimple_call_cannot_inline_p (edge->call_stmt)
              || !cgraph_check_inline_limits (edge->caller, edge->callee,
                                              &edge->inline_failed, true))
            {
              if (dump_file)
                fprintf (dump_file, " Not inlining into %s:%s.\n",
-                        cgraph_node_name (edge->caller), edge->inline_failed);
+                        cgraph_node_name (edge->caller),
+                        cgraph_inline_failed_string (edge->inline_failed));
              continue;
            }
          callee = edge->callee;
-         cgraph_mark_inline_edge (edge, true);
+         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);
        }
       where = edge->caller;
@@ -994,15 +1053,18 @@ cgraph_decide_inlining_of_small_functions (void)
            fprintf (dump_file, "New minimal insns reached: %i\n", min_insns);
        }
     }
-  while ((edge = fibheap_extract_min (heap)) != NULL)
+  while ((edge = (struct cgraph_edge *) fibheap_extract_min (heap)) != NULL)
     {
       gcc_assert (edge->aux);
       edge->aux = NULL;
       if (!edge->callee->local.disregard_inline_limits && edge->inline_failed
           && !cgraph_recursive_inlining_p (edge->caller, edge->callee,
                                           &edge->inline_failed))
-       edge->inline_failed = N_("--param inline-unit-growth limit reached");
+       edge->inline_failed = CIF_INLINE_UNIT_GROWTH_LIMIT;
     }
+
+  if (new_indirect_edges)
+    VEC_free (cgraph_edge_p, heap, new_indirect_edges);
   fibheap_delete (heap);
   BITMAP_FREE (updated_nodes);
 }
@@ -1020,6 +1082,9 @@ cgraph_decide_inlining (void)
   int old_insns = 0;
   int i;
   int initial_insns = 0;
+  bool redo_always_inline = true;
+
+  cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
 
   max_count = 0;
   for (node = cgraph_nodes; node; node = node->next)
@@ -1027,8 +1092,8 @@ cgraph_decide_inlining (void)
       {
        struct cgraph_edge *e;
 
-       initial_insns += node->local.self_insns;
-       gcc_assert (node->local.self_insns == node->global.insns);
+       initial_insns += inline_summary (node)->self_insns;
+       gcc_assert (inline_summary (node)->self_insns == node->global.insns);
        for (e = node->callees; e; e = e->next_callee)
          if (max_count < e->count)
            max_count = e->count;
@@ -1051,74 +1116,88 @@ cgraph_decide_inlining (void)
 
   /* In the first pass mark all always_inline edges.  Do this with a priority
      so none of our later choices will make this impossible.  */
-  for (i = nnodes - 1; i >= 0; i--)
+  while (redo_always_inline)
     {
-      struct cgraph_edge *e, *next;
+      redo_always_inline = false;
+      for (i = nnodes - 1; i >= 0; i--)
+       {
+         struct cgraph_edge *e, *next;
 
-      node = order[i];
+         node = order[i];
 
-      /* Handle nodes to be flattened, but don't update overall unit size.  */
-      if (lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
-        {
-         if (dump_file)
-           fprintf (dump_file,
-                    "Flattening %s\n", cgraph_node_name (node));
-         cgraph_decide_inlining_incrementally (node, INLINE_ALL, 0);
-        }
+         /* Handle nodes to be flattened, but don't update overall unit
+            size.  */
+         if (lookup_attribute ("flatten",
+                               DECL_ATTRIBUTES (node->decl)) != NULL)
+           {
+             if (dump_file)
+               fprintf (dump_file,
+                        "Flattening %s\n", cgraph_node_name (node));
+             cgraph_decide_inlining_incrementally (node, INLINE_ALL, 0);
+           }
 
-      if (!node->local.disregard_inline_limits)
-       continue;
-      if (dump_file)
-       fprintf (dump_file,
-                "\nConsidering %s %i insns (always inline)\n",
-                cgraph_node_name (node), node->global.insns);
-      old_insns = overall_insns;
-      for (e = node->callers; e; e = next)
-       {
-         next = e->next_caller;
-         if (!e->inline_failed || CALL_CANNOT_INLINE_P (e->call_stmt))
-           continue;
-         if (cgraph_recursive_inlining_p (e->caller, e->callee,
-                                          &e->inline_failed))
+         if (!node->local.disregard_inline_limits)
            continue;
-         cgraph_mark_inline_edge (e, true);
+         if (dump_file)
+           fprintf (dump_file,
+                    "\nConsidering %s %i insns (always inline)\n",
+                    cgraph_node_name (node), node->global.insns);
+         old_insns = overall_insns;
+         for (e = node->callers; e; e = next)
+           {
+             next = e->next_caller;
+             if (!e->inline_failed
+                 || gimple_call_cannot_inline_p (e->call_stmt))
+               continue;
+             if (cgraph_recursive_inlining_p (e->caller, e->callee,
+                                              &e->inline_failed))
+               continue;
+             if (!tree_can_inline_p (e->caller->decl, e->callee->decl))
+               {
+                 gimple_call_set_cannot_inline (e->call_stmt, true);
+                 continue;
+               }
+             if (cgraph_mark_inline_edge (e, true, NULL))
+               redo_always_inline = true;
+             if (dump_file)
+               fprintf (dump_file,
+                        " Inlined into %s which now has %i insns.\n",
+                        cgraph_node_name (e->caller),
+                        e->caller->global.insns);
+           }
+         /* Inlining self recursive function might introduce new calls to
+            themselves we didn't see in the loop above.  Fill in the proper
+            reason why inline failed.  */
+         for (e = node->callers; e; e = e->next_caller)
+           if (e->inline_failed)
+             e->inline_failed = CIF_RECURSIVE_INLINING;
          if (dump_file)
            fprintf (dump_file, 
-                    " Inlined into %s which now has %i insns.\n",
-                    cgraph_node_name (e->caller),
-                    e->caller->global.insns);
+                    " Inlined for a net change of %+i insns.\n",
+                    overall_insns - old_insns);
        }
-      /* Inlining self recursive function might introduce new calls to
-        themselves we didn't see in the loop above.  Fill in the proper
-        reason why inline failed.  */
-      for (e = node->callers; e; e = e->next_caller)
-       if (e->inline_failed)
-         e->inline_failed = N_("recursive inlining");
-      if (dump_file)
-       fprintf (dump_file, 
-                " Inlined for a net change of %+i insns.\n",
-                overall_insns - old_insns);
     }
 
-  if (!flag_really_no_inline)
-    cgraph_decide_inlining_of_small_functions ();
+  cgraph_decide_inlining_of_small_functions ();
 
-  if (!flag_really_no_inline
-      && flag_inline_functions_called_once)
+  if (flag_inline_functions_called_once)
     {
       if (dump_file)
        fprintf (dump_file, "\nDeciding on functions called once:\n");
 
       /* And finally decide what functions are called once.  */
-
       for (i = nnodes - 1; i >= 0; i--)
        {
          node = order[i];
 
-         if (node->callers && !node->callers->next_caller && !node->needed
-             && node->local.inlinable && node->callers->inline_failed
-             && !CALL_CANNOT_INLINE_P (node->callers->call_stmt)
-             && !DECL_EXTERNAL (node->decl) && !DECL_COMDAT (node->decl))
+         if (node->callers
+             && !node->callers->next_caller
+             && !node->needed
+             && node->local.inlinable
+             && node->callers->inline_failed
+             && !gimple_call_cannot_inline_p (node->callers->call_stmt)
+             && !DECL_EXTERNAL (node->decl)
+             && !DECL_COMDAT (node->decl))
            {
              if (dump_file)
                {
@@ -1155,6 +1234,10 @@ cgraph_decide_inlining (void)
        }
     }
 
+  /* Free ipa-prop structures if they are no longer needed.  */
+  if (flag_indirect_inlining)
+    free_all_ipa_structures_after_iinln ();
+
   if (dump_file)
     fprintf (dump_file,
             "\nInlined %i calls, eliminated %i functions, "
@@ -1182,7 +1265,7 @@ static bool
 try_inline (struct cgraph_edge *e, enum inlining_mode mode, int depth)
 {
   struct cgraph_node *callee = e->callee;
-  enum inlining_mode callee_mode = (size_t) callee->aux;
+  enum inlining_mode callee_mode = (enum inlining_mode) (size_t) callee->aux;
   bool always_inline = e->callee->local.disregard_inline_limits;
 
   /* We've hit cycle?  */
@@ -1213,7 +1296,7 @@ try_inline (struct cgraph_edge *e, enum inlining_mode mode, int depth)
                       cgraph_node_name (e->caller));
            }
          e->inline_failed = (e->callee->local.disregard_inline_limits
-                             ? N_("recursive inlining") : "");
+                             ? CIF_RECURSIVE_INLINING : CIF_UNSPECIFIED);
           return false;
        }
     }
@@ -1227,16 +1310,18 @@ try_inline (struct cgraph_edge *e, enum inlining_mode mode, int depth)
               cgraph_node_name (e->caller));
     }
   if (e->inline_failed)
-    cgraph_mark_inline (e);
+    {
+      cgraph_mark_inline (e);
 
-  /* In order to fully inline always_inline functions at -O0, we need to
-     recurse here, since the inlined functions might not be processed by
-     incremental inlining at all yet.  
+      /* In order to fully inline always_inline functions, we need to
+        recurse here, since the inlined functions might not be processed by
+        incremental inlining at all yet.  
 
-     Also flattening needs to be done recursively.  */
+        Also flattening needs to be done recursively.  */
 
-  if (!flag_unit_at_a_time || mode == INLINE_ALL || always_inline)
-    cgraph_decide_inlining_incrementally (e->callee, mode, depth + 1);
+      if (mode == INLINE_ALL || always_inline)
+       cgraph_decide_inlining_incrementally (e->callee, mode, depth + 1);
+    }
   callee->aux = (void *)(size_t) callee_mode;
   return true;
 }
@@ -1252,14 +1337,14 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
 {
   struct cgraph_edge *e;
   bool inlined = false;
-  const char *failed_reason;
+  cgraph_inline_failed_t failed_reason;
   enum inlining_mode old_mode;
 
 #ifdef ENABLE_CHECKING
   verify_cgraph_node (node);
 #endif
 
-  old_mode = (size_t)node->aux;
+  old_mode = (enum inlining_mode) (size_t)node->aux;
 
   if (mode != INLINE_ALWAYS_INLINE
       && lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
@@ -1280,7 +1365,7 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
       if (!e->callee->local.disregard_inline_limits
          && (mode != INLINE_ALL || !e->callee->local.inlinable))
        continue;
-      if (CALL_CANNOT_INLINE_P (e->call_stmt))
+      if (gimple_call_cannot_inline_p (e->call_stmt))
        continue;
       /* When the edge is already inlined, we just need to recurse into
         it in order to fully flatten the leaves.  */
@@ -1305,6 +1390,17 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
            }
          continue;
        }
+      if (!tree_can_inline_p (node->decl, e->callee->decl))
+       {
+         gimple_call_set_cannot_inline (e->call_stmt, true);
+         if (dump_file)
+           {
+             indent_to (dump_file, depth);
+             fprintf (dump_file,
+                      "Not inlining: Target specific option mismatch.\n");
+           }
+         continue;
+       }
       if (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
          != gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
        {
@@ -1315,7 +1411,7 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
            }
          continue;
        }
-      if (!DECL_SAVED_TREE (e->callee->decl) && !e->callee->inline_decl)
+      if (!e->callee->analyzed && !e->callee->inline_decl)
        {
          if (dump_file)
            {
@@ -1329,8 +1425,7 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
     }
 
   /* Now do the automatic inlining.  */
-  if (!flag_really_no_inline && mode != INLINE_ALL
-      && mode != INLINE_ALWAYS_INLINE)
+  if (mode != INLINE_ALL && mode != INLINE_ALWAYS_INLINE)
     for (e = node->callees; e; e = e->next_callee)
       {
        if (!e->callee->local.inlinable
@@ -1362,7 +1457,9 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
        /* When the function body would grow and inlining the function won't
           eliminate the need for offline copy of the function, don't inline.
         */
-       if (mode == INLINE_SIZE
+       if ((mode == INLINE_SIZE
+            || (!flag_inline_functions
+                && !DECL_DECLARED_INLINE_P (e->callee->decl)))
            && (cgraph_estimate_size_after_inlining (1, e->caller, e->callee)
                > e->caller->global.insns)
            && cgraph_estimate_growth (e->callee) > 0)
@@ -1380,16 +1477,17 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
          }
        if (!cgraph_check_inline_limits (node, e->callee, &e->inline_failed,
                                        false)
-           || CALL_CANNOT_INLINE_P (e->call_stmt))
+           || gimple_call_cannot_inline_p (e->call_stmt))
          {
            if (dump_file)
              {
                indent_to (dump_file, depth);
-               fprintf (dump_file, "Not inlining: %s.\n", e->inline_failed);
+               fprintf (dump_file, "Not inlining: %s.\n",
+                        cgraph_inline_failed_string (e->inline_failed));
              }
            continue;
          }
-       if (!DECL_SAVED_TREE (e->callee->decl) && !e->callee->inline_decl)
+       if (!e->callee->analyzed && !e->callee->inline_decl)
          {
            if (dump_file)
              {
@@ -1399,40 +1497,24 @@ cgraph_decide_inlining_incrementally (struct cgraph_node *node,
              }
            continue;
          }
+       if (!tree_can_inline_p (node->decl, e->callee->decl))
+         {
+           gimple_call_set_cannot_inline (e->call_stmt, true);
+           if (dump_file)
+             {
+               indent_to (dump_file, depth);
+               fprintf (dump_file,
+                        "Not inlining: Target specific option mismatch.\n");
+             }
+           continue;
+         }
        if (cgraph_default_inline_p (e->callee, &failed_reason))
          inlined |= try_inline (e, mode, depth);
-       else if (!flag_unit_at_a_time)
-         e->inline_failed = failed_reason;
       }
   node->aux = (void *)(size_t) old_mode;
   return inlined;
 }
 
-/* When inlining shall be performed.  */
-static bool
-cgraph_gate_inlining (void)
-{
-  return flag_inline_trees;
-}
-
-struct tree_opt_pass pass_ipa_inline = 
-{
-  "inline",                            /* name */
-  cgraph_gate_inlining,                        /* gate */
-  cgraph_decide_inlining,              /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  TV_INLINE_HEURISTICS,                        /* tv_id */
-  0,                                   /* properties_required */
-  PROP_cfg,                            /* properties_provided */
-  0,                                   /* properties_destroyed */
-  TODO_remove_functions,               /* todo_flags_finish */
-  TODO_dump_cgraph | TODO_dump_func
-  | TODO_remove_functions,             /* todo_flags_finish */
-  0                                    /* letter */
-};
-
 /* Because inlining might remove no-longer reachable nodes, we need to
    keep the array visible to garbage collector to avoid reading collected
    out nodes.  */
@@ -1450,14 +1532,13 @@ cgraph_early_inlining (void)
 
   if (sorrycount || errorcount)
     return 0;
-  if (cgraph_decide_inlining_incrementally (node,
-                                           flag_unit_at_a_time || optimize_size
-                                           ? INLINE_SIZE : INLINE_SPEED, 0))
+  if (cgraph_decide_inlining_incrementally (node, INLINE_SIZE, 0))
     {
       timevar_push (TV_INTEGRATION);
       todo = optimize_inline_calls (current_function_decl);
       timevar_pop (TV_INTEGRATION);
     }
+  cfun->always_inline_functions_inlined = true;
   return todo;
 }
 
@@ -1465,11 +1546,13 @@ cgraph_early_inlining (void)
 static bool
 cgraph_gate_early_inlining (void)
 {
-  return flag_inline_trees && flag_early_inlining;
+  return flag_early_inlining;
 }
 
-struct tree_opt_pass pass_early_inline = 
+struct gimple_opt_pass pass_early_inline = 
 {
+ {
+  GIMPLE_PASS,
   "einline",                           /* name */
   cgraph_gate_early_inlining,          /* gate */
   cgraph_early_inlining,               /* execute */
@@ -1481,23 +1564,25 @@ struct tree_opt_pass pass_early_inline =
   PROP_cfg,                            /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func,                      /* todo_flags_finish */
-  0                                    /* letter */
+  TODO_dump_func                       /* todo_flags_finish */
+ }
 };
 
 /* When inlining shall be performed.  */
 static bool
 cgraph_gate_ipa_early_inlining (void)
 {
-  return (flag_inline_trees && flag_early_inlining
+  return (flag_early_inlining
          && (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 tree_opt_pass pass_ipa_early_inline = 
+struct simple_ipa_opt_pass pass_ipa_early_inline = 
 {
+ {
+  SIMPLE_IPA_PASS,
   "einline_ipa",                       /* name */
   cgraph_gate_ipa_early_inlining,      /* gate */
   NULL,                                        /* execute */
@@ -1509,45 +1594,60 @@ struct tree_opt_pass pass_ipa_early_inline =
   PROP_cfg,                            /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_cgraph,                    /* todo_flags_finish */
-  0                                    /* letter */
+  TODO_dump_cgraph                     /* todo_flags_finish */
+ }
 };
 
 /* Compute parameters of functions used by inliner.  */
-static unsigned int
-compute_inline_parameters (void)
+unsigned int
+compute_inline_parameters (struct cgraph_node *node)
 {
-  struct cgraph_node *node = cgraph_node (current_function_decl);
+  HOST_WIDE_INT self_stack_size;
 
   gcc_assert (!node->global.inlined_to);
-  node->local.estimated_self_stack_size = estimated_stack_frame_size ();
-  node->global.estimated_stack_size = node->local.estimated_self_stack_size;
+
+  /* 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;
+  inline_summary (node)->estimated_self_stack_size = self_stack_size;
+  node->global.estimated_stack_size = self_stack_size;
   node->global.stack_frame_offset = 0;
+
+  /* Can this function be inlined at all?  */
   node->local.inlinable = tree_inlinable_function_p (current_function_decl);
-  node->local.self_insns = estimate_num_insns (current_function_decl,
-                                              &eni_inlining_weights);
-  if (node->local.inlinable)
+
+  /* Estimate the number of instructions for this function.
+     ??? At -O0 we don't use this information except for the dumps, and
+        even then only for always_inline functions.  But disabling this
+        causes ICEs in the inline heuristics...  */
+  inline_summary (node)->self_insns
+      = estimate_num_insns_fn (current_function_decl, &eni_inlining_weights);
+  if (node->local.inlinable && !node->local.disregard_inline_limits)
     node->local.disregard_inline_limits
-      = lang_hooks.tree_inlining.disregard_inline_limits (current_function_decl);
-  if (flag_really_no_inline && !node->local.disregard_inline_limits)
-    node->local.inlinable = 0;
+      = DECL_DISREGARD_INLINE_LIMITS (current_function_decl);
+
   /* Inlining characteristics are maintained by the cgraph_mark_inline.  */
-  node->global.insns = node->local.self_insns;
+  node->global.insns = inline_summary (node)->self_insns;
   return 0;
 }
 
-/* When inlining shall be performed.  */
-static bool
-gate_inline_passes (void)
+
+/* Compute parameters of functions used by inliner using
+   current_function_decl.  */
+static unsigned int
+compute_inline_parameters_for_current (void)
 {
-  return flag_inline_trees;
+  compute_inline_parameters (cgraph_node (current_function_decl));
+  return 0;
 }
 
-struct tree_opt_pass pass_inline_parameters = 
+struct gimple_opt_pass pass_inline_parameters = 
 {
+ {
+  GIMPLE_PASS,
   NULL,                                        /* name */
-  gate_inline_passes,                  /* gate */
-  compute_inline_parameters,           /* execute */
+  NULL,                                        /* gate */
+  compute_inline_parameters_for_current,/* execute */
   NULL,                                        /* sub */
   NULL,                                        /* next */
   0,                                   /* static_pass_number */
@@ -1556,53 +1656,117 @@ struct tree_opt_pass pass_inline_parameters =
   PROP_cfg,                            /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  0,                                   /* todo_flags_finish */
-  0                                    /* letter */
+  0                                    /* todo_flags_finish */
+ }
 };
 
-/* Apply inline plan to the function.  */
+/* This function performs intraprocedural analyzis in NODE that is required to
+   inline indirect calls.  */
+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_print_node_params (dump_file, node);
+      ipa_print_node_jump_functions (dump_file, node);
+    }
+}
+
+/* Note function body size.  */
+static void
+analyze_function (struct cgraph_node *node)
+{
+  push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+  current_function_decl = node->decl;
+
+  compute_inline_parameters (node);
+  if (flag_indirect_inlining)
+    inline_indirect_intraprocedural_analysis (node);
+
+  current_function_decl = NULL;
+  pop_cfun ();
+}
+
+/* Called when new function is inserted to callgraph late.  */
+static void
+add_new_function (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED)
+{
+  analyze_function (node);
+}
+
+/* Note function body size.  */
+static void
+inline_generate_summary (void)
+{
+  struct cgraph_node *node;
+
+  function_insertion_hook_holder =
+      cgraph_add_function_insertion_hook (&add_new_function, NULL);
+
+  if (flag_indirect_inlining)
+    {
+      ipa_register_cgraph_hooks ();
+      ipa_check_create_node_params ();
+      ipa_check_create_edge_args ();
+    }
+
+  for (node = cgraph_nodes; node; node = node->next)
+    if (node->analyzed)
+      analyze_function (node);
+  
+  return;
+}
+
+/* Apply inline plan to function.  */
 static unsigned int
-apply_inline (void)
+inline_transform (struct cgraph_node *node)
 {
   unsigned int todo = 0;
   struct cgraph_edge *e;
-  struct cgraph_node *node = cgraph_node (current_function_decl);
-
-  /* Even when not optimizing, ensure that always_inline functions get inlined.
-   */
-  if (!optimize)
-   cgraph_decide_inlining_incrementally (node, INLINE_SPEED, 0);
 
   /* We might need the body of this function so that we can expand
      it inline somewhere else.  */
-  if (cgraph_preserve_function_body_p (current_function_decl))
+  if (cgraph_preserve_function_body_p (node->decl))
     save_inline_function_body (node);
 
   for (e = node->callees; e; e = e->next_callee)
     if (!e->inline_failed || warn_inline)
       break;
+
   if (e)
     {
       timevar_push (TV_INTEGRATION);
       todo = optimize_inline_calls (current_function_decl);
       timevar_pop (TV_INTEGRATION);
     }
-  /* In non-unit-at-a-time we must mark all referenced functions as needed.  */
-  if (!flag_unit_at_a_time)
-    {
-      struct cgraph_edge *e;
-      for (e = node->callees; e; e = e->next_callee)
-       if (e->callee->analyzed)
-          cgraph_mark_needed_node (e->callee);
-    }
+  cfun->always_inline_functions_inlined = true;
+  cfun->after_inlining = true;
   return todo | execute_fixup_cfg ();
 }
 
-struct tree_opt_pass pass_apply_inline = 
+struct ipa_opt_pass pass_ipa_inline = 
 {
-  "apply_inline",                      /* name */
+ {
+  IPA_PASS,
+  "inline",                            /* name */
   NULL,                                        /* gate */
-  apply_inline,                                /* execute */
+  cgraph_decide_inlining,              /* execute */
   NULL,                                        /* sub */
   NULL,                                        /* next */
   0,                                   /* static_pass_number */
@@ -1610,10 +1774,18 @@ struct tree_opt_pass pass_apply_inline =
   0,                                   /* properties_required */
   PROP_cfg,                            /* properties_provided */
   0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  TODO_dump_func | TODO_verify_flow
-  | TODO_verify_stmts,                 /* todo_flags_finish */
-  0                                    /* letter */
+  TODO_remove_functions,               /* todo_flags_finish */
+  TODO_dump_cgraph | TODO_dump_func
+  | TODO_remove_functions              /* todo_flags_finish */
+ },
+ inline_generate_summary,              /* generate_summary */
+ NULL,                                 /* write_summary */
+ NULL,                                 /* read_summary */
+ NULL,                                 /* function_read_summary */
+ 0,                                    /* TODOs */
+ inline_transform,                     /* function_transform */
+ NULL,                                 /* variable_transform */
 };
 
+
 #include "gt-ipa-inline.h"