diff --git a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp --- a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp +++ b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp @@ -598,6 +598,23 @@ auto *ParentBB = SI.getParent(); + auto IsTriviallyUnswitchableExitBlock = [&](BasicBlock &BBToCheck) { + // BBToCheck is not an exit block if it is inside loop L. + if (L.contains(&BBToCheck)) + return false; + // BBToCheck is not trivial to unswitch if its phis aren't loop invariant. + if (!areLoopExitPHIsLoopInvariant(L, *ParentBB, BBToCheck)) + return false; + // We do not unswitch a block that only has an unreachable statement, as + // it's possible this is a previously unswitched block. Only unswitch if + // either the terminator is not unreachable, or, if it is, it's not the only + // instruction in the block. + auto *TI = BBToCheck.getTerminator(); + bool isUnreachable = isa(TI); + return !isUnreachable || + (isUnreachable && (BBToCheck.getFirstNonPHIOrDbg() != TI)); + }; + SmallVector ExitCaseIndices; for (auto Case : SI.cases()) { auto *SuccBB = Case.getCaseSuccessor(); @@ -608,9 +625,7 @@ BasicBlock *DefaultExitBB = nullptr; SwitchInstProfUpdateWrapper::CaseWeightOpt DefaultCaseWeight = SwitchInstProfUpdateWrapper::getSuccessorWeight(SI, 0); - if (!L.contains(SI.getDefaultDest()) && - areLoopExitPHIsLoopInvariant(L, *ParentBB, *SI.getDefaultDest()) && - !isa(SI.getDefaultDest()->getTerminator())) { + if (IsTriviallyUnswitchableExitBlock(*SI.getDefaultDest())) { DefaultExitBB = SI.getDefaultDest(); } else if (ExitCaseIndices.empty()) return false; diff --git a/llvm/test/Transforms/SimpleLoopUnswitch/trivial-unswitch.ll b/llvm/test/Transforms/SimpleLoopUnswitch/trivial-unswitch.ll --- a/llvm/test/Transforms/SimpleLoopUnswitch/trivial-unswitch.ll +++ b/llvm/test/Transforms/SimpleLoopUnswitch/trivial-unswitch.ll @@ -1243,3 +1243,52 @@ ; CHECK: loopexit: ; CHECK-NEXT: ret } + +declare void @f() +declare void @g() +define void @test_unswitch_switch_with_nonempty_unreachable() { +; CHECK-LABEL: @test_unswitch_switch_with_nonempty_unreachable() +entry: + br label %loop + +loop: + %cleanup.dest.slot.0 = select i1 undef, i32 5, i32 undef + br label %for.cond + +for.cond: + switch i32 %cleanup.dest.slot.0, label %NonEmptyUnreachableBlock [ + i32 0, label %for.cond + i32 1, label %NonEmptyUnreachableBlock + i32 2, label %loop.loopexit + ] + +loop.loopexit: + unreachable + +NonEmptyUnreachableBlock: + call void @f() + call void @g() + unreachable + +; CHECK:loop: +; CHECK-NEXT: %cleanup.dest.slot.0 = select i1 undef, i32 5, i32 undef +; CHECK-NEXT: switch i32 %cleanup.dest.slot.0, label %NonEmptyUnreachableBlock [ +; CHECK-NEXT: i32 1, label %NonEmptyUnreachableBlock +; CHECK-NEXT: i32 2, label %loop.loopexit +; CHECK-NEXT: i32 0, label %loop.split +; CHECK-NEXT: ] + +; CHECK:loop.split: +; CHECK-NEXT: br label %for.cond + +; CHECK:for.cond: +; CHECK-NEXT: br label %for.cond + +; CHECK:loop.loopexit: +; CHECK-NEXT: unreachable + +; CHECK:NonEmptyUnreachableBlock: +; CHECK-NEXT: call void @f() +; CHECK-NEXT: call void @g() +; CHECK-NEXT: unreachable +}