ipa-inline.h: New file.
authorJan Hubicka <jh@suse.cz>
Wed, 13 Apr 2011 17:26:50 +0000 (19:26 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Wed, 13 Apr 2011 17:26:50 +0000 (17:26 +0000)
* ipa-inline.h: New file.
* ipa-inline-analysis.c: New file. Broken out of ...
* ipa-inline.c: ... this file; update toplevel comment;
include ipa-inline.h
(inline_summary): Move to ipa-inline.h
(cgraph_estimate_edge_time): Rename to estimate_edge_time; move to
ipa-inline-analysis.c.
(cgraph_estimate_time_after_inlining): Rename to estiamte_time_after_inlining;
move to ipa-inline-analysis.c
(cgraph_estimate_edge_growth): Move to ipa-inline-analysis.c; rename
to estimate_edge_growth.
(cgraph_estimate_size_after_inlining): Move to ipa-inline-analysis.c;
rename to estimate_size_after_inlining.
(cgraph_mark_inline_edge): Update for new naming convention.
(cgraph_check_inline_limits): Likewise.
(cgraph_edge_badness): Likewise.
(cgraph_decide_recursive_inlining): Likewise.
(cgraph_decide_inlining_of_small_functions): Likewise.
(cgraph_decide_inlining_incrementally): Likewise.
(cgraph_estimate_growth): Rename to estimate_growth; move to ipa-inline-analysis.c.
(eliminated_by_inlining_prob): Move to ipa-inline-analysis.c.
(estimate_function_body_sizes): Move to ipa-inline-analysis.c.
(compute_inline_parameters): Likewise.
(compute_inline_parameters_for_current): Likewise.
(pass_inline_parameters): Likewise.
(inline_indirect_intraprocedural_analysis): Likewise.
(analyze_function): Rename to inline_analyze_function; likewise.
(add_new_function): Move to ipa-inline-analysis.c.
(inline_generate_summary): Likewise.
(inline_read_summary): Likewise.
(inline_write_summary): Likewise.
* Makefile.in (ipa-inline-analysis.c): New file.

From-SVN: r172388

gcc/ChangeLog
gcc/Makefile.in
gcc/ipa-inline-analysis.c [new file with mode: 0644]
gcc/ipa-inline.c
gcc/ipa-inline.h [new file with mode: 0644]

index 6847c995c56aa90d40515bd7bbc3d5835cd349a1..4347718324944b8c20045f3f9eed1ada06173e29 100644 (file)
@@ -1,3 +1,38 @@
+2011-04-13  Jan Hubicka  <jh@suse.cz>
+
+       * ipa-inline.h: New file.
+       * ipa-inline-analysis.c: New file. Broken out of ...
+       * ipa-inline.c: ... this file; update toplevel comment;
+       include ipa-inline.h
+       (inline_summary): Move to ipa-inline.h
+       (cgraph_estimate_edge_time): Rename to estimate_edge_time; move to
+       ipa-inline-analysis.c.
+       (cgraph_estimate_time_after_inlining): Rename to estiamte_time_after_inlining;
+       move to ipa-inline-analysis.c
+       (cgraph_estimate_edge_growth): Move to ipa-inline-analysis.c; rename
+       to estimate_edge_growth.
+       (cgraph_estimate_size_after_inlining): Move to ipa-inline-analysis.c;
+       rename to estimate_size_after_inlining.
+       (cgraph_mark_inline_edge): Update for new naming convention.
+       (cgraph_check_inline_limits): Likewise.
+       (cgraph_edge_badness): Likewise.
+       (cgraph_decide_recursive_inlining): Likewise.
+       (cgraph_decide_inlining_of_small_functions): Likewise.
+       (cgraph_decide_inlining_incrementally): Likewise.
+       (cgraph_estimate_growth): Rename to estimate_growth; move to ipa-inline-analysis.c.
+       (eliminated_by_inlining_prob): Move to ipa-inline-analysis.c.
+       (estimate_function_body_sizes): Move to ipa-inline-analysis.c.
+       (compute_inline_parameters): Likewise.
+       (compute_inline_parameters_for_current): Likewise.
+       (pass_inline_parameters): Likewise.
+       (inline_indirect_intraprocedural_analysis): Likewise.
+       (analyze_function): Rename to inline_analyze_function; likewise.
+       (add_new_function): Move to ipa-inline-analysis.c.
+       (inline_generate_summary): Likewise.
+       (inline_read_summary): Likewise.
+       (inline_write_summary): Likewise.
+       * Makefile.in (ipa-inline-analysis.c): New file.
+
 2011-04-13  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
 
        * configure.ac (gcc_cv_as_sparc_gotdata_op): Remove GNU ld check.
index 76b5d6b871b55d498f220c9ad2a1a17150be4453..1a0d98d2625167208de636f5cae50a7427bac936 100644 (file)
@@ -1468,6 +1468,7 @@ OBJS-archive = \
        ipa-cp.o \
         ipa-split.o \
        ipa-inline.o \
+       ipa-inline-analysis.o \
        ipa-prop.o \
        ipa-pure-const.o \
        ipa-reference.o \
@@ -3026,7 +3027,12 @@ ipa-inline.o : ipa-inline.c gt-ipa-inline.h $(CONFIG_H) $(SYSTEM_H) coretypes.h
    $(TREE_H) langhooks.h $(TREE_INLINE_H) $(FLAGS_H) $(CGRAPH_H) intl.h \
    $(DIAGNOSTIC_H) $(FIBHEAP_H) $(PARAMS_H) $(TIMEVAR_H) $(TREE_PASS_H) \
    $(HASHTAB_H) $(COVERAGE_H) $(GGC_H) $(TREE_FLOW_H) $(RTL_H) $(IPA_PROP_H) \
-   $(EXCEPT_H) gimple-pretty-print.h
+   $(EXCEPT_H) gimple-pretty-print.h ipa-inline.h
+ipa-inline-analysis.o : ipa-inline-analysis.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+   $(TREE_H) langhooks.h $(TREE_INLINE_H) $(FLAGS_H) $(CGRAPH_H) intl.h \
+   $(DIAGNOSTIC_H) $(PARAMS_H) $(TIMEVAR_H) $(TREE_PASS_H) \
+   $(HASHTAB_H) $(COVERAGE_H) $(GGC_H) $(TREE_FLOW_H) $(IPA_PROP_H) \
+   gimple-pretty-print.h ipa-inline.h
 ipa-utils.o : ipa-utils.c $(IPA_UTILS_H) $(CONFIG_H) $(SYSTEM_H) \
    coretypes.h $(TM_H) $(TREE_H) $(TREE_FLOW_H) $(TREE_INLINE_H) langhooks.h \
    pointer-set.h $(GGC_H) $(GIMPLE_H) $(SPLAY_TREE_H) \
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
new file mode 100644 (file)
index 0000000..8507c5e
--- /dev/null
@@ -0,0 +1,481 @@
+/* Inlining decision heuristics.
+   Copyright (C) 2003, 2004, 2007, 2008, 2009, 2010, 2011
+   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 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+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 COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Analysis used by the inliner and other passes limiting code size growth.
+
+   We estimate for each function
+     - function body size
+     - function runtime
+     - inlining size benefit (that is how much of function body size
+       and its call sequence is expected to disappear by inlining)
+     - inlining time benefit
+     - function frame size
+   For each call
+     - call sequence size
+
+   inlinie_summary datastructures store above information locally (i.e.
+   parameters of the function itself) and globally (i.e. parameters of
+   the function created by applying all the inline decisions already
+   present in the callgraph).
+
+   We also provide accestor to the inline_summary datastructure and
+   basic logic updating the parameters when inlining is performed. 
+
+   Finally pass_inline_parameters is exported.  This is used to drive
+   computation of function parameters used by the early inliner. IPA
+   inlined performs analysis via its analyze_function method. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "tree-inline.h"
+#include "langhooks.h"
+#include "flags.h"
+#include "cgraph.h"
+#include "diagnostic.h"
+#include "gimple-pretty-print.h"
+#include "timevar.h"
+#include "params.h"
+#include "tree-pass.h"
+#include "coverage.h"
+#include "ggc.h"
+#include "tree-flow.h"
+#include "ipa-prop.h"
+#include "ipa-inline.h"
+
+#define MAX_TIME 1000000000
+
+/* Holders of ipa cgraph hooks: */
+static struct cgraph_node_hook_list *function_insertion_hook_holder;
+
+/* See if statement might disappear after inlining.
+   0 - means not eliminated
+   1 - half of statements goes away
+   2 - for sure it is eliminated.
+   We are not terribly sophisticated, basically looking for simple abstraction
+   penalty wrappers.  */
+
+static int
+eliminated_by_inlining_prob (gimple stmt)
+{
+  enum gimple_code code = gimple_code (stmt);
+  switch (code)
+    {
+      case GIMPLE_RETURN:
+        return 2;
+      case GIMPLE_ASSIGN:
+       if (gimple_num_ops (stmt) != 2)
+         return 0;
+
+       /* Casts of parameters, loads from parameters passed by reference
+          and stores to return value or parameters are often free after
+          inlining dua to SRA and further combining.
+          Assume that half of statements goes away.  */
+       if (gimple_assign_rhs_code (stmt) == CONVERT_EXPR
+           || gimple_assign_rhs_code (stmt) == NOP_EXPR
+           || gimple_assign_rhs_code (stmt) == VIEW_CONVERT_EXPR
+           || 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) == MEM_REF)
+             inner_lhs = TREE_OPERAND (inner_lhs, 0);
+           while (handled_component_p (inner_rhs)
+                  || TREE_CODE (inner_rhs) == ADDR_EXPR
+                  || TREE_CODE (inner_rhs) == MEM_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 1;
+         }
+       return 0;
+      default:
+       return 0;
+    }
+}
+
+
+/* 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;
+  /* Estimate static overhead for function prologue/epilogue and alignment. */
+  int size = 2;
+  /* Benefits are scaled by probability of elimination that is in range
+     <0,2>.  */
+  int size_inlining_benefit = 2 * 2;
+  basic_block bb;
+  gimple_stmt_iterator bsi;
+  struct function *my_function = DECL_STRUCT_FUNCTION (node->decl);
+  int freq;
+
+  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);
+         int prob;
+
+         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;
+         prob = eliminated_by_inlining_prob (stmt);
+         if (prob == 1 && dump_file && (dump_flags & TDF_DETAILS))
+           fprintf (dump_file, "    50%% will be eliminated by inlining\n");
+         if (prob == 2 && dump_file && (dump_flags & TDF_DETAILS))
+           fprintf (dump_file, "    will eliminated by inlining\n");
+         size_inlining_benefit += this_size * prob;
+         time_inlining_benefit += this_time * prob;
+         gcc_assert (time >= 0);
+         gcc_assert (size >= 0);
+       }
+    }
+  time = (time + CGRAPH_FREQ_BASE / 2) / CGRAPH_FREQ_BASE;
+  time_inlining_benefit = ((time_inlining_benefit + CGRAPH_FREQ_BASE)
+                          / (CGRAPH_FREQ_BASE * 2));
+  size_inlining_benefit = (size_inlining_benefit + 1) / 2;
+  if (time_inlining_benefit > MAX_TIME)
+    time_inlining_benefit = MAX_TIME;
+  if (time > MAX_TIME)
+    time = MAX_TIME;
+  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);
+  inline_summary (node)->self_time = time;
+  inline_summary (node)->self_size = size;
+  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.  */
+
+void
+compute_inline_parameters (struct cgraph_node *node)
+{
+  HOST_WIDE_INT self_stack_size;
+  struct cgraph_edge *e;
+
+  gcc_assert (!node->global.inlined_to);
+
+  /* Estimate the stack size for the function if we're optimizing.  */
+  self_stack_size = optimize ? estimated_stack_frame_size (node) : 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 (node->decl);
+  if (!node->local.inlinable)
+    node->local.disregard_inline_limits = 0;
+
+  /* Inlinable functions always can change signature.  */
+  if (node->local.inlinable)
+    node->local.can_change_signature = true;
+  else
+    {
+      /* Functions calling builtin_apply can not change signature.  */
+      for (e = node->callees; e; e = e->next_callee)
+       if (DECL_BUILT_IN (e->callee->decl)
+           && DECL_BUILT_IN_CLASS (e->callee->decl) == BUILT_IN_NORMAL
+           && DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_APPLY_ARGS)
+         break;
+      node->local.can_change_signature = !e;
+    }
+  estimate_function_body_sizes (node);
+  /* Compute size of call statements.  We have to do this for callers here,
+     those sizes need to be present for edges _to_ us as early as
+     we are finished with early opts.  */
+  for (e = node->callers; e; e = e->next_caller)
+    if (e->call_stmt)
+      {
+       e->call_stmt_size
+         = estimate_num_insns (e->call_stmt, &eni_size_weights);
+       e->call_stmt_time
+         = estimate_num_insns (e->call_stmt, &eni_time_weights);
+      }
+  /* Inlining characteristics are maintained by the cgraph_mark_inline.  */
+  node->global.time = inline_summary (node)->self_time;
+  node->global.size = inline_summary (node)->self_size;
+}
+
+
+/* Compute parameters of functions used by inliner using
+   current_function_decl.  */
+
+static unsigned int
+compute_inline_parameters_for_current (void)
+{
+  compute_inline_parameters (cgraph_get_node (current_function_decl));
+  return 0;
+}
+
+struct gimple_opt_pass pass_inline_parameters =
+{
+ {
+  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 */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  0                                    /* todo_flags_finish */
+ }
+};
+
+
+/* Estimate the time cost for the caller when inlining EDGE.  */
+
+static inline int
+estimate_edge_time (struct cgraph_edge *edge)
+{
+  int call_stmt_time;
+  /* ???  We throw away cgraph edges all the time so the information
+     we store in edges doesn't persist for early inlining.  Ugh.  */
+  if (!edge->call_stmt)
+    call_stmt_time = edge->call_stmt_time;
+  else
+    call_stmt_time = estimate_num_insns (edge->call_stmt, &eni_time_weights);
+  return (((gcov_type)edge->callee->global.time
+          - inline_summary (edge->callee)->time_inlining_benefit
+          - call_stmt_time) * edge->frequency
+         + CGRAPH_FREQ_BASE / 2) / CGRAPH_FREQ_BASE;
+}
+
+
+/* Estimate self time of the function NODE after inlining EDGE.  */
+
+int
+estimate_time_after_inlining (struct cgraph_node *node,
+                             struct cgraph_edge *edge)
+{
+  gcov_type time = node->global.time + estimate_edge_time (edge);
+  if (time < 0)
+    time = 0;
+  if (time > MAX_TIME)
+    time = MAX_TIME;
+  return time;
+}
+
+
+/* Estimate the size of NODE after inlining EDGE which should be an
+   edge to either NODE or a call inlined into NODE.  */
+
+int
+estimate_size_after_inlining (struct cgraph_node *node,
+                                    struct cgraph_edge *edge)
+{
+  int size = node->global.size + estimate_edge_growth (edge);
+  gcc_assert (size >= 0);
+  return size;
+}
+
+
+/* Estimate the growth caused by inlining NODE into all callees.  */
+
+int
+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->caller == node)
+        self_recursive = true;
+      if (e->inline_failed)
+       growth += estimate_edge_growth (e);
+    }
+
+  /* ??? 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_will_be_removed_from_program_if_no_direct_calls (node)
+      && !DECL_EXTERNAL (node->decl) && !self_recursive)
+    growth -= node->global.size;
+  /* COMDAT functions are very often not shared across multiple units since they
+     come from various template instantiations.  Take this into account.  */
+  else  if (DECL_COMDAT (node->decl) && !self_recursive
+           && cgraph_can_remove_if_no_direct_calls_p (node))
+    growth -= (node->global.size
+              * (100 - PARAM_VALUE (PARAM_COMDAT_SHARING_PROBABILITY)) + 50) / 100;
+
+  node->global.estimated_growth = growth;
+  return growth;
+}
+
+/* This function performs intraprocedural analysis in NODE that is required to
+   inline indirect calls.  */
+static void
+inline_indirect_intraprocedural_analysis (struct cgraph_node *node)
+{
+  ipa_analyze_node (node);
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      ipa_print_node_params (dump_file, node);
+      ipa_print_node_jump_functions (dump_file, node);
+    }
+}
+
+
+/* Note function body size.  */
+
+static void
+inline_analyze_function (struct cgraph_node *node)
+{
+  push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+  current_function_decl = node->decl;
+
+  compute_inline_parameters (node);
+  /* FIXME: We should remove the optimize check after we ensure we never run
+     IPA passes when not optimizing.  */
+  if (flag_indirect_inlining && optimize)
+    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)
+{
+  inline_analyze_function (node);
+}
+
+
+/* Note function body size.  */
+
+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 ();
+
+  for (node = cgraph_nodes; node; node = node->next)
+    if (node->analyzed)
+      inline_analyze_function (node);
+
+  return;
+}
+
+
+/* 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.  */
+
+void
+inline_read_summary (void)
+{
+  if (flag_indirect_inlining)
+    {
+      ipa_register_cgraph_hooks ();
+      if (!flag_ipa_cp)
+        ipa_prop_read_jump_functions ();
+    }
+  function_insertion_hook_holder =
+      cgraph_add_function_insertion_hook (&add_new_function, NULL);
+}
+
+
+/* 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.  */
+
+void
+inline_write_summary (cgraph_node_set set,
+                     varpool_node_set vset ATTRIBUTE_UNUSED)
+{
+  if (flag_indirect_inlining && !flag_ipa_cp)
+    ipa_prop_write_jump_functions (set);
+}
+
+/* Release inline summary.  */
+
+void
+inline_free_summary (void)
+{
+  cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
+}
index 6f72bfad4fce67c2a81e0223b365c86b573c9fe1..36bc1c2dbd65cffa16fdeade95d7003708d28e0b 100644 (file)
@@ -63,18 +63,7 @@ along with GCC; see the file COPYING3.  If not see
       into account, while cgraph_decide_inlining_incrementally considers
       only one function at a time and is used by early inliner.
 
-   The inliner itself is split into several passes:
-
-   pass_inline_parameters
-
-     This pass computes local properties of functions that are used by inliner:
-     estimated function body size, whether function is inlinable at all and
-     stack frame consumption.
-
-     Before executing any of inliner passes, this local pass has to be applied
-     to each function in the callgraph (ie run as subpass of some earlier
-     IPA pass).  The results are made out of date by any optimization applied
-     on the function body.
+   The inliner itself is split into two passes:
 
    pass_early_inlining
 
@@ -123,6 +112,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl.h"
 #include "ipa-prop.h"
 #include "except.h"
+#include "ipa-inline.h"
 
 #define MAX_TIME 1000000000
 
@@ -133,76 +123,6 @@ static int nfunctions_inlined;
 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;
-
-static inline struct inline_summary *
-inline_summary (struct cgraph_node *node)
-{
-  return &node->local.inline_summary;
-}
-
-/* Estimate the time cost for the caller when inlining EDGE.  */
-
-static inline int
-cgraph_estimate_edge_time (struct cgraph_edge *edge)
-{
-  int call_stmt_time;
-  /* ???  We throw away cgraph edges all the time so the information
-     we store in edges doesn't persist for early inlining.  Ugh.  */
-  if (!edge->call_stmt)
-    call_stmt_time = edge->call_stmt_time;
-  else
-    call_stmt_time = estimate_num_insns (edge->call_stmt, &eni_time_weights);
-  return (((gcov_type)edge->callee->global.time
-          - inline_summary (edge->callee)->time_inlining_benefit
-          - call_stmt_time) * edge->frequency
-         + CGRAPH_FREQ_BASE / 2) / CGRAPH_FREQ_BASE;
-}
-
-/* Estimate self time of the function NODE after inlining EDGE.  */
-
-static int
-cgraph_estimate_time_after_inlining (struct cgraph_node *node,
-                                    struct cgraph_edge *edge)
-{
-  gcov_type time = node->global.time + cgraph_estimate_edge_time (edge);
-  if (time < 0)
-    time = 0;
-  if (time > MAX_TIME)
-    time = MAX_TIME;
-  return time;
-}
-
-/* Estimate the growth of the caller when inlining EDGE.  */
-
-static inline int
-cgraph_estimate_edge_growth (struct cgraph_edge *edge)
-{
-  int call_stmt_size;
-  /* ???  We throw away cgraph edges all the time so the information
-     we store in edges doesn't persist for early inlining.  Ugh.  */
-  if (!edge->call_stmt)
-    call_stmt_size = edge->call_stmt_size;
-  else
-    call_stmt_size = estimate_num_insns (edge->call_stmt, &eni_size_weights);
-  return (edge->callee->global.size
-         - inline_summary (edge->callee)->size_inlining_benefit
-         - call_stmt_size);
-}
-
-/* Estimate the size of NODE after inlining EDGE which should be an
-   edge to either NODE or a call inlined into NODE.  */
-
-static inline int
-cgraph_estimate_size_after_inlining (struct cgraph_node *node,
-                                    struct cgraph_edge *edge)
-{
-  int size = node->global.size + cgraph_estimate_edge_growth (edge);
-  gcc_assert (size >= 0);
-  return size;
-}
-
 /* Scale frequency of NODE edges by FREQ_SCALE and increase loop nest
    by NEST.  */
 
@@ -329,9 +249,9 @@ cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
     {
       to = e->caller;
       old_size = e->caller->global.size;
-      new_size = cgraph_estimate_size_after_inlining (to, curr);
+      new_size = estimate_size_after_inlining (to, curr);
       to->global.size = new_size;
-      to->global.time = cgraph_estimate_time_after_inlining (to, curr);
+      to->global.time = estimate_time_after_inlining (to, curr);
     }
   gcc_assert (curr->callee->global.inlined_to == to);
   if (new_size > old_size)
@@ -346,44 +266,6 @@ cgraph_mark_inline_edge (struct cgraph_edge *e, bool update_original,
     return false;
 }
 
-/* Estimate the growth caused by inlining NODE into all callees.  */
-
-static int
-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->caller == node)
-        self_recursive = true;
-      if (e->inline_failed)
-       growth += cgraph_estimate_edge_growth (e);
-    }
-
-  /* ??? 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_will_be_removed_from_program_if_no_direct_calls (node)
-      && !DECL_EXTERNAL (node->decl) && !self_recursive)
-    growth -= node->global.size;
-  /* COMDAT functions are very often not shared across multiple units since they
-     come from various template instantiations.  Take this into account.  */
-  else  if (DECL_COMDAT (node->decl) && !self_recursive
-           && cgraph_can_remove_if_no_direct_calls_p (node))
-    growth -= (node->global.size
-              * (100 - PARAM_VALUE (PARAM_COMDAT_SHARING_PROBABILITY)) + 50) / 100;
-
-  node->global.estimated_growth = growth;
-  return growth;
-}
-
 /* Return false when inlining edge E is not good idea
    as it would cause too large growth of the callers function body
    or stack frame size.  *REASON if non-NULL is updated if the
@@ -413,7 +295,7 @@ cgraph_check_inline_limits (struct cgraph_edge *e,
 
   /* 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 (to, e);
+  newsize = estimate_size_after_inlining (to, e);
   if (newsize >= to->global.size
       && newsize > PARAM_VALUE (PARAM_LARGE_FUNCTION_INSNS)
       && newsize > limit)
@@ -507,7 +389,7 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
   if (edge->callee->local.disregard_inline_limits)
     return INT_MIN;
 
-  growth = cgraph_estimate_edge_growth (edge);
+  growth = estimate_edge_growth (edge);
 
   if (dump)
     {
@@ -585,7 +467,7 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
        div = 1;
       if (badness > 0)
        badness /= div;
-      growth_for_all = cgraph_estimate_growth (edge->callee);
+      growth_for_all = estimate_growth (edge->callee);
       badness += growth_for_all;
       if (badness > INT_MAX)
        badness = INT_MAX;
@@ -604,7 +486,7 @@ cgraph_edge_badness (struct cgraph_edge *edge, bool dump)
   else
     {
       int nest = MIN (edge->loop_nest, 8);
-      badness = cgraph_estimate_growth (edge->callee) * 256;
+      badness = estimate_growth (edge->callee) * 256;
 
       /* Decrease badness if call is nested.  */
       if (badness > 0)
@@ -843,7 +725,7 @@ cgraph_decide_recursive_inlining (struct cgraph_edge *edge,
 
   /* Make sure that function is small enough to be considered for inlining.  */
   if (!max_depth
-      || cgraph_estimate_size_after_inlining (node, edge)  >= limit)
+      || estimate_size_after_inlining (node, edge)  >= limit)
     return false;
   heap = fibheap_new ();
   lookup_recursive_calls (node, node, heap);
@@ -873,7 +755,7 @@ cgraph_decide_recursive_inlining (struct cgraph_edge *edge,
        = (struct cgraph_edge *) fibheap_extract_min (heap);
       struct cgraph_node *cnode;
 
-      if (cgraph_estimate_size_after_inlining (node, curr) > limit)
+      if (estimate_size_after_inlining (node, curr) > limit)
        break;
 
       depth = 1;
@@ -1075,7 +957,7 @@ cgraph_decide_inlining_of_small_functions (void)
        }
       
       callee = edge->callee;
-      growth = cgraph_estimate_edge_growth (edge);
+      growth = estimate_edge_growth (edge);
       if (dump_file)
        {
          fprintf (dump_file,
@@ -1090,7 +972,7 @@ cgraph_decide_inlining_of_small_functions (void)
                   flag_wpa ? "unknown"
                   : gimple_filename ((const_gimple) edge->call_stmt),
                   flag_wpa ? -1 : gimple_lineno ((const_gimple) edge->call_stmt),
-                  cgraph_estimate_growth (edge->callee),
+                  estimate_growth (edge->callee),
                   badness,
                   edge->frequency / (double)CGRAPH_FREQ_BASE);
          if (edge->count)
@@ -1142,7 +1024,7 @@ cgraph_decide_inlining_of_small_functions (void)
        not_good = CIF_NOT_DECLARED_INLINED;
       else if (optimize_function_for_size_p (DECL_STRUCT_FUNCTION(edge->caller->decl)))
        not_good = CIF_OPTIMIZING_FOR_SIZE;
-      if (not_good && growth > 0 && cgraph_estimate_growth (edge->callee) > 0)
+      if (not_good && growth > 0 && estimate_growth (edge->callee) > 0)
        {
          edge->inline_failed = not_good;
          if (dump_file)
@@ -1269,7 +1151,7 @@ cgraph_decide_inlining_of_small_functions (void)
                   flag_wpa ? "unknown"
                   : gimple_filename ((const_gimple) edge->call_stmt),
                   flag_wpa ? -1 : gimple_lineno ((const_gimple) edge->call_stmt),
-                  cgraph_estimate_growth (edge->callee),
+                  estimate_growth (edge->callee),
                   badness,
                   edge->frequency / (double)CGRAPH_FREQ_BASE);
          if (edge->count)
@@ -1394,7 +1276,6 @@ cgraph_decide_inlining (void)
   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 ();
   if (flag_indirect_inlining)
@@ -1534,6 +1415,7 @@ cgraph_decide_inlining (void)
             ncalls_inlined, nfunctions_inlined, initial_size,
             overall_size);
   free (order);
+  inline_free_summary ();
   return 0;
 }
 
@@ -1676,13 +1558,13 @@ 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 (cgraph_estimate_edge_growth (e) > allowed_growth
-         && cgraph_estimate_growth (e->callee) > allowed_growth)
+      if (estimate_edge_growth (e) > allowed_growth
+         && estimate_growth (e->callee) > allowed_growth)
        {
          if (dump_file)
            fprintf (dump_file,
                     "Not inlining: code size would grow by %i.\n",
-                    cgraph_estimate_edge_growth (e));
+                    estimate_edge_growth (e));
          continue;
        }
       if (!cgraph_check_inline_limits (e, &e->inline_failed))
@@ -1798,283 +1680,6 @@ struct gimple_opt_pass pass_early_inline =
 };
 
 
-/* See if statement might disappear after inlining.
-   0 - means not eliminated
-   1 - half of statements goes away
-   2 - for sure it is eliminated.
-   We are not terribly sophisticated, basically looking for simple abstraction
-   penalty wrappers.  */
-
-static int
-eliminated_by_inlining_prob (gimple stmt)
-{
-  enum gimple_code code = gimple_code (stmt);
-  switch (code)
-    {
-      case GIMPLE_RETURN:
-        return 2;
-      case GIMPLE_ASSIGN:
-       if (gimple_num_ops (stmt) != 2)
-         return 0;
-
-       /* Casts of parameters, loads from parameters passed by reference
-          and stores to return value or parameters are often free after
-          inlining dua to SRA and further combining.
-          Assume that half of statements goes away.  */
-       if (gimple_assign_rhs_code (stmt) == CONVERT_EXPR
-           || gimple_assign_rhs_code (stmt) == NOP_EXPR
-           || gimple_assign_rhs_code (stmt) == VIEW_CONVERT_EXPR
-           || 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) == MEM_REF)
-             inner_lhs = TREE_OPERAND (inner_lhs, 0);
-           while (handled_component_p (inner_rhs)
-                  || TREE_CODE (inner_rhs) == ADDR_EXPR
-                  || TREE_CODE (inner_rhs) == MEM_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 1;
-         }
-       return 0;
-      default:
-       return 0;
-    }
-}
-
-/* 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;
-  /* Estimate static overhead for function prologue/epilogue and alignment. */
-  int size = 2;
-  /* Benefits are scaled by probability of elimination that is in range
-     <0,2>.  */
-  int size_inlining_benefit = 2 * 2;
-  basic_block bb;
-  gimple_stmt_iterator bsi;
-  struct function *my_function = DECL_STRUCT_FUNCTION (node->decl);
-  int freq;
-
-  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);
-         int prob;
-
-         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;
-         prob = eliminated_by_inlining_prob (stmt);
-         if (prob == 1 && dump_file && (dump_flags & TDF_DETAILS))
-           fprintf (dump_file, "    50%% will be eliminated by inlining\n");
-         if (prob == 2 && dump_file && (dump_flags & TDF_DETAILS))
-           fprintf (dump_file, "    will eliminated by inlining\n");
-         size_inlining_benefit += this_size * prob;
-         time_inlining_benefit += this_time * prob;
-         gcc_assert (time >= 0);
-         gcc_assert (size >= 0);
-       }
-    }
-  time = (time + CGRAPH_FREQ_BASE / 2) / CGRAPH_FREQ_BASE;
-  time_inlining_benefit = ((time_inlining_benefit + CGRAPH_FREQ_BASE)
-                          / (CGRAPH_FREQ_BASE * 2));
-  size_inlining_benefit = (size_inlining_benefit + 1) / 2;
-  if (time_inlining_benefit > MAX_TIME)
-    time_inlining_benefit = MAX_TIME;
-  if (time > MAX_TIME)
-    time = MAX_TIME;
-  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);
-  inline_summary (node)->self_time = time;
-  inline_summary (node)->self_size = size;
-  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.  */
-void
-compute_inline_parameters (struct cgraph_node *node)
-{
-  HOST_WIDE_INT self_stack_size;
-  struct cgraph_edge *e;
-
-  gcc_assert (!node->global.inlined_to);
-
-  /* Estimate the stack size for the function if we're optimizing.  */
-  self_stack_size = optimize ? estimated_stack_frame_size (node) : 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 (node->decl);
-  if (!node->local.inlinable)
-    node->local.disregard_inline_limits = 0;
-
-  /* Inlinable functions always can change signature.  */
-  if (node->local.inlinable)
-    node->local.can_change_signature = true;
-  else
-    {
-      /* Functions calling builtin_apply can not change signature.  */
-      for (e = node->callees; e; e = e->next_callee)
-       if (DECL_BUILT_IN (e->callee->decl)
-           && DECL_BUILT_IN_CLASS (e->callee->decl) == BUILT_IN_NORMAL
-           && DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_APPLY_ARGS)
-         break;
-      node->local.can_change_signature = !e;
-    }
-  estimate_function_body_sizes (node);
-  /* Compute size of call statements.  We have to do this for callers here,
-     those sizes need to be present for edges _to_ us as early as
-     we are finished with early opts.  */
-  for (e = node->callers; e; e = e->next_caller)
-    if (e->call_stmt)
-      {
-       e->call_stmt_size
-         = estimate_num_insns (e->call_stmt, &eni_size_weights);
-       e->call_stmt_time
-         = estimate_num_insns (e->call_stmt, &eni_time_weights);
-      }
-  /* Inlining characteristics are maintained by the cgraph_mark_inline.  */
-  node->global.time = inline_summary (node)->self_time;
-  node->global.size = inline_summary (node)->self_size;
-}
-
-
-/* Compute parameters of functions used by inliner using
-   current_function_decl.  */
-static unsigned int
-compute_inline_parameters_for_current (void)
-{
-  compute_inline_parameters (cgraph_get_node (current_function_decl));
-  return 0;
-}
-
-struct gimple_opt_pass pass_inline_parameters =
-{
- {
-  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 */
-  0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  0                                    /* todo_flags_finish */
- }
-};
-
-/* This function performs intraprocedural analysis in NODE that is required to
-   inline indirect calls.  */
-static void
-inline_indirect_intraprocedural_analysis (struct cgraph_node *node)
-{
-  ipa_analyze_node (node);
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    {
-      ipa_print_node_params (dump_file, node);
-      ipa_print_node_jump_functions (dump_file, node);
-    }
-}
-
-/* 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);
-  /* FIXME: We should remove the optimize check after we ensure we never run
-     IPA passes when not optimizing.  */
-  if (flag_indirect_inlining && optimize)
-    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 ();
-
-  for (node = cgraph_nodes; node; node = node->next)
-    if (node->analyzed)
-      analyze_function (node);
-
-  return;
-}
-
 /* Apply inline plan to function.  */
 static unsigned int
 inline_transform (struct cgraph_node *node)
@@ -2111,35 +1716,6 @@ inline_transform (struct cgraph_node *node)
   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)
-    {
-      ipa_register_cgraph_hooks ();
-      if (!flag_ipa_cp)
-        ipa_prop_read_jump_functions ();
-    }
-  function_insertion_hook_holder =
-      cgraph_add_function_insertion_hook (&add_new_function, NULL);
-}
-
-/* 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,
-                     varpool_node_set vset ATTRIBUTE_UNUSED)
-{
-  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.  */
 
diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h
new file mode 100644 (file)
index 0000000..d76a492
--- /dev/null
@@ -0,0 +1,52 @@
+/* Inlining decision heuristics.
+   Copyright (C) 2003, 2004, 2007, 2008, 2009, 2010, 2011
+   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 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+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 COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+void inline_generate_summary (void);
+void inline_read_summary (void);
+void inline_write_summary (cgraph_node_set, varpool_node_set);
+void inline_free_summary (void);
+int estimate_time_after_inlining (struct cgraph_node *, struct cgraph_edge *);
+int estimate_size_after_inlining (struct cgraph_node *, struct cgraph_edge *);
+int estimate_growth (struct cgraph_node *);
+
+static inline struct inline_summary *
+inline_summary (struct cgraph_node *node)
+{
+  return &node->local.inline_summary;
+}
+
+/* Estimate the growth of the caller when inlining EDGE.  */
+
+static inline int
+estimate_edge_growth (struct cgraph_edge *edge)
+{
+  int call_stmt_size;
+  /* ???  We throw away cgraph edges all the time so the information
+     we store in edges doesn't persist for early inlining.  Ugh.  */
+  if (!edge->call_stmt)
+    call_stmt_size = edge->call_stmt_size;
+  else
+    call_stmt_size = estimate_num_insns (edge->call_stmt, &eni_size_weights);
+  return (edge->callee->global.size
+         - inline_summary (edge->callee)->size_inlining_benefit
+         - call_stmt_size);
+}
+