splitCriticalEdges may crash while updating LI in case one of the
predecessors has a indbr and is in a different loop than the
predecessor, because it cannot split blocks with indbr terminators.
This crash was exposed by 189efe295b6e.
Differential D81494
[GVN] Do not try to split critical edges with indbr preds in loop. fhahn on Jun 9 2020, 11:45 AM. Authored by
Details
Diff Detail
Event TimelineComment Actions There's a separate bailout "COULD NOT PRE LOAD BECAUSE OF INDBR CRITICAL EDGE" earlier, so any block in the CriticalEdgePred has a non-indirectbr terminator. However, if LoadBB has any predecessor which is an indirectbr, even if it's not the edge we want to split, the call to llvm::SplitCriticalEdge fails. This is because it's trying to do something more than just split the critical edge. I guess here, it's trying to preserve LoopSimplify form: %exit is an exit block, so it should only have predecessors inside the loop. It should be possible to preserve the invariant here. For the testcase, the resulting function looks something like the following. We create a new basic block exit.latch1 to split the edge. Then we split exit at the beginning of the block, before any non-PHI instructions. Then exit.latch1 branches to the split block. (See also llvm::SplitIndirectBrCriticalEdges.) bb: br label %loop.header loop.header: store i32 0, i32* %p3, align 4 indirectbr i8* %p1, [label %loop.latch1, label %loop.latch2] loop.latch1: %c = call i1 @cond() br i1 %c, label %loop.header, label %exit.latch1 loop.latch2: indirectbr i8* %p2, [label %loop.header, label %exit] exit.latch1: br label %exit.split exit: br label %exit.split exit.split: %x = load i32, i32* %p3, align 4 %y = add i32 %x, 0 ret i32 %y I'm a little skeptical it's a good idea to bail out of GVN, as opposed to fixing SplitCriticalEdge. If SplitCriticalEdge was private to GVN, maybe it would be okay. But as it is, I think it's a ticking time bomb if SplitCriticalEdge "randomly" crashes on certain edges due to undocumented constraints. Comment Actions Exactly! Sorry for not making it clear in the description.
Right, that should work. I tried, but unfortunately it means moving instructions to a different block & potentially introducing new PHIs and some users of SplitCriticalEdge do not seem to like it (especially the MemoryDependenceAnalysis caching....). I've put up the patch there D81584, but some of the issues are not resolved yet (and I am not sure if it is the best way forward at the moment, given that users seem not prepared for instructions being moved).
yes it seems a bit odd to have the check here in GVN. I've put up D81582, which updates SplitCriticalEdge to return nullptr before making any changes, if it would have to split a block with a indirectbr terminator. That should ensure we do not run into the crash for other users. GVN itself seems to bail out on blocks it *things* SplitCriticalEdge cannot handle, but that seems a bit fragile in general. |