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 and walk back into the unreachable forwarder block. We
then want to look at the predecessors and boom we ICE.
--
If we look at try_crossjump_to_edge we see the check to verify that SRC1
and SRC2 both have predecessors:
/* 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;
Clearly someone was aware that we could have unreachable blocks when
this code runs.
Then later we find call flow_find_cross_jump which walks up the insn
chain, potentially leaving SRC1/SRC2 (by design). When that happens we
reset SRC1/SRC2:
/* ... 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);
[ ... ]
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.
The fix is pretty simple. Check that SRC1/SRC2 have preds after the
call to flow_find_cross_jump.
Bootstrapped and regression tested on x86_64-linux-gnu.
OK for the trunk?
Jeff
PR middle-end/80422
* cfgcleanup.c (try_crossjump_to_edge): Verify SRC1 and SRC2 have
predecessors after walking up the insn chain.
PR middle-end/80422
* gcc.c-torture/compile/pr80422.c: New test.
diff --git a/gcc/cfgcleanup.c b/gcc/cfgcleanup.c
index d55b0ce..f68a964 100644
--- a/gcc/cfgcleanup.c
+++ b/gcc/cfgcleanup.c
@@ -2017,6 +2017,11 @@ try_crossjump_to_edge (int mode, edge e1, edge e2,
if (newpos2 != NULL_RTX)
src2 = BLOCK_FOR_INSN (newpos2);
+ /* Check that SRC1 and SRC2 have preds again. They may have changed
+ above due to the call to flow_find_cross_jump. */
+ if (EDGE_COUNT (src1->preds) == 0 || EDGE_COUNT (src2->preds) == 0)
+ return false;
+
if (dir == dir_backward)
{
#define SWAP(T, X, Y) do { T tmp = (X); (X) = (Y); (Y) = tmp; } while (0)
diff --git a/gcc/testsuite/gcc.c-torture/compile/pr80422.c
b/gcc/testsuite/gcc.c-torture/compile/pr80422.c
new file mode 100644
index 0000000..2cece67
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/compile/pr80422.c
@@ -0,0 +1,26 @@
+
+int a, c, f;
+short b, d, e;
+
+int fn1 (int h)
+{
+ return a > 2 || h > a ? h : h << a;
+}
+
+void fn2 ()
+{
+ int j, k;
+ while (1)
+ {
+ k = c && b;
+ f &= e > (fn1 (k) && j);
+ if (!d)
+ break;
+ }
+}
+
+int main ()
+{
+ fn2 ();
+ return 0;
+}