forked from OSchip/llvm-project
[SimplifyCFG] Thread branches on same condition in more cases (PR54980)
SimplifyCFG implements basic jump threading, if a branch is performed on a phi node with constant operands. However, InstCombine canonicalizes such phis to the condition value of a previous branch, if possible. SimplifyCFG does support this as well, but only in the very limited case where the same condition is used in a direct predecessor -- notably, this does not include the common diamond pattern (i.e. two consecutive if/elses on the same condition). This patch extends the code to look back a limited number of blocks to find a branch on the same value, rather than only looking at the direct predecessor. Fixes https://github.com/llvm/llvm-project/issues/54980. Differential Revision: https://reviews.llvm.org/D124159
This commit is contained in:
parent
db1cec371c
commit
4e545bdb35
|
@ -25,11 +25,12 @@ void f1(void) {
|
|||
// CHECK-NEXT: icmp
|
||||
// CHECK-NEXT: br i1
|
||||
@try {
|
||||
// CHECK: call void asm sideeffect "", "=*m"
|
||||
// CHECK: call void asm sideeffect "", "*m"
|
||||
// CHECK-NEXT: call void @foo()
|
||||
foo();
|
||||
// CHECK: call void @objc_exception_try_exit
|
||||
// CHECK: try.handler:
|
||||
// CHECK: call void asm sideeffect "", "=*m"
|
||||
|
||||
} @finally {
|
||||
break;
|
||||
|
@ -53,12 +54,6 @@ int f2(void) {
|
|||
// CHECK-NEXT: [[CAUGHT:%.*]] = icmp eq i32 [[SETJMP]], 0
|
||||
// CHECK-NEXT: br i1 [[CAUGHT]]
|
||||
@try {
|
||||
// Landing pad. Note that we elide the re-enter.
|
||||
// CHECK: call void asm sideeffect "", "=*m,=*m"(i32* nonnull elementtype(i32) [[X]]
|
||||
// CHECK-NEXT: call i8* @objc_exception_extract
|
||||
// CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[X]]
|
||||
// CHECK-NEXT: [[T2:%.*]] = add nsw i32 [[T1]], -1
|
||||
|
||||
// CHECK: store i32 6, i32* [[X]]
|
||||
x++;
|
||||
// CHECK-NEXT: call void asm sideeffect "", "*m,*m"(i32* nonnull elementtype(i32) [[X]]
|
||||
|
@ -67,6 +62,12 @@ int f2(void) {
|
|||
// CHECK-NEXT: [[T:%.*]] = load i32, i32* [[X]]
|
||||
foo();
|
||||
} @catch (id) {
|
||||
// Landing pad. Note that we elide the re-enter.
|
||||
// CHECK: call void asm sideeffect "", "=*m,=*m"(i32* nonnull elementtype(i32) [[X]]
|
||||
// CHECK-NEXT: call i8* @objc_exception_extract
|
||||
// CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[X]]
|
||||
// CHECK-NEXT: [[T2:%.*]] = add nsw i32 [[T1]], -1
|
||||
|
||||
x--;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,20 +63,19 @@ void test1(id obj, bool *failed) {
|
|||
// Body.
|
||||
// CHECK: invoke void @_Z3foov()
|
||||
|
||||
// Catch handler. Reload of 'failed' address is unnecessary.
|
||||
// CHECK: [[T0:%.*]] = load i8*, i8**
|
||||
// CHECK-NEXT: store i8 1, i8* [[T0]],
|
||||
// CHECK-NEXT: br label
|
||||
|
||||
// Leave the @try.
|
||||
// CHECK: call void @objc_exception_try_exit([[BUF_T]]* nonnull [[BUF]])
|
||||
// CHECK-NEXT: br label
|
||||
// CHECK: ret void
|
||||
|
||||
|
||||
// Real EH cleanup.
|
||||
// CHECK: [[T0:%.*]] = landingpad
|
||||
// CHECK-NEXT: cleanup
|
||||
// CHECK-NEXT: call void @objc_exception_try_exit([[BUF_T]]* nonnull [[BUF]])
|
||||
// CHECK-NEXT: resume
|
||||
|
||||
// Catch handler. Reload of 'failed' address is unnecessary.
|
||||
// CHECK: [[T0:%.*]] = load i8*, i8**
|
||||
// CHECK-NEXT: store i8 1, i8* [[T0]],
|
||||
// CHECK-NEXT: br label
|
||||
|
||||
|
|
|
@ -2975,8 +2975,10 @@ static bool BlockIsSimpleEnoughToThreadThrough(BasicBlock *BB) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static ConstantInt *getKnownValueOnEdge(Value *V, BasicBlock *From,
|
||||
BasicBlock *To) {
|
||||
static ConstantInt *
|
||||
getKnownValueOnEdge(Value *V, BasicBlock *From, BasicBlock *To,
|
||||
SmallDenseMap<std::pair<BasicBlock *, BasicBlock *>,
|
||||
ConstantInt *> &Visited) {
|
||||
// Don't look past the block defining the value, we might get the value from
|
||||
// a previous loop iteration.
|
||||
auto *I = dyn_cast<Instruction>(V);
|
||||
|
@ -2990,7 +2992,23 @@ static ConstantInt *getKnownValueOnEdge(Value *V, BasicBlock *From,
|
|||
return BI->getSuccessor(0) == To ? ConstantInt::getTrue(BI->getContext())
|
||||
: ConstantInt::getFalse(BI->getContext());
|
||||
|
||||
return nullptr;
|
||||
// Limit the amount of blocks we inspect.
|
||||
if (Visited.size() >= 8)
|
||||
return nullptr;
|
||||
|
||||
auto Pair = Visited.try_emplace({From, To}, nullptr);
|
||||
if (!Pair.second)
|
||||
return Pair.first->second;
|
||||
|
||||
// Check whether the known value is the same for all predecessors.
|
||||
ConstantInt *Common = nullptr;
|
||||
for (BasicBlock *Pred : predecessors(From)) {
|
||||
ConstantInt *C = getKnownValueOnEdge(V, Pred, From, Visited);
|
||||
if (!C || (Common && Common != C))
|
||||
return nullptr;
|
||||
Common = C;
|
||||
}
|
||||
return Visited[{From, To}] = Common;
|
||||
}
|
||||
|
||||
/// If we have a conditional branch on something for which we know the constant
|
||||
|
@ -3015,8 +3033,9 @@ FoldCondBranchOnValueKnownInPredecessorImpl(BranchInst *BI, DomTreeUpdater *DTU,
|
|||
if (auto *CB = dyn_cast<ConstantInt>(U))
|
||||
KnownValues.insert({PN->getIncomingBlock(U), CB});
|
||||
} else {
|
||||
SmallDenseMap<std::pair<BasicBlock *, BasicBlock *>, ConstantInt *> Visited;
|
||||
for (BasicBlock *Pred : predecessors(BB)) {
|
||||
if (ConstantInt *CB = getKnownValueOnEdge(Cond, Pred, BB))
|
||||
if (ConstantInt *CB = getKnownValueOnEdge(Cond, Pred, BB, Visited))
|
||||
KnownValues.insert({Pred, CB});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ if.then3: ; preds = %_ZNK7WebCore4Node10
|
|||
if.end5: ; preds = %_ZNK7WebCore4Node10hasTagNameERKNS_13QualifiedNameE.exit, %lor.rhs.i.i.i
|
||||
; CHECK: %if.end5
|
||||
; CHECK: tbz
|
||||
br i1 %tobool.i.i.i, label %if.end12, label %land.rhs.i19, !prof !1
|
||||
br i1 %IsEditable, label %if.end12, label %land.rhs.i19, !prof !1
|
||||
|
||||
land.rhs.i19: ; preds = %if.end5
|
||||
%cmp.i.i.i18 = icmp eq i8* %str6, %str7
|
||||
|
|
|
@ -604,7 +604,7 @@ succ:
|
|||
declare void @g()
|
||||
|
||||
; CHECK-LABEL: test_pr30292
|
||||
; CHECK: phi i32 [ 0, %entry ], [ %add1, %succ ], [ %add2, %two ]
|
||||
; CHECK: phi i32 [ 0, %entry ], [ %add1, %succ ]
|
||||
|
||||
define zeroext i1 @test_pr30244(i1 zeroext %flag, i1 zeroext %flag2, i32 %blksA, i32 %blksB, i32 %nblks) {
|
||||
|
||||
|
|
|
@ -885,9 +885,9 @@ define void @test_pr30292(i1 %cond, i1 %cond2, i32 %a, i32 %b) {
|
|||
; CHECK: two:
|
||||
; CHECK-NEXT: call void @g()
|
||||
; CHECK-NEXT: [[ADD2:%.*]] = add i32 [[A]], 1
|
||||
; CHECK-NEXT: br label [[SUCC]]
|
||||
; CHECK-NEXT: br label [[TWO:%.*]]
|
||||
; CHECK: succ:
|
||||
; CHECK-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD1]], [[SUCC]] ], [ [[ADD2]], [[TWO:%.*]] ]
|
||||
; CHECK-NEXT: [[P:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD1]], [[SUCC]] ]
|
||||
; CHECK-NEXT: br i1 [[COND:%.*]], label [[TWO]], label [[SUCC]]
|
||||
;
|
||||
entry:
|
||||
|
|
|
@ -144,16 +144,10 @@ define void @test_same_cond_simple(i1 %c) {
|
|||
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[JOIN:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[JOIN]]
|
||||
; CHECK: join:
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF2:%.*]], label [[ELSE2:%.*]]
|
||||
; CHECK: if2:
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[JOIN2:%.*]]
|
||||
; CHECK: else2:
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[JOIN2]]
|
||||
; CHECK: join2:
|
||||
|
@ -189,17 +183,12 @@ define void @test_same_cond_extra_use(i1 %c) {
|
|||
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[JOIN:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[JOIN]]
|
||||
; CHECK: join:
|
||||
; CHECK-NEXT: call void @use.i1(i1 [[C]])
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF2:%.*]], label [[ELSE2:%.*]]
|
||||
; CHECK: if2:
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[JOIN2:%.*]]
|
||||
; CHECK: else2:
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: call void @use.i1(i1 [[C]])
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[JOIN2]]
|
||||
; CHECK: join2:
|
||||
|
@ -236,17 +225,11 @@ define void @test_same_cond_extra_use_different_block(i1 %c) {
|
|||
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
|
||||
; CHECK: if:
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[JOIN:%.*]]
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[JOIN]]
|
||||
; CHECK: join:
|
||||
; CHECK-NEXT: br i1 [[C]], label [[IF2:%.*]], label [[ELSE2:%.*]]
|
||||
; CHECK: if2:
|
||||
; CHECK-NEXT: call void @use.i1(i1 [[C]])
|
||||
; CHECK-NEXT: call void @foo()
|
||||
; CHECK-NEXT: br label [[JOIN2:%.*]]
|
||||
; CHECK: else2:
|
||||
; CHECK: else:
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: call void @use.i1(i1 [[C]])
|
||||
; CHECK-NEXT: call void @bar()
|
||||
; CHECK-NEXT: br label [[JOIN2]]
|
||||
|
|
|
@ -269,17 +269,18 @@ return:
|
|||
define i32 @neg_loop(i1 %cond_0, i1 %cond_1) {
|
||||
; CHECK-LABEL: @neg_loop(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[GUARDED:%.*]]
|
||||
; CHECK-NEXT: call void @unknown()
|
||||
; CHECK-NEXT: br i1 [[COND_1:%.*]], label [[LOOP:%.*]], label [[DEOPT2:%.*]], !prof [[PROF0]]
|
||||
; CHECK: loop.critedge:
|
||||
; CHECK-NEXT: call void @unknown()
|
||||
; CHECK-NEXT: br label [[LOOP]]
|
||||
; CHECK: loop:
|
||||
; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
|
||||
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]]
|
||||
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED]], label [[DEOPT:%.*]], !prof [[PROF0]]
|
||||
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[LOOP_CRITEDGE:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
|
||||
; CHECK: deopt:
|
||||
; CHECK-NEXT: [[DEOPTRET:%.*]] = call i32 (...) @llvm.experimental.deoptimize.i32() [ "deopt"() ]
|
||||
; CHECK-NEXT: ret i32 [[DEOPTRET]]
|
||||
; CHECK: guarded:
|
||||
; CHECK-NEXT: call void @unknown()
|
||||
; CHECK-NEXT: br i1 [[COND_1:%.*]], label [[LOOP:%.*]], label [[DEOPT2:%.*]], !prof [[PROF0]]
|
||||
; CHECK: deopt2:
|
||||
; CHECK-NEXT: [[DEOPTRET2:%.*]] = call i32 (...) @llvm.experimental.deoptimize.i32() [ "deopt"() ]
|
||||
; CHECK-NEXT: ret i32 [[DEOPTRET2]]
|
||||
|
|
Loading…
Reference in New Issue