[gcc r10-9449] ipa: Fix resolving speculations through cgraph_edge::set_call_stmt

Martin Jambor jamborm@gcc.gnu.org
Tue Mar 16 15:44:27 GMT 2021


https://gcc.gnu.org/g:27dca33bc5c52971c00d1657560aeabae8f35d18

commit r10-9449-g27dca33bc5c52971c00d1657560aeabae8f35d18
Author: Martin Jambor <mjambor@suse.cz>
Date:   Tue Mar 16 16:42:41 2021 +0100

    ipa: Fix resolving speculations through cgraph_edge::set_call_stmt
    
    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.
    
    Bootstrapped and tested on x86_64-linux, also profile-LTO-bootstrapped
    on the same system.
    
    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.
    
    (cherry picked from commit b8188b7d7382e4a74af5dd6a125e76e8d43a68a5)

Diff:
---
 gcc/cgraph.c | 37 ++++++++++++++++++++++---------------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 2a9813df2d9..4a44b4a4896 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -782,9 +782,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;
@@ -814,6 +827,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)
@@ -824,16 +840,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);
@@ -1270,14 +1276,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)


More information about the Gcc-cvs mailing list