Fix ICE in speculative_call_info
authorJan Hubicka <jh@suse.cz>
Sun, 19 Jan 2020 12:49:38 +0000 (13:49 +0100)
committerJan Hubicka <jh@suse.cz>
Sun, 19 Jan 2020 12:49:38 +0000 (13:49 +0100)
this fixes two issues with the new multi-target speculation code which reproduce
on Firefox.  I can now build firefox with FDO locally but on Mozilla build bots
it still fails with ICE in speculative_call_info.

One problem is that speuclative code compares call_stmt and lto_stmt_uid in
a way that may get unwanted effect when these gets out of sync.  It does not
make sense to have both non-zero so I added code clearing it and sanity check
that it is kept this way.

Other problem is cgraph_edge::make_direct not working well with multiple
targets.  In this case it removed one speuclative target and the indirect call
leaving other targets in the tree.

This is fixed by iterating across all targets and removing all except the good
one (if it exists).

PR lto/93318
* cgraph.c (cgraph_edge::resolve_speculation): Fix foramting.
(cgraph_edge::make_direct): Remove all indirect targets.
(cgraph_edge::redirect_call_stmt_to_callee): Use make_direct..
(cgraph_node::verify_node): Verify that only one call_stmt or
lto_stmt_uid is set.
* cgraphclones.c (cgraph_edge::clone): Set only one call_stmt or
lto_stmt_uid.
* lto-cgraph.c (lto_output_edge): Simplify streaming of stmt.
(lto_output_ref): Simplify streaming of stmt.
* lto-streamer-in.c (fixup_call_stmt_edges_1): Clear lto_stmt_uid.

gcc/ChangeLog
gcc/cgraph.c
gcc/cgraphclones.c
gcc/lto-cgraph.c
gcc/lto-streamer-in.c

index 61a96f154c8fec8b9193cc4c55ff0d7184f54371..0e24b4a64721432a0cbd19ce447030096e12e771 100644 (file)
@@ -1,3 +1,17 @@
+2020-01-18  Jan Hubicka  <hubicka@ucw.cz>
+
+       PR lto/93318
+       * cgraph.c (cgraph_edge::resolve_speculation): Fix foramting.
+       (cgraph_edge::make_direct): Remove all indirect targets.
+       (cgraph_edge::redirect_call_stmt_to_callee): Use make_direct..
+       (cgraph_node::verify_node): Verify that only one call_stmt or
+       lto_stmt_uid is set.
+       * cgraphclones.c (cgraph_edge::clone): Set only one call_stmt or
+       lto_stmt_uid.
+       * lto-cgraph.c (lto_output_edge): Simplify streaming of stmt.
+       (lto_output_ref): Simplify streaming of stmt.
+       * lto-streamer-in.c (fixup_call_stmt_edges_1): Clear lto_stmt_uid.
+
 2020-01-18  Tamar Christina  <tamar.christina@arm.com>
 
        * config/aarch64/aarch64-sve-builtins-base.cc (memory_vector_mode):
index 95b523d6be552a3cc618e3bfbfcc346f041adb8e..c442f334fe24632022ff854a385c04c22424d5b1 100644 (file)
@@ -1226,8 +1226,8 @@ cgraph_edge::resolve_speculation (cgraph_edge *edge, tree callee_decl)
         fprintf (dump_file, "Speculative call turned into direct call.\n");
       edge = e2;
       e2 = tmp;
-      /* FIXME:  If EDGE is inlined, we should scale up the frequencies and counts
-         in the functions inlined through it.  */
+      /* FIXME:  If EDGE is inlined, we should scale up the frequencies
+        and counts in the functions inlined through it.  */
     }
   edge->count += e2->count;
   if (edge->num_speculative_call_targets_p ())
@@ -1263,11 +1263,52 @@ cgraph_edge::make_direct (cgraph_edge *edge, cgraph_node *callee)
   /* If we are redirecting speculative call, make it non-speculative.  */
   if (edge->speculative)
     {
-      edge = resolve_speculation (edge, callee->decl);
+      cgraph_edge *found = NULL;
+      cgraph_edge *direct, *next;
+      ipa_ref *ref;
+
+      edge->speculative_call_info (direct, edge, ref);
 
-      /* On successful speculation just return the pre existing direct edge.  */
-      if (!edge->indirect_unknown_callee)
-        return edge;
+      /* Look all speculative targets and remove all but one corresponding
+        to callee (if it exists).
+        If there is only one target we can save one extra call to
+        speculative_call_info.  */
+      if (edge->num_speculative_call_targets_p () != 1)
+       for (direct = edge->caller->callees; direct; direct = next)
+         {
+           next = direct->next_callee;
+           if (direct->call_stmt == edge->call_stmt
+               && direct->lto_stmt_uid == edge->lto_stmt_uid)
+             {
+               direct->speculative_call_info (direct, edge, ref);
+
+               /* Compare ref not direct->callee.  Direct edge is possibly
+                  inlined or redirected.  */
+               if (!ref->referred->semantically_equivalent_p (callee))
+                 edge = direct->resolve_speculation (direct, NULL);
+               else
+                 {
+                   gcc_checking_assert (!found);
+                   found = direct;
+                 }
+             }
+         }
+       else if (!ref->referred->semantically_equivalent_p (callee))
+         edge = direct->resolve_speculation (direct, NULL);
+       else
+         found = direct;
+
+      /* On successful speculation just remove the indirect edge and
+        return the pre existing direct edge.
+        It is important to not remove it and redirect because the direct
+        edge may be inlined or redirected.  */
+      if (found)
+       {
+         resolve_speculation (edge, callee->decl);
+         gcc_checking_assert (!found->speculative);
+         return found;
+       }
+      gcc_checking_assert (!edge->speculative);
     }
 
   edge->indirect_unknown_callee = 0;
@@ -1328,7 +1369,7 @@ cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
       /* If there already is an direct call (i.e. as a result of inliner's
         substitution), forget about speculating.  */
       if (decl)
-       e = resolve_speculation (e, decl);
+       e = make_direct (e, cgraph_node::get (decl));
       else
        {
          /* Expand speculation into GIMPLE code.  */
@@ -3116,6 +3157,8 @@ cgraph_node::verify_node (void)
   basic_block this_block;
   gimple_stmt_iterator gsi;
   bool error_found = false;
+  int i;
+  ipa_ref *ref = NULL;
 
   if (seen_error ())
     return;
@@ -3201,6 +3244,11 @@ cgraph_node::verify_node (void)
          cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
          error_found = true;
        }
+      if (e->call_stmt && e->lto_stmt_uid)
+       {
+         error ("edge has both cal_stmt and lto_stmt_uid set");
+         error_found = true;
+       }
     }
   bool check_comdat = comdat_local_p ();
   for (e = callers; e; e = e->next_caller)
@@ -3267,6 +3315,11 @@ cgraph_node::verify_node (void)
          fprintf (stderr, "\n");
          error_found = true;
        }
+      if (e->call_stmt && e->lto_stmt_uid)
+       {
+         error ("edge has both cal_stmt and lto_stmt_uid set");
+         error_found = true;
+       }
     }
   for (e = indirect_calls; e; e = e->next_callee)
     {
@@ -3398,8 +3451,6 @@ cgraph_node::verify_node (void)
       if (this_cfun->cfg)
        {
          hash_set<gimple *> stmts;
-         int i;
-         ipa_ref *ref = NULL;
 
          /* Reach the trees by walking over the CFG, and note the
             enclosing basic-blocks in the call edges.  */
@@ -3468,6 +3519,13 @@ cgraph_node::verify_node (void)
        /* No CFG available?!  */
        gcc_unreachable ();
 
+      for (i = 0; iterate_reference (i, ref); i++)
+       if (ref->stmt && ref->lto_stmt_uid)
+         {
+           error ("reference has both cal_stmt and lto_stmt_uid set");
+           error_found = true;
+         }
+
       for (e = callees; e; e = e->next_callee)
        {
          if (!e->aux && !e->speculative)
index e73e0696b9169132b59ab3e06082ddff92fe9c71..417488bca1f8397f8ab0363130d40c262a6c46bd 100644 (file)
@@ -132,7 +132,8 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
 
   new_edge->inline_failed = inline_failed;
   new_edge->indirect_inlining_edge = indirect_inlining_edge;
-  new_edge->lto_stmt_uid = stmt_uid;
+  if (!call_stmt)
+    new_edge->lto_stmt_uid = stmt_uid;
   new_edge->speculative_id = speculative_id;
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = can_throw_external;
index 621607499e1fd47e68f74e665061db4782b919d0..b0c7ebf775bd81f7a748da2e918847bd521829e5 100644 (file)
@@ -257,10 +257,11 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   edge->count.stream_out (ob->main_stream);
 
   bp = bitpack_create (ob->main_stream);
-  uid = (!gimple_has_body_p (edge->caller->decl) || edge->caller->thunk.thunk_p
-        ? edge->lto_stmt_uid : gimple_uid (edge->call_stmt) + 1);
+  uid = !edge->call_stmt ? edge->lto_stmt_uid
+                        : gimple_uid (edge->call_stmt) + 1;
   bp_pack_enum (&bp, cgraph_inline_failed_t,
                CIF_N_REASONS, edge->inline_failed);
+  gcc_checking_assert (uid || edge->caller->thunk.thunk_p);
   bp_pack_var_len_unsigned (&bp, uid);
   bp_pack_value (&bp, edge->speculative_id, 16);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
@@ -669,7 +670,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
 {
   struct bitpack_d bp;
   int nref;
-  int uid = ref->lto_stmt_uid;
+  int uid = !ref->stmt ? ref->lto_stmt_uid : gimple_uid (ref->stmt) + 1;
   struct cgraph_node *node;
 
   bp = bitpack_create (ob->main_stream);
index 3e64371094e0de874b87a3b2f7b2933e8344d0d0..9566e5ee102f2e09f07bf59a00d600d38d87dedd 100644 (file)
@@ -892,6 +892,7 @@ fixup_call_stmt_edges_1 (struct cgraph_node *node, gimple **stmts,
         fatal_error (input_location,
                     "Cgraph edge statement index out of range");
       cedge->call_stmt = as_a <gcall *> (stmts[cedge->lto_stmt_uid - 1]);
+      cedge->lto_stmt_uid = 0;
       if (!cedge->call_stmt)
         fatal_error (input_location,
                     "Cgraph edge statement index not found");
@@ -902,6 +903,7 @@ fixup_call_stmt_edges_1 (struct cgraph_node *node, gimple **stmts,
         fatal_error (input_location,
                     "Cgraph edge statement index out of range");
       cedge->call_stmt = as_a <gcall *> (stmts[cedge->lto_stmt_uid - 1]);
+      cedge->lto_stmt_uid = 0;
       if (!cedge->call_stmt)
         fatal_error (input_location, "Cgraph edge statement index not found");
     }
@@ -912,6 +914,7 @@ fixup_call_stmt_edges_1 (struct cgraph_node *node, gimple **stmts,
          fatal_error (input_location,
                       "Reference statement index out of range");
        ref->stmt = stmts[ref->lto_stmt_uid - 1];
+       ref->lto_stmt_uid = 0;
        if (!ref->stmt)
          fatal_error (input_location, "Reference statement index not found");
       }