[llvm] 4997af9 - [SimplifyCFG] Simplify nested branches (#97067)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Jun 30 12:35:43 PDT 2024
Author: Yingwei Zheng
Date: 2024-07-01T03:35:39+08:00
New Revision: 4997af98a008e71a3df61707559710d1c2769839
URL: https://github.com/llvm/llvm-project/commit/4997af98a008e71a3df61707559710d1c2769839
DIFF: https://github.com/llvm/llvm-project/commit/4997af98a008e71a3df61707559710d1c2769839.diff
LOG: [SimplifyCFG] Simplify nested branches (#97067)
This patch folds the following pattern (I don't know what to call this):
```
bb0:
br i1 %cond1, label %bb1, label %bb2
bb1:
br i1 %cond2, label %bb3, label %bb4
bb2:
br i1 %cond2, label %bb4, label %bb3
bb3:
...
bb4:
...
```
into
```
bb0:
%cond = xor i1 %cond1, %cond2
br i1 %cond, label %bb4, label %bb3
bb3:
...
bb4:
...
```
Alive2: https://alive2.llvm.org/ce/z/5iOJEL
Closes https://github.com/llvm/llvm-project/issues/97022.
Closes https://github.com/llvm/llvm-project/issues/83417.
I found this pattern in some verilator-generated code, which is widely
used in RTL simulation. This fold will reduces branches and improves the
performance of CPU frontend. To my surprise, this pattern is also common
in C/C++ code base.
Affected libraries/applications:
cmake/cvc5/freetype/git/gromacs/jq/linux/openblas/openmpi/openssl/php/postgres/ruby/sqlite/wireshark/z3/...
Added:
llvm/test/Transforms/SimplifyCFG/branch-nested.ll
Modified:
llvm/lib/Transforms/Utils/SimplifyCFG.cpp
llvm/test/CodeGen/ARM/and-cmp0-sink.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 6847bb7502429..69daa6352f95e 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -7361,6 +7361,95 @@ static BasicBlock *allPredecessorsComeFromSameSource(BasicBlock *BB) {
return PredPred;
}
+/// Fold the following pattern:
+/// bb0:
+/// br i1 %cond1, label %bb1, label %bb2
+/// bb1:
+/// br i1 %cond2, label %bb3, label %bb4
+/// bb2:
+/// br i1 %cond2, label %bb4, label %bb3
+/// bb3:
+/// ...
+/// bb4:
+/// ...
+/// into
+/// bb0:
+/// %cond = xor i1 %cond1, %cond2
+/// br i1 %cond, label %bb4, label %bb3
+/// bb3:
+/// ...
+/// bb4:
+/// ...
+/// NOTE: %cond2 always dominates the terminator of bb0.
+static bool mergeNestedCondBranch(BranchInst *BI, DomTreeUpdater *DTU) {
+ BasicBlock *BB = BI->getParent();
+ BasicBlock *BB1 = BI->getSuccessor(0);
+ BasicBlock *BB2 = BI->getSuccessor(1);
+ auto IsSimpleSuccessor = [BB](BasicBlock *Succ, BranchInst *&SuccBI) {
+ if (Succ == BB)
+ return false;
+ if (&Succ->front() != Succ->getTerminator())
+ return false;
+ SuccBI = dyn_cast<BranchInst>(Succ->getTerminator());
+ if (!SuccBI || !SuccBI->isConditional())
+ return false;
+ BasicBlock *Succ1 = SuccBI->getSuccessor(0);
+ BasicBlock *Succ2 = SuccBI->getSuccessor(1);
+ return Succ1 != Succ && Succ2 != Succ && Succ1 != BB && Succ2 != BB &&
+ !isa<PHINode>(Succ1->front()) && !isa<PHINode>(Succ2->front());
+ };
+ BranchInst *BB1BI, *BB2BI;
+ if (!IsSimpleSuccessor(BB1, BB1BI) || !IsSimpleSuccessor(BB2, BB2BI))
+ return false;
+
+ if (BB1BI->getCondition() != BB2BI->getCondition() ||
+ BB1BI->getSuccessor(0) != BB2BI->getSuccessor(1) ||
+ BB1BI->getSuccessor(1) != BB2BI->getSuccessor(0))
+ return false;
+
+ BasicBlock *BB3 = BB1BI->getSuccessor(0);
+ BasicBlock *BB4 = BB1BI->getSuccessor(1);
+ IRBuilder<> Builder(BI);
+ BI->setCondition(
+ Builder.CreateXor(BI->getCondition(), BB1BI->getCondition()));
+ BB1->removePredecessor(BB);
+ BI->setSuccessor(0, BB4);
+ BB2->removePredecessor(BB);
+ BI->setSuccessor(1, BB3);
+ if (DTU) {
+ SmallVector<DominatorTree::UpdateType, 4> Updates;
+ Updates.push_back({DominatorTree::Delete, BB, BB1});
+ Updates.push_back({DominatorTree::Insert, BB, BB4});
+ Updates.push_back({DominatorTree::Delete, BB, BB2});
+ Updates.push_back({DominatorTree::Insert, BB, BB3});
+
+ DTU->applyUpdates(Updates);
+ }
+ bool HasWeight = false;
+ uint64_t BBTWeight, BBFWeight;
+ if (extractBranchWeights(*BI, BBTWeight, BBFWeight))
+ HasWeight = true;
+ else
+ BBTWeight = BBFWeight = 1;
+ uint64_t BB1TWeight, BB1FWeight;
+ if (extractBranchWeights(*BB1BI, BB1TWeight, BB1FWeight))
+ HasWeight = true;
+ else
+ BB1TWeight = BB1FWeight = 1;
+ uint64_t BB2TWeight, BB2FWeight;
+ if (extractBranchWeights(*BB2BI, BB2TWeight, BB2FWeight))
+ HasWeight = true;
+ else
+ BB2TWeight = BB2FWeight = 1;
+ if (HasWeight) {
+ uint64_t Weights[2] = {BBTWeight * BB1FWeight + BBFWeight * BB2TWeight,
+ BBTWeight * BB1TWeight + BBFWeight * BB2FWeight};
+ FitWeights(Weights);
+ setBranchWeights(BI, Weights[0], Weights[1], /*IsExpected=*/false);
+ }
+ return true;
+}
+
bool SimplifyCFGOpt::simplifyCondBranch(BranchInst *BI, IRBuilder<> &Builder) {
assert(
!isa<ConstantInt>(BI->getCondition()) &&
@@ -7468,6 +7557,10 @@ bool SimplifyCFGOpt::simplifyCondBranch(BranchInst *BI, IRBuilder<> &Builder) {
if (mergeConditionalStores(PBI, BI, DTU, DL, TTI))
return requestResimplify();
+ // Look for nested conditional branches.
+ if (mergeNestedCondBranch(BI, DTU))
+ return requestResimplify();
+
return false;
}
diff --git a/llvm/test/CodeGen/ARM/and-cmp0-sink.ll b/llvm/test/CodeGen/ARM/and-cmp0-sink.ll
index 27203e274a4aa..fb9139c0d1285 100644
--- a/llvm/test/CodeGen/ARM/and-cmp0-sink.ll
+++ b/llvm/test/CodeGen/ARM/and-cmp0-sink.ll
@@ -186,7 +186,7 @@ exit:
}
; Test with a mask that can be encoded with T32 instruction set, but not with A32.
-define i32 @f0(i1 %c0, i32 %v) {
+define i32 @f0(i1 %c0, i32 %v, ptr %p) {
; V7M-LABEL: f0:
; V7M: @ %bb.0: @ %E
; V7M-NEXT: lsls r0, r0, #31
@@ -198,7 +198,9 @@ define i32 @f0(i1 %c0, i32 %v) {
; V7M-NEXT: bxeq lr
; V7M-NEXT: b .LBB1_3
; V7M-NEXT: .LBB1_2: @ %B
+; V7M-NEXT: movs r0, #1
; V7M-NEXT: tst.w r1, #16843009
+; V7M-NEXT: str r0, [r2]
; V7M-NEXT: itt ne
; V7M-NEXT: movne r0, #0
; V7M-NEXT: bxne lr
@@ -208,10 +210,10 @@ define i32 @f0(i1 %c0, i32 %v) {
;
; V7A-LABEL: f0:
; V7A: @ %bb.0: @ %E
-; V7A-NEXT: movw r2, #257
+; V7A-NEXT: movw r3, #257
; V7A-NEXT: tst r0, #1
-; V7A-NEXT: movt r2, #257
-; V7A-NEXT: and r1, r1, r2
+; V7A-NEXT: movt r3, #257
+; V7A-NEXT: and r1, r1, r3
; V7A-NEXT: beq .LBB1_3
; V7A-NEXT: @ %bb.1: @ %A
; V7A-NEXT: cmp r1, #0
@@ -221,8 +223,10 @@ define i32 @f0(i1 %c0, i32 %v) {
; V7A-NEXT: mov r0, #1
; V7A-NEXT: bx lr
; V7A-NEXT: .LBB1_3: @ %B
-; V7A-NEXT: mov r0, #0
+; V7A-NEXT: mov r0, #1
; V7A-NEXT: cmp r1, #0
+; V7A-NEXT: str r0, [r2]
+; V7A-NEXT: mov r0, #0
; V7A-NEXT: moveq r0, #1
; V7A-NEXT: bx lr
;
@@ -237,7 +241,9 @@ define i32 @f0(i1 %c0, i32 %v) {
; V7A-T-NEXT: bxeq lr
; V7A-T-NEXT: b .LBB1_3
; V7A-T-NEXT: .LBB1_2: @ %B
+; V7A-T-NEXT: movs r0, #1
; V7A-T-NEXT: tst.w r1, #16843009
+; V7A-T-NEXT: str r0, [r2]
; V7A-T-NEXT: itt ne
; V7A-T-NEXT: movne r0, #0
; V7A-T-NEXT: bxne lr
@@ -247,18 +253,20 @@ define i32 @f0(i1 %c0, i32 %v) {
;
; V6M-LABEL: f0:
; V6M: @ %bb.0: @ %E
-; V6M-NEXT: ldr r2, .LCPI1_0
-; V6M-NEXT: ands r2, r1
+; V6M-NEXT: ldr r3, .LCPI1_0
+; V6M-NEXT: ands r3, r1
; V6M-NEXT: lsls r0, r0, #31
; V6M-NEXT: beq .LBB1_3
; V6M-NEXT: @ %bb.1: @ %A
-; V6M-NEXT: cmp r2, #0
+; V6M-NEXT: cmp r3, #0
; V6M-NEXT: bne .LBB1_5
; V6M-NEXT: @ %bb.2:
; V6M-NEXT: movs r0, #0
; V6M-NEXT: bx lr
; V6M-NEXT: .LBB1_3: @ %B
-; V6M-NEXT: cmp r2, #0
+; V6M-NEXT: movs r0, #1
+; V6M-NEXT: str r0, [r2]
+; V6M-NEXT: cmp r3, #0
; V6M-NEXT: beq .LBB1_5
; V6M-NEXT: @ %bb.4:
; V6M-NEXT: movs r0, #0
@@ -280,6 +288,7 @@ A:
B:
%c2 = icmp eq i32 %a, 0
+ store i32 1, ptr %p, align 4
br i1 %c2, label %D, label %C
C:
@@ -294,7 +303,7 @@ X:
}
; Test with a mask that can be encoded both with T32 and A32 instruction sets.
-define i32 @f1(i1 %c0, i32 %v) {
+define i32 @f1(i1 %c0, i32 %v, ptr %p) {
; V7M-LABEL: f1:
; V7M: @ %bb.0: @ %E
; V7M-NEXT: lsls r0, r0, #31
@@ -306,7 +315,9 @@ define i32 @f1(i1 %c0, i32 %v) {
; V7M-NEXT: bxeq lr
; V7M-NEXT: b .LBB2_3
; V7M-NEXT: .LBB2_2: @ %B
+; V7M-NEXT: movs r0, #1
; V7M-NEXT: tst.w r1, #100663296
+; V7M-NEXT: str r0, [r2]
; V7M-NEXT: itt ne
; V7M-NEXT: movne r0, #0
; V7M-NEXT: bxne lr
@@ -326,8 +337,10 @@ define i32 @f1(i1 %c0, i32 %v) {
; V7A-NEXT: mov r0, #1
; V7A-NEXT: bx lr
; V7A-NEXT: .LBB2_3: @ %B
-; V7A-NEXT: mov r0, #0
+; V7A-NEXT: mov r0, #1
; V7A-NEXT: tst r1, #100663296
+; V7A-NEXT: str r0, [r2]
+; V7A-NEXT: mov r0, #0
; V7A-NEXT: moveq r0, #1
; V7A-NEXT: bx lr
;
@@ -342,7 +355,9 @@ define i32 @f1(i1 %c0, i32 %v) {
; V7A-T-NEXT: bxeq lr
; V7A-T-NEXT: b .LBB2_3
; V7A-T-NEXT: .LBB2_2: @ %B
+; V7A-T-NEXT: movs r0, #1
; V7A-T-NEXT: tst.w r1, #100663296
+; V7A-T-NEXT: str r0, [r2]
; V7A-T-NEXT: itt ne
; V7A-T-NEXT: movne r0, #0
; V7A-T-NEXT: bxne lr
@@ -352,19 +367,21 @@ define i32 @f1(i1 %c0, i32 %v) {
;
; V6M-LABEL: f1:
; V6M: @ %bb.0: @ %E
-; V6M-NEXT: movs r2, #3
-; V6M-NEXT: lsls r2, r2, #25
-; V6M-NEXT: ands r2, r1
+; V6M-NEXT: movs r3, #3
+; V6M-NEXT: lsls r3, r3, #25
+; V6M-NEXT: ands r3, r1
; V6M-NEXT: lsls r0, r0, #31
; V6M-NEXT: beq .LBB2_3
; V6M-NEXT: @ %bb.1: @ %A
-; V6M-NEXT: cmp r2, #0
+; V6M-NEXT: cmp r3, #0
; V6M-NEXT: bne .LBB2_5
; V6M-NEXT: @ %bb.2:
; V6M-NEXT: movs r0, #0
; V6M-NEXT: bx lr
; V6M-NEXT: .LBB2_3: @ %B
-; V6M-NEXT: cmp r2, #0
+; V6M-NEXT: movs r0, #1
+; V6M-NEXT: str r0, [r2]
+; V6M-NEXT: cmp r3, #0
; V6M-NEXT: beq .LBB2_5
; V6M-NEXT: @ %bb.4:
; V6M-NEXT: movs r0, #0
@@ -382,6 +399,7 @@ A:
B:
%c2 = icmp eq i32 %a, 0
+ store i32 1, ptr %p, align 4
br i1 %c2, label %D, label %C
C:
diff --git a/llvm/test/Transforms/SimplifyCFG/branch-nested.ll b/llvm/test/Transforms/SimplifyCFG/branch-nested.ll
new file mode 100644
index 0000000000000..13218e6d2049c
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/branch-nested.ll
@@ -0,0 +1,351 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s
+
+define void @fold_nested_branch(i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: define void @fold_nested_branch(
+; CHECK-SAME: i1 [[COND1:%.*]], i1 [[COND2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[COND1]], [[COND2]]
+; CHECK-NEXT: br i1 [[TMP0]], label %[[BB4:.*]], label %[[BB3:.*]]
+; CHECK: [[COMMON_RET:.*]]:
+; CHECK-NEXT: ret void
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void @sideeffect1()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: call void @sideeffect2()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+;
+entry:
+ br i1 %cond1, label %bb1, label %bb2
+
+bb1:
+ br i1 %cond2, label %bb3, label %bb4
+
+bb2:
+ br i1 %cond2, label %bb4, label %bb3
+
+bb3:
+ call void @sideeffect1()
+ ret void
+
+bb4:
+ call void @sideeffect2()
+ ret void
+}
+
+define void @fold_nested_branch_extra_predecessors(i1 %cond0, i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: define void @fold_nested_branch_extra_predecessors(
+; CHECK-SAME: i1 [[COND0:%.*]], i1 [[COND1:%.*]], i1 [[COND2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND0]], label %[[BB0:.*]], label %[[BB1_PRED:.*]]
+; CHECK: [[BB1_PRED]]:
+; CHECK-NEXT: call void @sideeffect1()
+; CHECK-NEXT: br i1 [[COND2]], label %[[BB3:.*]], label %[[BB4:.*]]
+; CHECK: [[BB0]]:
+; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[COND1]], [[COND2]]
+; CHECK-NEXT: br i1 [[TMP0]], label %[[BB4]], label %[[BB3]]
+; CHECK: [[COMMON_RET:.*]]:
+; CHECK-NEXT: ret void
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void @sideeffect1()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: call void @sideeffect2()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+;
+entry:
+ br i1 %cond0, label %bb0, label %bb1_pred
+
+bb1_pred:
+ call void @sideeffect1()
+ br label %bb1
+
+bb0:
+ br i1 %cond1, label %bb1, label %bb2
+
+bb1:
+ br i1 %cond2, label %bb3, label %bb4
+
+bb2:
+ br i1 %cond2, label %bb4, label %bb3
+
+bb3:
+ call void @sideeffect1()
+ ret void
+
+bb4:
+ call void @sideeffect2()
+ ret void
+}
+
+; Negative tests
+
+define void @fold_nested_branch_cfg_mismatch(i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: define void @fold_nested_branch_cfg_mismatch(
+; CHECK-SAME: i1 [[COND1:%.*]], i1 [[COND2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK: [[BB1]]:
+; CHECK-NEXT: br i1 [[COND2]], label %[[BB3:.*]], label %[[COMMON_RET:.*]]
+; CHECK: [[BB2]]:
+; CHECK-NEXT: br i1 [[COND2]], label %[[BB4:.*]], label %[[BB3]]
+; CHECK: [[COMMON_RET]]:
+; CHECK-NEXT: ret void
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void @sideeffect1()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: call void @sideeffect2()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+;
+entry:
+ br i1 %cond1, label %bb1, label %bb2
+
+bb1:
+ br i1 %cond2, label %bb3, label %bb5
+
+bb2:
+ br i1 %cond2, label %bb4, label %bb3
+
+bb3:
+ call void @sideeffect1()
+ ret void
+
+bb4:
+ call void @sideeffect2()
+ ret void
+
+bb5:
+ ret void
+}
+
+define void @fold_nested_branch_cond_mismatch(i1 %cond1, i1 %cond2, i1 %cond3) {
+; CHECK-LABEL: define void @fold_nested_branch_cond_mismatch(
+; CHECK-SAME: i1 [[COND1:%.*]], i1 [[COND2:%.*]], i1 [[COND3:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK: [[BB1]]:
+; CHECK-NEXT: br i1 [[COND2]], label %[[BB3:.*]], label %[[BB4:.*]]
+; CHECK: [[BB2]]:
+; CHECK-NEXT: br i1 [[COND3]], label %[[BB4]], label %[[BB3]]
+; CHECK: [[COMMON_RET:.*]]:
+; CHECK-NEXT: ret void
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void @sideeffect1()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: call void @sideeffect2()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+;
+entry:
+ br i1 %cond1, label %bb1, label %bb2
+
+bb1:
+ br i1 %cond2, label %bb3, label %bb4
+
+bb2:
+ br i1 %cond3, label %bb4, label %bb3
+
+bb3:
+ call void @sideeffect1()
+ ret void
+
+bb4:
+ call void @sideeffect2()
+ ret void
+}
+
+define void @fold_nested_branch_non_trivial_succ(i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: define void @fold_nested_branch_non_trivial_succ(
+; CHECK-SAME: i1 [[COND1:%.*]], i1 [[COND2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK: [[BB1]]:
+; CHECK-NEXT: call void @sideeffect1()
+; CHECK-NEXT: br i1 [[COND2]], label %[[BB3:.*]], label %[[BB4:.*]]
+; CHECK: [[BB2]]:
+; CHECK-NEXT: br i1 [[COND2]], label %[[BB4]], label %[[BB3]]
+; CHECK: [[COMMON_RET:.*]]:
+; CHECK-NEXT: ret void
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void @sideeffect1()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: call void @sideeffect2()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+;
+entry:
+ br i1 %cond1, label %bb1, label %bb2
+
+bb1:
+ call void @sideeffect1()
+ br i1 %cond2, label %bb3, label %bb4
+
+bb2:
+ br i1 %cond2, label %bb4, label %bb3
+
+bb3:
+ call void @sideeffect1()
+ ret void
+
+bb4:
+ call void @sideeffect2()
+ ret void
+}
+
+define i32 @fold_nested_branch_with_phi(i1 %cond1, i1 %cond2, i32 %x) {
+; CHECK-LABEL: define i32 @fold_nested_branch_with_phi(
+; CHECK-SAME: i1 [[COND1:%.*]], i1 [[COND2:%.*]], i32 [[X:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK: [[BB1]]:
+; CHECK-NEXT: br i1 [[COND2]], label %[[COMMON_RET:.*]], label %[[BB4:.*]]
+; CHECK: [[BB2]]:
+; CHECK-NEXT: br i1 [[COND2]], label %[[BB4]], label %[[COMMON_RET]]
+; CHECK: [[COMMON_RET]]:
+; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i32 [ 0, %[[BB4]] ], [ 0, %[[BB1]] ], [ [[X]], %[[BB2]] ]
+; CHECK-NEXT: ret i32 [[COMMON_RET_OP]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: call void @sideeffect2()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+;
+entry:
+ br i1 %cond1, label %bb1, label %bb2
+
+bb1:
+ br i1 %cond2, label %bb3, label %bb4
+
+bb2:
+ br i1 %cond2, label %bb4, label %bb3
+
+bb3:
+ %ret = phi i32 [ 0, %bb1 ], [ %x, %bb2 ]
+ ret i32 %ret
+
+bb4:
+ call void @sideeffect2()
+ ret i32 0
+}
+
+define void @fold_nested_branch_loop1(i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: define void @fold_nested_branch_loop1(
+; CHECK-SAME: i1 [[COND1:%.*]], i1 [[COND2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[COND1_NOT:%.*]] = xor i1 [[COND1]], true
+; CHECK-NEXT: [[BRMERGE:%.*]] = select i1 [[COND1_NOT]], i1 true, i1 [[COND2]]
+; CHECK-NEXT: br i1 [[BRMERGE]], label %[[BB3:.*]], label %[[BB4:.*]]
+; CHECK: [[COMMON_RET:.*]]:
+; CHECK-NEXT: ret void
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void @sideeffect1()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: call void @sideeffect2()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+;
+entry:
+ br i1 %cond1, label %bb1, label %bb2
+
+bb1:
+ br i1 %cond2, label %bb3, label %bb4
+
+bb2:
+ br i1 %cond2, label %bb1, label %bb3
+
+bb3:
+ call void @sideeffect1()
+ ret void
+
+bb4:
+ call void @sideeffect2()
+ ret void
+}
+
+define void @fold_nested_branch_loop2(i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: define void @fold_nested_branch_loop2(
+; CHECK-SAME: i1 [[COND1:%.*]], i1 [[COND2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br label %[[BB0:.*]]
+; CHECK: [[BB0]]:
+; CHECK-NEXT: br i1 [[COND1]], label %[[BB1:.*]], label %[[BB2:.*]]
+; CHECK: [[BB1]]:
+; CHECK-NEXT: br i1 [[COND2]], label %[[BB3:.*]], label %[[BB4:.*]]
+; CHECK: [[BB2]]:
+; CHECK-NEXT: br i1 [[COND2]], label %[[BB0]], label %[[BB3]]
+; CHECK: [[COMMON_RET:.*]]:
+; CHECK-NEXT: ret void
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void @sideeffect1()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: call void @sideeffect2()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+;
+entry:
+ br label %bb0
+
+bb0:
+ br i1 %cond1, label %bb1, label %bb2
+
+bb1:
+ br i1 %cond2, label %bb3, label %bb4
+
+bb2:
+ br i1 %cond2, label %bb0, label %bb3
+
+bb3:
+ call void @sideeffect1()
+ ret void
+
+bb4:
+ call void @sideeffect2()
+ ret void
+}
+
+; Make sure that branch weights are correctly preserved
+; freq(bb4) = 1 * 4 + 2 * 5 = 14
+; freq(bb3) = 1 * 3 + 2 * 6 = 15
+define void @fold_nested_branch_prof(i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: define void @fold_nested_branch_prof(
+; CHECK-SAME: i1 [[COND1:%.*]], i1 [[COND2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[TMP0:%.*]] = xor i1 [[COND1]], [[COND2]]
+; CHECK-NEXT: br i1 [[TMP0]], label %[[BB4:.*]], label %[[BB3:.*]], !prof [[PROF0:![0-9]+]]
+; CHECK: [[COMMON_RET:.*]]:
+; CHECK-NEXT: ret void
+; CHECK: [[BB3]]:
+; CHECK-NEXT: call void @sideeffect1()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+; CHECK: [[BB4]]:
+; CHECK-NEXT: call void @sideeffect2()
+; CHECK-NEXT: br label %[[COMMON_RET]]
+;
+entry:
+ br i1 %cond1, label %bb1, label %bb2, !prof !0 ; 1:2
+
+bb1:
+ br i1 %cond2, label %bb3, label %bb4, !prof !1 ; 3:4
+
+bb2:
+ br i1 %cond2, label %bb4, label %bb3, !prof !2 ; 5:6
+
+bb3:
+ call void @sideeffect1()
+ ret void
+
+bb4:
+ call void @sideeffect2()
+ ret void
+}
+
+!0 = !{!"branch_weights", i32 1, i32 2}
+!1 = !{!"branch_weights", i32 3, i32 4}
+!2 = !{!"branch_weights", i32 5, i32 6}
+
+
+declare void @sideeffect1()
+declare void @sideeffect2()
+;.
+; CHECK: [[PROF0]] = !{!"branch_weights", i32 14, i32 15}
+;.
More information about the llvm-commits
mailing list