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)) &&
|
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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]]
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 [
|
||||||
|
|
Loading…
Reference in New Issue