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