https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80422

--- Comment #2 from Jeffrey A. Law <law at redhat dot com> ---

This is a latent bug in cross jumping AFAICT.

Essentially a forwarder block becomes unreachable during cfg_cleanup. Later
we're cross jumping an indirect (via more forwarders) successor of the now
unreachable forwarder and walk up the CFG looking for hunks of equivalent
insns. 

flow_find_cross_jump walks the insn chain backwards from two given points to
find identical insns for crossjumping.  That walk can cross basic block
boundaries (by design, see walk_to_nondebug_insn).

As a result flow_find_cross_jump can return in F1 and F2 insns that are not in
blocks BB1 and BB2.

So given this code in try_crossjump_to_edge:

  /* Likewise with dead code (possibly newly created by the other optimizations
     of cfg_cleanup).  */
  if (EDGE_COUNT (src1->preds) == 0 || EDGE_COUNT (src2->preds) == 0)
    return false;

[ ... ]

  /* ... and part the second.  */
  nmatch = flow_find_cross_jump (src1, src2, &newpos1, &newpos2, &dir);

  osrc1 = src1;
  osrc2 = src2;
  if (newpos1 != NULL_RTX)
    src1 = BLOCK_FOR_INSN (newpos1);
  if (newpos2 != NULL_RTX)
    src2 = BLOCK_FOR_INSN (newpos2);


The EDGE_COUNT checks verify that the original starting points for cross
jumping are reachable.  But as we walk up the CFG we may run into cases where
flow_find_cross_jump walks us into one or more new blocks and we never check to
see if those new blocks are reachable or not.

A short time later we do this:

  /* Avoid splitting if possible.  We must always split when SRC2 has
     EH predecessor edges, or we may end up with basic blocks with both
     normal and EH predecessor edges.  */
  if (newpos2 == BB_HEAD (src2)
      && !(EDGE_PRED (src2, 0)->flags & EDGE_EH))
    redirect_to = src2;


If SRC2 is unreachable, then EDGE_PRED will read outside the vec's boundary
triggering the fault.

I'm pretty sure the fix is to verify the both blocks are reachable after
flow_find_cross_jump as well.

Reply via email to