forked from OSchip/llvm-project
[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 llvm-svn: 317690
This commit is contained in:
parent
8fc64a7732
commit
74913e1c70
|
@ -27,3 +27,6 @@ def CC_RISCV32 : CallingConv<[
|
|||
]>;
|
||||
|
||||
def CSR : CalleeSavedRegs<(add X1, X3, X4, X8, X9, (sequence "X%u", 18, 27))>;
|
||||
|
||||
// Needed for implementation of RISCVRegisterInfo::getNoPreservedMask()
|
||||
def CSR_NoRegs : CalleeSavedRegs<(add)>;
|
||||
|
|
|
@ -55,6 +55,7 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
|
|||
// TODO: add all necessary setOperationAction calls.
|
||||
setOperationAction(ISD::GlobalAddress, XLenVT, Custom);
|
||||
|
||||
setOperationAction(ISD::BR_CC, XLenVT, Expand);
|
||||
setBooleanContents(ZeroOrOneBooleanContent);
|
||||
|
||||
// Function alignments (log2).
|
||||
|
|
|
@ -41,3 +41,36 @@ void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
|
|||
.addReg(SrcReg, getKillRegState(KillSrc))
|
||||
.addImm(0);
|
||||
}
|
||||
|
||||
void RISCVInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator I,
|
||||
unsigned SrcReg, bool IsKill, int FI,
|
||||
const TargetRegisterClass *RC,
|
||||
const TargetRegisterInfo *TRI) const {
|
||||
DebugLoc DL;
|
||||
if (I != MBB.end())
|
||||
DL = I->getDebugLoc();
|
||||
|
||||
if (RC == &RISCV::GPRRegClass)
|
||||
BuildMI(MBB, I, DL, get(RISCV::SW))
|
||||
.addReg(SrcReg, getKillRegState(IsKill))
|
||||
.addFrameIndex(FI)
|
||||
.addImm(0);
|
||||
else
|
||||
llvm_unreachable("Can't store this register to stack slot");
|
||||
}
|
||||
|
||||
void RISCVInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator I,
|
||||
unsigned DstReg, int FI,
|
||||
const TargetRegisterClass *RC,
|
||||
const TargetRegisterInfo *TRI) const {
|
||||
DebugLoc DL;
|
||||
if (I != MBB.end())
|
||||
DL = I->getDebugLoc();
|
||||
|
||||
if (RC == &RISCV::GPRRegClass)
|
||||
BuildMI(MBB, I, DL, get(RISCV::LW), DstReg).addFrameIndex(FI).addImm(0);
|
||||
else
|
||||
llvm_unreachable("Can't load this register from stack slot");
|
||||
}
|
||||
|
|
|
@ -30,7 +30,17 @@ public:
|
|||
void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
|
||||
const DebugLoc &DL, unsigned DstReg, unsigned SrcReg,
|
||||
bool KillSrc) const override;
|
||||
|
||||
void storeRegToStackSlot(MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator MBBI, unsigned SrcReg,
|
||||
bool IsKill, int FrameIndex,
|
||||
const TargetRegisterClass *RC,
|
||||
const TargetRegisterInfo *TRI) const override;
|
||||
|
||||
void loadRegFromStackSlot(MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator MBBI, unsigned DstReg,
|
||||
int FrameIndex, const TargetRegisterClass *RC,
|
||||
const TargetRegisterInfo *TRI) const override;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -67,7 +67,7 @@ def uimm12 : Operand<XLenVT> {
|
|||
}
|
||||
|
||||
// A 13-bit signed immediate where the least significant bit is zero.
|
||||
def simm13_lsb0 : Operand<XLenVT> {
|
||||
def simm13_lsb0 : Operand<OtherVT> {
|
||||
let ParserMatchClass = SImmAsmOperand<13, "Lsb0">;
|
||||
let EncoderMethod = "getImmOpValueAsr1";
|
||||
let DecoderMethod = "decodeSImmOperandAndLsl1<13>";
|
||||
|
@ -80,7 +80,7 @@ def uimm20 : Operand<XLenVT> {
|
|||
}
|
||||
|
||||
// A 21-bit signed immediate where the least significant bit is zero.
|
||||
def simm21_lsb0 : Operand<XLenVT> {
|
||||
def simm21_lsb0 : Operand<OtherVT> {
|
||||
let ParserMatchClass = SImmAsmOperand<21, "Lsb0">;
|
||||
let EncoderMethod = "getImmOpValueAsr1";
|
||||
let DecoderMethod = "decodeSImmOperandAndLsl1<21>";
|
||||
|
@ -308,6 +308,38 @@ def : PatGprSimm12<setult, SLTIU>;
|
|||
|
||||
/// Branches and jumps
|
||||
|
||||
// Match `(brcond (CondOp ..), ..)` and lower to the appropriate RISC-V branch
|
||||
// instruction.
|
||||
class BccPat<PatFrag CondOp, RVInstB Inst>
|
||||
: Pat<(brcond (i32 (CondOp GPR:$rs1, GPR:$rs2)), bb:$imm12),
|
||||
(Inst GPR:$rs1, GPR:$rs2, simm13_lsb0:$imm12)>;
|
||||
|
||||
def : BccPat<seteq, BEQ>;
|
||||
def : BccPat<setne, BNE>;
|
||||
def : BccPat<setlt, BLT>;
|
||||
def : BccPat<setge, BGE>;
|
||||
def : BccPat<setult, BLTU>;
|
||||
def : BccPat<setuge, BGEU>;
|
||||
|
||||
class BccSwapPat<PatFrag CondOp, RVInst InstBcc>
|
||||
: Pat<(brcond (i32 (CondOp GPR:$rs1, GPR:$rs2)), bb:$imm12),
|
||||
(InstBcc GPR:$rs2, GPR:$rs1, bb:$imm12)>;
|
||||
|
||||
// Condition codes that don't have matching RISC-V branch instructions, but
|
||||
// are trivially supported by swapping the two input operands
|
||||
def : BccSwapPat<setgt, BLT>;
|
||||
def : BccSwapPat<setle, BGE>;
|
||||
def : BccSwapPat<setugt, BLTU>;
|
||||
def : BccSwapPat<setule, BGEU>;
|
||||
|
||||
// An extra pattern is needed for a brcond without a setcc (i.e. where the
|
||||
// condition was calculated elsewhere).
|
||||
def : Pat<(brcond GPR:$cond, bb:$imm12), (BNE GPR:$cond, X0, bb:$imm12)>;
|
||||
|
||||
let isBarrier = 1, isBranch = 1, isTerminator = 1 in
|
||||
def PseudoBR : Pseudo<(outs), (ins simm21_lsb0:$imm20), [(br bb:$imm20)]>,
|
||||
PseudoInstExpansion<(JAL X0, simm21_lsb0:$imm20)>;
|
||||
|
||||
let isBarrier = 1, isReturn = 1, isTerminator = 1 in
|
||||
def PseudoRET : Pseudo<(outs), (ins), [(RetFlag)]>,
|
||||
PseudoInstExpansion<(JALR X0, X1, 0)>;
|
||||
|
|
|
@ -71,6 +71,10 @@ bool llvm::LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO,
|
|||
case MachineOperand::MO_Immediate:
|
||||
MCOp = MCOperand::createImm(MO.getImm());
|
||||
break;
|
||||
case MachineOperand::MO_MachineBasicBlock:
|
||||
MCOp = MCOperand::createExpr(
|
||||
MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), AP.OutContext));
|
||||
break;
|
||||
case MachineOperand::MO_GlobalAddress:
|
||||
MCOp = lowerSymbolOperand(MO, AP.getSymbol(MO.getGlobal()), AP);
|
||||
break;
|
||||
|
|
|
@ -50,10 +50,39 @@ BitVector RISCVRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
|
|||
return Reserved;
|
||||
}
|
||||
|
||||
const uint32_t *RISCVRegisterInfo::getNoPreservedMask() const {
|
||||
return CSR_NoRegs_RegMask;
|
||||
}
|
||||
|
||||
void RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
|
||||
int SPAdj, unsigned FIOperandNum,
|
||||
RegScavenger *RS) const {
|
||||
report_fatal_error("Subroutines not supported yet");
|
||||
// TODO: this implementation is a temporary placeholder which does just
|
||||
// enough to allow other aspects of code generation to be tested
|
||||
|
||||
assert(SPAdj == 0 && "Unexpected non-zero SPAdj value");
|
||||
|
||||
MachineInstr &MI = *II;
|
||||
MachineFunction &MF = *MI.getParent()->getParent();
|
||||
const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
|
||||
DebugLoc DL = MI.getDebugLoc();
|
||||
|
||||
unsigned FrameReg = getFrameRegister(MF);
|
||||
int FrameIndex = MI.getOperand(FIOperandNum).getIndex();
|
||||
int Offset = TFI->getFrameIndexReference(MF, FrameIndex, FrameReg);
|
||||
Offset += MI.getOperand(FIOperandNum + 1).getImm();
|
||||
|
||||
assert(TFI->hasFP(MF) && "eliminateFrameIndex currently requires hasFP");
|
||||
|
||||
// Offsets must be directly encoded in a 12-bit immediate field
|
||||
if (!isInt<12>(Offset)) {
|
||||
report_fatal_error(
|
||||
"Frame offsets outside of the signed 12-bit range not supported");
|
||||
}
|
||||
|
||||
MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg, false);
|
||||
MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Offset);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned RISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
|
||||
|
|
|
@ -29,6 +29,8 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo {
|
|||
|
||||
BitVector getReservedRegs(const MachineFunction &MF) const override;
|
||||
|
||||
const uint32_t *getNoPreservedMask() const override;
|
||||
|
||||
void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj,
|
||||
unsigned FIOperandNum,
|
||||
RegScavenger *RS = nullptr) const override;
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
||||
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
|
||||
; RUN: | FileCheck -check-prefix=RV32I %s
|
||||
|
||||
define void @foo(i32 %a, i32 *%b, i1 %c) {
|
||||
; RV32I-LABEL: foo:
|
||||
; RV32I: # BB#0:
|
||||
; RV32I-NEXT: lw a3, 0(a1)
|
||||
; RV32I-NEXT: beq a3, a0, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_1
|
||||
; RV32I-NEXT: .LBB0_1: # %test2
|
||||
; RV32I-NEXT: lw a3, 0(a1)
|
||||
; RV32I-NEXT: bne a3, a0, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_2
|
||||
; RV32I-NEXT: .LBB0_2: # %test3
|
||||
; RV32I-NEXT: lw a3, 0(a1)
|
||||
; RV32I-NEXT: blt a3, a0, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_3
|
||||
; RV32I-NEXT: .LBB0_3: # %test4
|
||||
; RV32I-NEXT: lw a3, 0(a1)
|
||||
; RV32I-NEXT: bge a3, a0, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_4
|
||||
; RV32I-NEXT: .LBB0_4: # %test5
|
||||
; RV32I-NEXT: lw a3, 0(a1)
|
||||
; RV32I-NEXT: bltu a3, a0, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_5
|
||||
; RV32I-NEXT: .LBB0_5: # %test6
|
||||
; RV32I-NEXT: lw a3, 0(a1)
|
||||
; RV32I-NEXT: bgeu a3, a0, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_6
|
||||
; RV32I-NEXT: .LBB0_6: # %test7
|
||||
; RV32I-NEXT: lw a3, 0(a1)
|
||||
; RV32I-NEXT: blt a0, a3, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_7
|
||||
; RV32I-NEXT: .LBB0_7: # %test8
|
||||
; RV32I-NEXT: lw a3, 0(a1)
|
||||
; RV32I-NEXT: bge a0, a3, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_8
|
||||
; RV32I-NEXT: .LBB0_8: # %test9
|
||||
; RV32I-NEXT: lw a3, 0(a1)
|
||||
; RV32I-NEXT: bltu a0, a3, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_9
|
||||
; RV32I-NEXT: .LBB0_9: # %test10
|
||||
; RV32I-NEXT: lw a3, 0(a1)
|
||||
; RV32I-NEXT: bgeu a0, a3, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_10
|
||||
; RV32I-NEXT: .LBB0_10: # %test11
|
||||
; RV32I-NEXT: lw a0, 0(a1)
|
||||
; RV32I-NEXT: andi a0, a2, 1
|
||||
; RV32I-NEXT: bne a0, zero, .LBB0_12
|
||||
; RV32I-NEXT: jal zero, .LBB0_11
|
||||
; RV32I-NEXT: .LBB0_11: # %test12
|
||||
; RV32I-NEXT: lw a0, 0(a1)
|
||||
; RV32I-NEXT: .LBB0_12: # %end
|
||||
; RV32I-NEXT: jalr zero, ra, 0
|
||||
|
||||
%val1 = load volatile i32, i32* %b
|
||||
%tst1 = icmp eq i32 %val1, %a
|
||||
br i1 %tst1, label %end, label %test2
|
||||
|
||||
test2:
|
||||
%val2 = load volatile i32, i32* %b
|
||||
%tst2 = icmp ne i32 %val2, %a
|
||||
br i1 %tst2, label %end, label %test3
|
||||
|
||||
test3:
|
||||
%val3 = load volatile i32, i32* %b
|
||||
%tst3 = icmp slt i32 %val3, %a
|
||||
br i1 %tst3, label %end, label %test4
|
||||
|
||||
test4:
|
||||
%val4 = load volatile i32, i32* %b
|
||||
%tst4 = icmp sge i32 %val4, %a
|
||||
br i1 %tst4, label %end, label %test5
|
||||
|
||||
test5:
|
||||
%val5 = load volatile i32, i32* %b
|
||||
%tst5 = icmp ult i32 %val5, %a
|
||||
br i1 %tst5, label %end, label %test6
|
||||
|
||||
test6:
|
||||
%val6 = load volatile i32, i32* %b
|
||||
%tst6 = icmp uge i32 %val6, %a
|
||||
br i1 %tst6, label %end, label %test7
|
||||
|
||||
; Check for condition codes that don't have a matching instruction
|
||||
|
||||
test7:
|
||||
%val7 = load volatile i32, i32* %b
|
||||
%tst7 = icmp sgt i32 %val7, %a
|
||||
br i1 %tst7, label %end, label %test8
|
||||
|
||||
test8:
|
||||
%val8 = load volatile i32, i32* %b
|
||||
%tst8 = icmp sle i32 %val8, %a
|
||||
br i1 %tst8, label %end, label %test9
|
||||
|
||||
test9:
|
||||
%val9 = load volatile i32, i32* %b
|
||||
%tst9 = icmp ugt i32 %val9, %a
|
||||
br i1 %tst9, label %end, label %test10
|
||||
|
||||
test10:
|
||||
%val10 = load volatile i32, i32* %b
|
||||
%tst10 = icmp ule i32 %val10, %a
|
||||
br i1 %tst10, label %end, label %test11
|
||||
|
||||
; Check the case of a branch where the condition was generated in another
|
||||
; function
|
||||
|
||||
test11:
|
||||
%val11 = load volatile i32, i32* %b
|
||||
br i1 %c, label %end, label %test12
|
||||
|
||||
test12:
|
||||
%val12 = load volatile i32, i32* %b
|
||||
br label %end
|
||||
|
||||
end:
|
||||
ret void
|
||||
}
|
Loading…
Reference in New Issue