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
RISCVInsertReadWriteCSR()395f757f3fSDimitry Andric RISCVInsertReadWriteCSR() : MachineFunctionPass(ID) {}
4006c3fb27SDimitry Andric
4106c3fb27SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override;
4206c3fb27SDimitry Andric
getAnalysisUsage(AnalysisUsage & AU) const4306c3fb27SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override {
4406c3fb27SDimitry Andric AU.setPreservesCFG();
4506c3fb27SDimitry Andric MachineFunctionPass::getAnalysisUsage(AU);
4606c3fb27SDimitry Andric }
4706c3fb27SDimitry Andric
getPassName() const4806c3fb27SDimitry 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
INITIALIZE_PASS(RISCVInsertReadWriteCSR,DEBUG_TYPE,RISCV_INSERT_READ_WRITE_CSR_NAME,false,false)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.
emitWriteRoundingMode(MachineBasicBlock & MBB)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
runOnMachineFunction(MachineFunction & MF)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
createRISCVInsertReadWriteCSRPass()19506c3fb27SDimitry Andric FunctionPass *llvm::createRISCVInsertReadWriteCSRPass() {
19606c3fb27SDimitry Andric return new RISCVInsertReadWriteCSR();
19706c3fb27SDimitry Andric }
198