Message ID | ri61reevcen.fsf@suse.cz |
---|---|
State | New |
Headers | show |
Series | ipa: Fix resolving speculations through cgraph_edge::set_call_stmt (PR 98078) | expand |
Hi, ping, please. Martin On Thu, Jan 21 2021, Martin Jambor wrote: > Hi, > > in the PR 98078 testcase, speculative call-graph edges which were > created by IPA-CP are confirmed during inlining but > cgraph_edge::set_call_stmt does not take it very well. > > The function enters the update_speculative branch and updates the edges > in the speculation bundle separately (by a recursive call), but when it > processes the first direct edge, most of the bundle actually ceases to > exist because it is devirtualized. It nevertheless goes on to attempt > to update the indirect edge (that has just been removed), which > surprisingly gets as far as adding the edge to the call_site_hash, the > same devirtualized edge for the second time, and that triggers an > assert. > > Fixed by this patch which makes the function aware that it is about to > resolve a speculation and do so instead of updating components of > speculation. Also, it does so before dealing with the hash because > the speculation resolution code needs the hash to point to the first > speculative direct edge and also cleans the hash up by calling > update_call_stmt_hash_for_removing_direct_edge. > > I don't have a testcase, at least not yet, the one in BZ does not link > when it does not ICE. I can try to find some time to make it link it is > deemed very important. > > Bootstrapped and tested on x86_64-linux, also profile-LTO-bootstrapped > on the same system. OK for trunk? What about gcc10, where we cannot > trigger it but I suppose the bug is there? > > Thanks, > > Martin > > > gcc/ChangeLog: > > 2021-01-20 Martin Jambor <mjambor@suse.cz> > > PR ipa/98078 > * cgraph.c (cgraph_edge::set_call_stmt): Do not update all > corresponding speculative edges if we are about to resolve > sepculation. Make edge direct (and so resolve speculations) before > removing it from call_site_hash. > (cgraph_edge::make_direct): Relax the initial assert to allow calling > the function on speculative direct edges. > --- > gcc/cgraph.c | 37 ++++++++++++++++++++++--------------- > 1 file changed, 22 insertions(+), 15 deletions(-) > > diff --git a/gcc/cgraph.c b/gcc/cgraph.c > index db038306e19..80140757d16 100644 > --- a/gcc/cgraph.c > +++ b/gcc/cgraph.c > @@ -789,9 +789,22 @@ cgraph_edge::set_call_stmt (cgraph_edge *e, gcall *new_stmt, > { > tree decl; > > + cgraph_node *new_direct_callee = NULL; > + if ((e->indirect_unknown_callee || e->speculative) > + && (decl = gimple_call_fndecl (new_stmt))) > + { > + /* Constant propagation and especially inlining can turn an indirect call > + into a direct one. */ > + new_direct_callee = cgraph_node::get (decl); > + gcc_checking_assert (new_direct_callee); > + } > + > /* Speculative edges has three component, update all of them > when asked to. */ > - if (update_speculative && e->speculative) > + if (update_speculative && e->speculative > + /* If we are about to resolve the speculation by calling make_direct > + below, do not bother going over all the speculative edges now. */ > + && !new_direct_callee) > { > cgraph_edge *direct, *indirect, *next; > ipa_ref *ref; > @@ -821,6 +834,9 @@ cgraph_edge::set_call_stmt (cgraph_edge *e, gcall *new_stmt, > return e_indirect ? indirect : direct; > } > > + if (new_direct_callee) > + e = make_direct (e, new_direct_callee); > + > /* Only direct speculative edges go to call_site_hash. */ > if (e->caller->call_site_hash > && (!e->speculative || !e->indirect_unknown_callee) > @@ -831,16 +847,6 @@ cgraph_edge::set_call_stmt (cgraph_edge *e, gcall *new_stmt, > (e->call_stmt, cgraph_edge_hasher::hash (e->call_stmt)); > > e->call_stmt = new_stmt; > - if (e->indirect_unknown_callee > - && (decl = gimple_call_fndecl (new_stmt))) > - { > - /* Constant propagation (and possibly also inlining?) can turn an > - indirect call into a direct one. */ > - cgraph_node *new_callee = cgraph_node::get (decl); > - > - gcc_checking_assert (new_callee); > - e = make_direct (e, new_callee); > - } > > function *fun = DECL_STRUCT_FUNCTION (e->caller->decl); > e->can_throw_external = stmt_can_throw_external (fun, new_stmt); > @@ -1279,14 +1285,15 @@ cgraph_edge::speculative_call_for_target (cgraph_node *target) > return NULL; > } > > -/* Make an indirect edge with an unknown callee an ordinary edge leading to > - CALLEE. Speculations can be resolved in the process and EDGE can be removed > - and deallocated. Return the edge that now represents the call. */ > +/* Make an indirect or speculative EDGE with an unknown callee an ordinary edge > + leading to CALLEE. Speculations can be resolved in the process and EDGE can > + be removed and deallocated. Return the edge that now represents the > + call. */ > > cgraph_edge * > cgraph_edge::make_direct (cgraph_edge *edge, cgraph_node *callee) > { > - gcc_assert (edge->indirect_unknown_callee); > + gcc_assert (edge->indirect_unknown_callee || edge->speculative); > > /* If we are redirecting speculative call, make it non-speculative. */ > if (edge->speculative) > -- > 2.29.2
On 1/21/21 2:59 AM, Martin Jambor wrote: > Hi, > > in the PR 98078 testcase, speculative call-graph edges which were > created by IPA-CP are confirmed during inlining but > cgraph_edge::set_call_stmt does not take it very well. > > The function enters the update_speculative branch and updates the edges > in the speculation bundle separately (by a recursive call), but when it > processes the first direct edge, most of the bundle actually ceases to > exist because it is devirtualized. It nevertheless goes on to attempt > to update the indirect edge (that has just been removed), which > surprisingly gets as far as adding the edge to the call_site_hash, the > same devirtualized edge for the second time, and that triggers an > assert. > > Fixed by this patch which makes the function aware that it is about to > resolve a speculation and do so instead of updating components of > speculation. Also, it does so before dealing with the hash because > the speculation resolution code needs the hash to point to the first > speculative direct edge and also cleans the hash up by calling > update_call_stmt_hash_for_removing_direct_edge. > > I don't have a testcase, at least not yet, the one in BZ does not link > when it does not ICE. I can try to find some time to make it link it is > deemed very important. > > Bootstrapped and tested on x86_64-linux, also profile-LTO-bootstrapped > on the same system. OK for trunk? What about gcc10, where we cannot > trigger it but I suppose the bug is there? > > Thanks, > > Martin > > > gcc/ChangeLog: > > 2021-01-20 Martin Jambor <mjambor@suse.cz> > > PR ipa/98078 > * cgraph.c (cgraph_edge::set_call_stmt): Do not update all > corresponding speculative edges if we are about to resolve > sepculation. Make edge direct (and so resolve speculations) before > removing it from call_site_hash. > (cgraph_edge::make_direct): Relax the initial assert to allow calling > the function on speculative direct edges. OK. jeff
diff --git a/gcc/cgraph.c b/gcc/cgraph.c index db038306e19..80140757d16 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -789,9 +789,22 @@ cgraph_edge::set_call_stmt (cgraph_edge *e, gcall *new_stmt, { tree decl; + cgraph_node *new_direct_callee = NULL; + if ((e->indirect_unknown_callee || e->speculative) + && (decl = gimple_call_fndecl (new_stmt))) + { + /* Constant propagation and especially inlining can turn an indirect call + into a direct one. */ + new_direct_callee = cgraph_node::get (decl); + gcc_checking_assert (new_direct_callee); + } + /* Speculative edges has three component, update all of them when asked to. */ - if (update_speculative && e->speculative) + if (update_speculative && e->speculative + /* If we are about to resolve the speculation by calling make_direct + below, do not bother going over all the speculative edges now. */ + && !new_direct_callee) { cgraph_edge *direct, *indirect, *next; ipa_ref *ref; @@ -821,6 +834,9 @@ cgraph_edge::set_call_stmt (cgraph_edge *e, gcall *new_stmt, return e_indirect ? indirect : direct; } + if (new_direct_callee) + e = make_direct (e, new_direct_callee); + /* Only direct speculative edges go to call_site_hash. */ if (e->caller->call_site_hash && (!e->speculative || !e->indirect_unknown_callee) @@ -831,16 +847,6 @@ cgraph_edge::set_call_stmt (cgraph_edge *e, gcall *new_stmt, (e->call_stmt, cgraph_edge_hasher::hash (e->call_stmt)); e->call_stmt = new_stmt; - if (e->indirect_unknown_callee - && (decl = gimple_call_fndecl (new_stmt))) - { - /* Constant propagation (and possibly also inlining?) can turn an - indirect call into a direct one. */ - cgraph_node *new_callee = cgraph_node::get (decl); - - gcc_checking_assert (new_callee); - e = make_direct (e, new_callee); - } function *fun = DECL_STRUCT_FUNCTION (e->caller->decl); e->can_throw_external = stmt_can_throw_external (fun, new_stmt); @@ -1279,14 +1285,15 @@ cgraph_edge::speculative_call_for_target (cgraph_node *target) return NULL; } -/* Make an indirect edge with an unknown callee an ordinary edge leading to - CALLEE. Speculations can be resolved in the process and EDGE can be removed - and deallocated. Return the edge that now represents the call. */ +/* Make an indirect or speculative EDGE with an unknown callee an ordinary edge + leading to CALLEE. Speculations can be resolved in the process and EDGE can + be removed and deallocated. Return the edge that now represents the + call. */ cgraph_edge * cgraph_edge::make_direct (cgraph_edge *edge, cgraph_node *callee) { - gcc_assert (edge->indirect_unknown_callee); + gcc_assert (edge->indirect_unknown_callee || edge->speculative); /* If we are redirecting speculative call, make it non-speculative. */ if (edge->speculative)