diff options
author | Jan Hubicka <jh@suse.cz> | 2013-08-13 14:21:16 +0200 |
---|---|---|
committer | Jan Hubicka <hubicka@gcc.gnu.org> | 2013-08-13 12:21:16 +0000 |
commit | 09ce36608d268b1a3124411a3ad2f701e5cf24ec (patch) | |
tree | e903dc537f27c5dd690cb3a98029321225e8f7b4 /gcc | |
parent | 537e035c48955b3ea88dcb3a4b0ff03ef4bb2e31 (diff) | |
download | gcc-09ce36608d268b1a3124411a3ad2f701e5cf24ec.zip gcc-09ce36608d268b1a3124411a3ad2f701e5cf24ec.tar.gz gcc-09ce36608d268b1a3124411a3ad2f701e5cf24ec.tar.bz2 |
cgraph.c (cgraph_turn_edge_to_speculative): Return newly introduced edge; fix typo in sanity check.
* cgraph.c (cgraph_turn_edge_to_speculative): Return newly
introduced edge; fix typo in sanity check.
(cgraph_resolve_speculation): Export; improve diagnostic.
(cgraph_redirect_edge_call_stmt_to_callee): Better diagnostic; cancel
speculation at type mismatch.
* cgraph.h (cgraph_turn_edge_to_speculative): Update.
(cgraph_resolve_speculation): Declare.
(symtab_can_be_discarded): New function.
* value-prof.c (gimple_ic_transform): Remove actual transform code.
* ipa-inline-transform.c (speculation_removed): New global var.
(clone_inlined_nodes): See if speculation can be removed.
(inline_call): If speculations was removed, we growths may not match.
* ipa-inline.c (can_inline_edge_p): Add DISREGARD_LIMITS parameter.
(speculation_useful_p): New function.
(resolve_noninline_speculation): New function.
(inline_small_functions): Resolve useless speculations.
* ipa-inline.h (speculation_useful_p): Declare
* ipa.c (can_replace_by_local_alias): Simplify.
(ipa_profile): Produce speculative calls in non-lto, too;
add simple cost model; produce local aliases.
From-SVN: r201683
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 23 | ||||
-rw-r--r-- | gcc/cgraph.c | 48 | ||||
-rw-r--r-- | gcc/cgraph.h | 15 | ||||
-rw-r--r-- | gcc/ipa-inline-transform.c | 18 | ||||
-rw-r--r-- | gcc/ipa-inline.c | 255 | ||||
-rw-r--r-- | gcc/ipa-inline.h | 1 | ||||
-rw-r--r-- | gcc/ipa.c | 156 | ||||
-rw-r--r-- | gcc/value-prof.c | 36 |
8 files changed, 401 insertions, 151 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0e8e6e6..508a14a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,26 @@ +2013-08-13 Jan Hubicka <jh@suse.cz> + + * cgraph.c (cgraph_turn_edge_to_speculative): Return newly + introduced edge; fix typo in sanity check. + (cgraph_resolve_speculation): Export; improve diagnostic. + (cgraph_redirect_edge_call_stmt_to_callee): Better diagnostic; cancel + speculation at type mismatch. + * cgraph.h (cgraph_turn_edge_to_speculative): Update. + (cgraph_resolve_speculation): Declare. + (symtab_can_be_discarded): New function. + * value-prof.c (gimple_ic_transform): Remove actual transform code. + * ipa-inline-transform.c (speculation_removed): New global var. + (clone_inlined_nodes): See if speculation can be removed. + (inline_call): If speculations was removed, we growths may not match. + * ipa-inline.c (can_inline_edge_p): Add DISREGARD_LIMITS parameter. + (speculation_useful_p): New function. + (resolve_noninline_speculation): New function. + (inline_small_functions): Resolve useless speculations. + * ipa-inline.h (speculation_useful_p): Declare + * ipa.c (can_replace_by_local_alias): Simplify. + (ipa_profile): Produce speculative calls in non-lto, too; + add simple cost model; produce local aliases. + 2013-08-13 David Malcolm <dmalcolm@redhat.com> * config/i386/t-i386 (i386.o): Rename stray PIPELINE_H to diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 50d13ab..a939ae8 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -1040,9 +1040,11 @@ cgraph_set_edge_callee (struct cgraph_edge *e, struct cgraph_node *n) At this time the function just creates the direct call, the referencd representing the if conditional and attaches - them all to the orginal indirect call statement. */ + them all to the orginal indirect call statement. -void + Return direct edge created. */ + +struct cgraph_edge * cgraph_turn_edge_to_speculative (struct cgraph_edge *e, struct cgraph_node *n2, gcov_type direct_count, @@ -1073,6 +1075,7 @@ cgraph_turn_edge_to_speculative (struct cgraph_edge *e, IPA_REF_ADDR, e->call_stmt); ref->lto_stmt_uid = e->lto_stmt_uid; ref->speculative = e->speculative; + return e2; } /* Speculative call consist of three components: @@ -1107,7 +1110,7 @@ cgraph_speculative_call_info (struct cgraph_edge *e, if (e2->call_stmt) { e = cgraph_edge (e->caller, e2->call_stmt); - gcc_assert (!e->speculative && !e->indirect_unknown_callee); + gcc_assert (e->speculative && !e->indirect_unknown_callee); } else for (e = e->caller->callees; @@ -1147,7 +1150,7 @@ cgraph_redirect_edge_callee (struct cgraph_edge *e, struct cgraph_node *n) Remove the speculative call sequence and return edge representing the call. It is up to caller to redirect the call as appropriate. */ -static struct cgraph_edge * +struct cgraph_edge * cgraph_resolve_speculation (struct cgraph_edge *edge, tree callee_decl) { struct cgraph_edge *e2; @@ -1159,12 +1162,21 @@ cgraph_resolve_speculation (struct cgraph_edge *edge, tree callee_decl) { if (dump_file) { - fprintf (dump_file, "Speculative indirect call %s/%i => %s/%i has " - "turned out to have contradicitng known target ", - xstrdup (cgraph_node_name (edge->caller)), edge->caller->symbol.order, - xstrdup (cgraph_node_name (e2->callee)), e2->callee->symbol.order); - print_generic_expr (dump_file, callee_decl, 0); - fprintf (dump_file, "\n"); + if (callee_decl) + { + fprintf (dump_file, "Speculative indirect call %s/%i => %s/%i has " + "turned out to have contradicting known target ", + xstrdup (cgraph_node_name (edge->caller)), edge->caller->symbol.order, + xstrdup (cgraph_node_name (e2->callee)), e2->callee->symbol.order); + print_generic_expr (dump_file, callee_decl, 0); + fprintf (dump_file, "\n"); + } + else + { + fprintf (dump_file, "Removing speculative call %s/%i => %s/%i\n", + xstrdup (cgraph_node_name (edge->caller)), edge->caller->symbol.order, + xstrdup (cgraph_node_name (e2->callee)), e2->callee->symbol.order); + } } } else @@ -1264,12 +1276,24 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e) cgraph_speculative_call_info (e, e, e2, ref); if (gimple_call_fndecl (e->call_stmt)) e = cgraph_resolve_speculation (e, gimple_call_fndecl (e->call_stmt)); - else + if (!gimple_check_call_matching_types (e->call_stmt, e->callee->symbol.decl, + true)) { + e = cgraph_resolve_speculation (e, NULL); if (dump_file) - fprintf (dump_file, "Expanding speculative call of %s/%i -> %s/%i\n", + fprintf (dump_file, "Not expanding speculative call of %s/%i -> %s/%i\n" + "Type mismatch.\n", xstrdup (cgraph_node_name (e->caller)), e->caller->symbol.order, xstrdup (cgraph_node_name (e->callee)), e->callee->symbol.order); + } + else + { + if (dump_file) + fprintf (dump_file, "Expanding speculative call of %s/%i -> %s/%i count:" + HOST_WIDEST_INT_PRINT_DEC"\n", + xstrdup (cgraph_node_name (e->caller)), e->caller->symbol.order, + xstrdup (cgraph_node_name (e->callee)), e->callee->symbol.order, + (HOST_WIDEST_INT)e->count); gcc_assert (e2->speculative); push_cfun (DECL_STRUCT_FUNCTION (e->caller->symbol.decl)); new_stmt = gimple_ic (e->call_stmt, cgraph (ref->referred), diff --git a/gcc/cgraph.h b/gcc/cgraph.h index e430533..6c25bf2 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -726,7 +726,7 @@ bool cgraph_propagate_frequency (struct cgraph_node *node); struct cgraph_node * cgraph_function_node (struct cgraph_node *, enum availability *avail = NULL); bool cgraph_get_body (struct cgraph_node *node); -void +struct cgraph_edge * cgraph_turn_edge_to_speculative (struct cgraph_edge *, struct cgraph_node *, gcov_type, int); @@ -783,6 +783,7 @@ struct cgraph_node *cgraph_function_versioning (struct cgraph_node *, basic_block, const char *); void tree_function_versioning (tree, tree, vec<ipa_replace_map_p, va_gc> *, bool, bitmap, bool, bitmap, basic_block); +struct cgraph_edge *cgraph_resolve_speculation (struct cgraph_edge *, tree); /* In cgraphbuild.c */ unsigned int rebuild_cgraph_edges (void); @@ -1398,4 +1399,16 @@ symtab_real_symbol_p (symtab_node node) return false; return true; } + +/* Return true if NODE can be discarded by linker from the binary. */ + +static inline bool +symtab_can_be_discarded (symtab_node node) +{ + return (DECL_EXTERNAL (node->symbol.decl) + || (DECL_ONE_ONLY (node->symbol.decl) + && node->symbol.resolution != LDPR_PREVAILING_DEF + && node->symbol.resolution != LDPR_PREVAILING_DEF_IRONLY + && node->symbol.resolution != LDPR_PREVAILING_DEF_IRONLY_EXP)); +} #endif /* GCC_CGRAPH_H */ diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c index 54b113a..8ead336 100644 --- a/gcc/ipa-inline-transform.c +++ b/gcc/ipa-inline-transform.c @@ -46,6 +46,7 @@ along with GCC; see the file COPYING3. If not see int ncalls_inlined; int nfunctions_inlined; +bool speculation_removed; /* Scale frequency of NODE edges by FREQ_SCALE. */ @@ -134,6 +135,7 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, bool update_original, int *overall_size) { struct cgraph_node *inlining_into; + struct cgraph_edge *next; if (e->caller->global.inlined_to) inlining_into = e->caller->global.inlined_to; @@ -186,9 +188,17 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate, e->callee->global.inlined_to = inlining_into; /* Recursively clone all bodies. */ - for (e = e->callee->callees; e; e = e->next_callee) - if (!e->inline_failed) - clone_inlined_nodes (e, duplicate, update_original, overall_size); + for (e = e->callee->callees; e; e = next) + { + next = e->next_callee; + if (!e->inline_failed) + clone_inlined_nodes (e, duplicate, update_original, overall_size); + if (e->speculative && !speculation_useful_p (e, true)) + { + cgraph_resolve_speculation (e, NULL); + speculation_removed = true; + } + } } @@ -218,6 +228,7 @@ inline_call (struct cgraph_edge *e, bool update_original, bool predicated = inline_edge_summary (e)->predicate != NULL; #endif + speculation_removed = false; /* Don't inline inlined edges. */ gcc_assert (e->inline_failed); /* Don't even think of inlining inline clone. */ @@ -267,6 +278,7 @@ inline_call (struct cgraph_edge *e, bool update_original, error due to INLINE_SIZE_SCALE roudoff errors. */ gcc_assert (!update_overall_summary || !overall_size || new_edges_found || abs (estimated_growth - (new_size - old_size)) <= 1 + || speculation_removed /* FIXME: a hack. Edges with false predicate are accounted wrong, we should remove them from callgraph. */ || predicated); diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index 033b314..a9eb1ad 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -229,10 +229,13 @@ report_inline_failed_reason (struct cgraph_edge *e) We check whether inlining is possible at all and whether caller growth limits allow doing so. - if REPORT is true, output reason to the dump file. */ + if REPORT is true, output reason to the dump file. + + if DISREGARD_LIMITES is true, ignore size limits.*/ static bool -can_inline_edge_p (struct cgraph_edge *e, bool report) +can_inline_edge_p (struct cgraph_edge *e, bool report, + bool disregard_limits = false) { bool inlinable = true; enum availability avail; @@ -309,6 +312,7 @@ can_inline_edge_p (struct cgraph_edge *e, bool report) } /* Check if caller growth allows the inlining. */ else if (!DECL_DISREGARD_INLINE_LIMITS (callee->symbol.decl) + && !disregard_limits && !lookup_attribute ("flatten", DECL_ATTRIBUTES (e->caller->global.inlined_to @@ -1400,6 +1404,79 @@ heap_edge_removal_hook (struct cgraph_edge *e, void *data) } } +/* Return true if speculation of edge E seems useful. + If ANTICIPATE_INLINING is true, be conservative and hope that E + may get inlined. */ + +bool +speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining) +{ + enum availability avail; + struct cgraph_node *target = cgraph_function_or_thunk_node (e->callee, &avail); + struct cgraph_edge *direct, *indirect; + struct ipa_ref *ref; + + gcc_assert (e->speculative && !e->indirect_unknown_callee); + + if (!cgraph_maybe_hot_edge_p (e)) + return false; + + /* See if IP optimizations found something potentially useful about the + function. For now we look only for CONST/PURE flags. Almost everything + else we propagate is useless. */ + if (avail >= AVAIL_AVAILABLE) + { + int ecf_flags = flags_from_decl_or_type (target->symbol.decl); + if (ecf_flags & ECF_CONST) + { + cgraph_speculative_call_info (e, direct, indirect, ref); + if (!(indirect->indirect_info->ecf_flags & ECF_CONST)) + return true; + } + else if (ecf_flags & ECF_PURE) + { + cgraph_speculative_call_info (e, direct, indirect, ref); + if (!(indirect->indirect_info->ecf_flags & ECF_PURE)) + return true; + } + } + /* If we did not managed to inline the function nor redirect + to an ipa-cp clone (that are seen by having local flag set), + it is probably pointless to inline it unless hardware is missing + indirect call predictor. */ + if (!anticipate_inlining && e->inline_failed && !target->local.local) + return false; + /* For overwritable targets there is not much to do. */ + if (e->inline_failed && !can_inline_edge_p (e, false, true)) + return false; + /* OK, speculation seems interesting. */ + return true; +} + +/* We know that EDGE is not going to be inlined. + See if we can remove speculation. */ + +static void +resolve_noninline_speculation (fibheap_t edge_heap, struct cgraph_edge *edge) +{ + if (edge->speculative && !speculation_useful_p (edge, false)) + { + struct cgraph_node *node = edge->caller; + struct cgraph_node *where = node->global.inlined_to + ? node->global.inlined_to : node; + bitmap updated_nodes = BITMAP_ALLOC (NULL); + + cgraph_resolve_speculation (edge, NULL); + reset_node_growth_cache (where); + reset_edge_caches (where); + inline_update_overall_summary (where); + update_caller_keys (edge_heap, where, + updated_nodes, NULL); + reset_node_growth_cache (where); + BITMAP_FREE (updated_nodes); + } +} + /* We use greedy algorithm for inlining of small functions: All inline candidates are put into prioritized heap ordered in increasing badness. @@ -1478,14 +1555,19 @@ inline_small_functions (void) /* Populate the heeap with all edges we might inline. */ FOR_EACH_DEFINED_FUNCTION (node) - if (!node->global.inlined_to) - { - if (dump_file) - fprintf (dump_file, "Enqueueing calls of %s/%i.\n", - cgraph_node_name (node), node->symbol.order); + { + bool update = false; + struct cgraph_edge *next; - for (edge = node->callers; edge; edge = edge->next_caller) + if (dump_file) + fprintf (dump_file, "Enqueueing calls in %s/%i.\n", + cgraph_node_name (node), node->symbol.order); + + for (edge = node->callees; edge; edge = next) + { + next = edge->next_callee; if (edge->inline_failed + && !edge->aux && can_inline_edge_p (edge, true) && want_inline_small_function_p (edge, true) && edge->inline_failed) @@ -1493,7 +1575,24 @@ inline_small_functions (void) gcc_assert (!edge->aux); update_edge_key (edge_heap, edge); } - } + if (edge->speculative && !speculation_useful_p (edge, edge->aux != NULL)) + { + cgraph_resolve_speculation (edge, NULL); + update = true; + } + } + if (update) + { + struct cgraph_node *where = node->global.inlined_to + ? node->global.inlined_to : node; + inline_update_overall_summary (where); + reset_node_growth_cache (where); + reset_edge_caches (where); + update_caller_keys (edge_heap, where, + updated_nodes, NULL); + bitmap_clear (updated_nodes); + } + } gcc_assert (in_lto_p || !max_count @@ -1534,7 +1633,10 @@ inline_small_functions (void) } if (!can_inline_edge_p (edge, true)) - continue; + { + resolve_noninline_speculation (edge_heap, edge); + continue; + } callee = cgraph_function_or_thunk_node (edge->callee, NULL); growth = estimate_edge_growth (edge); @@ -1568,11 +1670,15 @@ inline_small_functions (void) { edge->inline_failed = CIF_INLINE_UNIT_GROWTH_LIMIT; report_inline_failed_reason (edge); + resolve_noninline_speculation (edge_heap, edge); continue; } if (!want_inline_small_function_p (edge, true)) - continue; + { + resolve_noninline_speculation (edge_heap, edge); + continue; + } /* Heuristics for inlining small functions works poorly for recursive calls where we do efect similar to loop unrolling. @@ -1588,6 +1694,7 @@ inline_small_functions (void) ? &new_indirect_edges : NULL)) { edge->inline_failed = CIF_RECURSIVE_INLINING; + resolve_noninline_speculation (edge_heap, edge); continue; } reset_edge_caches (where); @@ -1596,6 +1703,7 @@ inline_small_functions (void) if (flag_indirect_inlining) add_new_edges_to_heap (edge_heap, new_indirect_edges); update_callee_keys (edge_heap, where, updated_nodes); + bitmap_clear (updated_nodes); } else { @@ -1621,6 +1729,7 @@ inline_small_functions (void) edge->inline_failed = (DECL_DISREGARD_INLINE_LIMITS (edge->callee->symbol.decl) ? CIF_RECURSIVE_INLINING : CIF_UNSPECIFIED); + resolve_noninline_speculation (edge_heap, edge); continue; } else if (depth && dump_file) @@ -1773,6 +1882,7 @@ ipa_inline (void) struct cgraph_node **order = XCNEWVEC (struct cgraph_node *, cgraph_n_nodes); int i; + int cold; if (in_lto_p && optimize) ipa_update_after_lto_read (); @@ -1820,66 +1930,83 @@ ipa_inline (void) code size will shrink because the out-of-line copy is eliminated. We do this regardless on the callee size as long as function growth limits are met. */ - if (flag_inline_functions_called_once) + if (dump_file) + fprintf (dump_file, + "\nDeciding on functions to be inlined into all callers and removing useless speculations:\n"); + + /* Inlining one function called once has good chance of preventing + inlining other function into the same callee. Ideally we should + work in priority order, but probably inlining hot functions first + is good cut without the extra pain of maintaining the queue. + + ??? this is not really fitting the bill perfectly: inlining function + into callee often leads to better optimization of callee due to + increased context for optimization. + For example if main() function calls a function that outputs help + and then function that does the main optmization, we should inline + the second with priority even if both calls are cold by themselves. + + We probably want to implement new predicate replacing our use of + maybe_hot_edge interpreted as maybe_hot_edge || callee is known + to be hot. */ + for (cold = 0; cold <= 1; cold ++) { - int cold; - if (dump_file) - fprintf (dump_file, - "\nDeciding on functions to be inlined into all callers:\n"); - - /* Inlining one function called once has good chance of preventing - inlining other function into the same callee. Ideally we should - work in priority order, but probably inlining hot functions first - is good cut without the extra pain of maintaining the queue. - - ??? this is not really fitting the bill perfectly: inlining function - into callee often leads to better optimization of callee due to - increased context for optimization. - For example if main() function calls a function that outputs help - and then function that does the main optmization, we should inline - the second with priority even if both calls are cold by themselves. - - We probably want to implement new predicate replacing our use of - maybe_hot_edge interpreted as maybe_hot_edge || callee is known - to be hot. */ - for (cold = 0; cold <= 1; cold ++) + FOR_EACH_DEFINED_FUNCTION (node) { - FOR_EACH_DEFINED_FUNCTION (node) + struct cgraph_edge *edge, *next; + bool update=false; + + for (edge = node->callees; edge; edge = next) { - if (want_inline_function_to_all_callers_p (node, cold)) + next = edge->next_callee; + if (edge->speculative && !speculation_useful_p (edge, false)) { - int num_calls = 0; - struct cgraph_edge *e; - for (e = node->callers; e; e = e->next_caller) - num_calls++; - while (node->callers && !node->global.inlined_to) + cgraph_resolve_speculation (edge, NULL); + update = true; + } + } + if (update) + { + struct cgraph_node *where = node->global.inlined_to + ? node->global.inlined_to : node; + reset_node_growth_cache (where); + reset_edge_caches (where); + inline_update_overall_summary (where); + } + if (flag_inline_functions_called_once + && want_inline_function_to_all_callers_p (node, cold)) + { + int num_calls = 0; + struct cgraph_edge *e; + for (e = node->callers; e; e = e->next_caller) + num_calls++; + while (node->callers && !node->global.inlined_to) + { + struct cgraph_node *caller = node->callers->caller; + + if (dump_file) { - struct cgraph_node *caller = node->callers->caller; + fprintf (dump_file, + "\nInlining %s size %i.\n", + cgraph_node_name (node), + inline_summary (node)->size); + fprintf (dump_file, + " Called once from %s %i insns.\n", + cgraph_node_name (node->callers->caller), + inline_summary (node->callers->caller)->size); + } + inline_call (node->callers, true, NULL, NULL, true); + if (dump_file) + fprintf (dump_file, + " Inlined into %s which now has %i size\n", + cgraph_node_name (caller), + inline_summary (caller)->size); + if (!num_calls--) + { if (dump_file) - { - fprintf (dump_file, - "\nInlining %s size %i.\n", - cgraph_node_name (node), - inline_summary (node)->size); - fprintf (dump_file, - " Called once from %s %i insns.\n", - cgraph_node_name (node->callers->caller), - inline_summary (node->callers->caller)->size); - } - - inline_call (node->callers, true, NULL, NULL, true); - if (dump_file) - fprintf (dump_file, - " Inlined into %s which now has %i size\n", - cgraph_node_name (caller), - inline_summary (caller)->size); - if (!num_calls--) - { - if (dump_file) - fprintf (dump_file, "New calls found; giving up.\n"); - break; - } + fprintf (dump_file, "New calls found; giving up.\n"); + break; } } } diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h index b34cb52..000d1ab 100644 --- a/gcc/ipa-inline.h +++ b/gcc/ipa-inline.h @@ -226,6 +226,7 @@ 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); +bool speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining); /* In ipa-inline-transform.c */ bool inline_call (struct cgraph_edge *, bool, vec<cgraph_edge_p> *, int *, bool); @@ -768,11 +768,7 @@ bool can_replace_by_local_alias (symtab_node node) { return (symtab_node_availability (node) > AVAIL_OVERWRITABLE - && !DECL_EXTERNAL (node->symbol.decl) - && (!DECL_ONE_ONLY (node->symbol.decl) - || node->symbol.resolution == LDPR_PREVAILING_DEF - || node->symbol.resolution == LDPR_PREVAILING_DEF_IRONLY - || node->symbol.resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)); + && !symtab_can_be_discarded (node)); } /* Mark visibility of all functions. @@ -1407,53 +1403,9 @@ ipa_profile (void) bool something_changed = false; int i; gcov_type overall_time = 0, cutoff = 0, cumulated = 0, overall_size = 0; - - /* Produce speculative calls: we saved common traget from porfiling into - e->common_target_id. Now, at link time, we can look up corresponding - function node and produce speculative call. */ - if (in_lto_p) - { - struct cgraph_edge *e; - struct cgraph_node *n,*n2; - - init_node_map (false); - FOR_EACH_DEFINED_FUNCTION (n) - { - bool update = false; - - for (e = n->indirect_calls; e; e = e->next_callee) - if (e->indirect_info->common_target_id) - { - n2 = find_func_by_profile_id (e->indirect_info->common_target_id); - if (n2) - { - if (dump_file) - { - fprintf (dump_file, "Indirect call -> direct call from" - " other module %s/%i => %s/%i, prob %3.2f\n", - xstrdup (cgraph_node_name (n)), n->symbol.order, - xstrdup (cgraph_node_name (n2)), n2->symbol.order, - e->indirect_info->common_target_probability - / (float)REG_BR_PROB_BASE); - } - cgraph_turn_edge_to_speculative - (e, n2, - apply_scale (e->count, - e->indirect_info->common_target_probability), - apply_scale (e->frequency, - e->indirect_info->common_target_probability)); - update = true; - } - else - if (dump_file) - fprintf (dump_file, "Function with profile-id %i not found.\n", - e->indirect_info->common_target_id); - } - if (update) - inline_update_overall_summary (n); - } - del_node_map (); - } + struct cgraph_node *n,*n2; + int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0; + bool node_map_initialized = false; if (dump_file) dump_histogram (dump_file, histogram); @@ -1523,6 +1475,106 @@ ipa_profile (void) histogram.release(); free_alloc_pool (histogram_pool); + /* Produce speculative calls: we saved common traget from porfiling into + e->common_target_id. Now, at link time, we can look up corresponding + function node and produce speculative call. */ + + FOR_EACH_DEFINED_FUNCTION (n) + { + bool update = false; + + for (e = n->indirect_calls; e; e = e->next_callee) + { + if (n->count) + nindirect++; + if (e->indirect_info->common_target_id) + { + if (!node_map_initialized) + init_node_map (false); + node_map_initialized = true; + ncommon++; + n2 = find_func_by_profile_id (e->indirect_info->common_target_id); + if (n2) + { + if (dump_file) + { + fprintf (dump_file, "Indirect call -> direct call from" + " other module %s/%i => %s/%i, prob %3.2f\n", + xstrdup (cgraph_node_name (n)), n->symbol.order, + xstrdup (cgraph_node_name (n2)), n2->symbol.order, + e->indirect_info->common_target_probability + / (float)REG_BR_PROB_BASE); + } + if (e->indirect_info->common_target_probability + < REG_BR_PROB_BASE / 2) + { + nuseless++; + if (dump_file) + fprintf (dump_file, + "Not speculating: probability is too low.\n"); + } + else if (!cgraph_maybe_hot_edge_p (e)) + { + nuseless++; + if (dump_file) + fprintf (dump_file, + "Not speculating: call is cold.\n"); + } + else if (cgraph_function_body_availability (n2) + <= AVAIL_OVERWRITABLE + && symtab_can_be_discarded ((symtab_node) n2)) + { + nuseless++; + if (dump_file) + fprintf (dump_file, + "Not speculating: target is overwritable " + "and can be discarded.\n"); + } + else + { + /* Target may be overwritable, but profile says that + control flow goes to this particular implementation + of N2. Speculate on the local alias to allow inlining. + */ + if (!symtab_can_be_discarded ((symtab_node) n2)) + n2 = cgraph (symtab_nonoverwritable_alias ((symtab_node)n2)); + nconverted++; + cgraph_turn_edge_to_speculative + (e, n2, + apply_scale (e->count, + e->indirect_info->common_target_probability), + apply_scale (e->frequency, + e->indirect_info->common_target_probability)); + update = true; + } + } + else + { + if (dump_file) + fprintf (dump_file, "Function with profile-id %i not found.\n", + e->indirect_info->common_target_id); + nunknown++; + } + } + } + if (update) + inline_update_overall_summary (n); + } + if (node_map_initialized) + del_node_map (); + if (dump_file && nindirect) + fprintf (dump_file, + "%i indirect calls trained.\n" + "%i (%3.2f%%) have common target.\n" + "%i (%3.2f%%) targets was not found.\n" + "%i (%3.2f%%) speculations seems useless.\n" + "%i (%3.2f%%) speculations produced.\n", + nindirect, + ncommon, ncommon * 100.0 / nindirect, + nunknown, nunknown * 100.0 / nindirect, + nuseless, nuseless * 100.0 / nindirect, + nconverted, nconverted * 100.0 / nindirect); + order_pos = ipa_reverse_postorder (order); for (i = order_pos - 1; i >= 0; i--) { diff --git a/gcc/value-prof.c b/gcc/value-prof.c index f110277..8aa9fcd 100644 --- a/gcc/value-prof.c +++ b/gcc/value-prof.c @@ -1431,8 +1431,6 @@ gimple_ic_transform (gimple_stmt_iterator *gsi) gimple stmt = gsi_stmt (*gsi); histogram_value histogram; gcov_type val, count, all, bb_all; - gcov_type prob; - gimple modify; struct cgraph_node *direct_call; if (gimple_code (stmt) != GIMPLE_CALL) @@ -1452,12 +1450,6 @@ gimple_ic_transform (gimple_stmt_iterator *gsi) count = histogram->hvalue.counters [1]; all = histogram->hvalue.counters [2]; - if (4 * count <= 3 * all) - { - gimple_remove_histogram_value (cfun, stmt, histogram); - return false; - } - bb_all = gimple_bb (stmt)->count; /* The order of CHECK_COUNTER calls is important - since check_counter can correct the third parameter @@ -1469,10 +1461,9 @@ gimple_ic_transform (gimple_stmt_iterator *gsi) return false; } - if (all > 0) - prob = GCOV_COMPUTE_SCALE (count, all); - else - prob = 0; + if (4 * count <= 3 * all) + return false; + direct_call = find_func_by_profile_id ((int)val); if (direct_call == NULL) @@ -1488,12 +1479,21 @@ gimple_ic_transform (gimple_stmt_iterator *gsi) } return false; } - gimple_remove_histogram_value (cfun, stmt, histogram); if (!check_ic_target (stmt, direct_call)) - return false; - - modify = gimple_ic (stmt, direct_call, prob, count, all); + { + if (dump_file) + { + fprintf (dump_file, "Indirect call -> direct call "); + print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM); + fprintf (dump_file, "=> "); + print_generic_expr (dump_file, direct_call->symbol.decl, TDF_SLIM); + fprintf (dump_file, " transformation skipped because of type mismatch"); + print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); + } + gimple_remove_histogram_value (cfun, stmt, histogram); + return false; + } if (dump_file) { @@ -1501,10 +1501,8 @@ gimple_ic_transform (gimple_stmt_iterator *gsi) print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM); fprintf (dump_file, "=> "); print_generic_expr (dump_file, direct_call->symbol.decl, TDF_SLIM); - fprintf (dump_file, " transformation on insn "); + fprintf (dump_file, " transformation on insn postponned to ipa-profile"); print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); - fprintf (dump_file, " to "); - print_gimple_stmt (dump_file, modify, 0, TDF_SLIM); fprintf (dump_file, "hist->count "HOST_WIDEST_INT_PRINT_DEC " hist->all "HOST_WIDEST_INT_PRINT_DEC"\n", count, all); } |