Skip to content

Commit 8716513

Browse files
wangleiatSixWeining
authored andcommitted
[LoongArch] Implement branch analysis
This allows a number of optimisation passes to work. E.g. BranchFolding and MachineBlockPlacement. Differential Revision: https://reviews.llvm.org/D131316
1 parent f35cb7b commit 8716513

File tree

6 files changed

+331
-70
lines changed

6 files changed

+331
-70
lines changed

llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,192 @@ void LoongArchInstrInfo::loadRegFromStackSlot(
113113
.addImm(0)
114114
.addMemOperand(MMO);
115115
}
116+
117+
unsigned LoongArchInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
118+
return MI.getDesc().getSize();
119+
}
120+
121+
MachineBasicBlock *
122+
LoongArchInstrInfo::getBranchDestBlock(const MachineInstr &MI) const {
123+
assert(MI.getDesc().isBranch() && "Unexpected opcode!");
124+
// The branch target is always the last operand.
125+
return MI.getOperand(MI.getNumExplicitOperands() - 1).getMBB();
126+
}
127+
128+
static void parseCondBranch(MachineInstr &LastInst, MachineBasicBlock *&Target,
129+
SmallVectorImpl<MachineOperand> &Cond) {
130+
// Block ends with fall-through condbranch.
131+
assert(LastInst.getDesc().isConditionalBranch() &&
132+
"Unknown conditional branch");
133+
int NumOp = LastInst.getNumExplicitOperands();
134+
Target = LastInst.getOperand(NumOp - 1).getMBB();
135+
136+
Cond.push_back(MachineOperand::CreateImm(LastInst.getOpcode()));
137+
for (int i = 0; i < NumOp - 1; i++)
138+
Cond.push_back(LastInst.getOperand(i));
139+
}
140+
141+
bool LoongArchInstrInfo::analyzeBranch(MachineBasicBlock &MBB,
142+
MachineBasicBlock *&TBB,
143+
MachineBasicBlock *&FBB,
144+
SmallVectorImpl<MachineOperand> &Cond,
145+
bool AllowModify) const {
146+
TBB = FBB = nullptr;
147+
Cond.clear();
148+
149+
// If the block has no terminators, it just falls into the block after it.
150+
MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr();
151+
if (I == MBB.end() || !isUnpredicatedTerminator(*I))
152+
return false;
153+
154+
// Count the number of terminators and find the first unconditional or
155+
// indirect branch.
156+
MachineBasicBlock::iterator FirstUncondOrIndirectBr = MBB.end();
157+
int NumTerminators = 0;
158+
for (auto J = I.getReverse(); J != MBB.rend() && isUnpredicatedTerminator(*J);
159+
J++) {
160+
NumTerminators++;
161+
if (J->getDesc().isUnconditionalBranch() ||
162+
J->getDesc().isIndirectBranch()) {
163+
FirstUncondOrIndirectBr = J.getReverse();
164+
}
165+
}
166+
167+
// If AllowModify is true, we can erase any terminators after
168+
// FirstUncondOrIndirectBR.
169+
if (AllowModify && FirstUncondOrIndirectBr != MBB.end()) {
170+
while (std::next(FirstUncondOrIndirectBr) != MBB.end()) {
171+
std::next(FirstUncondOrIndirectBr)->eraseFromParent();
172+
NumTerminators--;
173+
}
174+
I = FirstUncondOrIndirectBr;
175+
}
176+
177+
// Handle a single unconditional branch.
178+
if (NumTerminators == 1 && I->getDesc().isUnconditionalBranch()) {
179+
TBB = getBranchDestBlock(*I);
180+
return false;
181+
}
182+
183+
// Handle a single conditional branch.
184+
if (NumTerminators == 1 && I->getDesc().isConditionalBranch()) {
185+
parseCondBranch(*I, TBB, Cond);
186+
return false;
187+
}
188+
189+
// Handle a conditional branch followed by an unconditional branch.
190+
if (NumTerminators == 2 && std::prev(I)->getDesc().isConditionalBranch() &&
191+
I->getDesc().isUnconditionalBranch()) {
192+
parseCondBranch(*std::prev(I), TBB, Cond);
193+
FBB = getBranchDestBlock(*I);
194+
return false;
195+
}
196+
197+
// Otherwise, we can't handle this.
198+
return true;
199+
}
200+
201+
unsigned LoongArchInstrInfo::removeBranch(MachineBasicBlock &MBB,
202+
int *BytesRemoved) const {
203+
if (BytesRemoved)
204+
*BytesRemoved = 0;
205+
MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr();
206+
if (I == MBB.end())
207+
return 0;
208+
209+
if (!I->getDesc().isBranch())
210+
return 0;
211+
212+
// Remove the branch.
213+
if (BytesRemoved)
214+
*BytesRemoved += getInstSizeInBytes(*I);
215+
I->eraseFromParent();
216+
217+
I = MBB.end();
218+
219+
if (I == MBB.begin())
220+
return 1;
221+
--I;
222+
if (!I->getDesc().isConditionalBranch())
223+
return 1;
224+
225+
// Remove the branch.
226+
if (BytesRemoved)
227+
*BytesRemoved += getInstSizeInBytes(*I);
228+
I->eraseFromParent();
229+
return 2;
230+
}
231+
232+
// Inserts a branch into the end of the specific MachineBasicBlock, returning
233+
// the number of instructions inserted.
234+
unsigned LoongArchInstrInfo::insertBranch(
235+
MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB,
236+
ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const {
237+
if (BytesAdded)
238+
*BytesAdded = 0;
239+
240+
// Shouldn't be a fall through.
241+
assert(TBB && "insertBranch must not be told to insert a fallthrough");
242+
assert(Cond.size() <= 3 && Cond.size() != 1 &&
243+
"LoongArch branch conditions have at most two components!");
244+
245+
// Unconditional branch.
246+
if (Cond.empty()) {
247+
MachineInstr &MI = *BuildMI(&MBB, DL, get(LoongArch::PseudoBR)).addMBB(TBB);
248+
if (BytesAdded)
249+
*BytesAdded += getInstSizeInBytes(MI);
250+
return 1;
251+
}
252+
253+
// Either a one or two-way conditional branch.
254+
MachineInstrBuilder MIB = BuildMI(&MBB, DL, get(Cond[0].getImm()));
255+
for (unsigned i = 1; i < Cond.size(); ++i)
256+
MIB.add(Cond[i]);
257+
MIB.addMBB(TBB);
258+
if (BytesAdded)
259+
*BytesAdded += getInstSizeInBytes(*MIB);
260+
261+
// One-way conditional branch.
262+
if (!FBB)
263+
return 1;
264+
265+
// Two-way conditional branch.
266+
MachineInstr &MI = *BuildMI(&MBB, DL, get(LoongArch::PseudoBR)).addMBB(FBB);
267+
if (BytesAdded)
268+
*BytesAdded += getInstSizeInBytes(MI);
269+
return 2;
270+
}
271+
272+
static unsigned getOppositeBranchOpc(unsigned Opc) {
273+
switch (Opc) {
274+
default:
275+
llvm_unreachable("Unrecognized conditional branch");
276+
case LoongArch::BEQ:
277+
return LoongArch::BNE;
278+
case LoongArch::BNE:
279+
return LoongArch::BEQ;
280+
case LoongArch::BEQZ:
281+
return LoongArch::BNEZ;
282+
case LoongArch::BNEZ:
283+
return LoongArch::BEQZ;
284+
case LoongArch::BCEQZ:
285+
return LoongArch::BCNEZ;
286+
case LoongArch::BCNEZ:
287+
return LoongArch::BCEQZ;
288+
case LoongArch::BLT:
289+
return LoongArch::BGE;
290+
case LoongArch::BGE:
291+
return LoongArch::BLT;
292+
case LoongArch::BLTU:
293+
return LoongArch::BGEU;
294+
case LoongArch::BGEU:
295+
return LoongArch::BLTU;
296+
}
297+
}
298+
299+
bool LoongArchInstrInfo::reverseBranchCondition(
300+
SmallVectorImpl<MachineOperand> &Cond) const {
301+
assert((Cond.size() && Cond.size() <= 3) && "Invalid branch condition!");
302+
Cond[0].setImm(getOppositeBranchOpc(Cond[0].getImm()));
303+
return false;
304+
}

llvm/lib/Target/LoongArch/LoongArchInstrInfo.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,26 @@ class LoongArchInstrInfo : public LoongArchGenInstrInfo {
4040
MachineBasicBlock::iterator MBBI, Register DstReg,
4141
int FrameIndex, const TargetRegisterClass *RC,
4242
const TargetRegisterInfo *TRI) const override;
43+
44+
unsigned getInstSizeInBytes(const MachineInstr &MI) const override;
45+
46+
MachineBasicBlock *getBranchDestBlock(const MachineInstr &MI) const override;
47+
48+
bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB,
49+
MachineBasicBlock *&FBB,
50+
SmallVectorImpl<MachineOperand> &Cond,
51+
bool AllowModify) const override;
52+
53+
unsigned removeBranch(MachineBasicBlock &MBB,
54+
int *BytesRemoved = nullptr) const override;
55+
56+
unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB,
57+
MachineBasicBlock *FBB, ArrayRef<MachineOperand> Cond,
58+
const DebugLoc &dl,
59+
int *BytesAdded = nullptr) const override;
60+
61+
bool
62+
reverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const override;
4363
};
4464

4565
} // end namespace llvm
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc --mtriple=loongarch64 < %s | FileCheck %s
3+
4+
;; This test checks that LLVM can do basic stripping and reapplying of branches
5+
;; to basic blocks.
6+
7+
declare void @test_true()
8+
declare void @test_false()
9+
10+
;; !0 corresponds to a branch being taken, !1 to not being taken.
11+
!0 = !{!"branch_weights", i32 64, i32 4}
12+
!1 = !{!"branch_weights", i32 4, i32 64}
13+
14+
define void @test_bcc_fallthrough_taken(i64 %in) nounwind {
15+
; CHECK-LABEL: test_bcc_fallthrough_taken:
16+
; CHECK: # %bb.0:
17+
; CHECK-NEXT: addi.d $sp, $sp, -16
18+
; CHECK-NEXT: st.d $ra, $sp, 8 # 8-byte Folded Spill
19+
; CHECK-NEXT: ori $a1, $zero, 42
20+
; CHECK-NEXT: bne $a0, $a1, .LBB0_3
21+
; CHECK-NEXT: # %bb.1: # %true
22+
; CHECK-NEXT: bl test_true
23+
; CHECK-NEXT: .LBB0_2: # %true
24+
; CHECK-NEXT: ld.d $ra, $sp, 8 # 8-byte Folded Reload
25+
; CHECK-NEXT: addi.d $sp, $sp, 16
26+
; CHECK-NEXT: jirl $zero, $ra, 0
27+
; CHECK-NEXT: .LBB0_3: # %false
28+
; CHECK-NEXT: bl test_false
29+
; CHECK-NEXT: b .LBB0_2
30+
%tst = icmp eq i64 %in, 42
31+
br i1 %tst, label %true, label %false, !prof !0
32+
33+
;; Expected layout order is: Entry, TrueBlock, FalseBlock
34+
;; Entry->TrueBlock is the common path, which should be taken whenever the
35+
;; conditional branch is false.
36+
37+
true:
38+
call void @test_true()
39+
ret void
40+
41+
false:
42+
call void @test_false()
43+
ret void
44+
}
45+
46+
define void @test_bcc_fallthrough_nottaken(i64 %in) nounwind {
47+
; CHECK-LABEL: test_bcc_fallthrough_nottaken:
48+
; CHECK: # %bb.0:
49+
; CHECK-NEXT: addi.d $sp, $sp, -16
50+
; CHECK-NEXT: st.d $ra, $sp, 8 # 8-byte Folded Spill
51+
; CHECK-NEXT: ori $a1, $zero, 42
52+
; CHECK-NEXT: beq $a0, $a1, .LBB1_1
53+
; CHECK-NEXT: # %bb.3: # %false
54+
; CHECK-NEXT: bl test_false
55+
; CHECK-NEXT: .LBB1_2: # %true
56+
; CHECK-NEXT: ld.d $ra, $sp, 8 # 8-byte Folded Reload
57+
; CHECK-NEXT: addi.d $sp, $sp, 16
58+
; CHECK-NEXT: jirl $zero, $ra, 0
59+
; CHECK-NEXT: .LBB1_1: # %true
60+
; CHECK-NEXT: bl test_true
61+
; CHECK-NEXT: b .LBB1_2
62+
%tst = icmp eq i64 %in, 42
63+
br i1 %tst, label %true, label %false, !prof !1
64+
65+
;; Expected layout order is: Entry, FalseBlock, TrueBlock
66+
;; Entry->FalseBlock is the common path, which should be taken whenever the
67+
;; conditional branch is false.
68+
69+
true:
70+
call void @test_true()
71+
ret void
72+
73+
false:
74+
call void @test_false()
75+
ret void
76+
}

0 commit comments

Comments
 (0)