From eb7c606e1e1222b20023e10832593ae0e5478c8d Mon Sep 17 00:00:00 2001 From: hubicka Date: Sun, 19 Aug 2012 05:55:20 +0000 Subject: [PATCH] PR lto/45375 * ipa-inline.c (want_inline_small_function_p): Bypass inline limits for hinted functions. (edge_badness): Dump hints; decrease badness for hinted funcitons. * ipa-inline.h (enum inline_hints_vals): New enum. (inline_hints): New type. (edge_growth_cache_entry): Add hints. (dump_inline_summary): Update. (dump_inline_hints): Declare. (do_estimate_edge_hints): Declare. (estimate_edge_hints): New inline function. (reset_edge_growth_cache): Update. * predict.c (cgraph_maybe_hot_edge_p): Do not ice on indirect edges. * ipa-inline-analysis.c (dump_inline_hints): New function. (estimate_edge_devirt_benefit): Return true when function should be hinted. (estimate_calls_size_and_time): New hints argument; set it when devritualization happens. (estimate_node_size_and_time): New hints argument. (do_estimate_edge_time): Cache hints. (do_estimate_edge_growth): Update. (do_estimate_edge_hints): New function git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@190509 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 25 +++++++ gcc/ipa-inline-analysis.c | 125 +++++++++++++++++++++++------------ gcc/ipa-inline.c | 22 +++++- gcc/ipa-inline.h | 30 ++++++++- gcc/predict.c | 6 +- gcc/testsuite/ChangeLog | 4 ++ gcc/testsuite/gcc.dg/ipa/iinline-1.c | 3 +- 7 files changed, 165 insertions(+), 50 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1f313e9..6dabed6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,28 @@ +2012-08-18 Jan Hubicka + + PR lto/45375 + * ipa-inline.c (want_inline_small_function_p): Bypass + inline limits for hinted functions. + (edge_badness): Dump hints; decrease badness for hinted funcitons. + * ipa-inline.h (enum inline_hints_vals): New enum. + (inline_hints): New type. + (edge_growth_cache_entry): Add hints. + (dump_inline_summary): Update. + (dump_inline_hints): Declare. + (do_estimate_edge_hints): Declare. + (estimate_edge_hints): New inline function. + (reset_edge_growth_cache): Update. + * predict.c (cgraph_maybe_hot_edge_p): Do not ice on indirect edges. + * ipa-inline-analysis.c (dump_inline_hints): New function. + (estimate_edge_devirt_benefit): Return true when function should be + hinted. + (estimate_calls_size_and_time): New hints argument; set it when + devritualization happens. + (estimate_node_size_and_time): New hints argument. + (do_estimate_edge_time): Cache hints. + (do_estimate_edge_growth): Update. + (do_estimate_edge_hints): New function + 2012-08-18 John David Anglin PR middle-end/53823 diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c index 9f96ca8..c30e81a 100644 --- a/gcc/ipa-inline-analysis.c +++ b/gcc/ipa-inline-analysis.c @@ -615,6 +615,22 @@ dump_predicate (FILE *f, conditions conds, struct predicate *pred) } +/* Dump inline hints. */ +void +dump_inline_hints (FILE *f, inline_hints hints) +{ + if (!hints) + return; + fprintf (f, "inline hints:"); + if (hints & INLINE_HINT_indirect_call) + { + hints &= ~INLINE_HINT_indirect_call; + fprintf (f, " indirect_call"); + } + gcc_assert (!hints); +} + + /* Record SIZE and TIME under condition PRED into the inline summary. */ static void @@ -2302,7 +2318,7 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *time, /* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS and KNOWN_BINFOS. */ -static void +static bool estimate_edge_devirt_benefit (struct cgraph_edge *ie, int *size, int *time, int prob, VEC (tree, heap) *known_vals, @@ -2311,14 +2327,16 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie, { tree target; int time_diff, size_diff; + struct cgraph_node *callee; + struct inline_summary *isummary; if (!known_vals && !known_binfos) - return; + return false; target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos, known_aggs); if (!target) - return; + return false; /* Account for difference in cost between indirect and direct calls. */ size_diff = ((eni_size_weights.indirect_call_cost - eni_size_weights.call_cost) @@ -2328,40 +2346,11 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie, * INLINE_TIME_SCALE * prob / REG_BR_PROB_BASE); *time -= time_diff; - /* TODO: This code is trying to benefit indirect calls that will be inlined later. - The logic however do not belong into local size/time estimates and can not be - done here, or the accounting of changes will get wrong and we result with - negative function body sizes. We need to introduce infrastructure for independent - benefits to the inliner. */ -#if 0 - struct cgraph_node *callee; - struct inline_summary *isummary; - int edge_size, edge_time, time_diff, size_diff; - callee = cgraph_get_node (target); if (!callee || !callee->analyzed) - return; + return false; isummary = inline_summary (callee); - if (!isummary->inlinable) - return; - - estimate_edge_size_and_time (ie, &edge_size, &edge_time, prob); - - /* Count benefit only from functions that definitely will be inlined - if additional context from NODE's caller were available. - - We just account overall size change by inlining. TODO: - we really need to add sort of benefit metrics for these kind of - cases. */ - if (edge_size - size_diff >= isummary->size * INLINE_SIZE_SCALE) - { - /* Subtract size and time that we added for edge IE. */ - *size -= edge_size - size_diff; - - /* Account inlined call. */ - *size += isummary->size * INLINE_SIZE_SCALE; - } -#endif + return isummary->inlinable; } @@ -2371,6 +2360,7 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie, static void estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time, + inline_hints *hints, clause_t possible_truths, VEC (tree, heap) *known_vals, VEC (tree, heap) *known_binfos, @@ -2389,7 +2379,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time, estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE); } else - estimate_calls_size_and_time (e->callee, size, time, + estimate_calls_size_and_time (e->callee, size, time, hints, possible_truths, known_vals, known_binfos, known_aggs); } @@ -2400,8 +2390,11 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time, if (!es->predicate || evaluate_predicate (es->predicate, possible_truths)) { estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE); - estimate_edge_devirt_benefit (e, size, time, REG_BR_PROB_BASE, - known_vals, known_binfos, known_aggs); + if (estimate_edge_devirt_benefit (e, size, time, REG_BR_PROB_BASE, + known_vals, known_binfos, known_aggs) + && hints + && cgraph_maybe_hot_edge_p (e)) + *hints |= INLINE_HINT_indirect_call; } } } @@ -2418,12 +2411,14 @@ estimate_node_size_and_time (struct cgraph_node *node, VEC (tree, heap) *known_binfos, VEC (ipa_agg_jump_function_p, heap) *known_aggs, int *ret_size, int *ret_time, + inline_hints *ret_hints, VEC (inline_param_summary_t, heap) *inline_param_summary) { struct inline_summary *info = inline_summary (node); size_time_entry *e; int size = 0, time = 0; + inline_hints hints = 0; int i; if (dump_file @@ -2467,7 +2462,7 @@ estimate_node_size_and_time (struct cgraph_node *node, if (time > MAX_TIME * INLINE_TIME_SCALE) time = MAX_TIME * INLINE_TIME_SCALE; - estimate_calls_size_and_time (node, &size, &time, possible_truths, + estimate_calls_size_and_time (node, &size, &time, &hints, possible_truths, known_vals, known_binfos, known_aggs); time = (time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE; size = (size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE; @@ -2480,6 +2475,8 @@ estimate_node_size_and_time (struct cgraph_node *node, *ret_time = time; if (ret_size) *ret_size = size; + if (ret_hints) + *ret_hints = hints; return; } @@ -2499,7 +2496,7 @@ estimate_ipcp_clone_size_and_time (struct cgraph_node *node, clause = evaluate_conditions_for_known_args (node, false, known_vals, NULL); estimate_node_size_and_time (node, clause, known_vals, known_binfos, NULL, - ret_size, ret_time, + ret_size, ret_time, NULL, NULL); } @@ -2871,7 +2868,7 @@ inline_update_overall_summary (struct cgraph_node *node) info->time = 0; for (i = 0; VEC_iterate (size_time_entry, info->entry, i, e); i++) info->size += e->size, info->time += e->time; - estimate_calls_size_and_time (node, &info->size, &info->time, + estimate_calls_size_and_time (node, &info->size, &info->time, NULL, ~(clause_t)(1 << predicate_false_condition), NULL, NULL, NULL); info->time = (info->time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE; @@ -2890,6 +2887,7 @@ do_estimate_edge_time (struct cgraph_edge *edge) { int time; int size; + inline_hints hints; gcov_type ret; struct cgraph_node *callee; clause_t clause; @@ -2905,7 +2903,7 @@ do_estimate_edge_time (struct cgraph_edge *edge) &clause, &known_vals, &known_binfos, &known_aggs); estimate_node_size_and_time (callee, clause, known_vals, known_binfos, - known_aggs, &size, &time, es->param); + known_aggs, &size, &time, &hints, es->param); VEC_free (tree, heap, known_vals); VEC_free (tree, heap, known_binfos); VEC_free (ipa_agg_jump_function_p, heap, known_aggs); @@ -2929,6 +2927,8 @@ do_estimate_edge_time (struct cgraph_edge *edge) gcc_checking_assert (es->call_stmt_size); VEC_index (edge_growth_cache_entry, edge_growth_cache, edge->uid).size = ret_size + (ret_size >= 0); + VEC_index (edge_growth_cache_entry, edge_growth_cache, edge->uid).hints + = hints + 1; } return ret; } @@ -2967,7 +2967,7 @@ do_estimate_edge_growth (struct cgraph_edge *edge) &clause, &known_vals, &known_binfos, &known_aggs); estimate_node_size_and_time (callee, clause, known_vals, known_binfos, - known_aggs, &size, NULL, NULL); + known_aggs, &size, NULL, NULL, NULL); VEC_free (tree, heap, known_vals); VEC_free (tree, heap, known_binfos); VEC_free (ipa_agg_jump_function_p, heap, known_aggs); @@ -2976,6 +2976,47 @@ do_estimate_edge_growth (struct cgraph_edge *edge) } +/* Estimate the growth of the caller when inlining EDGE. + Only to be called via estimate_edge_size. */ + +inline_hints +do_estimate_edge_hints (struct cgraph_edge *edge) +{ + inline_hints hints; + struct cgraph_node *callee; + clause_t clause; + VEC (tree, heap) *known_vals; + VEC (tree, heap) *known_binfos; + VEC (ipa_agg_jump_function_p, heap) *known_aggs; + + /* When we do caching, use do_estimate_edge_time to populate the entry. */ + + if (edge_growth_cache) + { + do_estimate_edge_time (edge); + hints = VEC_index (edge_growth_cache_entry, + edge_growth_cache, + edge->uid).hints; + gcc_checking_assert (hints); + return hints - 1; + } + + callee = cgraph_function_or_thunk_node (edge->callee, NULL); + + /* Early inliner runs without caching, go ahead and do the dirty work. */ + gcc_checking_assert (edge->inline_failed); + evaluate_properties_for_edge (edge, true, + &clause, &known_vals, &known_binfos, + &known_aggs); + estimate_node_size_and_time (callee, clause, known_vals, known_binfos, + known_aggs, NULL, NULL, &hints, NULL); + VEC_free (tree, heap, known_vals); + VEC_free (tree, heap, known_binfos); + VEC_free (ipa_agg_jump_function_p, heap, known_aggs); + return hints; +} + + /* Estimate self time of the function NODE after inlining EDGE. */ int diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index 5215049..55d9a52 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -472,11 +472,15 @@ want_inline_small_function_p (struct cgraph_edge *e, bool report) else { int growth = estimate_edge_growth (e); + inline_hints hints = estimate_edge_hints (e); if (growth <= 0) ; + /* Apply MAX_INLINE_INSNS_SINGLE limit. Do not do so when + hints suggests that inlining given function is very profitable. */ else if (DECL_DECLARED_INLINE_P (callee->symbol.decl) - && growth >= MAX_INLINE_INSNS_SINGLE) + && growth >= MAX_INLINE_INSNS_SINGLE + && !(hints & INLINE_HINT_indirect_call)) { e->inline_failed = CIF_MAX_INLINE_INSNS_SINGLE_LIMIT; want_inline = false; @@ -523,8 +527,14 @@ want_inline_small_function_p (struct cgraph_edge *e, bool report) e->inline_failed = CIF_NOT_DECLARED_INLINED; want_inline = false; } + /* Apply MAX_INLINE_INSNS_AUTO limit for functions not declared inline + Upgrade it to MAX_INLINE_INSNS_SINGLE when hints suggests that + inlining given function is very profitable. */ else if (!DECL_DECLARED_INLINE_P (callee->symbol.decl) - && growth >= MAX_INLINE_INSNS_AUTO) + && growth >= ((hints & INLINE_HINT_indirect_call) + ? MAX (MAX_INLINE_INSNS_AUTO, + MAX_INLINE_INSNS_SINGLE) + : MAX_INLINE_INSNS_AUTO)) { e->inline_failed = CIF_MAX_INLINE_INSNS_AUTO_LIMIT; want_inline = false; @@ -743,21 +753,25 @@ edge_badness (struct cgraph_edge *edge, bool dump) struct cgraph_node *callee = cgraph_function_or_thunk_node (edge->callee, NULL); struct inline_summary *callee_info = inline_summary (callee); + inline_hints hints; if (DECL_DISREGARD_INLINE_LIMITS (callee->symbol.decl)) return INT_MIN; growth = estimate_edge_growth (edge); time_growth = estimate_edge_time (edge); + hints = estimate_edge_hints (edge); if (dump) { fprintf (dump_file, " Badness calculation for %s -> %s\n", xstrdup (cgraph_node_name (edge->caller)), xstrdup (cgraph_node_name (callee))); - fprintf (dump_file, " size growth %i, time growth %i\n", + fprintf (dump_file, " size growth %i, time growth %i ", growth, time_growth); + dump_inline_hints (dump_file, hints); + fprintf (dump_file, "\n"); } /* Always prefer inlining saving code size. */ @@ -849,6 +863,8 @@ edge_badness (struct cgraph_edge *edge, bool dump) if (dump) fprintf (dump_file, "Badness overflow\n"); } + if (hints & INLINE_HINT_indirect_call) + badness /= 8; if (dump) { fprintf (dump_file, diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h index d6909af..fca99e6 100644 --- a/gcc/ipa-inline.h +++ b/gcc/ipa-inline.h @@ -42,6 +42,13 @@ typedef struct GTY(()) condition unsigned by_ref : 1; } condition; +/* Inline hints are reasons why inline heuristics should preffer inlining given function. + They are represtented as bitmap of the following values. */ +enum inline_hints_vals { + INLINE_HINT_indirect_call = 1 +}; +typedef int inline_hints; + DEF_VEC_O (condition); DEF_VEC_ALLOC_O (condition, gc); @@ -158,6 +165,7 @@ extern VEC(inline_edge_summary_t,heap) *inline_edge_summary_vec; typedef struct edge_growth_cache_entry { int time, size; + inline_hints hints; } edge_growth_cache_entry; DEF_VEC_O(edge_growth_cache_entry); DEF_VEC_ALLOC_O(edge_growth_cache_entry,heap); @@ -168,7 +176,8 @@ extern VEC(edge_growth_cache_entry,heap) *edge_growth_cache; /* In ipa-inline-analysis.c */ void debug_inline_summary (struct cgraph_node *); void dump_inline_summaries (FILE *f); -void dump_inline_summary (FILE * f, struct cgraph_node *node); +void dump_inline_summary (FILE *f, struct cgraph_node *node); +void dump_inline_hints (FILE *f, inline_hints); void inline_generate_summary (void); void inline_read_summary (void); void inline_write_summary (void); @@ -185,6 +194,7 @@ void inline_merge_summary (struct cgraph_edge *edge); void inline_update_overall_summary (struct cgraph_node *node); int do_estimate_edge_growth (struct cgraph_edge *edge); int do_estimate_edge_time (struct cgraph_edge *edge); +inline_hints do_estimate_edge_hints (struct cgraph_edge *edge); void initialize_growth_caches (void); void free_growth_caches (void); void compute_inline_parameters (struct cgraph_node *, bool); @@ -257,6 +267,22 @@ estimate_edge_time (struct cgraph_edge *edge) } +/* Return estimated callee runtime increase after inlning + EDGE. */ + +static inline inline_hints +estimate_edge_hints (struct cgraph_edge *edge) +{ + inline_hints ret; + if ((int)VEC_length (edge_growth_cache_entry, edge_growth_cache) <= edge->uid + || !(ret = VEC_index (edge_growth_cache_entry, + edge_growth_cache, + edge->uid).hints)) + return do_estimate_edge_time (edge); + return ret - 1; +} + + /* Reset cached value for NODE. */ static inline void @@ -273,7 +299,7 @@ reset_edge_growth_cache (struct cgraph_edge *edge) { if ((int)VEC_length (edge_growth_cache_entry, edge_growth_cache) > edge->uid) { - struct edge_growth_cache_entry zero = {0, 0}; + struct edge_growth_cache_entry zero = {0, 0, 0}; VEC_replace (edge_growth_cache_entry, edge_growth_cache, edge->uid, zero); } } diff --git a/gcc/predict.c b/gcc/predict.c index c884d38..e1a064d 100644 --- a/gcc/predict.c +++ b/gcc/predict.c @@ -165,10 +165,12 @@ cgraph_maybe_hot_edge_p (struct cgraph_edge *edge) <= profile_info->sum_max / PARAM_VALUE (HOT_BB_COUNT_FRACTION))) return false; if (edge->caller->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED - || edge->callee->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED) + || (edge->callee + && edge->callee->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED)) return false; if (edge->caller->frequency > NODE_FREQUENCY_UNLIKELY_EXECUTED - && edge->callee->frequency <= NODE_FREQUENCY_EXECUTED_ONCE) + && (edge->callee + && edge->callee->frequency <= NODE_FREQUENCY_EXECUTED_ONCE)) return false; if (optimize_size) return false; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index e8a48c9..378e778 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2012-08-18 Jan Hubicka + + * gcc.dg/ipa/iinline-1.c: Update testcase to test inline hints. + 2012-08-18 Mikael Morin PR fortran/39290 diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-1.c b/gcc/testsuite/gcc.dg/ipa/iinline-1.c index 617c484..860b3e5 100644 --- a/gcc/testsuite/gcc.dg/ipa/iinline-1.c +++ b/gcc/testsuite/gcc.dg/ipa/iinline-1.c @@ -1,7 +1,7 @@ /* Verify that simple indirect calls are inlined even without early inlining.. */ /* { dg-do compile } */ -/* { dg-options "-O3 -c -fdump-ipa-inline -fno-early-inlining" } */ +/* { dg-options "-O3 -c -fdump-ipa-inline-details -fno-early-inlining -fno-ipa-cp" } */ extern void non_existent(int); @@ -22,5 +22,6 @@ int test (void) return 0; } +/* { dg-final { scan-ipa-dump "indirect_call" "inline" } } */ /* { dg-final { scan-ipa-dump "hooray\[^\\n\]*inline copy in test" "inline" } } */ /* { dg-final { cleanup-ipa-dump "inline" } } */ -- 2.7.4