OSDN Git Service

* ipa-inline.c (cgraph_mark_inline_edge): Avoid double accounting
[pf3gnuchains/gcc-fork.git] / gcc / ipa-inline.c
index d1983d9..601695a 100644 (file)
@@ -1,12 +1,13 @@
 /* Inlining decision heuristics.
-   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2007, 2008, 2009, 2010
+   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 +16,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 +61,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,19 +83,17 @@ 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++ effectivly 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
 
-     With profiling, the early inlining is also neccesary to reduce
+     With profiling, the early inlining is also necessary to reduce
      instrumentation costs on program with high abstraction penalty (doing
      many redundant calls).  This can't happen in parallel with early
      optimization and profile instrumentation, because we would end up
@@ -139,59 +137,139 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "coverage.h"
 #include "ggc.h"
 #include "tree-flow.h"
+#include "rtl.h"
+#include "ipa-prop.h"
+#include "except.h"
+
+#define MAX_TIME 1000000000
+
+/* Mode incremental inliner operate on:
+
+   In ALWAYS_INLINE only functions marked
+   always_inline are inlined.  This mode is used after detecting cycle during
+   flattening.
+
+   In SIZE mode, only functions that reduce function body size after inlining
+   are inlined, this is used during early inlining.
+
+   in ALL mode, everything is inlined.  This is used during flattening.  */
+enum inlining_mode {
+  INLINE_NONE = 0,
+  INLINE_ALWAYS_INLINE,
+  INLINE_SIZE_NORECURSIVE,
+  INLINE_SIZE,
+  INLINE_ALL
+};
+
+static bool
+cgraph_decide_inlining_incrementally (struct cgraph_node *, enum inlining_mode);
+static void cgraph_flatten (struct cgraph_node *node);
+
 
 /* Statistics we collect about inlining algorithm.  */
 static int ncalls_inlined;
 static int nfunctions_inlined;
-static int initial_insns;
-static int overall_insns;
-static int max_insns;
-static gcov_type max_count;
+static int overall_size;
+static gcov_type max_count, max_benefit;
+
+/* Holders of ipa cgraph hooks: */
+static struct cgraph_node_hook_list *function_insertion_hook_holder;
 
-/* Estimate size of the function after inlining WHAT into TO.  */
+static inline struct inline_summary *
+inline_summary (struct cgraph_node *node)
+{
+  return &node->local.inline_summary;
+}
+
+/* Estimate self time of the function after inlining WHAT into TO.  */
 
 static int
-cgraph_estimate_size_after_inlining (int times, struct cgraph_node *to,
+cgraph_estimate_time_after_inlining (int frequency, struct cgraph_node *to,
                                     struct cgraph_node *what)
 {
-  int size;
-  tree fndecl = what->decl, arg;
-  int call_insns = PARAM_VALUE (PARAM_INLINE_CALL_COST);
+  gcov_type time = (((gcov_type)what->global.time
+                    - inline_summary (what)->time_inlining_benefit)
+                   * frequency + CGRAPH_FREQ_BASE / 2) / CGRAPH_FREQ_BASE
+                   + to->global.time;
+  if (time < 0)
+    time = 0;
+  if (time > MAX_TIME)
+    time = MAX_TIME;
+  return time;
+}
+
+/* Estimate self time of the function after inlining WHAT into TO.  */
 
-  for (arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
-    call_insns += estimate_move_cost (TREE_TYPE (arg));
-  size = (what->global.insns - call_insns) * times + to->global.insns;
+static 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;
   gcc_assert (size >= 0);
   return size;
 }
 
+/* Scale frequency of NODE edges by FREQ_SCALE and increase loop nest
+   by NEST.  */
+
+static void
+update_noncloned_frequencies (struct cgraph_node *node,
+                             int freq_scale, int nest)
+{
+  struct cgraph_edge *e;
+
+  /* We do not want to ignore high loop nest after freq drops to 0.  */
+  if (!freq_scale)
+    freq_scale = 1;
+  for (e = node->callees; e; e = e->next_callee)
+    {
+      e->loop_nest += nest;
+      e->frequency = e->frequency * (gcov_type) freq_scale / CGRAPH_FREQ_BASE;
+      if (e->frequency > CGRAPH_FREQ_MAX)
+        e->frequency = CGRAPH_FREQ_MAX;
+      if (!e->inline_failed)
+        update_noncloned_frequencies (e->callee, freq_scale, nest);
+    }
+}
+
 /* E is expected to be an edge being inlined.  Clone destination node of
    the edge and redirect it to the new clone.
    DUPLICATE is used for bookkeeping on whether we are actually creating new
    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
-         && flag_unit_at_a_time)
+         && cgraph_can_remove_if_no_direct_calls_p (e->callee)
+         /* 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.  */
+         && !e->callee->same_comdat_group
+         && !cgraph_new_nodes)
        {
          gcc_assert (!e->callee->global.inlined_to);
-         if (DECL_SAVED_TREE (e->callee->decl))
-           overall_insns -= e->callee->global.insns, nfunctions_inlined++;
+         if (e->callee->analyzed)
+           {
+             overall_size -= e->callee->global.size;
+             nfunctions_inlined++;
+           }
          duplicate = false;
+         e->callee->local.externally_visible = false;
+          update_noncloned_frequencies (e->callee, e->frequency, e->loop_nest);
        }
       else
        {
          struct cgraph_node *n;
-         n = cgraph_clone_node (e->callee, e->count, e->loop_nest, 
-                                update_original);
+         n = cgraph_clone_node (e->callee, e->count, e->frequency, e->loop_nest,
+                                update_original, NULL);
          cgraph_redirect_edge_callee (e, n);
        }
     }
@@ -201,8 +279,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;
 
@@ -212,23 +292,25 @@ 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;
+  int old_size = 0, new_size = 0;
   struct cgraph_node *to = NULL, *what;
-
-  if (e->callee->inline_decl)
-    cgraph_redirect_edge_callee (e, cgraph_node (e->callee->inline_decl));
+  struct cgraph_edge *curr = e;
+  int freq;
 
   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;
 
@@ -236,34 +318,37 @@ cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original)
 
   what = e->callee;
 
+  freq = e->frequency;
   /* Now update size of caller and all functions caller is inlined into.  */
   for (;e && !e->inline_failed; e = e->caller->callers)
     {
-      old_insns = e->caller->global.insns;
-      new_insns = cgraph_estimate_size_after_inlining (1, e->caller,
-                                                      what);
-      gcc_assert (new_insns >= 0);
       to = e->caller;
-      to->global.insns = new_insns;
+      old_size = e->caller->global.size;
+      new_size = cgraph_estimate_size_after_inlining (1, to, what);
+      to->global.size = new_size;
+      to->global.time = cgraph_estimate_time_after_inlining (freq, to, what);
     }
   gcc_assert (what->global.inlined_to == to);
-  if (new_insns > old_insns)
-    overall_insns += new_insns - old_insns;
+  if (new_size > old_size)
+    overall_size += new_size - old_size;
   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.
-   Return following unredirected edge in the list of callers
-   of EDGE->CALLEE  */
+/* Mark all calls of EDGE->CALLEE inlined into EDGE->CALLER.  */
 
-static struct cgraph_edge *
+static void
 cgraph_mark_inline (struct cgraph_edge *edge)
 {
   struct cgraph_node *to = edge->caller;
   struct cgraph_node *what = edge->callee;
   struct cgraph_edge *e, *next;
-  int times = 0;
 
+  gcc_assert (!edge->call_stmt_cannot_inline_p);
   /* Look for all calls, mark them inline and clone recursively
      all inlined functions.  */
   for (e = what->callers; e; e = next)
@@ -271,14 +356,11 @@ 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;
-         times++;
        }
     }
-  gcc_assert (times);
-  return edge;
 }
 
 /* Estimate the growth caused by inlining NODE into all callees.  */
@@ -288,26 +370,34 @@ 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.size);
+    }
 
-  /* ??? 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))
-    growth -= node->global.insns;
+  /* ??? 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 (cgraph_only_called_directly_p (node)
+      && !DECL_EXTERNAL (node->decl) && !self_recursive)
+    growth -= node->global.size;
 
   node->global.estimated_growth = growth;
   return growth;
 }
 
 /* Return false when inlining WHAT into TO is not good idea
-   as it would cause too large growth of function bodies.  
+   as it would cause too large growth of function bodies.
    When ONE_ONLY is true, assume that only one call site is going
    to be inlined, otherwise figure out how many call sites in
    TO calls WHAT and verify that all can be inlined.
@@ -315,7 +405,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;
@@ -335,37 +425,37 @@ 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_size > inline_summary(what)->self_size)
+    limit = inline_summary (to)->self_size;
   else
-    limit = what->local.self_insns;
+    limit = inline_summary (what)->self_size;
 
   limit += limit * PARAM_VALUE (PARAM_LARGE_FUNCTION_GROWTH) / 100;
 
   /* Check the size after inlining against the function limits.  But allow
      the function to shrink if it went over the limits by forced inlining.  */
   newsize = cgraph_estimate_size_after_inlining (times, to, what);
-  if (newsize >= to->global.insns
+  if (newsize >= to->global.size
       && newsize > PARAM_VALUE (PARAM_LARGE_FUNCTION_INSNS)
       && 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;
@@ -373,42 +463,43 @@ 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 (n->local.disregard_inline_limits)
+    return true;
+
+  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 (!n->analyzed)
     {
       if (reason)
-       *reason = N_("function body not available");
+       *reason = CIF_BODY_NOT_AVAILABLE;
       return false;
     }
 
   if (DECL_DECLARED_INLINE_P (decl))
     {
-      if (n->global.insns >= MAX_INLINE_INSNS_SINGLE)
+      if (n->global.size >= MAX_INLINE_INSNS_SINGLE)
        {
          if (reason)
-           *reason = N_("--param max-inline-insns-single limit reached");
+           *reason = CIF_MAX_INLINE_INSNS_SINGLE_LIMIT;
          return false;
        }
     }
   else
     {
-      if (n->global.insns >= MAX_INLINE_INSNS_AUTO)
+      if (n->global.size >= MAX_INLINE_INSNS_AUTO)
        {
          if (reason)
-           *reason = N_("--param max-inline-insns-auto limit reached");
+           *reason = CIF_MAX_INLINE_INSNS_AUTO_LIMIT;
          return false;
        }
     }
@@ -423,7 +514,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)
@@ -434,63 +525,136 @@ 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;
-  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
    metrics may accurately depend on values such as number of inlinable callers
-   of the function or function body size.
-
-   With profiling we use number of executions of each edge to drive the cost.
-   We also should distinguish hot and cold calls where the cold calls are
-   inlined into only when code size is overall improved.  
-   */
+   of the function or function body size.  */
 
 static int
-cgraph_edge_badness (struct cgraph_edge *edge)
+cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
 {
-  if (max_count)
+  gcov_type badness;
+  int growth =
+    (cgraph_estimate_size_after_inlining (1, edge->caller, edge->callee)
+     - edge->caller->global.size);
+
+  if (dump)
+    {
+      fprintf (dump_file, "    Badness calculcation for %s -> %s\n",
+              cgraph_node_name (edge->caller),
+              cgraph_node_name (edge->callee));
+      fprintf (dump_file, "      growth %i, time %i-%i, size %i-%i\n",
+              growth,
+              edge->callee->global.time,
+              inline_summary (edge->callee)->time_inlining_benefit,
+              edge->callee->global.size,
+              inline_summary (edge->callee)->size_inlining_benefit);
+    }
+
+  /* Always prefer inlining saving code size.  */
+  if (growth <= 0)
+    {
+      badness = INT_MIN - growth;
+      if (dump)
+       fprintf (dump_file, "      %i: Growth %i < 0\n", (int) badness,
+                growth);
+    }
+
+  /* When profiling is available, base priorities -(#calls / growth).
+     So we optimize for overall number of "executed" inlined calls.  */
+  else if (max_count)
+    {
+      badness =
+       ((int)
+        ((double) edge->count * INT_MIN / max_count / (max_benefit + 1)) *
+        (inline_summary (edge->callee)->time_inlining_benefit + 1)) / growth;
+      if (dump)
+       {
+         fprintf (dump_file,
+                  "      %i (relative %f): profile info. Relative count %f"
+                  " * Relative benefit %f\n",
+                  (int) badness, (double) badness / INT_MIN,
+                  (double) edge->count / max_count,
+                  (double) (inline_summary (edge->callee)->
+                            time_inlining_benefit + 1) / (max_benefit + 1));
+       }
+    }
+
+  /* When function local profile is available, base priorities on
+     growth / frequency, so we optimize for overall frequency of inlined
+     calls.  This is not too accurate since while the call might be frequent
+     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 bias the
+     priorities slightly in this direction (so fewer times called functions
+     of the same size gets priority).  */
+  else if (flag_guess_branch_prob)
+    {
+      int div = edge->frequency * 100 / CGRAPH_FREQ_BASE + 1;
+      int benefitperc;
+      int growth_for_all;
+      badness = growth * 10000;
+      benefitperc =
+       MIN (100 * inline_summary (edge->callee)->time_inlining_benefit /
+            (edge->callee->global.time + 1) +1, 100);
+      div *= benefitperc;
+
+
+      /* Decrease badness if call is nested.  */
+      /* Compress the range so we don't overflow.  */
+      if (div > 10000)
+       div = 10000 + ceil_log2 (div) - 8;
+      if (div < 1)
+       div = 1;
+      if (badness > 0)
+       badness /= div;
+      growth_for_all = cgraph_estimate_growth (edge->callee);
+      badness += growth_for_all;
+      if (badness > INT_MAX)
+       badness = INT_MAX;
+      if (dump)
+       {
+         fprintf (dump_file,
+                  "      %i: guessed profile. frequency %i, overall growth %i,"
+                  " benefit %i%%, divisor %i\n",
+                  (int) badness, edge->frequency, growth_for_all, benefitperc, div);
+       }
+    }
+  /* When function local profile is not available or it does not give
+     useful information (ie frequency is zero), base the cost on
+     loop nest and overall size growth, so we optimize for overall number
+     of functions fully inlined in program.  */
+  else
     {
-      int growth =
-       cgraph_estimate_size_after_inlining (1, edge->caller, edge->callee);
-      growth -= edge->caller->global.insns;
-
-      /* Always prefer inlining saving code size.  */
-      if (growth <= 0)
-       return INT_MIN - growth;
-      return ((int)((double)edge->count * INT_MIN / max_count)) / growth;
+      int nest = MIN (edge->loop_nest, 8);
+      badness = cgraph_estimate_growth (edge->callee) * 256;
+
+      /* Decrease badness if call is nested.  */
+      if (badness > 0)
+       badness >>= nest;
+      else
+       {
+         badness <<= nest;
+       }
+      if (dump)
+       fprintf (dump_file, "      %i: no profile. nest %i\n", (int) badness,
+                nest);
     }
+
+  /* Ensure that we did not overflow in all the fixed point math above.  */
+  gcc_assert (badness >= INT_MIN);
+  gcc_assert (badness <= INT_MAX - 1);
+  /* Make recursive inlining happen always after other inlining is done.  */
+  if (cgraph_recursive_inlining_p (edge->caller, edge->callee, NULL))
+    return badness + 1;
   else
-  {
-    int nest = MIN (edge->loop_nest, 8);
-    int badness = cgraph_estimate_growth (edge->callee) * 256;
-
-    /* Decrease badness if call is nested.  */
-    if (badness > 0)    
-      badness >>= nest;
-    else
-      badness <<= nest;
-
-    /* Make recursive inlining happen always after other inlining is done.  */
-    if (cgraph_recursive_inlining_p (edge->caller, edge->callee, NULL))
-      return badness + 1;
-    else
-      return badness;
-  }
+    return badness;
 }
 
 /* Recompute heap nodes for each of caller edge.  */
@@ -500,7 +664,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)
@@ -518,7 +682,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;
@@ -529,18 +693,22 @@ update_caller_keys (fibheap_t heap, struct cgraph_node *node,
   for (edge = node->callers; edge; edge = edge->next_caller)
     if (edge->inline_failed)
       {
-       int badness = cgraph_edge_badness (edge);
+       int badness = cgraph_edge_badness (edge, false);
        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;
 
            /* fibheap_replace_key only increase the keys.  */
-           if (fibheap_replace_key (heap, n, badness))
-             continue;
-           fibheap_delete_node (heap, edge->aux);
+           if (badness < n->key)
+             {
+               fibheap_replace_key (heap, n, badness);
+               gcc_assert (n->key == badness);
+               continue;
+             }
+           fibheap_delete_node (heap, (fibnode_t) edge->aux);
          }
        edge->aux = fibheap_insert (heap, badness, edge);
       }
@@ -587,70 +755,14 @@ lookup_recursive_calls (struct cgraph_node *node, struct cgraph_node *where,
       lookup_recursive_calls (node, e->callee, heap);
 }
 
-/* Find callgraph nodes closing a circle in the graph.  The
-   resulting hashtab can be used to avoid walking the circles.
-   Uses the cgraph nodes ->aux field which needs to be zero
-   before and will be zero after operation.  */
-
-static void
-cgraph_find_cycles (struct cgraph_node *node, htab_t cycles)
-{
-  struct cgraph_edge *e;
-
-  if (node->aux)
-    {
-      void **slot;
-      slot = htab_find_slot (cycles, node, INSERT);
-      if (!*slot)
-       {
-         if (dump_file)
-           fprintf (dump_file, "Cycle contains %s\n", cgraph_node_name (node));
-         *slot = node;
-       }
-      return;
-    }
-
-  node->aux = node;
-  for (e = node->callees; e; e = e->next_callee)
-    cgraph_find_cycles (e->callee, cycles); 
-  node->aux = 0;
-}
-
-/* Flatten the cgraph node.  We have to be careful in recursing
-   as to not run endlessly in circles of the callgraph.
-   We do so by using a hashtab of cycle entering nodes as generated
-   by cgraph_find_cycles.  */
-
-static void
-cgraph_flatten_node (struct cgraph_node *node, htab_t cycles)
-{
-  struct cgraph_edge *e;
-
-  for (e = node->callees; e; e = e->next_callee)
-    {
-      /* Inline call, if possible, and recurse.  Be sure we are not
-        entering callgraph circles here.  */
-      if (e->inline_failed
-         && e->callee->local.inlinable
-         && !cgraph_recursive_inlining_p (node, e->callee,
-                                          &e->inline_failed)
-         && !htab_find (cycles, e->callee))
-       {
-         if (dump_file)
-           fprintf (dump_file, " inlining %s", cgraph_node_name (e->callee));
-          cgraph_mark_inline_edge (e, true);
-         cgraph_flatten_node (e->callee, cycles);
-       }
-      else if (dump_file)
-       fprintf (dump_file, " !inlining %s", cgraph_node_name (e->callee));
-    }
-}
-
 /* 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);
@@ -661,6 +773,16 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
   int depth = 0;
   int n = 0;
 
+  /* It does not make sense to recursively inline always-inline functions
+     as we are going to sorry() on the remaining calls anyway.  */
+  if (node->local.disregard_inline_limits
+      && lookup_attribute ("always_inline", DECL_ATTRIBUTES (node->decl)))
+    return false;
+
+  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))
     {
       limit = PARAM_VALUE (PARAM_MAX_INLINE_INSNS_RECURSIVE);
@@ -680,12 +802,13 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
     }
 
   if (dump_file)
-    fprintf (dump_file, 
+    fprintf (dump_file,
             "  Performing recursive inlining on %s\n",
             cgraph_node_name (node));
 
   /* We need original clone to copy around.  */
-  master_clone = cgraph_clone_node (node, node->count, 1, false);
+  master_clone = cgraph_clone_node (node, node->count, CGRAPH_FREQ_BASE, 1,
+                                   false, NULL);
   master_clone->needed = true;
   for (e = master_clone->callees; e; e = e->next_callee)
     if (!e->inline_failed)
@@ -696,7 +819,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;
@@ -707,8 +831,8 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
       if (depth > max_depth)
        {
           if (dump_file)
-           fprintf (dump_file, 
-                    "   maxmal depth reached\n");
+           fprintf (dump_file,
+                    "   maximal depth reached\n");
          continue;
        }
 
@@ -723,7 +847,7 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
           if (curr->count * 100 / node->count < probability)
            {
              if (dump_file)
-               fprintf (dump_file, 
+               fprintf (dump_file,
                         "   Probability of edge is too small\n");
              continue;
            }
@@ -731,7 +855,7 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
 
       if (dump_file)
        {
-         fprintf (dump_file, 
+         fprintf (dump_file,
                   "   Inlining call of depth %i", depth);
          if (node->count)
            {
@@ -741,7 +865,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++;
     }
@@ -750,9 +874,10 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
 
   fibheap_delete (heap);
   if (dump_file)
-    fprintf (dump_file, 
-            "\n   Inlined %i times, body grown from %i to %i insns\n", n,
-            master_clone->global.insns, node->global.insns);
+    fprintf (dump_file,
+            "\n   Inlined %i times, body grown from size %i to %i, time %i to %i\n", n,
+            master_clone->global.size, node->global.size,
+            master_clone->global.time, node->global.time);
 
   /* Remove master clone we used for inlining.  We rely that clones inlined
      into master clone gets queued just before master clone so we don't
@@ -775,17 +900,46 @@ 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;
 }
 
+/* Given whole compilation unit estimate of INSNS, compute how large we can
+   allow the unit to grow.  */
+static int
+compute_max_insns (int insns)
+{
+  int max_insns = insns;
+  if (max_insns < PARAM_VALUE (PARAM_LARGE_UNIT_INSNS))
+    max_insns = PARAM_VALUE (PARAM_LARGE_UNIT_INSNS);
+
+  return ((HOST_WIDEST_INT) max_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, false), 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.
@@ -798,9 +952,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_size, max_size;
+  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");
@@ -809,8 +968,7 @@ cgraph_decide_inlining_of_small_functions (void)
 
   for (node = cgraph_nodes; node; node = node->next)
     {
-      if (!node->local.inlinable || !node->callers
-         || node->local.disregard_inline_limits)
+      if (!node->local.inlinable || !node->callers)
        continue;
       if (dump_file)
        fprintf (dump_file, "Considering inline candidate %s.\n", cgraph_node_name (node));
@@ -826,38 +984,56 @@ cgraph_decide_inlining_of_small_functions (void)
        if (edge->inline_failed)
          {
            gcc_assert (!edge->aux);
-           edge->aux = fibheap_insert (heap, cgraph_edge_badness (edge), edge);
+           edge->aux = fibheap_insert (heap, cgraph_edge_badness (edge, false), edge);
          }
     }
-  while (overall_insns <= max_insns && (edge = fibheap_extract_min (heap)))
+
+  max_size = compute_max_insns (overall_size);
+  min_size = overall_size;
+
+  while (overall_size <= max_size
+        && !fibheap_empty (heap))
     {
-      int old_insns = overall_insns;
-      struct cgraph_node *where;
-      int growth =
-       cgraph_estimate_size_after_inlining (1, edge->caller, edge->callee);
+      int old_size = overall_size;
+      struct cgraph_node *where, *callee;
+      int badness = fibheap_min_key (heap);
+      int growth;
+      cgraph_inline_failed_t not_good = CIF_OK;
+
+      edge = (struct cgraph_edge *) fibheap_extract_min (heap);
+      gcc_assert (edge->aux);
+      edge->aux = NULL;
+      if (!edge->inline_failed)
+       continue;
+#ifdef ENABLE_CHECKING
+      gcc_assert (cgraph_edge_badness (edge, false) == badness);
+#endif
+      callee = edge->callee;
 
-      growth -= edge->caller->global.insns;
+      growth = (cgraph_estimate_size_after_inlining (1, edge->caller, edge->callee)
+               - edge->caller->global.size);
 
       if (dump_file)
        {
-         fprintf (dump_file, 
-                  "\nConsidering %s with %i insns\n",
+         fprintf (dump_file,
+                  "\nConsidering %s with %i size\n",
                   cgraph_node_name (edge->callee),
-                  edge->callee->global.insns);
-         fprintf (dump_file, 
-                  " to be inlined into %s\n"
+                  edge->callee->global.size);
+         fprintf (dump_file,
+                  " to be inlined into %s in %s:%i\n"
                   " Estimated growth after inlined into all callees is %+i insns.\n"
-                  " Estimated badness is %i.\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),
                   cgraph_estimate_growth (edge->callee),
-                  cgraph_edge_badness (edge));
+                  badness,
+                  edge->frequency / (double)CGRAPH_FREQ_BASE);
          if (edge->count)
            fprintf (dump_file," Called "HOST_WIDEST_INT_PRINT_DEC"x\n", edge->count);
+         if (dump_flags & TDF_DETAILS)
+           cgraph_edge_badness (edge, true);
        }
-      gcc_assert (edge->aux);
-      edge->aux = NULL;
-      if (!edge->inline_failed)
-       continue;
 
       /* When not having profile info ready we don't weight by any way the
          position of call in procedure itself.  This means if call of
@@ -867,8 +1043,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)
@@ -880,22 +1061,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) && 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;
        }
@@ -905,33 +1094,50 @@ 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))
+       {
+         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 (!cgraph_check_inline_limits (edge->caller, edge->callee,
-                                          &edge->inline_failed, true))
+         if (edge->call_stmt_cannot_inline_p
+             || !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;
@@ -945,31 +1151,157 @@ cgraph_decide_inlining_of_small_functions (void)
         called by function we inlined (since number of it inlinable callers
         might change).  */
       update_caller_keys (heap, where, updated_nodes);
+
+      /* We removed one call of the function we just inlined.  If offline
+        copy is still needed, be sure to update the keys.  */
+      if (callee != where && !callee->global.inlined_to)
+        update_caller_keys (heap, callee, updated_nodes);
       bitmap_clear (updated_nodes);
 
       if (dump_file)
        {
-         fprintf (dump_file, 
-                  " Inlined into %s which now has %i insns,"
-                  "net change of %+i insns.\n",
+         fprintf (dump_file,
+                  " Inlined into %s which now has size %i and self time %i,"
+                  "net change of %+i.\n",
                   cgraph_node_name (edge->caller),
-                  edge->caller->global.insns,
-                  overall_insns - old_insns);
+                  edge->caller->global.time,
+                  edge->caller->global.size,
+                  overall_size - old_size);
+       }
+      if (min_size > overall_size)
+       {
+         min_size = overall_size;
+         max_size = compute_max_insns (min_size);
+
+         if (dump_file)
+           fprintf (dump_file, "New minimal size reached: %i\n", min_size);
        }
     }
-  while ((edge = fibheap_extract_min (heap)) != NULL)
+  while (!fibheap_empty (heap))
     {
+      int badness = fibheap_min_key (heap);
+
+      edge = (struct cgraph_edge *) fibheap_extract_min (heap);
       gcc_assert (edge->aux);
       edge->aux = NULL;
+      if (!edge->inline_failed)
+       continue;
+#ifdef ENABLE_CHECKING
+      gcc_assert (cgraph_edge_badness (edge, false) == badness);
+#endif
+      if (dump_file)
+       {
+         fprintf (dump_file,
+                  "\nSkipping %s with %i size\n",
+                  cgraph_node_name (edge->callee),
+                  edge->callee->global.size);
+         fprintf (dump_file,
+                  " called by %s in %s:%i\n"
+                  " 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),
+                  cgraph_estimate_growth (edge->callee),
+                  badness,
+                  edge->frequency / (double)CGRAPH_FREQ_BASE);
+         if (edge->count)
+           fprintf (dump_file," Called "HOST_WIDEST_INT_PRINT_DEC"x\n", edge->count);
+         if (dump_flags & TDF_DETAILS)
+           cgraph_edge_badness (edge, true);
+       }
       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);
 }
 
+/* Flatten NODE from the IPA inliner.  */
+
+static void
+cgraph_flatten (struct cgraph_node *node)
+{
+  struct cgraph_edge *e;
+
+  /* We shouldn't be called recursively when we are being processed.  */
+  gcc_assert (node->aux == NULL);
+
+  node->aux = (void *)(size_t) INLINE_ALL;
+
+  for (e = node->callees; e; e = e->next_callee)
+    {
+      struct cgraph_node *orig_callee;
+
+      if (e->call_stmt_cannot_inline_p)
+       continue;
+
+      if (!e->callee->analyzed)
+       {
+         if (dump_file)
+           fprintf (dump_file,
+                    "Not inlining: Function body not available.\n");
+         continue;
+       }
+
+      /* We've hit cycle?  It is time to give up.  */
+      if (e->callee->aux)
+       {
+         if (dump_file)
+           fprintf (dump_file,
+                    "Not inlining %s into %s to avoid cycle.\n",
+                    cgraph_node_name (e->callee),
+                    cgraph_node_name (e->caller));
+         e->inline_failed = CIF_RECURSIVE_INLINING;
+         continue;
+       }
+
+      /* When the edge is already inlined, we just need to recurse into
+        it in order to fully flatten the leaves.  */
+      if (!e->inline_failed)
+       {
+         cgraph_flatten (e->callee);
+         continue;
+       }
+
+      if (cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed))
+       {
+         if (dump_file)
+           fprintf (dump_file, "Not inlining: recursive call.\n");
+         continue;
+       }
+
+      if (!tree_can_inline_p (e))
+       {
+         if (dump_file)
+           fprintf (dump_file, "Not inlining: %s",
+                    cgraph_inline_failed_string (e->inline_failed));
+         continue;
+       }
+
+      /* Inline the edge and flatten the inline clone.  Avoid
+         recursing through the original node if the node was cloned.  */
+      if (dump_file)
+       fprintf (dump_file, " Inlining %s into %s.\n",
+                cgraph_node_name (e->callee),
+                cgraph_node_name (e->caller));
+      orig_callee = e->callee;
+      cgraph_mark_inline_edge (e, true, NULL);
+      if (e->callee != orig_callee)
+       orig_callee->aux = (void *)(size_t) INLINE_ALL;
+      cgraph_flatten (e->callee);
+      if (e->callee != orig_callee)
+       orig_callee->aux = NULL;
+    }
+
+  node->aux = NULL;
+}
+
 /* Decide on the inlining.  We do so in the topological order to avoid
    expenses on updating data structures.  */
 
@@ -980,286 +1312,329 @@ cgraph_decide_inlining (void)
   int nnodes;
   struct cgraph_node **order =
     XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
-  int old_insns = 0;
+  int old_size = 0;
   int i;
+  int initial_size = 0;
+
+  cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
+  if (in_lto_p && flag_indirect_inlining)
+    ipa_update_after_lto_read ();
 
   max_count = 0;
+  max_benefit = 0;
   for (node = cgraph_nodes; node; node = node->next)
-    if (node->analyzed && (node->needed || node->reachable))
+    if (node->analyzed)
       {
        struct cgraph_edge *e;
 
-       initial_insns += node->local.self_insns;
-       gcc_assert (node->local.self_insns == node->global.insns);
+       gcc_assert (inline_summary (node)->self_size == node->global.size);
+       initial_size += node->global.size;
        for (e = node->callees; e; e = e->next_callee)
          if (max_count < e->count)
            max_count = e->count;
+       if (max_benefit < inline_summary (node)->time_inlining_benefit)
+         max_benefit = inline_summary (node)->time_inlining_benefit;
       }
-  overall_insns = initial_insns;
-  gcc_assert (!max_count || (profile_info && flag_branch_probabilities));
-
-  max_insns = overall_insns;
-  if (max_insns < PARAM_VALUE (PARAM_LARGE_UNIT_INSNS))
-    max_insns = PARAM_VALUE (PARAM_LARGE_UNIT_INSNS);
-
-  max_insns = ((HOST_WIDEST_INT) max_insns
-              * (100 + PARAM_VALUE (PARAM_INLINE_UNIT_GROWTH)) / 100);
+  gcc_assert (in_lto_p
+             || !max_count
+             || (profile_info && flag_branch_probabilities));
+  overall_size = initial_size;
 
   nnodes = cgraph_postorder (order);
 
   if (dump_file)
     fprintf (dump_file,
-            "\nDeciding on inlining.  Starting with %i insns.\n",
-            initial_insns);
+            "\nDeciding on inlining.  Starting with size %i.\n",
+            initial_size);
 
   for (node = cgraph_nodes; node; node = node->next)
     node->aux = 0;
 
   if (dump_file)
-    fprintf (dump_file, "\nInlining always_inline functions:\n");
+    fprintf (dump_file, "\nFlattening functions:\n");
 
-  /* In the first pass mark all always_inline edges.  Do this with a priority
-     so none of our later choices will make this impossible.  */
+  /* In the first pass handle functions to be flattened.  Do this with
+     a priority so none of our later choices will make this impossible.  */
   for (i = nnodes - 1; i >= 0; i--)
     {
-      struct cgraph_edge *e, *next;
-
       node = order[i];
 
-      /* Handle nodes to be flattened, but don't update overall unit size.  */
-      if (lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
-        {
-         int old_overall_insns = overall_insns;
-         htab_t cycles;
-         if (dump_file)
-           fprintf (dump_file,
-                    "Flattening %s\n", cgraph_node_name (node));
-         cycles = htab_create (7, htab_hash_pointer, htab_eq_pointer, NULL);
-         cgraph_find_cycles (node, cycles);
-         cgraph_flatten_node (node, cycles);
-         htab_delete (cycles);
-         overall_insns = old_overall_insns;
-         /* We don't need to consider always_inline functions inside the flattened
-            function anymore.  */
-         continue;
-        }
-
-      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)
+      /* Handle nodes to be flattened, but don't update overall unit
+        size.  Calling the incremental inliner here is lame,
+        a simple worklist should be enough.  What should be left
+        here from the early inliner (if it runs) is cyclic cases.
+        Ideally when processing callees we stop inlining at the
+        entry of cycles, possibly cloning that entry point and
+        try to flatten itself turning it into a self-recursive
+        function.  */
+      if (lookup_attribute ("flatten",
+                           DECL_ATTRIBUTES (node->decl)) != NULL)
        {
-         next = e->next_caller;
-         if (!e->inline_failed)
-           continue;
-         if (cgraph_recursive_inlining_p (e->caller, e->callee,
-                                          &e->inline_failed))
-           continue;
-         cgraph_mark_inline_edge (e, 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);
+           fprintf (dump_file,
+                    "Flattening %s\n", cgraph_node_name (node));
+         cgraph_flatten (node);
        }
-      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
-             && !DECL_EXTERNAL (node->decl) && !DECL_COMDAT (node->decl))
+         if (node->callers
+             && !node->callers->next_caller
+             && cgraph_only_called_directly_p (node)
+             && node->local.inlinable
+             && 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))
            {
+             cgraph_inline_failed_t reason;
+             old_size = overall_size;
              if (dump_file)
                {
                  fprintf (dump_file,
-                          "\nConsidering %s %i insns.\n",
-                          cgraph_node_name (node), node->global.insns);
+                          "\nConsidering %s size %i.\n",
+                          cgraph_node_name (node), node->global.size);
                  fprintf (dump_file,
                           " Called once from %s %i insns.\n",
                           cgraph_node_name (node->callers->caller),
-                          node->callers->caller->global.insns);
+                          node->callers->caller->global.size);
                }
 
-             old_insns = overall_insns;
-
              if (cgraph_check_inline_limits (node->callers->caller, node,
-                                             NULL, false))
+                                             &reason, false))
                {
                  cgraph_mark_inline (node->callers);
                  if (dump_file)
                    fprintf (dump_file,
-                            " Inlined into %s which now has %i insns"
-                            " for a net change of %+i insns.\n",
+                            " 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.insns,
-                            overall_insns - old_insns);
+                            node->callers->caller->global.size,
+                            overall_size - old_size);
                }
              else
                {
                  if (dump_file)
                    fprintf (dump_file,
-                            " Inline limit reached, not inlined.\n");
+                            " Not inlining: %s.\n",
+                             cgraph_inline_failed_string (reason));
                }
            }
        }
     }
 
+  /* 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, "
-            "%i insns turned to %i insns.\n\n",
-            ncalls_inlined, nfunctions_inlined, initial_insns,
-            overall_insns);
+            "size %i turned to %i size.\n\n",
+            ncalls_inlined, nfunctions_inlined, initial_size,
+            overall_size);
   free (order);
   return 0;
 }
 
-enum inlining_mode {
-  INLINE_SIZE,
-  INLINE_SPEED,
-  INLINE_ALL
-};
+/* Return true when N is leaf function.  Accept cheap (pure&const) 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)))
+      return false;
+  return true;
+}
 
 /* Decide on the inlining.  We do so in the topological order to avoid
    expenses on updating data structures.  */
 
-static unsigned int
-cgraph_decide_inlining_incrementally (struct cgraph_node *node, enum inlining_mode mode)
+static bool
+cgraph_decide_inlining_incrementally (struct cgraph_node *node,
+                                     enum inlining_mode mode)
 {
   struct cgraph_edge *e;
   bool inlined = false;
-  const char *failed_reason;
-  unsigned int todo = 0;
+  cgraph_inline_failed_t failed_reason;
 
 #ifdef ENABLE_CHECKING
   verify_cgraph_node (node);
 #endif
-  if (lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
+
+  if (mode != INLINE_ALWAYS_INLINE && mode != INLINE_SIZE_NORECURSIVE
+      && lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
     {
       if (dump_file)
-       fprintf (dump_file, "  Flattening %s\n", cgraph_node_name (node));
+       fprintf (dump_file, "Incrementally flattening %s\n",
+                cgraph_node_name (node));
       mode = INLINE_ALL;
     }
 
   /* First of all look for always inline functions.  */
-  for (e = node->callees; e; e = e->next_callee)
-    if ((e->callee->local.disregard_inline_limits
-        || (mode == INLINE_ALL && e->callee->local.inlinable))
-       && e->inline_failed
-       && (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
-           == gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
-        && !cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)
-       /* ??? It is possible that renaming variable removed the function body
-          in duplicate_decls. See gcc.c-torture/compile/20011119-2.c  */
-       && (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
+  if (mode != INLINE_SIZE_NORECURSIVE)
+    for (e = node->callees; e; e = e->next_callee)
       {
-        if (dump_file)
+       if (!e->callee->local.disregard_inline_limits
+           && (mode != INLINE_ALL || !e->callee->local.inlinable))
+         continue;
+       if (e->call_stmt_cannot_inline_p)
+         continue;
+       if (dump_file)
+         fprintf (dump_file,
+                  "Considering to always inline inline candidate %s.\n",
+                  cgraph_node_name (e->callee));
+       if (cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed))
          {
-           fprintf (dump_file, "  Inlining always_inline %s",
-                    cgraph_node_name (e->callee));
-           fprintf (dump_file, " into %s\n", cgraph_node_name (node));
+           if (dump_file)
+             fprintf (dump_file, "Not inlining: recursive call.\n");
+           continue;
+         }
+       if (!tree_can_inline_p (e))
+         {
+           if (dump_file)
+             fprintf (dump_file,
+                      "Not inlining: %s",
+                      cgraph_inline_failed_string (e->inline_failed));
+           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;
+         }
+       if (!e->callee->analyzed)
+         {
+           if (dump_file)
+             fprintf (dump_file,
+                      "Not inlining: Function body no longer available.\n");
+           continue;
+         }
+
+       if (dump_file)
+         fprintf (dump_file, " Inlining %s into %s.\n",
+                  cgraph_node_name (e->callee),
+                  cgraph_node_name (e->caller));
        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.  
-
-          Also flattening needs to be done recursively.  */
-       
-        if (!flag_unit_at_a_time || mode == INLINE_ALL)
-          cgraph_decide_inlining_incrementally (e->callee, mode);
-       
        inlined = true;
       }
 
   /* Now do the automatic inlining.  */
-  if (!flag_really_no_inline && mode != INLINE_ALL)
-    for (e = node->callees; e; e = e->next_callee)
-      if (e->callee->local.inlinable
-         && e->inline_failed
-         && !e->callee->local.disregard_inline_limits
-         && !cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)
-         && (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
-             == gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
-         && (mode != INLINE_SIZE
-             || (cgraph_estimate_size_after_inlining (1, e->caller, e->callee)
-                 <= e->caller->global.insns))
-         && cgraph_check_inline_limits (node, e->callee, &e->inline_failed,
-                                        false)
-         && (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
+  if (mode != INLINE_ALL && mode != INLINE_ALWAYS_INLINE
+      /* Never inline regular functions into always-inline functions
+        during incremental inlining.  */
+      && !node->local.disregard_inline_limits)
+    {
+      bitmap visited = BITMAP_ALLOC (NULL);
+      for (e = node->callees; e; e = e->next_callee)
        {
+         int allowed_growth = 0;
+         if (!e->callee->local.inlinable
+             || !e->inline_failed
+             || e->callee->local.disregard_inline_limits)
+           continue;
+         /* We are inlining a function to all call-sites in node
+            or to none.  So visit each candidate only once.  */
+         if (!bitmap_set_bit (visited, e->callee->uid))
+           continue;
+         if (dump_file)
+           fprintf (dump_file, "Considering inline candidate %s.\n",
+                    cgraph_node_name (e->callee));
+         if (cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed))
+           {
+             if (dump_file)
+               fprintf (dump_file, "Not inlining: recursive call.\n");
+             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;
+           }
+
+         if (cgraph_maybe_hot_edge_p (e) && leaf_node_p (e->callee)
+             && optimize_function_for_speed_p (cfun))
+           allowed_growth = PARAM_VALUE (PARAM_EARLY_INLINING_INSNS);
+
+         /* 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 || mode == INLINE_SIZE_NORECURSIVE)
+              || (!flag_inline_functions
+                  && !DECL_DECLARED_INLINE_P (e->callee->decl)))
+             && (cgraph_estimate_size_after_inlining (1, e->caller, e->callee)
+                 > e->caller->global.size + allowed_growth)
+             && cgraph_estimate_growth (e->callee) > allowed_growth)
+           {
+             if (dump_file)
+               fprintf (dump_file,
+                        "Not inlining: code size would grow by %i.\n",
+                        cgraph_estimate_size_after_inlining (1, e->caller,
+                                                             e->callee)
+                        - e->caller->global.size);
+             continue;
+           }
+         if (!cgraph_check_inline_limits (node, e->callee, &e->inline_failed,
+                                          false)
+             || e->call_stmt_cannot_inline_p)
+           {
+             if (dump_file)
+               fprintf (dump_file, "Not inlining: %s.\n",
+                        cgraph_inline_failed_string (e->inline_failed));
+             continue;
+           }
+         if (!e->callee->analyzed)
+           {
+             if (dump_file)
+               fprintf (dump_file,
+                        "Not inlining: Function body no longer available.\n");
+             continue;
+           }
+         if (!tree_can_inline_p (e))
+           {
+             if (dump_file)
+               fprintf (dump_file,
+                        "Not inlining: %s.",
+                        cgraph_inline_failed_string (e->inline_failed));
+             continue;
+           }
          if (cgraph_default_inline_p (e->callee, &failed_reason))
            {
              if (dump_file)
-               {
-                 fprintf (dump_file, "  Inlining %s",
-                          cgraph_node_name (e->callee));
-                 fprintf (dump_file, " into %s\n", cgraph_node_name (node));
-               }
+               fprintf (dump_file, " Inlining %s into %s.\n",
+                        cgraph_node_name (e->callee),
+                        cgraph_node_name (e->caller));
              cgraph_mark_inline (e);
              inlined = true;
-             if (!flag_unit_at_a_time)
-               cgraph_decide_inlining_incrementally (e->callee, mode);
            }
-         else if (!flag_unit_at_a_time)
-           e->inline_failed = failed_reason;
        }
-  if (flag_unit_at_a_time && inlined && !node->global.inlined_to)
-    {
-      timevar_push (TV_INTEGRATION);
-      todo = optimize_inline_calls (current_function_decl);
-      timevar_pop (TV_INTEGRATION);
+      BITMAP_FREE (visited);
     }
-  return todo;
-}
-
-/* When inlining shall be performed.  */
-static bool
-cgraph_gate_inlining (void)
-{
-  return flag_inline_trees;
+  return inlined;
 }
 
-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.  */
@@ -1273,51 +1648,82 @@ static unsigned int
 cgraph_early_inlining (void)
 {
   struct cgraph_node *node = cgraph_node (current_function_decl);
+  unsigned int todo = 0;
+  int iterations = 0;
 
   if (sorrycount || errorcount)
     return 0;
-  return cgraph_decide_inlining_incrementally (node,
-                                              flag_unit_at_a_time
-                                              ? INLINE_SIZE : INLINE_SPEED);
-}
 
-/* When inlining shall be performed.  */
-static bool
-cgraph_gate_early_inlining (void)
-{
-  return flag_inline_trees && flag_early_inlining;
+  if (!optimize
+      || flag_no_inline
+      || !flag_early_inlining)
+    {
+      /* When not optimizing or not inlining inline only always-inline
+        functions.  */
+      cgraph_decide_inlining_incrementally (node, INLINE_ALWAYS_INLINE);
+      timevar_push (TV_INTEGRATION);
+      todo |= optimize_inline_calls (current_function_decl);
+      timevar_pop (TV_INTEGRATION);
+    }
+  else
+    {
+      /* We iterate incremental inlining to get trivial cases of indirect
+        inlining.  */
+      while (iterations < PARAM_VALUE (PARAM_EARLY_INLINER_MAX_ITERATIONS)
+            && cgraph_decide_inlining_incrementally (node,
+                                                     iterations
+                                                     ? INLINE_SIZE_NORECURSIVE
+                                                     : INLINE_SIZE))
+       {
+         timevar_push (TV_INTEGRATION);
+         todo |= optimize_inline_calls (current_function_decl);
+         iterations++;
+         timevar_pop (TV_INTEGRATION);
+       }
+      if (dump_file)
+       fprintf (dump_file, "Iterations: %i\n", iterations);
+    }
+
+  cfun->always_inline_functions_inlined = true;
+
+  return todo;
 }
 
-struct tree_opt_pass pass_early_inline = 
+struct gimple_opt_pass pass_early_inline =
 {
+ {
+  GIMPLE_PASS,
   "einline",                           /* name */
-  cgraph_gate_early_inlining,          /* gate */
+  NULL,                                        /* gate */
   cgraph_early_inlining,               /* execute */
   NULL,                                        /* sub */
   NULL,                                        /* next */
   0,                                   /* static_pass_number */
   TV_INLINE_HEURISTICS,                        /* tv_id */
   0,                                   /* properties_required */
-  PROP_cfg,                            /* properties_provided */
+  0,                                   /* 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
+         && !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 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 */
@@ -1326,113 +1732,401 @@ struct tree_opt_pass pass_ipa_early_inline =
   0,                                   /* static_pass_number */
   TV_INLINE_HEURISTICS,                        /* tv_id */
   0,                                   /* properties_required */
-  PROP_cfg,                            /* properties_provided */
+  0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_cgraph,                    /* todo_flags_finish */
-  0                                    /* letter */
+  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.  */
+
+static bool
+likely_eliminated_by_inlining_p (gimple stmt)
+{
+  enum gimple_code code = gimple_code (stmt);
+  switch (code)
+    {
+      case GIMPLE_RETURN:
+        return true;
+      case GIMPLE_ASSIGN:
+       if (gimple_num_ops (stmt) != 2)
+         return false;
+
+       /* Casts of parameters, loads from parameters passed by reference
+          and stores to return value or parameters are probably free after
+          inlining.  */
+       if (gimple_assign_rhs_code (stmt) == CONVERT_EXPR
+           || gimple_assign_rhs_code (stmt) == NOP_EXPR
+           || gimple_assign_rhs_code (stmt) == VIEW_CONVERT_EXPR
+           || gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS)
+         {
+           tree rhs = gimple_assign_rhs1 (stmt);
+            tree lhs = gimple_assign_lhs (stmt);
+           tree inner_rhs = rhs;
+           tree inner_lhs = lhs;
+           bool rhs_free = false;
+           bool lhs_free = false;
+
+           while (handled_component_p (inner_lhs) || TREE_CODE (inner_lhs) == INDIRECT_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)
+             inner_rhs = TREE_OPERAND (inner_rhs, 0);
+
+
+           if (TREE_CODE (inner_rhs) == PARM_DECL
+               || (TREE_CODE (inner_rhs) == SSA_NAME
+                   && SSA_NAME_IS_DEFAULT_DEF (inner_rhs)
+                   && TREE_CODE (SSA_NAME_VAR (inner_rhs)) == PARM_DECL))
+             rhs_free = true;
+           if (rhs_free && is_gimple_reg (lhs))
+             lhs_free = true;
+           if (((TREE_CODE (inner_lhs) == PARM_DECL
+                 || (TREE_CODE (inner_lhs) == SSA_NAME
+                     && SSA_NAME_IS_DEFAULT_DEF (inner_lhs)
+                     && TREE_CODE (SSA_NAME_VAR (inner_lhs)) == PARM_DECL))
+                && inner_lhs != lhs)
+               || TREE_CODE (inner_lhs) == RESULT_DECL
+               || (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)))
+             rhs_free = true;
+           if (lhs_free && rhs_free)
+             return true;
+         }
+       return false;
+      default:
+       return false;
+    }
+}
+
+/* Compute function body size parameters for NODE.  */
+
+static void
+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;
+  basic_block bb;
+  gimple_stmt_iterator bsi;
+  struct function *my_function = DECL_STRUCT_FUNCTION (node->decl);
+  tree arg;
+  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));
+
+  gcc_assert (my_function && my_function->cfg);
+  FOR_EACH_BB_FN (bb, my_function)
+    {
+      freq = compute_call_stmt_bb_frequency (node->decl, bb);
+      for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
+       {
+         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);
+
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "  freq:%6i size:%3i time:%3i ",
+                      freq, this_size, this_time);
+             print_gimple_stmt (dump_file, stmt, 0, 0);
+           }
+         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");
+           }
+         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);
+  if (dump_file)
+    fprintf (dump_file, "Overall function body time: %i-%i size: %i-%i\n",
+            (int)time, (int)time_inlining_benefit,
+            size, size_inlining_benefit);
+  time_inlining_benefit += eni_time_weights.call_cost;
+  size_inlining_benefit += eni_size_weights.call_cost;
+  if (!VOID_TYPE_P (TREE_TYPE (funtype)))
+    {
+      int cost = estimate_move_cost (TREE_TYPE (funtype));
+      time_inlining_benefit += cost;
+      size_inlining_benefit += cost;
+    }
+  for (arg = DECL_ARGUMENTS (node->decl); arg; arg = TREE_CHAIN (arg))
+    if (!VOID_TYPE_P (TREE_TYPE (arg)))
+      {
+        int cost = estimate_move_cost (TREE_TYPE (arg));
+        time_inlining_benefit += cost;
+        size_inlining_benefit += cost;
+      }
+  if (time_inlining_benefit > MAX_TIME)
+    time_inlining_benefit = MAX_TIME;
+  if (time > MAX_TIME)
+    time = MAX_TIME;
+  inline_summary (node)->self_time = time;
+  inline_summary (node)->self_size = size;
+  if (dump_file)
+    fprintf (dump_file, "With function call overhead time: %i-%i size: %i-%i\n",
+            (int)time, (int)time_inlining_benefit,
+            size, size_inlining_benefit);
+  inline_summary (node)->time_inlining_benefit = time_inlining_benefit;
+  inline_summary (node)->size_inlining_benefit = size_inlining_benefit;
+}
+
 /* 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;
-  node->local.inlinable = tree_inlinable_function_p (current_function_decl);
-  node->local.self_insns = estimate_num_insns (current_function_decl);
-  if (node->local.inlinable)
+
+  /* Can this function be inlined at all?  */
+  node->local.inlinable = tree_inlinable_function_p (node->decl);
+  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 (node->decl);
+  estimate_function_body_sizes (node);
   /* Inlining characteristics are maintained by the cgraph_mark_inline.  */
-  node->global.insns = node->local.self_insns;
+  node->global.time = inline_summary (node)->self_time;
+  node->global.size = inline_summary (node)->self_size;
   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 =
 {
-  NULL,                                        /* name */
-  gate_inline_passes,                  /* gate */
-  compute_inline_parameters,           /* execute */
+ {
+  GIMPLE_PASS,
+  "inline_param",                      /* name */
+  NULL,                                        /* gate */
+  compute_inline_parameters_for_current,/* execute */
   NULL,                                        /* sub */
   NULL,                                        /* next */
   0,                                   /* static_pass_number */
   TV_INLINE_HEURISTICS,                        /* tv_id */
   0,                                   /* properties_required */
-  PROP_cfg,                            /* properties_provided */
+  0,                                   /* 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, false);
+  /* FIXME: Currently the passmanager is adding inline transform more than once to some
+     clones.  This needs revisiting after WPA cleanups.  */
+  if (cfun->after_inlining)
+    return 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)
+  cfun->always_inline_functions_inlined = true;
+  cfun->after_inlining = true;
+  return todo | execute_fixup_cfg ();
+}
+
+/* Read inline summary.  Jump functions are shared among ipa-cp
+   and inliner, so when ipa-cp is active, we don't need to write them
+   twice.  */
+
+static void
+inline_read_summary (void)
+{
+  if (flag_indirect_inlining)
     {
-      struct cgraph_edge *e;
-      for (e = node->callees; e; e = e->next_callee)
-       if (e->callee->analyzed)
-          cgraph_mark_needed_node (e->callee);
+      ipa_register_cgraph_hooks ();
+      if (!flag_ipa_cp)
+        ipa_prop_read_jump_functions ();
     }
-  return todo | execute_fixup_cfg ();
+  function_insertion_hook_holder =
+      cgraph_add_function_insertion_hook (&add_new_function, NULL);
 }
 
-struct tree_opt_pass pass_apply_inline = 
+/* Write inline summary for node in SET.
+   Jump functions are shared among ipa-cp and inliner, so when ipa-cp is
+   active, we don't need to write them twice.  */
+
+static void
+inline_write_summary (cgraph_node_set set)
 {
-  "apply_inline",                      /* name */
-  NULL,                                        /* gate */
-  apply_inline,                                /* execute */
+  if (flag_indirect_inlining && !flag_ipa_cp)
+    ipa_prop_write_jump_functions (set);
+}
+
+/* When to run IPA inlining.  Inlining of always-inline functions
+   happens during early inlining.  */
+
+static bool
+gate_cgraph_decide_inlining (void)
+{
+  /* ???  We'd like to skip this if not optimizing or not inlining as
+     all always-inline functions have been processed by early
+     inlining already.  But this at least breaks EH with C++ as
+     we need to unconditionally run fixup_cfg even at -O0.
+     So leave it on unconditionally for now.  */
+  return 1;
+}
+
+struct ipa_opt_pass_d pass_ipa_inline =
+{
+ {
+  IPA_PASS,
+  "inline",                            /* name */
+  gate_cgraph_decide_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_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 */
+ inline_write_summary,                 /* write_summary */
+ inline_read_summary,                  /* read_summary */
+ NULL,                                 /* function_read_summary */
+ lto_ipa_fixup_call_notes,             /* stmt_fixup */
+ 0,                                    /* TODOs */
+ inline_transform,                     /* function_transform */
+ NULL,                                 /* variable_transform */
 };
 
+
 #include "gt-ipa-inline.h"