Skip to content

Commit eacca30

Browse files
committed
[RISCV] Codegen for conditional branches
A good portion of this patch is the extra functions that needed to be implemented to support the test case. e.g. storeRegToStackSlot, loadRegFromStackSlot, eliminateFrameIndex. Setting ISD::BR_CC to Expand may appear non-obvious on an architecture with branch+cmp instructions. However, I found it much easier to deal with matching the expanded form. I had to change simm13_lsb0 and simm21_lsb0 to inherit from the Operand<OtherVT> class rather than Operand<i32> in order to keep tablegen happy. This isn't a big deal, but it does seem a shame to lose the uniformity across immediate types when there's not an obvious benefit (I'm hoping a tablegen expert will educate me on what I'm missing here!). Differential Revision: https://reviews.llvm.org/D29935 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@317690 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 6c9938c commit eacca30

File tree

9 files changed

+239
-4
lines changed

9 files changed

+239
-4
lines changed

lib/Target/RISCV/RISCVCallingConv.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ def CC_RISCV32 : CallingConv<[
2727
]>;
2828

2929
def CSR : CalleeSavedRegs<(add X1, X3, X4, X8, X9, (sequence "X%u", 18, 27))>;
30+
31+
// Needed for implementation of RISCVRegisterInfo::getNoPreservedMask()
32+
def CSR_NoRegs : CalleeSavedRegs<(add)>;

lib/Target/RISCV/RISCVISelLowering.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
5555
// TODO: add all necessary setOperationAction calls.
5656
setOperationAction(ISD::GlobalAddress, XLenVT, Custom);
5757

58+
setOperationAction(ISD::BR_CC, XLenVT, Expand);
5859
setBooleanContents(ZeroOrOneBooleanContent);
5960

6061
// Function alignments (log2).

lib/Target/RISCV/RISCVInstrInfo.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,36 @@ void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
4141
.addReg(SrcReg, getKillRegState(KillSrc))
4242
.addImm(0);
4343
}
44+
45+
void RISCVInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
46+
MachineBasicBlock::iterator I,
47+
unsigned SrcReg, bool IsKill, int FI,
48+
const TargetRegisterClass *RC,
49+
const TargetRegisterInfo *TRI) const {
50+
DebugLoc DL;
51+
if (I != MBB.end())
52+
DL = I->getDebugLoc();
53+
54+
if (RC == &RISCV::GPRRegClass)
55+
BuildMI(MBB, I, DL, get(RISCV::SW))
56+
.addReg(SrcReg, getKillRegState(IsKill))
57+
.addFrameIndex(FI)
58+
.addImm(0);
59+
else
60+
llvm_unreachable("Can't store this register to stack slot");
61+
}
62+
63+
void RISCVInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
64+
MachineBasicBlock::iterator I,
65+
unsigned DstReg, int FI,
66+
const TargetRegisterClass *RC,
67+
const TargetRegisterInfo *TRI) const {
68+
DebugLoc DL;
69+
if (I != MBB.end())
70+
DL = I->getDebugLoc();
71+
72+
if (RC == &RISCV::GPRRegClass)
73+
BuildMI(MBB, I, DL, get(RISCV::LW), DstReg).addFrameIndex(FI).addImm(0);
74+
else
75+
llvm_unreachable("Can't load this register from stack slot");
76+
}

lib/Target/RISCV/RISCVInstrInfo.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,17 @@ class RISCVInstrInfo : public RISCVGenInstrInfo {
3030
void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
3131
const DebugLoc &DL, unsigned DstReg, unsigned SrcReg,
3232
bool KillSrc) const override;
33+
34+
void storeRegToStackSlot(MachineBasicBlock &MBB,
35+
MachineBasicBlock::iterator MBBI, unsigned SrcReg,
36+
bool IsKill, int FrameIndex,
37+
const TargetRegisterClass *RC,
38+
const TargetRegisterInfo *TRI) const override;
39+
40+
void loadRegFromStackSlot(MachineBasicBlock &MBB,
41+
MachineBasicBlock::iterator MBBI, unsigned DstReg,
42+
int FrameIndex, const TargetRegisterClass *RC,
43+
const TargetRegisterInfo *TRI) const override;
3344
};
3445
}
35-
3646
#endif

lib/Target/RISCV/RISCVInstrInfo.td

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def uimm12 : Operand<XLenVT> {
6767
}
6868

6969
// A 13-bit signed immediate where the least significant bit is zero.
70-
def simm13_lsb0 : Operand<XLenVT> {
70+
def simm13_lsb0 : Operand<OtherVT> {
7171
let ParserMatchClass = SImmAsmOperand<13, "Lsb0">;
7272
let EncoderMethod = "getImmOpValueAsr1";
7373
let DecoderMethod = "decodeSImmOperandAndLsl1<13>";
@@ -80,7 +80,7 @@ def uimm20 : Operand<XLenVT> {
8080
}
8181

8282
// A 21-bit signed immediate where the least significant bit is zero.
83-
def simm21_lsb0 : Operand<XLenVT> {
83+
def simm21_lsb0 : Operand<OtherVT> {
8484
let ParserMatchClass = SImmAsmOperand<21, "Lsb0">;
8585
let EncoderMethod = "getImmOpValueAsr1";
8686
let DecoderMethod = "decodeSImmOperandAndLsl1<21>";
@@ -308,6 +308,38 @@ def : PatGprSimm12<setult, SLTIU>;
308308

309309
/// Branches and jumps
310310

311+
// Match `(brcond (CondOp ..), ..)` and lower to the appropriate RISC-V branch
312+
// instruction.
313+
class BccPat<PatFrag CondOp, RVInstB Inst>
314+
: Pat<(brcond (i32 (CondOp GPR:$rs1, GPR:$rs2)), bb:$imm12),
315+
(Inst GPR:$rs1, GPR:$rs2, simm13_lsb0:$imm12)>;
316+
317+
def : BccPat<seteq, BEQ>;
318+
def : BccPat<setne, BNE>;
319+
def : BccPat<setlt, BLT>;
320+
def : BccPat<setge, BGE>;
321+
def : BccPat<setult, BLTU>;
322+
def : BccPat<setuge, BGEU>;
323+
324+
class BccSwapPat<PatFrag CondOp, RVInst InstBcc>
325+
: Pat<(brcond (i32 (CondOp GPR:$rs1, GPR:$rs2)), bb:$imm12),
326+
(InstBcc GPR:$rs2, GPR:$rs1, bb:$imm12)>;
327+
328+
// Condition codes that don't have matching RISC-V branch instructions, but
329+
// are trivially supported by swapping the two input operands
330+
def : BccSwapPat<setgt, BLT>;
331+
def : BccSwapPat<setle, BGE>;
332+
def : BccSwapPat<setugt, BLTU>;
333+
def : BccSwapPat<setule, BGEU>;
334+
335+
// An extra pattern is needed for a brcond without a setcc (i.e. where the
336+
// condition was calculated elsewhere).
337+
def : Pat<(brcond GPR:$cond, bb:$imm12), (BNE GPR:$cond, X0, bb:$imm12)>;
338+
339+
let isBarrier = 1, isBranch = 1, isTerminator = 1 in
340+
def PseudoBR : Pseudo<(outs), (ins simm21_lsb0:$imm20), [(br bb:$imm20)]>,
341+
PseudoInstExpansion<(JAL X0, simm21_lsb0:$imm20)>;
342+
311343
let isBarrier = 1, isReturn = 1, isTerminator = 1 in
312344
def PseudoRET : Pseudo<(outs), (ins), [(RetFlag)]>,
313345
PseudoInstExpansion<(JALR X0, X1, 0)>;

lib/Target/RISCV/RISCVMCInstLower.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ bool llvm::LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO,
7171
case MachineOperand::MO_Immediate:
7272
MCOp = MCOperand::createImm(MO.getImm());
7373
break;
74+
case MachineOperand::MO_MachineBasicBlock:
75+
MCOp = MCOperand::createExpr(
76+
MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), AP.OutContext));
77+
break;
7478
case MachineOperand::MO_GlobalAddress:
7579
MCOp = lowerSymbolOperand(MO, AP.getSymbol(MO.getGlobal()), AP);
7680
break;

lib/Target/RISCV/RISCVRegisterInfo.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,39 @@ BitVector RISCVRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
5050
return Reserved;
5151
}
5252

53+
const uint32_t *RISCVRegisterInfo::getNoPreservedMask() const {
54+
return CSR_NoRegs_RegMask;
55+
}
56+
5357
void RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
5458
int SPAdj, unsigned FIOperandNum,
5559
RegScavenger *RS) const {
56-
report_fatal_error("Subroutines not supported yet");
60+
// TODO: this implementation is a temporary placeholder which does just
61+
// enough to allow other aspects of code generation to be tested
62+
63+
assert(SPAdj == 0 && "Unexpected non-zero SPAdj value");
64+
65+
MachineInstr &MI = *II;
66+
MachineFunction &MF = *MI.getParent()->getParent();
67+
const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
68+
DebugLoc DL = MI.getDebugLoc();
69+
70+
unsigned FrameReg = getFrameRegister(MF);
71+
int FrameIndex = MI.getOperand(FIOperandNum).getIndex();
72+
int Offset = TFI->getFrameIndexReference(MF, FrameIndex, FrameReg);
73+
Offset += MI.getOperand(FIOperandNum + 1).getImm();
74+
75+
assert(TFI->hasFP(MF) && "eliminateFrameIndex currently requires hasFP");
76+
77+
// Offsets must be directly encoded in a 12-bit immediate field
78+
if (!isInt<12>(Offset)) {
79+
report_fatal_error(
80+
"Frame offsets outside of the signed 12-bit range not supported");
81+
}
82+
83+
MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg, false);
84+
MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Offset);
85+
return;
5786
}
5887

5988
unsigned RISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const {

lib/Target/RISCV/RISCVRegisterInfo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo {
2929

3030
BitVector getReservedRegs(const MachineFunction &MF) const override;
3131

32+
const uint32_t *getNoPreservedMask() const override;
33+
3234
void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj,
3335
unsigned FIOperandNum,
3436
RegScavenger *RS = nullptr) const override;

test/CodeGen/RISCV/branch.ll

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
3+
; RUN: | FileCheck -check-prefix=RV32I %s
4+
5+
define void @foo(i32 %a, i32 *%b, i1 %c) {
6+
; RV32I-LABEL: foo:
7+
; RV32I: # BB#0:
8+
; RV32I-NEXT: lw a3, 0(a1)
9+
; RV32I-NEXT: beq a3, a0, .LBB0_12
10+
; RV32I-NEXT: jal zero, .LBB0_1
11+
; RV32I-NEXT: .LBB0_1: # %test2
12+
; RV32I-NEXT: lw a3, 0(a1)
13+
; RV32I-NEXT: bne a3, a0, .LBB0_12
14+
; RV32I-NEXT: jal zero, .LBB0_2
15+
; RV32I-NEXT: .LBB0_2: # %test3
16+
; RV32I-NEXT: lw a3, 0(a1)
17+
; RV32I-NEXT: blt a3, a0, .LBB0_12
18+
; RV32I-NEXT: jal zero, .LBB0_3
19+
; RV32I-NEXT: .LBB0_3: # %test4
20+
; RV32I-NEXT: lw a3, 0(a1)
21+
; RV32I-NEXT: bge a3, a0, .LBB0_12
22+
; RV32I-NEXT: jal zero, .LBB0_4
23+
; RV32I-NEXT: .LBB0_4: # %test5
24+
; RV32I-NEXT: lw a3, 0(a1)
25+
; RV32I-NEXT: bltu a3, a0, .LBB0_12
26+
; RV32I-NEXT: jal zero, .LBB0_5
27+
; RV32I-NEXT: .LBB0_5: # %test6
28+
; RV32I-NEXT: lw a3, 0(a1)
29+
; RV32I-NEXT: bgeu a3, a0, .LBB0_12
30+
; RV32I-NEXT: jal zero, .LBB0_6
31+
; RV32I-NEXT: .LBB0_6: # %test7
32+
; RV32I-NEXT: lw a3, 0(a1)
33+
; RV32I-NEXT: blt a0, a3, .LBB0_12
34+
; RV32I-NEXT: jal zero, .LBB0_7
35+
; RV32I-NEXT: .LBB0_7: # %test8
36+
; RV32I-NEXT: lw a3, 0(a1)
37+
; RV32I-NEXT: bge a0, a3, .LBB0_12
38+
; RV32I-NEXT: jal zero, .LBB0_8
39+
; RV32I-NEXT: .LBB0_8: # %test9
40+
; RV32I-NEXT: lw a3, 0(a1)
41+
; RV32I-NEXT: bltu a0, a3, .LBB0_12
42+
; RV32I-NEXT: jal zero, .LBB0_9
43+
; RV32I-NEXT: .LBB0_9: # %test10
44+
; RV32I-NEXT: lw a3, 0(a1)
45+
; RV32I-NEXT: bgeu a0, a3, .LBB0_12
46+
; RV32I-NEXT: jal zero, .LBB0_10
47+
; RV32I-NEXT: .LBB0_10: # %test11
48+
; RV32I-NEXT: lw a0, 0(a1)
49+
; RV32I-NEXT: andi a0, a2, 1
50+
; RV32I-NEXT: bne a0, zero, .LBB0_12
51+
; RV32I-NEXT: jal zero, .LBB0_11
52+
; RV32I-NEXT: .LBB0_11: # %test12
53+
; RV32I-NEXT: lw a0, 0(a1)
54+
; RV32I-NEXT: .LBB0_12: # %end
55+
; RV32I-NEXT: jalr zero, ra, 0
56+
57+
%val1 = load volatile i32, i32* %b
58+
%tst1 = icmp eq i32 %val1, %a
59+
br i1 %tst1, label %end, label %test2
60+
61+
test2:
62+
%val2 = load volatile i32, i32* %b
63+
%tst2 = icmp ne i32 %val2, %a
64+
br i1 %tst2, label %end, label %test3
65+
66+
test3:
67+
%val3 = load volatile i32, i32* %b
68+
%tst3 = icmp slt i32 %val3, %a
69+
br i1 %tst3, label %end, label %test4
70+
71+
test4:
72+
%val4 = load volatile i32, i32* %b
73+
%tst4 = icmp sge i32 %val4, %a
74+
br i1 %tst4, label %end, label %test5
75+
76+
test5:
77+
%val5 = load volatile i32, i32* %b
78+
%tst5 = icmp ult i32 %val5, %a
79+
br i1 %tst5, label %end, label %test6
80+
81+
test6:
82+
%val6 = load volatile i32, i32* %b
83+
%tst6 = icmp uge i32 %val6, %a
84+
br i1 %tst6, label %end, label %test7
85+
86+
; Check for condition codes that don't have a matching instruction
87+
88+
test7:
89+
%val7 = load volatile i32, i32* %b
90+
%tst7 = icmp sgt i32 %val7, %a
91+
br i1 %tst7, label %end, label %test8
92+
93+
test8:
94+
%val8 = load volatile i32, i32* %b
95+
%tst8 = icmp sle i32 %val8, %a
96+
br i1 %tst8, label %end, label %test9
97+
98+
test9:
99+
%val9 = load volatile i32, i32* %b
100+
%tst9 = icmp ugt i32 %val9, %a
101+
br i1 %tst9, label %end, label %test10
102+
103+
test10:
104+
%val10 = load volatile i32, i32* %b
105+
%tst10 = icmp ule i32 %val10, %a
106+
br i1 %tst10, label %end, label %test11
107+
108+
; Check the case of a branch where the condition was generated in another
109+
; function
110+
111+
test11:
112+
%val11 = load volatile i32, i32* %b
113+
br i1 %c, label %end, label %test12
114+
115+
test12:
116+
%val12 = load volatile i32, i32* %b
117+
br label %end
118+
119+
end:
120+
ret void
121+
}

0 commit comments

Comments
 (0)