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:
parent
7673c9623f
commit
03dfc36daf
5 changed files with 593 additions and 443 deletions
|
@ -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.
|
||||
|
|
|
@ -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
481
gcc/ipa-inline-analysis.c
Normal 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);
|
||||
}
|
460
gcc/ipa-inline.c
460
gcc/ipa-inline.c
|
@ -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
52
gcc/ipa-inline.h
Normal 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);
|
||||
}
|
||||
|
Loading…
Add table
Reference in a new issue