xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/RISCV/RISCVInsertReadWriteCSR.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
106c3fb27SDimitry Andric //===-- RISCVInsertReadWriteCSR.cpp - Insert Read/Write of RISC-V CSR -----===//
206c3fb27SDimitry Andric //
306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606c3fb27SDimitry Andric //
706c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
806c3fb27SDimitry Andric // This file implements the machine function pass to insert read/write of CSR-s
906c3fb27SDimitry Andric // of the RISC-V instructions.
1006c3fb27SDimitry Andric //
115f757f3fSDimitry Andric // Currently the pass implements:
125f757f3fSDimitry Andric // -Writing and saving frm before an RVV floating-point instruction with a
135f757f3fSDimitry Andric //  static rounding mode and restores the value after.
1406c3fb27SDimitry Andric //
1506c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
1606c3fb27SDimitry Andric 
1706c3fb27SDimitry Andric #include "MCTargetDesc/RISCVBaseInfo.h"
1806c3fb27SDimitry Andric #include "RISCV.h"
1906c3fb27SDimitry Andric #include "RISCVSubtarget.h"
2006c3fb27SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
2106c3fb27SDimitry Andric using namespace llvm;
2206c3fb27SDimitry Andric 
2306c3fb27SDimitry Andric #define DEBUG_TYPE "riscv-insert-read-write-csr"
2406c3fb27SDimitry Andric #define RISCV_INSERT_READ_WRITE_CSR_NAME "RISC-V Insert Read/Write CSR Pass"
2506c3fb27SDimitry Andric 
26*0fca6ea1SDimitry Andric static cl::opt<bool>
27*0fca6ea1SDimitry Andric     DisableFRMInsertOpt("riscv-disable-frm-insert-opt", cl::init(false),
28*0fca6ea1SDimitry Andric                         cl::Hidden,
29*0fca6ea1SDimitry Andric                         cl::desc("Disable optimized frm insertion."));
30*0fca6ea1SDimitry Andric 
3106c3fb27SDimitry Andric namespace {
3206c3fb27SDimitry Andric 
3306c3fb27SDimitry Andric class RISCVInsertReadWriteCSR : public MachineFunctionPass {
3406c3fb27SDimitry Andric   const TargetInstrInfo *TII;
3506c3fb27SDimitry Andric 
3606c3fb27SDimitry Andric public:
3706c3fb27SDimitry Andric   static char ID;
3806c3fb27SDimitry Andric 
395f757f3fSDimitry Andric   RISCVInsertReadWriteCSR() : MachineFunctionPass(ID) {}
4006c3fb27SDimitry Andric 
4106c3fb27SDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
4206c3fb27SDimitry Andric 
4306c3fb27SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
4406c3fb27SDimitry Andric     AU.setPreservesCFG();
4506c3fb27SDimitry Andric     MachineFunctionPass::getAnalysisUsage(AU);
4606c3fb27SDimitry Andric   }
4706c3fb27SDimitry Andric 
4806c3fb27SDimitry Andric   StringRef getPassName() const override {
4906c3fb27SDimitry Andric     return RISCV_INSERT_READ_WRITE_CSR_NAME;
5006c3fb27SDimitry Andric   }
5106c3fb27SDimitry Andric 
5206c3fb27SDimitry Andric private:
5306c3fb27SDimitry Andric   bool emitWriteRoundingMode(MachineBasicBlock &MBB);
54*0fca6ea1SDimitry Andric   bool emitWriteRoundingModeOpt(MachineBasicBlock &MBB);
5506c3fb27SDimitry Andric };
5606c3fb27SDimitry Andric 
5706c3fb27SDimitry Andric } // end anonymous namespace
5806c3fb27SDimitry Andric 
5906c3fb27SDimitry Andric char RISCVInsertReadWriteCSR::ID = 0;
6006c3fb27SDimitry Andric 
6106c3fb27SDimitry Andric INITIALIZE_PASS(RISCVInsertReadWriteCSR, DEBUG_TYPE,
6206c3fb27SDimitry Andric                 RISCV_INSERT_READ_WRITE_CSR_NAME, false, false)
6306c3fb27SDimitry Andric 
64*0fca6ea1SDimitry Andric // TODO: Use more accurate rounding mode at the start of MBB.
65*0fca6ea1SDimitry Andric bool RISCVInsertReadWriteCSR::emitWriteRoundingModeOpt(MachineBasicBlock &MBB) {
66*0fca6ea1SDimitry Andric   bool Changed = false;
67*0fca6ea1SDimitry Andric   MachineInstr *LastFRMChanger = nullptr;
68*0fca6ea1SDimitry Andric   unsigned CurrentRM = RISCVFPRndMode::DYN;
69*0fca6ea1SDimitry Andric   Register SavedFRM;
70*0fca6ea1SDimitry Andric 
71*0fca6ea1SDimitry Andric   for (MachineInstr &MI : MBB) {
72*0fca6ea1SDimitry Andric     if (MI.getOpcode() == RISCV::SwapFRMImm ||
73*0fca6ea1SDimitry Andric         MI.getOpcode() == RISCV::WriteFRMImm) {
74*0fca6ea1SDimitry Andric       CurrentRM = MI.getOperand(0).getImm();
75*0fca6ea1SDimitry Andric       SavedFRM = Register();
76*0fca6ea1SDimitry Andric       continue;
77*0fca6ea1SDimitry Andric     }
78*0fca6ea1SDimitry Andric 
79*0fca6ea1SDimitry Andric     if (MI.getOpcode() == RISCV::WriteFRM) {
80*0fca6ea1SDimitry Andric       CurrentRM = RISCVFPRndMode::DYN;
81*0fca6ea1SDimitry Andric       SavedFRM = Register();
82*0fca6ea1SDimitry Andric       continue;
83*0fca6ea1SDimitry Andric     }
84*0fca6ea1SDimitry Andric 
85*0fca6ea1SDimitry Andric     if (MI.isCall() || MI.isInlineAsm() ||
86*0fca6ea1SDimitry Andric         MI.readsRegister(RISCV::FRM, /*TRI=*/nullptr)) {
87*0fca6ea1SDimitry Andric       // Restore FRM before unknown operations.
88*0fca6ea1SDimitry Andric       if (SavedFRM.isValid())
89*0fca6ea1SDimitry Andric         BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(RISCV::WriteFRM))
90*0fca6ea1SDimitry Andric             .addReg(SavedFRM);
91*0fca6ea1SDimitry Andric       CurrentRM = RISCVFPRndMode::DYN;
92*0fca6ea1SDimitry Andric       SavedFRM = Register();
93*0fca6ea1SDimitry Andric       continue;
94*0fca6ea1SDimitry Andric     }
95*0fca6ea1SDimitry Andric 
96*0fca6ea1SDimitry Andric     assert(!MI.modifiesRegister(RISCV::FRM, /*TRI=*/nullptr) &&
97*0fca6ea1SDimitry Andric            "Expected that MI could not modify FRM.");
98*0fca6ea1SDimitry Andric 
99*0fca6ea1SDimitry Andric     int FRMIdx = RISCVII::getFRMOpNum(MI.getDesc());
100*0fca6ea1SDimitry Andric     if (FRMIdx < 0)
101*0fca6ea1SDimitry Andric       continue;
102*0fca6ea1SDimitry Andric     unsigned InstrRM = MI.getOperand(FRMIdx).getImm();
103*0fca6ea1SDimitry Andric 
104*0fca6ea1SDimitry Andric     LastFRMChanger = &MI;
105*0fca6ea1SDimitry Andric 
106*0fca6ea1SDimitry Andric     // Make MI implicit use FRM.
107*0fca6ea1SDimitry Andric     MI.addOperand(MachineOperand::CreateReg(RISCV::FRM, /*IsDef*/ false,
108*0fca6ea1SDimitry Andric                                             /*IsImp*/ true));
109*0fca6ea1SDimitry Andric     Changed = true;
110*0fca6ea1SDimitry Andric 
111*0fca6ea1SDimitry Andric     // Skip if MI uses same rounding mode as FRM.
112*0fca6ea1SDimitry Andric     if (InstrRM == CurrentRM)
113*0fca6ea1SDimitry Andric       continue;
114*0fca6ea1SDimitry Andric 
115*0fca6ea1SDimitry Andric     if (!SavedFRM.isValid()) {
116*0fca6ea1SDimitry Andric       // Save current FRM value to SavedFRM.
117*0fca6ea1SDimitry Andric       MachineRegisterInfo *MRI = &MBB.getParent()->getRegInfo();
118*0fca6ea1SDimitry Andric       SavedFRM = MRI->createVirtualRegister(&RISCV::GPRRegClass);
119*0fca6ea1SDimitry Andric       BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(RISCV::SwapFRMImm), SavedFRM)
120*0fca6ea1SDimitry Andric           .addImm(InstrRM);
121*0fca6ea1SDimitry Andric     } else {
122*0fca6ea1SDimitry Andric       // Don't need to save current FRM when SavedFRM having value.
123*0fca6ea1SDimitry Andric       BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(RISCV::WriteFRMImm))
124*0fca6ea1SDimitry Andric           .addImm(InstrRM);
125*0fca6ea1SDimitry Andric     }
126*0fca6ea1SDimitry Andric     CurrentRM = InstrRM;
127*0fca6ea1SDimitry Andric   }
128*0fca6ea1SDimitry Andric 
129*0fca6ea1SDimitry Andric   // Restore FRM if needed.
130*0fca6ea1SDimitry Andric   if (SavedFRM.isValid()) {
131*0fca6ea1SDimitry Andric     assert(LastFRMChanger && "Expected valid pointer.");
132*0fca6ea1SDimitry Andric     MachineInstrBuilder MIB =
133*0fca6ea1SDimitry Andric         BuildMI(*MBB.getParent(), {}, TII->get(RISCV::WriteFRM))
134*0fca6ea1SDimitry Andric             .addReg(SavedFRM);
135*0fca6ea1SDimitry Andric     MBB.insertAfter(LastFRMChanger, MIB);
136*0fca6ea1SDimitry Andric   }
137*0fca6ea1SDimitry Andric 
138*0fca6ea1SDimitry Andric   return Changed;
139*0fca6ea1SDimitry Andric }
140*0fca6ea1SDimitry Andric 
1415f757f3fSDimitry Andric // This function also swaps frm and restores it when encountering an RVV
1425f757f3fSDimitry Andric // floating point instruction with a static rounding mode.
14306c3fb27SDimitry Andric bool RISCVInsertReadWriteCSR::emitWriteRoundingMode(MachineBasicBlock &MBB) {
14406c3fb27SDimitry Andric   bool Changed = false;
14506c3fb27SDimitry Andric   for (MachineInstr &MI : MBB) {
1465f757f3fSDimitry Andric     int FRMIdx = RISCVII::getFRMOpNum(MI.getDesc());
1475f757f3fSDimitry Andric     if (FRMIdx < 0)
1485f757f3fSDimitry Andric       continue;
14906c3fb27SDimitry Andric 
1505f757f3fSDimitry Andric     unsigned FRMImm = MI.getOperand(FRMIdx).getImm();
15106c3fb27SDimitry Andric 
15206c3fb27SDimitry Andric     // The value is a hint to this pass to not alter the frm value.
15306c3fb27SDimitry Andric     if (FRMImm == RISCVFPRndMode::DYN)
15406c3fb27SDimitry Andric       continue;
15506c3fb27SDimitry Andric 
15606c3fb27SDimitry Andric     Changed = true;
15706c3fb27SDimitry Andric 
15806c3fb27SDimitry Andric     // Save
15906c3fb27SDimitry Andric     MachineRegisterInfo *MRI = &MBB.getParent()->getRegInfo();
16006c3fb27SDimitry Andric     Register SavedFRM = MRI->createVirtualRegister(&RISCV::GPRRegClass);
16106c3fb27SDimitry Andric     BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(RISCV::SwapFRMImm),
16206c3fb27SDimitry Andric             SavedFRM)
16306c3fb27SDimitry Andric         .addImm(FRMImm);
16406c3fb27SDimitry Andric     MI.addOperand(MachineOperand::CreateReg(RISCV::FRM, /*IsDef*/ false,
16506c3fb27SDimitry Andric                                             /*IsImp*/ true));
16606c3fb27SDimitry Andric     // Restore
16706c3fb27SDimitry Andric     MachineInstrBuilder MIB =
16806c3fb27SDimitry Andric         BuildMI(*MBB.getParent(), {}, TII->get(RISCV::WriteFRM))
16906c3fb27SDimitry Andric             .addReg(SavedFRM);
17006c3fb27SDimitry Andric     MBB.insertAfter(MI, MIB);
17106c3fb27SDimitry Andric   }
17206c3fb27SDimitry Andric   return Changed;
17306c3fb27SDimitry Andric }
17406c3fb27SDimitry Andric 
17506c3fb27SDimitry Andric bool RISCVInsertReadWriteCSR::runOnMachineFunction(MachineFunction &MF) {
17606c3fb27SDimitry Andric   // Skip if the vector extension is not enabled.
17706c3fb27SDimitry Andric   const RISCVSubtarget &ST = MF.getSubtarget<RISCVSubtarget>();
17806c3fb27SDimitry Andric   if (!ST.hasVInstructions())
17906c3fb27SDimitry Andric     return false;
18006c3fb27SDimitry Andric 
18106c3fb27SDimitry Andric   TII = ST.getInstrInfo();
18206c3fb27SDimitry Andric 
18306c3fb27SDimitry Andric   bool Changed = false;
18406c3fb27SDimitry Andric 
185*0fca6ea1SDimitry Andric   for (MachineBasicBlock &MBB : MF) {
186*0fca6ea1SDimitry Andric     if (DisableFRMInsertOpt)
18706c3fb27SDimitry Andric       Changed |= emitWriteRoundingMode(MBB);
188*0fca6ea1SDimitry Andric     else
189*0fca6ea1SDimitry Andric       Changed |= emitWriteRoundingModeOpt(MBB);
190*0fca6ea1SDimitry Andric   }
19106c3fb27SDimitry Andric 
19206c3fb27SDimitry Andric   return Changed;
19306c3fb27SDimitry Andric }
19406c3fb27SDimitry Andric 
19506c3fb27SDimitry Andric FunctionPass *llvm::createRISCVInsertReadWriteCSRPass() {
19606c3fb27SDimitry Andric   return new RISCVInsertReadWriteCSR();
19706c3fb27SDimitry Andric }
198