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
This commit is contained in:
Jan Hubicka 2013-08-13 14:21:16 +02:00 committed by Jan Hubicka
parent 537e035c48
commit 09ce36608d
8 changed files with 401 additions and 151 deletions

View File

@ -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

View File

@ -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));
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, "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\n",
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);
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),

View File

@ -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 */

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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);

156
gcc/ipa.c
View File

@ -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--)
{

View File

@ -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);
}