forked from OSchip/llvm-project
[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:
parent
74f0660160
commit
1f88d80408
|
@ -371,7 +371,19 @@ static bool removeNonFeasibleEdges(const SCCPSolver &Solver, BasicBlock *BB,
|
|||
isa<IndirectBrInst>(TI)) &&
|
||||
"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.
|
||||
BasicBlock *OnlyFeasibleSuccessor = *FeasibleSuccessors.begin();
|
||||
SmallVector<DominatorTree::UpdateType, 8> Updates;
|
||||
|
|
|
@ -1444,22 +1444,19 @@ void SCCPInstVisitor::solve() {
|
|||
}
|
||||
}
|
||||
|
||||
/// resolvedUndefsIn - While solving the dataflow for a function, we assume
|
||||
/// that branches on undef values cannot reach any of their successors.
|
||||
/// However, this is not a safe assumption. After we solve dataflow, this
|
||||
/// method should be use to handle this. If this returns true, the solver
|
||||
/// should be rerun.
|
||||
/// While solving the dataflow for a function, we don't compute a result for
|
||||
/// operations with an undef operand, to allow undef to be lowered to a
|
||||
/// constant later. For example, constant folding of "zext i8 undef to i16"
|
||||
/// would result in "i16 0", and if undef is later lowered to "i8 1", then the
|
||||
/// 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
|
||||
/// of the edges from the block as being feasible, even though the condition
|
||||
/// doesn't say it would otherwise be. This allows SCCP to find the rest of the
|
||||
/// CFG and only slightly pessimizes the analysis results (by marking one,
|
||||
/// 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.
|
||||
/// However, if the operand remains undef when the solver returns, we do need
|
||||
/// to assign some result to the instruction (otherwise we would treat it as
|
||||
/// unreachable). For simplicity, we mark any instructions that are still
|
||||
/// unknown/undef as overdefined.
|
||||
bool SCCPInstVisitor::resolvedUndefsIn(Function &F) {
|
||||
bool MadeChange = false;
|
||||
for (BasicBlock &BB : F) {
|
||||
|
@ -1520,91 +1517,6 @@ bool SCCPInstVisitor::resolvedUndefsIn(Function &F) {
|
|||
markOverdefined(&I);
|
||||
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;
|
||||
|
|
|
@ -34,7 +34,7 @@ for.body: ; preds = %for.cond
|
|||
|
||||
for.cond2: ; preds = %for.body2, %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
|
||||
%arrayidx = getelementptr inbounds %mystruct, %mystruct* %phi2, i64 0, i32 1, i64 3
|
||||
|
|
|
@ -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) {
|
||||
entry:
|
||||
%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
|
||||
ret void
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt < %s -passes=sccp -S | FileCheck %s
|
||||
|
||||
; This function definitely returns 1, even if we don't know the direction
|
||||
; of the branch.
|
||||
; Branch on undef is UB, so the T block is never executed, and we can return
|
||||
; undef (IPSCCP would replace the block with unreachable).
|
||||
|
||||
define i32 @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-NEXT: [[X:%.*]] = add i32 0, 1
|
||||
; CHECK-NEXT: ret i32 [[X]]
|
||||
; CHECK-NEXT: ret i32 undef
|
||||
;
|
||||
br i1 undef, label %T, label %T
|
||||
T:
|
||||
|
|
|
@ -7,21 +7,16 @@ define i32 @main() {
|
|||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[BB:%.*]]
|
||||
; CHECK: bb:
|
||||
; CHECK-NEXT: [[INDVAR:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[K:%.*]], [[BB_BACKEDGE:%.*]] ]
|
||||
; CHECK-NEXT: [[K]] = add i32 [[INDVAR]], 1
|
||||
; CHECK-NEXT: br i1 false, label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]]
|
||||
; CHECK-NEXT: br i1 undef, label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]]
|
||||
; 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-NEXT: br label [[BB]]
|
||||
; CHECK: cond_false:
|
||||
; CHECK-NEXT: [[TMP9:%.*]] = icmp slt i32 [[K]], 10
|
||||
; CHECK-NEXT: br i1 [[TMP9]], label [[BB_BACKEDGE]], label [[BB12]]
|
||||
; CHECK-NEXT: br i1 undef, label [[BB_BACKEDGE]], label [[BB12]]
|
||||
; CHECK: bb12:
|
||||
; CHECK-NEXT: [[TMP14:%.*]] = icmp eq i32 [[K]], 10
|
||||
; CHECK-NEXT: br i1 [[TMP14]], label [[COND_NEXT18:%.*]], label [[COND_TRUE17:%.*]]
|
||||
; CHECK-NEXT: br i1 undef, label [[COND_NEXT18:%.*]], label [[COND_TRUE17:%.*]]
|
||||
; CHECK: cond_true17:
|
||||
; CHECK-NEXT: tail call void @abort()
|
||||
; CHECK-NEXT: unreachable
|
||||
; CHECK: cond_next18:
|
||||
; CHECK-NEXT: ret i32 0
|
||||
|
|
|
@ -5,14 +5,15 @@ target triple = "x86_64-unknown-linux-gnu"
|
|||
|
||||
define void @fn2(i32* %P) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@fn2
|
||||
; CHECK-SAME: (i32* [[P:%.*]])
|
||||
; CHECK-SAME: (i32* [[P:%.*]]) {
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[IF_END:%.*]]
|
||||
; CHECK: for.cond1:
|
||||
; CHECK-NEXT: br i1 false, label [[IF_END]], label [[IF_END]]
|
||||
; CHECK-NEXT: unreachable
|
||||
; CHECK: if.end:
|
||||
; CHECK-NEXT: [[CALL:%.*]] = call i32 @fn1(i32 undef)
|
||||
; CHECK-NEXT: store i32 [[CALL]], i32* [[P]]
|
||||
; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* undef, align 4
|
||||
; CHECK-NEXT: [[CALL:%.*]] = call i32 @fn1(i32 [[TMP0]])
|
||||
; CHECK-NEXT: store i32 [[CALL]], i32* [[P]], align 4
|
||||
; CHECK-NEXT: br label [[FOR_COND1:%.*]]
|
||||
;
|
||||
entry:
|
||||
|
@ -31,10 +32,10 @@ if.end: ; preds = %lbl, %for.cond1
|
|||
|
||||
define internal i32 @fn1(i32 %p1) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@fn1
|
||||
; CHECK-SAME: (i32 [[P1:%.*]])
|
||||
; CHECK-SAME: (i32 [[P1:%.*]]) {
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 undef, 0
|
||||
; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 undef, i32 undef
|
||||
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0
|
||||
; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]]
|
||||
; CHECK-NEXT: ret i32 [[COND]]
|
||||
;
|
||||
entry:
|
||||
|
@ -45,15 +46,15 @@ entry:
|
|||
|
||||
define void @fn_no_null_opt(i32* %P) #0 {
|
||||
; CHECK-LABEL: define {{[^@]+}}@fn_no_null_opt
|
||||
; CHECK-SAME: (i32* [[P:%.*]])
|
||||
; CHECK-SAME: (i32* [[P:%.*]]) #[[ATTR0:[0-9]+]] {
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[IF_END:%.*]]
|
||||
; CHECK: for.cond1:
|
||||
; CHECK-NEXT: br i1 false, label [[IF_END]], label [[IF_END]]
|
||||
; CHECK-NEXT: unreachable
|
||||
; 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: store i32 [[CALL]], i32* [[P]]
|
||||
; CHECK-NEXT: store i32 [[CALL]], i32* [[P]], align 4
|
||||
; CHECK-NEXT: br label [[FOR_COND1:%.*]]
|
||||
;
|
||||
entry:
|
||||
|
@ -72,7 +73,7 @@ if.end: ; preds = %lbl, %for.cond1
|
|||
|
||||
define internal i32 @fn0(i32 %p1) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@fn0
|
||||
; CHECK-SAME: (i32 [[P1:%.*]])
|
||||
; CHECK-SAME: (i32 [[P1:%.*]]) {
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[P1]], 0
|
||||
; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 [[P1]], i32 [[P1]]
|
||||
|
|
|
@ -74,15 +74,12 @@ BB1:
|
|||
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) {
|
||||
; CHECK-LABEL: @indbrtest4(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[BB0:%.*]]
|
||||
; CHECK: BB0:
|
||||
; CHECK-NEXT: call void @BB0_f()
|
||||
; CHECK-NEXT: ret void
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
entry:
|
||||
indirectbr i8* undef, [label %BB0, label %BB1]
|
||||
|
|
|
@ -13,9 +13,7 @@ define void @main() {
|
|||
define internal i1 @patatino(i1 %a) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@patatino
|
||||
; CHECK-SAME: (i1 [[A:%.*]]) {
|
||||
; CHECK-NEXT: br label [[ONFALSE:%.*]]
|
||||
; CHECK: onfalse:
|
||||
; CHECK-NEXT: ret i1 undef
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
br i1 %a, label %ontrue, label %onfalse
|
||||
ontrue:
|
||||
|
|
|
@ -1,22 +1,17 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
|
||||
; RUN: opt < %s -S -passes=ipsccp | FileCheck %s
|
||||
|
||||
; After the first round of Solver.Solve(), the return value of @testf still
|
||||
; undefined as we hit a branch on undef. Therefore the conditional branch on
|
||||
; @testf's return value in @bar is unknown. In ResolvedUndefsIn, we force the
|
||||
; false branch to be feasible. We later discover that @testf actually
|
||||
; returns true, so we end up with an unfolded "br i1 true".
|
||||
; testf() performs an unconditional branch on undef, as such the testf() return
|
||||
; value used in test1() will remain "unknown" and the following branch on it
|
||||
; replaced by unreachable. This is fine, as the call to testf() will already
|
||||
; trigger undefined behavior.
|
||||
define void @test1() {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test1() {
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[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: br i1 true, label [[IF_END:%.*]], label [[IF_THEN]]
|
||||
; CHECK: if.end:
|
||||
; CHECK-NEXT: ret void
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
entry:
|
||||
br label %if.then
|
||||
|
@ -33,9 +28,7 @@ if.end: ; preds = %if.then, %entry
|
|||
define internal i1 @testf() {
|
||||
; CHECK-LABEL: define {{[^@]+}}@testf() {
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[IF_END3:%.*]]
|
||||
; CHECK: if.end3:
|
||||
; CHECK-NEXT: ret i1 undef
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
entry:
|
||||
br i1 undef, label %if.then1, label %if.end3
|
||||
|
@ -54,7 +47,7 @@ define i1 @test2() {
|
|||
; CHECK-NEXT: br label [[IF_END:%.*]]
|
||||
; CHECK: if.end:
|
||||
; CHECK-NEXT: [[CALL2:%.*]] = call i1 @testf()
|
||||
; CHECK-NEXT: ret i1 true
|
||||
; CHECK-NEXT: ret i1 undef
|
||||
;
|
||||
entry:
|
||||
br label %if.end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
; 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
|
||||
; RUN: opt -passes=ipsccp < %s -S | FileCheck %s
|
||||
; RUN: opt -passes='ipsccp,ipsccp' < %s -S | FileCheck %s
|
||||
|
||||
define void @barney() {
|
||||
; CHECK-LABEL: define {{[^@]+}}@barney() {
|
||||
|
@ -62,10 +62,6 @@ bb38: ; preds = %bb16
|
|||
define void @hoge() {
|
||||
; CHECK-LABEL: define {{[^@]+}}@hoge() {
|
||||
; CHECK-NEXT: bb:
|
||||
; CHECK-NEXT: br label [[BB2:%.*]]
|
||||
; CHECK: bb2:
|
||||
; CHECK-NEXT: br label [[BB3:%.*]]
|
||||
; CHECK: bb3:
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
bb:
|
||||
|
@ -94,35 +90,10 @@ bb4: ; preds = %bb2, %bb2, %bb2
|
|||
; 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]]
|
||||
; CHECK-LABEL: define {{[^@]+}}@pr49573_main() {
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[TGT:%.*]] = call i16 @pr49573_fn()
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
entry:
|
||||
%tgt = call i16 @pr49573_fn()
|
||||
|
@ -154,26 +125,10 @@ next:
|
|||
|
||||
; 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
|
||||
; CHECK-LABEL: define {{[^@]+}}@pr49573_main_2() {
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[TGT:%.*]] = call i16 @pr49573_fn()
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
entry:
|
||||
%tgt = call i16 @pr49573_fn()
|
||||
|
@ -199,9 +154,7 @@ case.2:
|
|||
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
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
entry:
|
||||
br i1 undef, label %then, label %else
|
||||
|
@ -216,9 +169,7 @@ else:
|
|||
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
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
entry:
|
||||
br i1 undef, label %then, label %else
|
||||
|
|
|
@ -29,9 +29,7 @@ define internal i16 @f3(i16 %p1) {
|
|||
; CHECK-LABEL: define {{[^@]+}}@f3
|
||||
; CHECK-SAME: (i16 [[P1:%.*]]) {
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[LAND_END:%.*]]
|
||||
; CHECK: land.end:
|
||||
; CHECK-NEXT: ret i16 undef
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
entry:
|
||||
switch i16 %p1, label %land.end [
|
||||
|
|
Loading…
Reference in New Issue