ipa-inline.h: New file.

* 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
This commit is contained in:
Jan Hubicka 2011-04-13 19:26:50 +02:00 committed by Jan Hubicka
parent 7673c9623f
commit 03dfc36daf
5 changed files with 593 additions and 443 deletions

View 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.

View 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) \

481
gcc/ipa-inline-analysis.c Normal file
View file

@ -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);
}

View 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. */

52
gcc/ipa-inline.h Normal file
View file

@ -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);
}