From 74913e1c70097babd15de705b6c4bd3dbcfebfa6 Mon Sep 17 00:00:00 2001 From: Alex Bradbury Date: Wed, 8 Nov 2017 13:31:40 +0000 Subject: [PATCH] [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 class rather than Operand 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 --- llvm/lib/Target/RISCV/RISCVCallingConv.td | 3 + llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 1 + llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 33 ++++++ llvm/lib/Target/RISCV/RISCVInstrInfo.h | 12 +- llvm/lib/Target/RISCV/RISCVInstrInfo.td | 36 +++++- llvm/lib/Target/RISCV/RISCVMCInstLower.cpp | 4 + llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp | 31 ++++- llvm/lib/Target/RISCV/RISCVRegisterInfo.h | 2 + llvm/test/CodeGen/RISCV/branch.ll | 121 ++++++++++++++++++++ 9 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 llvm/test/CodeGen/RISCV/branch.ll diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.td b/llvm/lib/Target/RISCV/RISCVCallingConv.td index e0c25e32e012..0b7a523424c5 100644 --- a/llvm/lib/Target/RISCV/RISCVCallingConv.td +++ b/llvm/lib/Target/RISCV/RISCVCallingConv.td @@ -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)>; diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 49bc6a29e45b..9f19cda89101 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -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). diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index ebb7174002d1..63178bb668d1 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -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"); +} diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h index 8f0c2c4f7eb3..05c8378445cf 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h @@ -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 diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index 7dfdd5b044de..2c21ad31ae53 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -67,7 +67,7 @@ def uimm12 : Operand { } // A 13-bit signed immediate where the least significant bit is zero. -def simm13_lsb0 : Operand { +def simm13_lsb0 : Operand { let ParserMatchClass = SImmAsmOperand<13, "Lsb0">; let EncoderMethod = "getImmOpValueAsr1"; let DecoderMethod = "decodeSImmOperandAndLsl1<13>"; @@ -80,7 +80,7 @@ def uimm20 : Operand { } // A 21-bit signed immediate where the least significant bit is zero. -def simm21_lsb0 : Operand { +def simm21_lsb0 : Operand { let ParserMatchClass = SImmAsmOperand<21, "Lsb0">; let EncoderMethod = "getImmOpValueAsr1"; let DecoderMethod = "decodeSImmOperandAndLsl1<21>"; @@ -308,6 +308,38 @@ def : PatGprSimm12; /// Branches and jumps +// Match `(brcond (CondOp ..), ..)` and lower to the appropriate RISC-V branch +// instruction. +class BccPat + : Pat<(brcond (i32 (CondOp GPR:$rs1, GPR:$rs2)), bb:$imm12), + (Inst GPR:$rs1, GPR:$rs2, simm13_lsb0:$imm12)>; + +def : BccPat; +def : BccPat; +def : BccPat; +def : BccPat; +def : BccPat; +def : BccPat; + +class BccSwapPat + : 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; +def : BccSwapPat; +def : BccSwapPat; +def : BccSwapPat; + +// 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)>; diff --git a/llvm/lib/Target/RISCV/RISCVMCInstLower.cpp b/llvm/lib/Target/RISCV/RISCVMCInstLower.cpp index 4cc8cc8c0c2a..a354a34fb43e 100644 --- a/llvm/lib/Target/RISCV/RISCVMCInstLower.cpp +++ b/llvm/lib/Target/RISCV/RISCVMCInstLower.cpp @@ -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; diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp index 9447f9e7ec36..5d64de23ba25 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -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 { diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h index 94af9f44ecde..a9b6f0717dbc 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h @@ -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; diff --git a/llvm/test/CodeGen/RISCV/branch.ll b/llvm/test/CodeGen/RISCV/branch.ll new file mode 100644 index 000000000000..194083b07c71 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/branch.ll @@ -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 +}