Commit 845bb366 by Jan Hubicka

ipa: fix handling of multiple speculations (PR93318)

This patch started as work to resole Richard's comment on quadratic lookups
in resolve_speculation. While doing it I however noticed multiple problems
in the new speuclative call code which made the patch quite big. In
particular:
 1) Before applying speculation we consider only targets with at lest
    probability 1/2.
    If profile is sane at most two targets can have probability greater or
    equal to 1/2. So the new multi-target speculation code got enabled only
    in very special scenario when there ae precisely two target with precise
    probability 1/2 (which is tested by the single testcase).

    As a conseuqence the multiple target logic got minimal test coverage and
    this made us to miss several ICEs.
 2) Profile updating in profile merging, tree-inline and indirect call
    expansion was wrong which led to inconsistent profiles (as already seen
    on the testcase).
 3) Code responsible to turn speculative call to direct call was broken for
    anything with more than one target.
 4) There were multiple cases where call_site_hash went out of sync which
    eventually leads to an ICE..
 5) Some code expects that all speculative call targets forms a sequence in
    the callee linked list but there is no code to maintain that invariant
    nor a verifier.
Fixing this it became obvious that the current API of speculative_call_info is
not useful because it really builds on fact tht there are precisely three
components (direct call, ref and indirect call) in every speculative call
sequence.  I ended up replacing it with iterator API for direct call
(first_speculative_call_target, next_speculative_call_target) and accessors for
the other coponents updating comment in cgraph.h.

Finally I made the work with call site hash more effetive by updating edge
manipulation to keep them in sequence. So first one can be looked up from the
hash and then they can be iterated by callee.

There are other things that can be improved (for example the speculation should
start with most common target first), but I will try to keep that for next
stage1. This patch is mostly about getting rid of ICE and profile corruption
which is a regression from GCC 9.

gcc/ChangeLog:

	PR lto/93318
	* cgraph.c (cgraph_add_edge_to_call_site_hash): Update call site
	hash only when edge is first within the sequence.
	(cgraph_edge::set_call_stmt): Update handling of speculative calls.
	(symbol_table::create_edge): Do not set target_prob.
	(cgraph_edge::remove_caller): Watch for speculative calls when updating
	the call site hash.
	(cgraph_edge::make_speculative): Drop target_prob parameter.
	(cgraph_edge::speculative_call_info): Remove.
	(cgraph_edge::first_speculative_call_target): New member function.
	(update_call_stmt_hash_for_removing_direct_edge): New function.
	(cgraph_edge::resolve_speculation): Rewrite to new API.
	(cgraph_edge::speculative_call_for_target): New member function.
	(cgraph_edge::make_direct): Rewrite to new API; fix handling of
	multiple speculation targets.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise; fix updating
	of profile.
	(verify_speculative_call): Verify that targets form an interval.
	* cgraph.h (cgraph_edge::speculative_call_info): Remove.
	(cgraph_edge::first_speculative_call_target): New member function.
	(cgraph_edge::next_speculative_call_target): New member function.
	(cgraph_edge::speculative_call_target_ref): New member function.
	(cgraph_edge;:speculative_call_indirect_edge): New member funtion.
	(cgraph_edge): Remove target_prob.
	* cgraphclones.c (cgraph_node::set_call_stmt_including_clones):
	Fix handling of speculative calls.
	* ipa-devirt.c (ipa_devirt): Fix handling of speculative cals.
	* ipa-fnsummary.c (analyze_function_body): Likewise.
	* ipa-inline.c (speculation_useful_p): Use new speculative call API.
	* ipa-profile.c (dump_histogram): Fix formating.
	(ipa_profile_generate_summary): Watch for overflows.
	(ipa_profile): Do not require probablity to be 1/2; update to new API.
	* ipa-prop.c (ipa_make_edge_direct_to_target): Update to new API.
	(update_indirect_edges_after_inlining): Update to new API.
	* ipa-utils.c (ipa_merge_profiles): Rewrite merging of speculative call
	profiles.
	* profile-count.h: (profile_probability::adjusted): New.
	* tree-inline.c (copy_bb): Update to new speculative call API; fix
	updating of profile.
	* value-prof.c (gimple_ic_transform): Rename to ...
	(dump_ic_profile): ... this one; update dumping.
	(stream_in_histogram_value): Fix formating.
	(gimple_value_profile_transformations): Update.

gcc/testsuite/ChangeLog:

	* g++.dg/tree-prof/indir-call-prof.C: Update template.
	* gcc.dg/tree-prof/crossmodule-indircall-1.c: Add more targets.
	* gcc.dg/tree-prof/crossmodule-indircall-1a.c: Add more targets.
	* gcc.dg/tree-prof/indir-call-prof.c: Update template.
parent dd9b529f
...@@ -1765,14 +1765,87 @@ public: ...@@ -1765,14 +1765,87 @@ public:
the profile so the direct call is taken COUNT times the profile so the direct call is taken COUNT times
with FREQUENCY. speculative_id is used to link direct calls with their with FREQUENCY. speculative_id is used to link direct calls with their
corresponding IPA_REF_ADDR references when representing speculative calls. corresponding IPA_REF_ADDR references when representing speculative calls.
target_prob is the probability of the speculative call. */ */
cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count, cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
unsigned int speculative_id = 0, unsigned int speculative_id = 0);
int target_prob = 0);
/* Given speculative call edge, return all three components. */ /* Speculative call consists of an indirect edge and one or more
void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect, direct edge+ref pairs. Speculative will expand to the following sequence:
ipa_ref *&reference);
if (call_dest == target1) // reference to target1
target1 (); // direct call to target1
else if (call_dest == target2) // reference to targt2
target2 (); // direct call to target2
else
call_dest (); // indirect call
Before the expansion we will have indirect call and the direct call+ref
pairs all linked to single statement.
Note that ref may point to different symbol than the corresponding call
becuase the speculated edge may have been optimized (redirected to
a clone) or inlined.
Given an edge which is part of speculative call, return the first
direct call edge in the speculative call sequence.
In the example above called on any cgraph edge in the sequence it will
return direct call to target1. */
cgraph_edge *first_speculative_call_target ();
/* Return next speculative call target or NULL if there is none.
All targets are required to form an interval in the callee list.
In example above, if called on call to target1 it will return call to
target2. */
cgraph_edge *next_speculative_call_target ()
{
cgraph_edge *e = this;
gcc_checking_assert (speculative && callee);
if (e->next_callee && e->next_callee->speculative
&& e->next_callee->call_stmt == e->call_stmt
&& e->next_callee->lto_stmt_uid == e->lto_stmt_uid)
return e->next_callee;
return NULL;
}
/* When called on any edge in the speculative call return the (unique)
indirect call edge in the speculative call sequence. */
cgraph_edge *speculative_call_indirect_edge ()
{
gcc_checking_assert (speculative);
if (!callee)
return this;
for (cgraph_edge *e2 = caller->indirect_calls;
true; e2 = e2->next_callee)
if (e2->speculative
&& call_stmt == e2->call_stmt
&& lto_stmt_uid == e2->lto_stmt_uid)
return e2;
}
/* When called on any edge in speculative call and when given any target
of ref which is speculated to it returns the corresponding direct call.
In example above if called on function target2 it will return call to
target2. */
cgraph_edge *speculative_call_for_target (cgraph_node *);
/* Return REF corresponding to direct call in the specualtive call
sequence. */
ipa_ref *speculative_call_target_ref ()
{
ipa_ref *ref;
gcc_checking_assert (speculative);
for (unsigned int i = 0; caller->iterate_reference (i, ref); i++)
if (ref->speculative && ref->speculative_id == speculative_id
&& ref->stmt == (gimple *)call_stmt
&& ref->lto_stmt_uid == lto_stmt_uid)
return ref;
gcc_unreachable ();
}
/* Speculative call edge turned out to be direct call to CALLEE_DECL. Remove /* Speculative call edge turned out to be direct call to CALLEE_DECL. Remove
the speculative call sequence and return edge representing the call, the the speculative call sequence and return edge representing the call, the
...@@ -1874,8 +1947,6 @@ public: ...@@ -1874,8 +1947,6 @@ public:
/* The stmt_uid of call_stmt. This is used by LTO to recover the call_stmt /* The stmt_uid of call_stmt. This is used by LTO to recover the call_stmt
when the function is serialized in. */ when the function is serialized in. */
unsigned int lto_stmt_uid; unsigned int lto_stmt_uid;
/* target_prob is the probability of the speculative call. */
unsigned int target_prob;
/* speculative id is used to link direct calls with their corresponding /* speculative id is used to link direct calls with their corresponding
IPA_REF_ADDR references when representing speculative calls. */ IPA_REF_ADDR references when representing speculative calls. */
unsigned int speculative_id : 16; unsigned int speculative_id : 16;
......
...@@ -765,14 +765,18 @@ cgraph_node::set_call_stmt_including_clones (gimple *old_stmt, ...@@ -765,14 +765,18 @@ cgraph_node::set_call_stmt_including_clones (gimple *old_stmt,
callgraph edges. */ callgraph edges. */
if (edge->speculative && !update_speculative) if (edge->speculative && !update_speculative)
{ {
cgraph_edge *direct, *indirect; cgraph_edge *indirect = edge->speculative_call_indirect_edge ();
ipa_ref *ref;
for (cgraph_edge *next, *direct
gcc_assert (!edge->indirect_unknown_callee); = edge->first_speculative_call_target ();
edge->speculative_call_info (direct, indirect, ref); direct;
direct->speculative = false; direct = next)
{
next = direct->next_speculative_call_target ();
direct->speculative_call_target_ref ()->speculative = false;
direct->speculative = false;
}
indirect->speculative = false; indirect->speculative = false;
ref->speculative = false;
} }
} }
if (node->clones) if (node->clones)
......
...@@ -3757,11 +3757,8 @@ ipa_devirt (void) ...@@ -3757,11 +3757,8 @@ ipa_devirt (void)
with the speculation. */ with the speculation. */
if (e->speculative) if (e->speculative)
{ {
struct cgraph_edge *e2; bool found = e->speculative_call_for_target (likely_target);
struct ipa_ref *ref; if (found)
e->speculative_call_info (e2, e, ref);
if (e2->callee->ultimate_alias_target ()
== likely_target->ultimate_alias_target ())
{ {
fprintf (dump_file, "We agree with speculation\n\n"); fprintf (dump_file, "We agree with speculation\n\n");
nok++; nok++;
......
...@@ -2604,34 +2604,26 @@ analyze_function_body (struct cgraph_node *node, bool early) ...@@ -2604,34 +2604,26 @@ analyze_function_body (struct cgraph_node *node, bool early)
edge_set_predicate (edge, &bb_predicate); edge_set_predicate (edge, &bb_predicate);
if (edge->speculative) if (edge->speculative)
{ {
cgraph_edge *direct, *indirect, *next_direct; cgraph_edge *indirect
ipa_ref *ref; = edge->speculative_call_indirect_edge ();
edge->speculative_call_info (direct, indirect, ref);
gcc_assert (direct == edge);
ipa_call_summary *es2 ipa_call_summary *es2
= ipa_call_summaries->get_create (indirect); = ipa_call_summaries->get_create (indirect);
ipa_call_summaries->duplicate (edge, indirect, ipa_call_summaries->duplicate (edge, indirect,
es, es2); es, es2);
/* Create and duplicate call summaries for multiple /* Edge is the first direct call.
create and duplicate call summaries for multiple
speculative call targets. */ speculative call targets. */
int num_specs = indirect->num_speculative_call_targets_p (); for (cgraph_edge *direct
if (num_specs > 1) = edge->next_speculative_call_target ();
for (next_direct = edge->next_callee; direct;
next_direct && --num_specs; direct = direct->next_speculative_call_target ())
next_direct = next_direct->next_callee) {
{ ipa_call_summary *es3
next_direct->speculative_call_info (direct, indirect, = ipa_call_summaries->get_create (direct);
ref); ipa_call_summaries->duplicate (edge, direct,
if (direct == next_direct && next_direct->speculative es, es3);
&& edge->call_stmt == stmt) }
{
ipa_call_summary *es3
= ipa_call_summaries->get_create (next_direct);
ipa_call_summaries->duplicate (edge, next_direct,
es, es3);
}
}
} }
} }
......
...@@ -1778,8 +1778,6 @@ speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining) ...@@ -1778,8 +1778,6 @@ speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining)
enum availability avail; enum availability avail;
struct cgraph_node *target = e->callee->ultimate_alias_target (&avail, struct cgraph_node *target = e->callee->ultimate_alias_target (&avail,
e->caller); e->caller);
struct cgraph_edge *direct, *indirect;
struct ipa_ref *ref;
gcc_assert (e->speculative && !e->indirect_unknown_callee); gcc_assert (e->speculative && !e->indirect_unknown_callee);
...@@ -1794,14 +1792,14 @@ speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining) ...@@ -1794,14 +1792,14 @@ speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining)
int ecf_flags = flags_from_decl_or_type (target->decl); int ecf_flags = flags_from_decl_or_type (target->decl);
if (ecf_flags & ECF_CONST) if (ecf_flags & ECF_CONST)
{ {
e->speculative_call_info (direct, indirect, ref); if (!(e->speculative_call_indirect_edge ()->indirect_info
if (!(indirect->indirect_info->ecf_flags & ECF_CONST)) ->ecf_flags & ECF_CONST))
return true; return true;
} }
else if (ecf_flags & ECF_PURE) else if (ecf_flags & ECF_PURE)
{ {
e->speculative_call_info (direct, indirect, ref); if (!(e->speculative_call_indirect_edge ()->indirect_info
if (!(indirect->indirect_info->ecf_flags & ECF_PURE)) ->ecf_flags & ECF_PURE))
return true; return true;
} }
} }
......
...@@ -134,7 +134,8 @@ static void ...@@ -134,7 +134,8 @@ static void
dump_histogram (FILE *file, vec<histogram_entry *> histogram) dump_histogram (FILE *file, vec<histogram_entry *> histogram)
{ {
unsigned int i; unsigned int i;
gcov_type overall_time = 0, cumulated_time = 0, cumulated_size = 0, overall_size = 0; gcov_type overall_time = 0, cumulated_time = 0, cumulated_size = 0,
overall_size = 0;
fprintf (dump_file, "Histogram:\n"); fprintf (dump_file, "Histogram:\n");
for (i = 0; i < histogram.length (); i++) for (i = 0; i < histogram.length (); i++)
...@@ -266,7 +267,8 @@ ipa_profile_generate_summary (void) ...@@ -266,7 +267,8 @@ ipa_profile_generate_summary (void)
call_sums = new ipa_profile_call_summaries (symtab); call_sums = new ipa_profile_call_summaries (symtab);
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
if (ENTRY_BLOCK_PTR_FOR_FN (DECL_STRUCT_FUNCTION (node->decl))->count.ipa_p ()) if (ENTRY_BLOCK_PTR_FOR_FN
(DECL_STRUCT_FUNCTION (node->decl))->count.ipa_p ())
FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (node->decl)) FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (node->decl))
{ {
int time = 0; int time = 0;
...@@ -300,30 +302,31 @@ ipa_profile_generate_summary (void) ...@@ -300,30 +302,31 @@ ipa_profile_generate_summary (void)
j)) j))
continue; continue;
if (val == 0) if (val == 0 || count == 0)
continue; continue;
speculative_call_target item ( if (count > all)
val, GCOV_COMPUTE_SCALE (count, all));
if (item.target_probability > REG_BR_PROB_BASE)
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, fprintf (dump_file,
"Probability capped to 1\n"); "Probability capped to 1\n");
item.target_probability = REG_BR_PROB_BASE; count = all;
} }
speculative_call_target item (
val, GCOV_COMPUTE_SCALE (count, all));
csum->speculative_call_targets.safe_push (item); csum->speculative_call_targets.safe_push (item);
} }
gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl), gimple_remove_histogram_value
stmt, h); (DECL_STRUCT_FUNCTION (node->decl), stmt, h);
} }
} }
time += estimate_num_insns (stmt, &eni_time_weights); time += estimate_num_insns (stmt, &eni_time_weights);
size += estimate_num_insns (stmt, &eni_size_weights); size += estimate_num_insns (stmt, &eni_size_weights);
} }
if (bb->count.ipa_p () && bb->count.initialized_p ()) if (bb->count.ipa_p () && bb->count.initialized_p ())
account_time_size (&hashtable, histogram, bb->count.ipa ().to_gcov_type (), account_time_size (&hashtable, histogram,
bb->count.ipa ().to_gcov_type (),
time, size); time, size);
} }
histogram.qsort (cmp_counts); histogram.qsort (cmp_counts);
...@@ -864,6 +867,7 @@ ipa_profile (void) ...@@ -864,6 +867,7 @@ ipa_profile (void)
} }
unsigned speculative_id = 0; unsigned speculative_id = 0;
profile_count orig = e->count;
for (unsigned i = 0; i < spec_count; i++) for (unsigned i = 0; i < spec_count; i++)
{ {
speculative_call_target item speculative_call_target item
...@@ -881,7 +885,8 @@ ipa_profile (void) ...@@ -881,7 +885,8 @@ ipa_profile (void)
item.target_probability item.target_probability
/ (float) REG_BR_PROB_BASE); / (float) REG_BR_PROB_BASE);
} }
if (item.target_probability < REG_BR_PROB_BASE / 2) if (item.target_probability
< REG_BR_PROB_BASE / GCOV_TOPN_VALUES / 2)
{ {
nuseless++; nuseless++;
if (dump_file) if (dump_file)
...@@ -939,11 +944,12 @@ ipa_profile (void) ...@@ -939,11 +944,12 @@ ipa_profile (void)
n2 = alias; n2 = alias;
} }
nconverted++; nconverted++;
profile_probability prob
= profile_probability::from_reg_br_prob_base
(item.target_probability).adjusted ();
e->make_speculative (n2, e->make_speculative (n2,
e->count.apply_probability ( orig.apply_probability (prob),
item.target_probability), speculative_id);
speculative_id,
item.target_probability);
update = true; update = true;
speculative_id++; speculative_id++;
} }
......
...@@ -3237,7 +3237,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target, ...@@ -3237,7 +3237,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, "ipa-prop: Discovered call to a known target " fprintf (dump_file, "ipa-prop: Discovered call to a known target "
"(%s -> %s) but cannot refer to it. Giving up.\n", "(%s -> %s) but cannot refer to it. Giving up.\n",
ie->caller->dump_name (), ie->caller->dump_name (),
ie->callee->dump_name ()); ie->callee->dump_name ());
return NULL; return NULL;
...@@ -3248,25 +3248,26 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target, ...@@ -3248,25 +3248,26 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
/* If the edge is already speculated. */ /* If the edge is already speculated. */
if (speculative && ie->speculative) if (speculative && ie->speculative)
{ {
struct cgraph_edge *e2; if (dump_file)
struct ipa_ref *ref;
ie->speculative_call_info (e2, ie, ref);
if (e2->callee->ultimate_alias_target ()
!= callee->ultimate_alias_target ())
{
if (dump_file)
fprintf (dump_file, "ipa-prop: Discovered call to a speculative "
"target (%s -> %s) but the call is already "
"speculated to %s. Giving up.\n",
ie->caller->dump_name (), callee->dump_name (),
e2->callee->dump_name ());
}
else
{ {
if (dump_file) cgraph_edge *e2 = ie->speculative_call_for_target (callee);
fprintf (dump_file, "ipa-prop: Discovered call to a speculative target " if (!e2)
"(%s -> %s) this agree with previous speculation.\n", {
ie->caller->dump_name (), callee->dump_name ()); if (dump_file)
fprintf (dump_file, "ipa-prop: Discovered call to a "
"speculative target (%s -> %s) but the call is "
"already speculated to different target. "
"Giving up.\n",
ie->caller->dump_name (), callee->dump_name ());
}
else
{
if (dump_file)
fprintf (dump_file,
"ipa-prop: Discovered call to a speculative target "
"(%s -> %s) this agree with previous speculation.\n",
ie->caller->dump_name (), callee->dump_name ());
}
} }
return NULL; return NULL;
} }
...@@ -3771,7 +3772,6 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs, ...@@ -3771,7 +3772,6 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs,
class cgraph_indirect_call_info *ici = ie->indirect_info; class cgraph_indirect_call_info *ici = ie->indirect_info;
struct ipa_jump_func *jfunc; struct ipa_jump_func *jfunc;
int param_index; int param_index;
cgraph_node *spec_target = NULL;
next_ie = ie->next_callee; next_ie = ie->next_callee;
...@@ -3787,14 +3787,11 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs, ...@@ -3787,14 +3787,11 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs,
param_index = ici->param_index; param_index = ici->param_index;
jfunc = ipa_get_ith_jump_func (top, param_index); jfunc = ipa_get_ith_jump_func (top, param_index);
cgraph_node *spec_target;
/* FIXME: This may need updating for multiple calls. */
if (ie->speculative) if (ie->speculative)
{ spec_target = ie->first_speculative_call_target ()->callee;
struct cgraph_edge *de;
struct ipa_ref *ref;
ie->speculative_call_info (de, ie, ref);
spec_target = de->callee;
}
if (!opt_for_fn (node->decl, flag_indirect_inlining)) if (!opt_for_fn (node->decl, flag_indirect_inlining))
new_direct_edge = NULL; new_direct_edge = NULL;
......
...@@ -674,201 +674,75 @@ ipa_merge_profiles (struct cgraph_node *dst, ...@@ -674,201 +674,75 @@ ipa_merge_profiles (struct cgraph_node *dst,
for (e = dst->indirect_calls, e2 = src->indirect_calls; e; for (e = dst->indirect_calls, e2 = src->indirect_calls; e;
e2 = (e2 ? e2->next_callee : NULL), e = e->next_callee) e2 = (e2 ? e2->next_callee : NULL), e = e->next_callee)
{ {
profile_count count = gimple_bb (e->call_stmt)->count; if (!e->speculative && !e2->speculative)
/* Below code are introduced by r279373 of "Fix merging of common
traget info.".
ipa-icf runs after ipa-profile, common_target_id with
common_target_probablity are useless in ipa-icf since they are
moved from cgraph.h to ipa-profile.c and processed already.
Need double circulation to find out each mapped direct speculative
edge and do prob merge. Not easy to construct a case to cover all
circumstances here. For src and dst both have multiple speculative
targets, only N:N maps are implemented, 2:0, 2:1, 0:2, 1:2 are not
implemented yet as too complicated and no test cases to cover. */
if (copy_counts)
{ {
/* copy if both e and e2 have same num_speculative_call_targets. /* FIXME: we need to also merge ipa-profile histograms
*/ because with LTO merging happens from lto-symtab before
if (e->num_speculative_call_targets_p () these are converted to indirect edges. */
== e2->num_speculative_call_targets_p ()) e->count = gimple_bb (e->call_stmt)->count;
{ continue;
int num_specs = e->num_speculative_call_targets_p ();
cgraph_edge *direct, *indirect, *next_direct;
cgraph_edge *direct2, *indirect2, *next_direct2;
ipa_ref *ref;
for (next_direct = e; next_direct && num_specs--;
next_direct = direct->next_callee)
{
next_direct->speculative_call_info (direct, indirect,
ref);
int num_specs2 = e2->num_speculative_call_targets_p ();
for (next_direct2 = e2; next_direct2 && num_specs2--;
next_direct2 = direct2->next_callee)
{
if (e2 && e2->speculative)
next_direct2->speculative_call_info (direct2,
indirect2,
ref);
if (direct->speculative_id == direct2->speculative_id
&& direct->lto_stmt_uid == direct2->lto_stmt_uid)
{
direct->target_prob = direct2->target_prob;
break;
}
}
}
}
else
gcc_assert (e->num_speculative_call_targets_p ()
&& e->num_speculative_call_targets_p ());
} }
else if (e->num_speculative_call_targets_p ()
|| e2->num_speculative_call_targets_p ()) /* When copying just remove all speuclations on dst and then copy
one from src. */
if (copy_counts)
{ {
if (e->num_speculative_call_targets_p () while (e->speculative)
== e2->num_speculative_call_targets_p ()) cgraph_edge::resolve_speculation (e, NULL);
e->count = gimple_bb (e->call_stmt)->count;
if (e2->speculative)
{ {
int num_specs = e->num_speculative_call_targets_p (); for (cgraph_edge *e3 = e2->first_speculative_call_target ();
cgraph_edge *direct, *indirect, *next_direct; e3;
cgraph_edge *direct2, *indirect2, *next_direct2; e3 = e3->next_speculative_call_target ())
ipa_ref *ref;
for (next_direct = e; next_direct && num_specs--;
next_direct = direct->next_callee)
{ {
next_direct->speculative_call_info (direct, indirect, cgraph_edge *ns;
ref); ns = e->make_speculative
(dyn_cast <cgraph_node *>
int num_specs2 = e2->num_speculative_call_targets_p (); (e3->speculative_call_target_ref ()->referred),
for (next_direct2 = e2; next_direct2 && num_specs2--; e3->count, e3->speculative_id);
next_direct2 = direct2->next_callee) /* Target may differ from ref (for example it may be
{ redirected to local alias. */
if (e2 && e2->speculative) ns->redirect_callee (e3->callee);
next_direct2->speculative_call_info (direct2,
indirect2,
ref);
if (direct->speculative_id == direct2->speculative_id
&& direct->lto_stmt_uid == direct2->lto_stmt_uid)
{
sreal scale1
= e->count.ipa ().to_sreal_scale (count);
sreal scale2
= e2->count.ipa ().to_sreal_scale (count);
if (scale1 == 0 && scale2 == 0)
scale1 = scale2 = 1;
sreal sum = scale1 + scale2;
int scaled_prob1
= (((sreal)direct->target_prob)
* scale1 / sum).to_int ();
int scaled_prob2
= (((sreal)direct2->target_prob)
* scale2 / sum).to_int ();
if (symtab->dump_file)
{
fprintf (
symtab->dump_file,
"Merging speculative id %i prob %i"
" and %i prob %i with scales %f %f\n",
direct->speculative_id, direct->target_prob,
direct2->speculative_id,
direct2->target_prob, scale1.to_double (),
scale2.to_double ());
fprintf (symtab->dump_file,
"Combined BB count ");
count.dump (symtab->dump_file);
fprintf (symtab->dump_file,
" dst edge count ");
e->count.dump (symtab->dump_file);
fprintf (symtab->dump_file,
" src edge count ");
e2->count.dump (symtab->dump_file);
fprintf (symtab->dump_file, "\n");
}
direct->target_prob = scaled_prob1 + scaled_prob2;
break;
}
}
} }
} }
else if (e->num_speculative_call_targets_p ()) continue;
{
/* Process if only dst is speculative. */
gcc_assert (!e->num_speculative_call_targets_p ());
}
else if (e2->num_speculative_call_targets_p ())
{
/* Process if only src is speculative. */
gcc_assert (!e2->num_speculative_call_targets_p ());
}
} }
/* When call is speculative, we need to re-distribute probabilities /* Iterate all speculations in SRC, see if corresponding ones exist
the same way as they was. This is not really correct because int DST and if so, sum the counts. Otherwise create new
in the other copy the speculation may differ; but probably it speculation. */
is not really worth the effort. */ int max_spec = 0;
if (e->speculative) for (cgraph_edge *e3 = e->first_speculative_call_target ();
e3;
e3 = e3->next_speculative_call_target ())
if (e3->speculative_id > max_spec)
max_spec = e3->speculative_id;
for (cgraph_edge *e3 = e2->first_speculative_call_target ();
e3;
e3 = e3->next_speculative_call_target ())
{ {
cgraph_edge *direct, *indirect; cgraph_edge *te
cgraph_edge *direct2 = NULL, *indirect2 = NULL; = e->speculative_call_for_target
ipa_ref *ref; (dyn_cast <cgraph_node *>
(e3->speculative_call_target_ref ()->referred));
e->speculative_call_info (direct, indirect, ref); if (te)
gcc_assert (e == indirect); te->count = te->count + e3->count;
if (e2 && e2->speculative) else
e2->speculative_call_info (direct2, indirect2, ref);
if (indirect->count > profile_count::zero ()
|| direct->count > profile_count::zero ())
{ {
/* We should mismatch earlier if there is no matching e->count = e->count + e3->count;
indirect edge. */ cgraph_edge *ns;
if (!e2) ns = e->make_speculative
{ (dyn_cast <cgraph_node *>
if (symtab->dump_file) (e3->speculative_call_target_ref ()
fprintf (symtab->dump_file, ->referred),
"Mismatch in merging indirect edges\n"); e3->count,
} e3->speculative_id + max_spec + 1);
else if (!e2->speculative) /* Target may differ from ref (for example it may be
indirect->count += e2->count; redirected to local alias. */
else if (e2->speculative) ns->redirect_callee (e3->callee);
{
if (DECL_ASSEMBLER_NAME (direct2->callee->decl)
!= DECL_ASSEMBLER_NAME (direct->callee->decl))
{
if (direct2->count >= direct->count)
{
direct->redirect_callee (direct2->callee);
indirect->count += indirect2->count
+ direct->count;
direct->count = direct2->count;
}
else
indirect->count += indirect2->count + direct2->count;
}
else
{
direct->count += direct2->count;
indirect->count += indirect2->count;
}
}
} }
else
/* At the moment we should have only profile feedback based
speculations when merging. */
gcc_unreachable ();
} }
else if (e2 && e2->speculative)
{
cgraph_edge *direct, *indirect;
ipa_ref *ref;
e2->speculative_call_info (direct, indirect, ref);
e->count = count;
e->make_speculative (direct->callee, direct->count);
}
else
e->count = count;
} }
if (!preserve_body) if (!preserve_body)
src->release_body (); src->release_body ();
...@@ -882,7 +756,8 @@ ipa_merge_profiles (struct cgraph_node *dst, ...@@ -882,7 +756,8 @@ ipa_merge_profiles (struct cgraph_node *dst,
src->decl = oldsrcdecl; src->decl = oldsrcdecl;
} }
/* Return true if call to DEST is known to be self-recusive call withing FUNC. */ /* Return true if call to DEST is known to be self-recusive
call withing FUNC. */
bool bool
recursive_call_p (tree func, tree dest) recursive_call_p (tree func, tree dest)
......
...@@ -262,6 +262,16 @@ public: ...@@ -262,6 +262,16 @@ public:
return ret; return ret;
} }
/* Return THIS with quality set to ADJUSTED. */
profile_probability adjusted () const
{
profile_probability ret = *this;
if (!initialized_p ())
return *this;
ret.m_quality = ADJUSTED;
return ret;
}
int to_reg_br_prob_base () const int to_reg_br_prob_base () const
{ {
gcc_checking_assert (initialized_p ()); gcc_checking_assert (initialized_p ());
......
...@@ -38,6 +38,6 @@ main (void) ...@@ -38,6 +38,6 @@ main (void)
return 0; return 0;
} }
/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* AA transformation on insn" "profile" } } */ /* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* AA .will resolve by ipa-profile" "profile" } } */
/* { dg-final-use-autofdo { scan-ipa-dump "Indirect call -> direct call.* AA transformation on insn" "afdo" } } */ /* { dg-final-use-autofdo { scan-ipa-dump "Indirect call -> direct call.* AA .will resolve by ipa-profile" "afdo" } } */
/* { dg-final-use-not-autofdo { scan-tree-dump-not "Invalid sum" "optimized" } } */ /* { dg-final-use-not-autofdo { scan-tree-dump-not "Invalid sum" "optimized" } } */
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
/* { dg-options "-O3 -flto -DDOJOB=1" } */ /* { dg-options "-O3 -flto -DDOJOB=1" } */
int a; int a;
extern void (*p[2])(int n); extern void (*p[5])(int n);
void abort (void); void abort (void);
int int
main() main()
...@@ -14,8 +14,8 @@ main() ...@@ -14,8 +14,8 @@ main()
p[0](1); p[0](1);
/* This call shall not be converted. */ /* This call shall not be converted. */
for (i = 0;i<1000;i++) for (i = 0;i<1000;i++)
p[i%2](2); p[i%5](2);
if (a != 1000) if (a != -1000)
abort (); abort ();
return 0; return 0;
......
...@@ -30,8 +30,23 @@ sub(int i) ...@@ -30,8 +30,23 @@ sub(int i)
{ {
a -= i; a -= i;
} }
void
add2(int i)
{
a -= 2*i;
}
void
sub2(int i)
{
a -= 2*i;
}
void
nothing(int i)
{
a -= i;
}
__attribute__ ((externally_visible)) __attribute__ ((externally_visible))
void (*p[2])(int)={add, sub}; void (*p[5])(int)={add, sub, add2, sub2, nothing};
#else #else
int int
main() main()
......
...@@ -37,6 +37,6 @@ main (void) ...@@ -37,6 +37,6 @@ main (void)
return 0; return 0;
} }
/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* a1 transformation on insn" "profile"} } */ /* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* a1 .will resolve by ipa-profile" "profile"} } */
/* { dg-final-use-autofdo { scan-ipa-dump "Indirect call -> direct call.* a1 transformation on insn" "afdo"} } */ /* { dg-final-use-autofdo { scan-ipa-dump "Indirect call -> direct call.* a1 .will resolve by ipa-profile" "afdo"} } */
/* { dg-final-use { scan-tree-dump-not "Invalid sum" "optimized"} } */ /* { dg-final-use { scan-tree-dump-not "Invalid sum" "optimized"} } */
...@@ -2181,47 +2181,51 @@ copy_bb (copy_body_data *id, basic_block bb, ...@@ -2181,47 +2181,51 @@ copy_bb (copy_body_data *id, basic_block bb,
if (edge) if (edge)
{ {
struct cgraph_edge *old_edge = edge; struct cgraph_edge *old_edge = edge;
profile_count old_cnt = edge->count;
edge = edge->clone (id->dst_node, call_stmt, /* A speculative call is consist of multiple
gimple_uid (stmt), edges - indirect edge and one or more direct edges
num, den, Duplicate the whole thing and distribute frequencies
true); accordingly. */
/* A speculative call is consist of edges - indirect edge
and direct edges (one indirect edeg may has multiple
direct edges). Duplicate the whole thing and
distribute frequencies accordingly. */
if (edge->speculative) if (edge->speculative)
{ {
struct cgraph_edge *direct, *indirect; int n = 0;
struct ipa_ref *ref; profile_count direct_cnt
= profile_count::zero ();
gcc_assert (!edge->indirect_unknown_callee);
old_edge->speculative_call_info (direct, indirect, ref); /* First figure out the distribution of counts
while (old_edge->next_callee so we can re-scale BB profile accordingly. */
&& old_edge->next_callee->speculative for (cgraph_edge *e = old_edge; e;
&& indirect->num_speculative_call_targets_p () e = e->next_speculative_call_target ())
> 1) direct_cnt = direct_cnt + e->count;
cgraph_edge *indirect
= old_edge->speculative_call_indirect_edge ();
profile_count indir_cnt = indirect->count;
/* Next iterate all direct edges, clone it and its
corresponding reference and update profile. */
for (cgraph_edge *e = old_edge;
e;
e = e->next_speculative_call_target ())
{ {
id->dst_node->clone_reference (ref, stmt); profile_count cnt = e->count;
edge = old_edge->next_callee; id->dst_node->clone_reference
edge = edge->clone (id->dst_node, call_stmt, (e->speculative_call_target_ref (), stmt);
gimple_uid (stmt), num, den, edge = e->clone (id->dst_node, call_stmt,
true); gimple_uid (stmt), num, den,
old_edge = old_edge->next_callee; true);
gcc_assert (!edge->indirect_unknown_callee); profile_probability prob
= cnt.probability_in (direct_cnt
/* If the indirect edge has multiple speculative + indir_cnt);
calls, iterate through all direct calls edge->count
associated to the speculative call and clone = copy_basic_block->count.apply_probability
all related direct edges before cloning the (prob);
related indirect edge. */ n++;
old_edge->speculative_call_info (direct, indirect,
ref);
} }
gcc_checking_assert
profile_count indir_cnt = indirect->count; (indirect->num_speculative_call_targets_p ()
== n);
/* Duplicate the indirect edge after all direct edges /* Duplicate the indirect edge after all direct edges
cloned. */ cloned. */
...@@ -2231,14 +2235,19 @@ copy_bb (copy_body_data *id, basic_block bb, ...@@ -2231,14 +2235,19 @@ copy_bb (copy_body_data *id, basic_block bb,
true); true);
profile_probability prob profile_probability prob
= indir_cnt.probability_in (old_cnt + indir_cnt); = indir_cnt.probability_in (direct_cnt
+ indir_cnt);
indirect->count indirect->count
= copy_basic_block->count.apply_probability (prob); = copy_basic_block->count.apply_probability (prob);
edge->count = copy_basic_block->count - indirect->count;
id->dst_node->clone_reference (ref, stmt);
} }
else else
edge->count = copy_basic_block->count; {
edge = edge->clone (id->dst_node, call_stmt,
gimple_uid (stmt),
num, den,
true);
edge->count = copy_basic_block->count;
}
} }
break; break;
......
...@@ -106,7 +106,7 @@ static bool gimple_divmod_fixed_value_transform (gimple_stmt_iterator *); ...@@ -106,7 +106,7 @@ static bool gimple_divmod_fixed_value_transform (gimple_stmt_iterator *);
static bool gimple_mod_pow2_value_transform (gimple_stmt_iterator *); static bool gimple_mod_pow2_value_transform (gimple_stmt_iterator *);
static bool gimple_mod_subtract_transform (gimple_stmt_iterator *); static bool gimple_mod_subtract_transform (gimple_stmt_iterator *);
static bool gimple_stringops_transform (gimple_stmt_iterator *); static bool gimple_stringops_transform (gimple_stmt_iterator *);
static void gimple_ic_transform (gimple_stmt_iterator *); static void dump_ic_profile (gimple_stmt_iterator *gsi);
/* Allocate histogram value. */ /* Allocate histogram value. */
...@@ -386,7 +386,9 @@ stream_in_histogram_value (class lto_input_block *ib, gimple *stmt) ...@@ -386,7 +386,9 @@ stream_in_histogram_value (class lto_input_block *ib, gimple *stmt)
default: default:
gcc_unreachable (); gcc_unreachable ();
} }
new_val->hvalue.counters = XNEWVAR (gcov_type, sizeof (*new_val->hvalue.counters) * ncounters); new_val->hvalue.counters = XNEWVAR (gcov_type,
sizeof (*new_val->hvalue.counters)
* ncounters);
new_val->n_counters = ncounters; new_val->n_counters = ncounters;
for (i = 0; i < ncounters; i++) for (i = 0; i < ncounters; i++)
new_val->hvalue.counters[i] = streamer_read_gcov_count (ib); new_val->hvalue.counters[i] = streamer_read_gcov_count (ib);
...@@ -629,7 +631,8 @@ gimple_value_profile_transformations (void) ...@@ -629,7 +631,8 @@ gimple_value_profile_transformations (void)
} }
/* The function never thansforms a GIMPLE statement. */ /* The function never thansforms a GIMPLE statement. */
gimple_ic_transform (&gsi); if (dump_enabled_p ())
dump_ic_profile (&gsi);
} }
} }
...@@ -1388,13 +1391,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call, ...@@ -1388,13 +1391,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
return dcall_stmt; return dcall_stmt;
} }
/* There maybe multiple indirect targets in histogram. Check every /* Dump info about indirect call profile. */
indirect/virtual call if callee function exists, if not exist, leave it to
LTO stage for later process. Modify code of this indirect call to an if-else
structure in ipa-profile finally. */
static void static void
gimple_ic_transform (gimple_stmt_iterator *gsi) dump_ic_profile (gimple_stmt_iterator *gsi)
{ {
gcall *stmt; gcall *stmt;
histogram_value histogram; histogram_value histogram;
...@@ -1423,37 +1423,25 @@ gimple_ic_transform (gimple_stmt_iterator *gsi) ...@@ -1423,37 +1423,25 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val, if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
&count, &all, j)) &count, &all, j))
return; return;
if (!count)
/* Minimum probability. should be higher than 25%. */ continue;
if (4 * count <= all)
return;
direct_call = find_func_by_profile_id ((int) val); direct_call = find_func_by_profile_id ((int) val);
if (direct_call == NULL) if (direct_call == NULL)
{ dump_printf_loc (
if (val) MSG_MISSED_OPTIMIZATION, stmt,
{ "Indirect call -> direct call from other "
if (dump_enabled_p ()) "module %T=> %i (will resolve by ipa-profile only with LTO)\n",
dump_printf_loc ( gimple_call_fn (stmt), (int) val);
MSG_MISSED_OPTIMIZATION, stmt, else
"Indirect call -> direct call from other " dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
"module %T=> %i (will resolve only with LTO)\n", "Indirect call -> direct call "
gimple_call_fn (stmt), (int) val); "%T => %T (will resolve by ipa-profile)\n",
} gimple_call_fn (stmt), direct_call->decl);
return; dump_printf_loc (MSG_NOTE, stmt,
} "hist->count %" PRId64 " hist->all %" PRId64 "\n",
count, all);
if (dump_enabled_p ())
{
dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
"Indirect call -> direct call "
"%T => %T transformation on insn postponed\n",
gimple_call_fn (stmt), direct_call->decl);
dump_printf_loc (MSG_NOTE, stmt,
"hist->count %" PRId64 " hist->all %" PRId64 "\n",
count, all);
}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment