[IPSCCP] Support unfeasible default dests for switch.

At the moment, unfeasible default destinations are not handled properly
in removeNonFeasibleEdges. So far, only unfeasible cases are removed,
but later code expects unreachable blocks to have no predecessors.

This is causing the crash reported in PR49573.

If the default destination is unfeasible it won't be executed. Create
a new unreachable block on demand and use that as default
destination.

Note that at the moment this only is relevant for cases where
resolvedUndefsIn marks the first case as executable. Regular switch
handling has a FIXME/TODO to support determining whether the default
case is feasible or not.

Fixes #48917.

Differential Revision: https://reviews.llvm.org/D113497
This commit is contained in:
Florian Hahn 2022-04-26 12:41:41 +01:00
parent 5b3ca24a35
commit 857c612d89
No known key found for this signature in database
GPG Key ID: EEF712BB5E80EBBA
2 changed files with 166 additions and 7 deletions

View File

@ -336,7 +336,8 @@ static void findReturnsToZap(Function &F,
}
static bool removeNonFeasibleEdges(const SCCPSolver &Solver, BasicBlock *BB,
DomTreeUpdater &DTU) {
DomTreeUpdater &DTU,
BasicBlock *&NewUnreachableBB) {
SmallPtrSet<BasicBlock *, 8> FeasibleSuccessors;
bool HasNonFeasibleEdges = false;
for (BasicBlock *Succ : successors(BB)) {
@ -379,6 +380,23 @@ static bool removeNonFeasibleEdges(const SCCPSolver &Solver, BasicBlock *BB,
} else if (FeasibleSuccessors.size() > 1) {
SwitchInstProfUpdateWrapper SI(*cast<SwitchInst>(TI));
SmallVector<DominatorTree::UpdateType, 8> 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 @@ bool llvm::runIPSCCP(
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())

View File

@ -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 @@ bb9: ; preds = %bb
}
define void @blam() {
; CHECK-LABEL: @blam(
; CHECK-LABEL: define {{[^@]+}}@blam() {
; CHECK-NEXT: bb:
; CHECK-NEXT: br label [[BB16:%.*]]
; CHECK: bb16:
@ -59,7 +60,7 @@ bb38: ; preds = %bb16
define void @hoge() {
; CHECK-LABEL: @hoge(
; CHECK-LABEL: define {{[^@]+}}@hoge() {
; CHECK-NEXT: bb:
; CHECK-NEXT: br label [[BB2:%.*]]
; CHECK: bb2:
@ -89,3 +90,142 @@ bb3: ; preds = %bb2
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
}