diff options
author | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-04-13 17:26:50 +0000 |
---|---|---|
committer | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-04-13 17:26:50 +0000 |
commit | 99c67f243f8b4e3b58cc421e64b1ca4094a3e231 (patch) | |
tree | 70e379a6f9ceffd51efd849ff820056e3148cba8 /gcc/ipa-inline.c | |
parent | dc360156c378b3d81df2e1751c3961c6ef70c4d3 (diff) | |
download | gcc-99c67f243f8b4e3b58cc421e64b1ca4094a3e231.tar.gz |
* 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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@172388 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/ipa-inline.c')
-rw-r--r-- | gcc/ipa-inline.c | 460 |
1 files changed, 18 insertions, 442 deletions
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index 6f72bfad4fc..36bc1c2dbd6 100644 --- a/gcc/ipa-inline.c +++ b/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. */ |