[SCCP] Don't mark edges feasible when resolving undefs

As branch on undef is immediate undefined behavior, there is no need
to mark one of the edges as feasible. We can leave all the edges
non-feasible. In IPSCCP, we can replace the branch with an unreachable
terminator.

Differential Revision: https://reviews.llvm.org/D126962
This commit is contained in:
Nikita Popov 2022-06-03 11:42:07 +02:00
parent 74f0660160
commit 1f88d80408
12 changed files with 71 additions and 215 deletions

View File

@ -371,7 +371,19 @@ static bool removeNonFeasibleEdges(const SCCPSolver &Solver, BasicBlock *BB,
isa<IndirectBrInst>(TI)) && isa<IndirectBrInst>(TI)) &&
"Terminator must be a br, switch or indirectbr"); "Terminator must be a br, switch or indirectbr");
if (FeasibleSuccessors.size() == 1) { if (FeasibleSuccessors.size() == 0) {
// Branch on undef/poison, replace with unreachable.
SmallPtrSet<BasicBlock *, 8> SeenSuccs;
SmallVector<DominatorTree::UpdateType, 8> Updates;
for (BasicBlock *Succ : successors(BB)) {
Succ->removePredecessor(BB);
if (SeenSuccs.insert(Succ).second)
Updates.push_back({DominatorTree::Delete, BB, Succ});
}
TI->eraseFromParent();
new UnreachableInst(BB->getContext(), BB);
DTU.applyUpdatesPermissive(Updates);
} else if (FeasibleSuccessors.size() == 1) {
// Replace with an unconditional branch to the only feasible successor. // Replace with an unconditional branch to the only feasible successor.
BasicBlock *OnlyFeasibleSuccessor = *FeasibleSuccessors.begin(); BasicBlock *OnlyFeasibleSuccessor = *FeasibleSuccessors.begin();
SmallVector<DominatorTree::UpdateType, 8> Updates; SmallVector<DominatorTree::UpdateType, 8> Updates;

View File

@ -1444,22 +1444,19 @@ void SCCPInstVisitor::solve() {
} }
} }
/// resolvedUndefsIn - While solving the dataflow for a function, we assume /// While solving the dataflow for a function, we don't compute a result for
/// that branches on undef values cannot reach any of their successors. /// operations with an undef operand, to allow undef to be lowered to a
/// However, this is not a safe assumption. After we solve dataflow, this /// constant later. For example, constant folding of "zext i8 undef to i16"
/// method should be use to handle this. If this returns true, the solver /// would result in "i16 0", and if undef is later lowered to "i8 1", then the
/// should be rerun. /// zext result would become "i16 1" and would result into an overdefined
/// lattice value once merged with the previous result. Not computing the
/// result of the zext (treating undef the same as unknown) allows us to handle
/// a later undef->constant lowering more optimally.
/// ///
/// This method handles this by finding an unresolved branch and marking it one /// However, if the operand remains undef when the solver returns, we do need
/// of the edges from the block as being feasible, even though the condition /// to assign some result to the instruction (otherwise we would treat it as
/// doesn't say it would otherwise be. This allows SCCP to find the rest of the /// unreachable). For simplicity, we mark any instructions that are still
/// CFG and only slightly pessimizes the analysis results (by marking one, /// unknown/undef as overdefined.
/// potentially infeasible, edge feasible). This cannot usefully modify the
/// constraints on the condition of the branch, as that would impact other users
/// of the value.
///
/// This scan also checks for values that use undefs. It conservatively marks
/// them as overdefined.
bool SCCPInstVisitor::resolvedUndefsIn(Function &F) { bool SCCPInstVisitor::resolvedUndefsIn(Function &F) {
bool MadeChange = false; bool MadeChange = false;
for (BasicBlock &BB : F) { for (BasicBlock &BB : F) {
@ -1520,91 +1517,6 @@ bool SCCPInstVisitor::resolvedUndefsIn(Function &F) {
markOverdefined(&I); markOverdefined(&I);
MadeChange = true; MadeChange = true;
} }
// Check to see if we have a branch or switch on an undefined value. If so
// we force the branch to go one way or the other to make the successor
// values live. It doesn't really matter which way we force it.
Instruction *TI = BB.getTerminator();
if (auto *BI = dyn_cast<BranchInst>(TI)) {
if (!BI->isConditional())
continue;
if (!getValueState(BI->getCondition()).isUnknownOrUndef())
continue;
// If the input to SCCP is actually branch on undef, fix the undef to
// false.
if (isa<UndefValue>(BI->getCondition())) {
BI->setCondition(ConstantInt::getFalse(BI->getContext()));
markEdgeExecutable(&BB, TI->getSuccessor(1));
MadeChange = true;
continue;
}
// Otherwise, it is a branch on a symbolic value which is currently
// considered to be undef. Make sure some edge is executable, so a
// branch on "undef" always flows somewhere.
// FIXME: Distinguish between dead code and an LLVM "undef" value.
BasicBlock *DefaultSuccessor = TI->getSuccessor(1);
if (markEdgeExecutable(&BB, DefaultSuccessor))
MadeChange = true;
continue;
}
if (auto *IBR = dyn_cast<IndirectBrInst>(TI)) {
// Indirect branch with no successor ?. Its ok to assume it branches
// to no target.
if (IBR->getNumSuccessors() < 1)
continue;
if (!getValueState(IBR->getAddress()).isUnknownOrUndef())
continue;
// If the input to SCCP is actually branch on undef, fix the undef to
// the first successor of the indirect branch.
if (isa<UndefValue>(IBR->getAddress())) {
IBR->setAddress(BlockAddress::get(IBR->getSuccessor(0)));
markEdgeExecutable(&BB, IBR->getSuccessor(0));
MadeChange = true;
continue;
}
// Otherwise, it is a branch on a symbolic value which is currently
// considered to be undef. Make sure some edge is executable, so a
// branch on "undef" always flows somewhere.
// FIXME: IndirectBr on "undef" doesn't actually need to go anywhere:
// we can assume the branch has undefined behavior instead.
BasicBlock *DefaultSuccessor = IBR->getSuccessor(0);
if (markEdgeExecutable(&BB, DefaultSuccessor))
MadeChange = true;
continue;
}
if (auto *SI = dyn_cast<SwitchInst>(TI)) {
if (!SI->getNumCases() ||
!getValueState(SI->getCondition()).isUnknownOrUndef())
continue;
// If the input to SCCP is actually switch on undef, fix the undef to
// the first constant.
if (isa<UndefValue>(SI->getCondition())) {
SI->setCondition(SI->case_begin()->getCaseValue());
markEdgeExecutable(&BB, SI->case_begin()->getCaseSuccessor());
MadeChange = true;
continue;
}
// Otherwise, it is a branch on a symbolic value which is currently
// considered to be undef. Make sure some edge is executable, so a
// branch on "undef" always flows somewhere.
// FIXME: Distinguish between dead code and an LLVM "undef" value.
BasicBlock *DefaultSuccessor = SI->case_begin()->getCaseSuccessor();
if (markEdgeExecutable(&BB, DefaultSuccessor))
MadeChange = true;
continue;
}
} }
return MadeChange; return MadeChange;

View File

@ -34,7 +34,7 @@ for.body: ; preds = %for.cond
for.cond2: ; preds = %for.body2, %for.cond for.cond2: ; preds = %for.body2, %for.cond
%phi2 = phi %mystruct* [ undef, %for.body2 ], [ null, %for.cond ] %phi2 = phi %mystruct* [ undef, %for.body2 ], [ null, %for.cond ]
br i1 undef, label %for.end, label %for.body2 br i1 false, label %for.end, label %for.body2
for.body2: ; preds = %for.cond2 for.body2: ; preds = %for.cond2
%arrayidx = getelementptr inbounds %mystruct, %mystruct* %phi2, i64 0, i32 1, i64 3 %arrayidx = getelementptr inbounds %mystruct, %mystruct* %phi2, i64 0, i32 1, i64 3

View File

@ -10,7 +10,7 @@ declare hidden { i8, ptr } @getType(ptr) align 2
define internal void @foo(ptr %TLI, ptr %DL, ptr %Ty, ptr %ValueVTs, ptr %Offsets, i64 %StartingOffset) { define internal void @foo(ptr %TLI, ptr %DL, ptr %Ty, ptr %ValueVTs, ptr %Offsets, i64 %StartingOffset) {
entry: entry:
%VT = alloca i64, align 8 %VT = alloca i64, align 8
br i1 undef, label %if.then, label %if.end4 br i1 false, label %if.then, label %if.end4
if.then: ; preds = %entry if.then: ; preds = %entry
ret void ret void

View File

@ -1,15 +1,14 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=sccp -S | FileCheck %s ; RUN: opt < %s -passes=sccp -S | FileCheck %s
; This function definitely returns 1, even if we don't know the direction ; Branch on undef is UB, so the T block is never executed, and we can return
; of the branch. ; undef (IPSCCP would replace the block with unreachable).
define i32 @foo() { define i32 @foo() {
; CHECK-LABEL: @foo( ; CHECK-LABEL: @foo(
; CHECK-NEXT: br i1 false, label [[T:%.*]], label [[T]] ; CHECK-NEXT: br i1 undef, label [[T:%.*]], label [[T]]
; CHECK: T: ; CHECK: T:
; CHECK-NEXT: [[X:%.*]] = add i32 0, 1 ; CHECK-NEXT: ret i32 undef
; CHECK-NEXT: ret i32 [[X]]
; ;
br i1 undef, label %T, label %T br i1 undef, label %T, label %T
T: T:

View File

@ -7,21 +7,16 @@ define i32 @main() {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[BB:%.*]] ; CHECK-NEXT: br label [[BB:%.*]]
; CHECK: bb: ; CHECK: bb:
; CHECK-NEXT: [[INDVAR:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[K:%.*]], [[BB_BACKEDGE:%.*]] ] ; CHECK-NEXT: br i1 undef, label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]]
; CHECK-NEXT: [[K]] = add i32 [[INDVAR]], 1
; CHECK-NEXT: br i1 false, label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]]
; CHECK: cond_true: ; CHECK: cond_true:
; CHECK-NEXT: br i1 undef, label [[BB_BACKEDGE]], label [[BB12:%.*]] ; CHECK-NEXT: br i1 undef, label [[BB_BACKEDGE:%.*]], label [[BB12:%.*]]
; CHECK: bb.backedge: ; CHECK: bb.backedge:
; CHECK-NEXT: br label [[BB]] ; CHECK-NEXT: br label [[BB]]
; CHECK: cond_false: ; CHECK: cond_false:
; CHECK-NEXT: [[TMP9:%.*]] = icmp slt i32 [[K]], 10 ; CHECK-NEXT: br i1 undef, label [[BB_BACKEDGE]], label [[BB12]]
; CHECK-NEXT: br i1 [[TMP9]], label [[BB_BACKEDGE]], label [[BB12]]
; CHECK: bb12: ; CHECK: bb12:
; CHECK-NEXT: [[TMP14:%.*]] = icmp eq i32 [[K]], 10 ; CHECK-NEXT: br i1 undef, label [[COND_NEXT18:%.*]], label [[COND_TRUE17:%.*]]
; CHECK-NEXT: br i1 [[TMP14]], label [[COND_NEXT18:%.*]], label [[COND_TRUE17:%.*]]
; CHECK: cond_true17: ; CHECK: cond_true17:
; CHECK-NEXT: tail call void @abort()
; CHECK-NEXT: unreachable ; CHECK-NEXT: unreachable
; CHECK: cond_next18: ; CHECK: cond_next18:
; CHECK-NEXT: ret i32 0 ; CHECK-NEXT: ret i32 0

View File

@ -5,14 +5,15 @@ target triple = "x86_64-unknown-linux-gnu"
define void @fn2(i32* %P) { define void @fn2(i32* %P) {
; CHECK-LABEL: define {{[^@]+}}@fn2 ; CHECK-LABEL: define {{[^@]+}}@fn2
; CHECK-SAME: (i32* [[P:%.*]]) ; CHECK-SAME: (i32* [[P:%.*]]) {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[IF_END:%.*]] ; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: for.cond1: ; CHECK: for.cond1:
; CHECK-NEXT: br i1 false, label [[IF_END]], label [[IF_END]] ; CHECK-NEXT: unreachable
; CHECK: if.end: ; CHECK: if.end:
; CHECK-NEXT: [[CALL:%.*]] = call i32 @fn1(i32 undef) ; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* undef, align 4
; CHECK-NEXT: store i32 [[CALL]], i32* [[P]] ; CHECK-NEXT: [[CALL:%.*]] = call i32 @fn1(i32 [[TMP0]])
; CHECK-NEXT: store i32 [[CALL]], i32* [[P]], align 4
; CHECK-NEXT: br label [[FOR_COND1:%.*]] ; CHECK-NEXT: br label [[FOR_COND1:%.*]]
; ;
entry: entry:
@ -31,10 +32,10 @@ if.end: ; preds = %lbl, %for.cond1
define internal i32 @fn1(i32 %p1) { define internal i32 @fn1(i32 %p1) {
; CHECK-LABEL: define {{[^@]+}}@fn1 ; CHECK-LABEL: define {{[^@]+}}@fn1
; CHECK-SAME: (i32 [[P1:%.*]]) ; CHECK-SAME: (i32 [[P1:%.*]]) {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 undef, 0 ; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0
; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 undef, i32 undef ; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]]
; CHECK-NEXT: ret i32 [[COND]] ; CHECK-NEXT: ret i32 [[COND]]
; ;
entry: entry:
@ -45,15 +46,15 @@ entry:
define void @fn_no_null_opt(i32* %P) #0 { define void @fn_no_null_opt(i32* %P) #0 {
; CHECK-LABEL: define {{[^@]+}}@fn_no_null_opt ; CHECK-LABEL: define {{[^@]+}}@fn_no_null_opt
; CHECK-SAME: (i32* [[P:%.*]]) ; CHECK-SAME: (i32* [[P:%.*]]) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[IF_END:%.*]] ; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: for.cond1: ; CHECK: for.cond1:
; CHECK-NEXT: br i1 false, label [[IF_END]], label [[IF_END]] ; CHECK-NEXT: unreachable
; CHECK: if.end: ; CHECK: if.end:
; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* null, align 4 ; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* undef, align 4
; CHECK-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]]) ; CHECK-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]])
; CHECK-NEXT: store i32 [[CALL]], i32* [[P]] ; CHECK-NEXT: store i32 [[CALL]], i32* [[P]], align 4
; CHECK-NEXT: br label [[FOR_COND1:%.*]] ; CHECK-NEXT: br label [[FOR_COND1:%.*]]
; ;
entry: entry:
@ -72,7 +73,7 @@ if.end: ; preds = %lbl, %for.cond1
define internal i32 @fn0(i32 %p1) { define internal i32 @fn0(i32 %p1) {
; CHECK-LABEL: define {{[^@]+}}@fn0 ; CHECK-LABEL: define {{[^@]+}}@fn0
; CHECK-SAME: (i32 [[P1:%.*]]) ; CHECK-SAME: (i32 [[P1:%.*]]) {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0 ; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0
; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]] ; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]]

View File

@ -74,15 +74,12 @@ BB1:
ret void ret void
} }
; Make sure we eliminate BB1 as we pick the first successor on undef. ; Branch on undef is UB, so we can convert the indirectbr to unreachable.
define void @indbrtest4(i8** %Q) { define void @indbrtest4(i8** %Q) {
; CHECK-LABEL: @indbrtest4( ; CHECK-LABEL: @indbrtest4(
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[BB0:%.*]] ; CHECK-NEXT: unreachable
; CHECK: BB0:
; CHECK-NEXT: call void @BB0_f()
; CHECK-NEXT: ret void
; ;
entry: entry:
indirectbr i8* undef, [label %BB0, label %BB1] indirectbr i8* undef, [label %BB0, label %BB1]

View File

@ -13,9 +13,7 @@ define void @main() {
define internal i1 @patatino(i1 %a) { define internal i1 @patatino(i1 %a) {
; CHECK-LABEL: define {{[^@]+}}@patatino ; CHECK-LABEL: define {{[^@]+}}@patatino
; CHECK-SAME: (i1 [[A:%.*]]) { ; CHECK-SAME: (i1 [[A:%.*]]) {
; CHECK-NEXT: br label [[ONFALSE:%.*]] ; CHECK-NEXT: unreachable
; CHECK: onfalse:
; CHECK-NEXT: ret i1 undef
; ;
br i1 %a, label %ontrue, label %onfalse br i1 %a, label %ontrue, label %onfalse
ontrue: ontrue:

View File

@ -1,22 +1,17 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
; RUN: opt < %s -S -passes=ipsccp | FileCheck %s ; RUN: opt < %s -S -passes=ipsccp | FileCheck %s
; After the first round of Solver.Solve(), the return value of @testf still ; testf() performs an unconditional branch on undef, as such the testf() return
; undefined as we hit a branch on undef. Therefore the conditional branch on ; value used in test1() will remain "unknown" and the following branch on it
; @testf's return value in @bar is unknown. In ResolvedUndefsIn, we force the ; replaced by unreachable. This is fine, as the call to testf() will already
; false branch to be feasible. We later discover that @testf actually ; trigger undefined behavior.
; returns true, so we end up with an unfolded "br i1 true".
define void @test1() { define void @test1() {
; CHECK-LABEL: define {{[^@]+}}@test1() { ; CHECK-LABEL: define {{[^@]+}}@test1() {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[IF_THEN:%.*]] ; CHECK-NEXT: br label [[IF_THEN:%.*]]
; CHECK: if.then: ; CHECK: if.then:
; CHECK-NEXT: [[FOO:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[NEXT:%.*]], [[IF_THEN]] ]
; CHECK-NEXT: [[NEXT]] = add i32 [[FOO]], 1
; CHECK-NEXT: [[CALL:%.*]] = call i1 @testf() ; CHECK-NEXT: [[CALL:%.*]] = call i1 @testf()
; CHECK-NEXT: br i1 true, label [[IF_END:%.*]], label [[IF_THEN]] ; CHECK-NEXT: unreachable
; CHECK: if.end:
; CHECK-NEXT: ret void
; ;
entry: entry:
br label %if.then br label %if.then
@ -33,9 +28,7 @@ if.end: ; preds = %if.then, %entry
define internal i1 @testf() { define internal i1 @testf() {
; CHECK-LABEL: define {{[^@]+}}@testf() { ; CHECK-LABEL: define {{[^@]+}}@testf() {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[IF_END3:%.*]] ; CHECK-NEXT: unreachable
; CHECK: if.end3:
; CHECK-NEXT: ret i1 undef
; ;
entry: entry:
br i1 undef, label %if.then1, label %if.end3 br i1 undef, label %if.then1, label %if.end3
@ -54,7 +47,7 @@ define i1 @test2() {
; CHECK-NEXT: br label [[IF_END:%.*]] ; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.end: ; CHECK: if.end:
; CHECK-NEXT: [[CALL2:%.*]] = call i1 @testf() ; CHECK-NEXT: [[CALL2:%.*]] = call i1 @testf()
; CHECK-NEXT: ret i1 true ; CHECK-NEXT: ret i1 undef
; ;
entry: entry:
br label %if.end br label %if.end

View File

@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature ; 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 < %s -S | FileCheck %s
; RUN: opt -passes='ipsccp,ipsccp' < %s -S | FileCheck --check-prefixes=CHECK,TWICE %s ; RUN: opt -passes='ipsccp,ipsccp' < %s -S | FileCheck %s
define void @barney() { define void @barney() {
; CHECK-LABEL: define {{[^@]+}}@barney() { ; CHECK-LABEL: define {{[^@]+}}@barney() {
@ -62,10 +62,6 @@ bb38: ; preds = %bb16
define void @hoge() { define void @hoge() {
; CHECK-LABEL: define {{[^@]+}}@hoge() { ; CHECK-LABEL: define {{[^@]+}}@hoge() {
; CHECK-NEXT: bb: ; CHECK-NEXT: bb:
; CHECK-NEXT: br label [[BB2:%.*]]
; CHECK: bb2:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb3:
; CHECK-NEXT: unreachable ; CHECK-NEXT: unreachable
; ;
bb: bb:
@ -94,35 +90,10 @@ bb4: ; preds = %bb2, %bb2, %bb2
; Test case from PR49573. %default.bb is unfeasible. Make sure it gets replaced ; Test case from PR49573. %default.bb is unfeasible. Make sure it gets replaced
; by an unreachable block. ; by an unreachable block.
define void @pr49573_main() { define void @pr49573_main() {
; ONCE-LABEL: define {{[^@]+}}@pr49573_main() { ; CHECK-LABEL: define {{[^@]+}}@pr49573_main() {
; ONCE-NEXT: entry: ; CHECK-NEXT: entry:
; ONCE-NEXT: [[TGT:%.*]] = call i16 @pr49573_fn() ; CHECK-NEXT: [[TGT:%.*]] = call i16 @pr49573_fn()
; ONCE-NEXT: switch i16 2, label [[DEFAULT_UNREACHABLE:%.*]] [ ; CHECK-NEXT: 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: entry:
%tgt = call i16 @pr49573_fn() %tgt = call i16 @pr49573_fn()
@ -154,26 +125,10 @@ next:
; Make sure a new unreachable BB is created. ; Make sure a new unreachable BB is created.
define void @pr49573_main_2() { define void @pr49573_main_2() {
; ONCE-LABEL: define {{[^@]+}}@pr49573_main_2() { ; CHECK-LABEL: define {{[^@]+}}@pr49573_main_2() {
; ONCE-NEXT: entry: ; CHECK-NEXT: entry:
; ONCE-NEXT: [[TGT:%.*]] = call i16 @pr49573_fn() ; CHECK-NEXT: [[TGT:%.*]] = call i16 @pr49573_fn()
; ONCE-NEXT: switch i16 2, label [[DEFAULT_UNREACHABLE:%.*]] [ ; CHECK-NEXT: 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: entry:
%tgt = call i16 @pr49573_fn() %tgt = call i16 @pr49573_fn()
@ -199,9 +154,7 @@ case.2:
define internal i16 @pr49573_fn() { define internal i16 @pr49573_fn() {
; CHECK-LABEL: define {{[^@]+}}@pr49573_fn() { ; CHECK-LABEL: define {{[^@]+}}@pr49573_fn() {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[ELSE:%.*]] ; CHECK-NEXT: unreachable
; CHECK: else:
; CHECK-NEXT: ret i16 undef
; ;
entry: entry:
br i1 undef, label %then, label %else br i1 undef, label %then, label %else
@ -216,9 +169,7 @@ else:
define internal i16 @pr49573_fn_2() { define internal i16 @pr49573_fn_2() {
; CHECK-LABEL: define {{[^@]+}}@pr49573_fn_2() { ; CHECK-LABEL: define {{[^@]+}}@pr49573_fn_2() {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[ELSE:%.*]] ; CHECK-NEXT: unreachable
; CHECK: else:
; CHECK-NEXT: ret i16 undef
; ;
entry: entry:
br i1 undef, label %then, label %else br i1 undef, label %then, label %else

View File

@ -29,9 +29,7 @@ define internal i16 @f3(i16 %p1) {
; CHECK-LABEL: define {{[^@]+}}@f3 ; CHECK-LABEL: define {{[^@]+}}@f3
; CHECK-SAME: (i16 [[P1:%.*]]) { ; CHECK-SAME: (i16 [[P1:%.*]]) {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LAND_END:%.*]] ; CHECK-NEXT: unreachable
; CHECK: land.end:
; CHECK-NEXT: ret i16 undef
; ;
entry: entry:
switch i16 %p1, label %land.end [ switch i16 %p1, label %land.end [