As mentioned in the BZ, EDGE_IGNORE is leaking out of the threader. If we have a jump threading path through a block with one or more EDGE_IGNORE outgoing edges, then the duplicate block's outgoing edges will have the EDGE_IGNORE flag set.
I looked at doing the switch statement and edge cleanups earlier, but it's just not going to fly. That's too bad because it'd be cheaper to copy the simplified block. Oh well. I considered adding the newly created block and edges to the queue of things to clean up. But that means having to share a vec across elements (or copy it I guess). It also meant some rather gross layering violations. So the final option was just to wipe the EDGE_IGNORE flag as we create the duplicates, which is what this patch does. Bootstrapped and regression tested on x86_64, ppc64le, ppc64, s390x aarch64. Also regression tested on a ton of *-elf targets. Installing on the trunk. jeff
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b0b85bac352..f9a12f88a60 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2018-11-23 Jeff Law <l...@redhat.com> + + PR rtl-optimization/87468 + * tree-ssa-threadupdate.c (create_block_for_threading): Clear + EDGE_IGNORE on all outgoing edges of the duplicate block. + 2018-11-23 Vladimir Makarov <vmaka...@redhat.com> PR bootstrap/88157 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index e31d40d7924..587526e7443 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2018-11-23 Jeff Law <l...@redhat.com> + + PR rtl-optimization/84768 + * gcc.c-torture/compile/pr84768.c: New test. + 2018-11-23 Vladimir Makarov <vmaka...@redhat.com> * gcc.target/powerpc/pr70669.c: Use unary minus instead of diff --git a/gcc/testsuite/gcc.c-torture/compile/pr87468.c b/gcc/testsuite/gcc.c-torture/compile/pr87468.c new file mode 100644 index 00000000000..2f5cf80bf9f --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/compile/pr87468.c @@ -0,0 +1,15 @@ +a; +b() { + int c = 1; + for (; c <= 3;) { + int d = e() && !0; + switch (c) + case 1: + if (d) + case 2: + case 3: + f(); + if (a) + c++; + } +} diff --git a/gcc/tree-ssa-threadupdate.c b/gcc/tree-ssa-threadupdate.c index 6630516b99a..e7c7ca6fb15 100644 --- a/gcc/tree-ssa-threadupdate.c +++ b/gcc/tree-ssa-threadupdate.c @@ -336,7 +336,17 @@ create_block_for_threading (basic_block bb, rd->dup_blocks[count] = duplicate_block (bb, NULL, NULL); FOR_EACH_EDGE (e, ei, rd->dup_blocks[count]->succs) - e->aux = NULL; + { + e->aux = NULL; + + /* If we duplicate a block with an outgoing edge marked as + EDGE_IGNORE, we must clear EDGE_IGNORE so that it doesn't + leak out of the current pass. + + It would be better to simplify switch statements and remove + the edges before we get here, but the sequencing is nontrivial. */ + e->flags &= ~EDGE_IGNORE; + } /* Zero out the profile, since the block is unreachable for now. */ rd->dup_blocks[count]->count = profile_count::uninitialized ();