diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp --- a/llvm/lib/Transforms/Scalar/SCCP.cpp +++ b/llvm/lib/Transforms/Scalar/SCCP.cpp @@ -336,7 +336,8 @@ } static bool removeNonFeasibleEdges(const SCCPSolver &Solver, BasicBlock *BB, - DomTreeUpdater &DTU) { + DomTreeUpdater &DTU, + BasicBlock *&NewUnreachableBB) { SmallPtrSet FeasibleSuccessors; bool HasNonFeasibleEdges = false; for (BasicBlock *Succ : successors(BB)) { @@ -379,6 +380,23 @@ } else if (FeasibleSuccessors.size() > 1) { SwitchInstProfUpdateWrapper SI(*cast(TI)); SmallVector Updates; + + // If the default destination is unfeasible it will never be taken. Replace + // it with a new block with a single Unreachable instruction. + BasicBlock *DefaultDest = SI->getDefaultDest(); + if (!FeasibleSuccessors.contains(DefaultDest)) { + if (!NewUnreachableBB) { + NewUnreachableBB = + BasicBlock::Create(DefaultDest->getContext(), "default.unreachable", + DefaultDest->getParent(), DefaultDest); + new UnreachableInst(DefaultDest->getContext(), NewUnreachableBB); + } + + SI->setDefaultDest(NewUnreachableBB); + Updates.push_back({DominatorTree::Delete, BB, DefaultDest}); + Updates.push_back({DominatorTree::Insert, BB, NewUnreachableBB}); + } + for (auto CI = SI->case_begin(); CI != SI->case_end();) { if (FeasibleSuccessors.contains(CI->getCaseSuccessor())) { ++CI; @@ -526,8 +544,9 @@ NumInstRemoved += changeToUnreachable(F.front().getFirstNonPHI(), /*PreserveLCSSA=*/false, &DTU); + BasicBlock *NewUnreachableBB = nullptr; for (BasicBlock &BB : F) - MadeChanges |= removeNonFeasibleEdges(Solver, &BB, DTU); + MadeChanges |= removeNonFeasibleEdges(Solver, &BB, DTU, NewUnreachableBB); for (BasicBlock *DeadBB : BlocksToErase) if (!DeadBB->hasAddressTaken()) diff --git a/llvm/test/Transforms/SCCP/switch-constantfold-crash.ll b/llvm/test/Transforms/SCCP/switch-constantfold-crash.ll --- a/llvm/test/Transforms/SCCP/switch-constantfold-crash.ll +++ b/llvm/test/Transforms/SCCP/switch-constantfold-crash.ll @@ -1,8 +1,9 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -passes=ipsccp < %s -S | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature +; RUN: opt -passes=ipsccp < %s -S | FileCheck --check-prefixes=CHECK,ONCE %s +; RUN: opt -passes='ipsccp,ipsccp' < %s -S | FileCheck --check-prefixes=CHECK,TWICE %s define void @barney() { -; CHECK-LABEL: @barney( +; CHECK-LABEL: define {{[^@]+}}@barney() { ; CHECK-NEXT: bb: ; CHECK-NEXT: br label [[BB9:%.*]] ; CHECK: bb6: @@ -26,7 +27,7 @@ } define void @blam() { -; CHECK-LABEL: @blam( +; CHECK-LABEL: define {{[^@]+}}@blam() { ; CHECK-NEXT: bb: ; CHECK-NEXT: br label [[BB16:%.*]] ; CHECK: bb16: @@ -59,7 +60,7 @@ define void @hoge() { -; CHECK-LABEL: @hoge( +; CHECK-LABEL: define {{[^@]+}}@hoge() { ; CHECK-NEXT: bb: ; CHECK-NEXT: br label [[BB2:%.*]] ; CHECK: bb2: @@ -89,3 +90,142 @@ bb4: ; preds = %bb2, %bb2, %bb2 unreachable } + +; Test case from PR49573. %default.bb is unfeasible. Make sure it gets replaced +; by an unreachable block. +define void @pr49573_main() { +; ONCE-LABEL: define {{[^@]+}}@pr49573_main() { +; ONCE-NEXT: entry: +; ONCE-NEXT: [[TGT:%.*]] = call i16 @pr49573_fn() +; ONCE-NEXT: switch i16 2, label [[DEFAULT_UNREACHABLE:%.*]] [ +; ONCE-NEXT: i16 0, label [[CASE_0:%.*]] +; ONCE-NEXT: i16 2, label [[CASE_2:%.*]] +; ONCE-NEXT: ] +; ONCE: case.0: +; ONCE-NEXT: unreachable +; ONCE: default.unreachable: +; ONCE-NEXT: unreachable +; ONCE: case.2: +; ONCE-NEXT: br label [[NEXT:%.*]] +; ONCE: next: +; ONCE-NEXT: [[TGT_2:%.*]] = call i16 @pr49573_fn_2() +; ONCE-NEXT: switch i16 2, label [[DEFAULT_UNREACHABLE]] [ +; ONCE-NEXT: i16 0, label [[CASE_0]] +; ONCE-NEXT: i16 2, label [[CASE_2]] +; ONCE-NEXT: ] +; +; TWICE-LABEL: define {{[^@]+}}@pr49573_main() { +; TWICE-NEXT: entry: +; TWICE-NEXT: [[TGT:%.*]] = call i16 @pr49573_fn() +; TWICE-NEXT: br label [[CASE_2:%.*]] +; TWICE: case.2: +; TWICE-NEXT: br label [[NEXT:%.*]] +; TWICE: next: +; TWICE-NEXT: [[TGT_2:%.*]] = call i16 @pr49573_fn_2() +; TWICE-NEXT: br label [[CASE_2]] +; +entry: + %tgt = call i16 @pr49573_fn() + switch i16 %tgt, label %default.bb [ + i16 0, label %case.0 + i16 1, label %case.1 + i16 2, label %case.2 + ] + +case.0: + unreachable + +default.bb: + ret void + +case.1: + ret void + +case.2: + br label %next + +next: + %tgt.2 = call i16 @pr49573_fn_2() + switch i16 %tgt.2, label %default.bb [ + i16 0, label %case.0 + i16 2, label %case.2 + ] +} + +; Make sure a new unreachable BB is created. +define void @pr49573_main_2() { +; ONCE-LABEL: define {{[^@]+}}@pr49573_main_2() { +; ONCE-NEXT: entry: +; ONCE-NEXT: [[TGT:%.*]] = call i16 @pr49573_fn() +; ONCE-NEXT: switch i16 2, label [[DEFAULT_UNREACHABLE:%.*]] [ +; ONCE-NEXT: i16 0, label [[CASE_0:%.*]] +; ONCE-NEXT: i16 2, label [[CASE_2:%.*]] +; ONCE-NEXT: ] +; ONCE: case.0: +; ONCE-NEXT: unreachable +; ONCE: default.unreachable: +; ONCE-NEXT: unreachable +; ONCE: case.2: +; ONCE-NEXT: ret void +; +; TWICE-LABEL: define {{[^@]+}}@pr49573_main_2() { +; TWICE-NEXT: entry: +; TWICE-NEXT: [[TGT:%.*]] = call i16 @pr49573_fn() +; TWICE-NEXT: br label [[CASE_2:%.*]] +; TWICE: case.2: +; TWICE-NEXT: ret void +; +entry: + %tgt = call i16 @pr49573_fn() + switch i16 %tgt, label %default.bb [ + i16 0, label %case.0 + i16 1, label %case.1 + i16 2, label %case.2 + ] + +case.0: + unreachable + +default.bb: + ret void + +case.1: + ret void + +case.2: + ret void +} + +define internal i16 @pr49573_fn() { +; CHECK-LABEL: define {{[^@]+}}@pr49573_fn() { +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[ELSE:%.*]] +; CHECK: else: +; CHECK-NEXT: ret i16 undef +; +entry: + br i1 undef, label %then, label %else + +then: + ret i16 0 + +else: + ret i16 2 +} + +define internal i16 @pr49573_fn_2() { +; CHECK-LABEL: define {{[^@]+}}@pr49573_fn_2() { +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[ELSE:%.*]] +; CHECK: else: +; CHECK-NEXT: ret i16 undef +; +entry: + br i1 undef, label %then, label %else + +then: + ret i16 0 + +else: + ret i16 2 +}