From: Jan Hubicka Date: Wed, 13 Apr 2011 17:26:50 +0000 (+0200) Subject: ipa-inline.h: New file. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=03dfc36daf0f94adc8eee3f8ddb88be5f3c4e576;p=gcc.git 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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6847c995c56..43477183249 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,38 @@ +2011-04-13 Jan Hubicka + + * 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 * configure.ac (gcc_cv_as_sparc_gotdata_op): Remove GNU ld check. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 76b5d6b871b..1a0d98d2625 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1468,6 +1468,7 @@ OBJS-archive = \ ipa-cp.o \ ipa-split.o \ ipa-inline.o \ + ipa-inline-analysis.o \ ipa-prop.o \ ipa-pure-const.o \ ipa-reference.o \ @@ -3026,7 +3027,12 @@ ipa-inline.o : ipa-inline.c gt-ipa-inline.h $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) langhooks.h $(TREE_INLINE_H) $(FLAGS_H) $(CGRAPH_H) intl.h \ $(DIAGNOSTIC_H) $(FIBHEAP_H) $(PARAMS_H) $(TIMEVAR_H) $(TREE_PASS_H) \ $(HASHTAB_H) $(COVERAGE_H) $(GGC_H) $(TREE_FLOW_H) $(RTL_H) $(IPA_PROP_H) \ - $(EXCEPT_H) gimple-pretty-print.h + $(EXCEPT_H) gimple-pretty-print.h ipa-inline.h +ipa-inline-analysis.o : ipa-inline-analysis.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ + $(TREE_H) langhooks.h $(TREE_INLINE_H) $(FLAGS_H) $(CGRAPH_H) intl.h \ + $(DIAGNOSTIC_H) $(PARAMS_H) $(TIMEVAR_H) $(TREE_PASS_H) \ + $(HASHTAB_H) $(COVERAGE_H) $(GGC_H) $(TREE_FLOW_H) $(IPA_PROP_H) \ + gimple-pretty-print.h ipa-inline.h ipa-utils.o : ipa-utils.c $(IPA_UTILS_H) $(CONFIG_H) $(SYSTEM_H) \ coretypes.h $(TM_H) $(TREE_H) $(TREE_FLOW_H) $(TREE_INLINE_H) langhooks.h \ pointer-set.h $(GGC_H) $(GIMPLE_H) $(SPLAY_TREE_H) \ diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c new file mode 100644 index 00000000000..8507c5e27d7 --- /dev/null +++ b/gcc/ipa-inline-analysis.c @@ -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 +. */ + +/* 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); +} 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. */ diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h new file mode 100644 index 00000000000..d76a492a1d2 --- /dev/null +++ b/gcc/ipa-inline.h @@ -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 +. */ + +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); +} +