| //===- RISCVVMV0Elimination.cpp - VMV0 Elimination -----------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===---------------------------------------------------------------------===// | |
| // | |
| // Mask operands in vector pseudos have to be in v0. We select them as a virtual | |
| // register in the singleton vmv0 register class instead of copying them to $v0 | |
| // straight away, to make optimizing masks easier. | |
| // | |
| // However register coalescing may end up coleascing copies into vmv0, resulting | |
| // in instructions with multiple uses of vmv0 that the register allocator can't | |
| // allocate: | |
| // | |
| // %x:vrnov0 = PseudoVADD_VV_M1_MASK %0:vrnov0, %1:vr, %2:vmv0, %3:vmv0, ... | |
| // | |
| // To avoid this, this pass replaces any uses* of vmv0 with copies to $v0 before | |
| // register coalescing and allocation: | |
| // | |
| // %x:vrnov0 = PseudoVADD_VV_M1_MASK %0:vrnov0, %1:vr, %2:vr, %3:vmv0, ... | |
| // -> | |
| // $v0 = COPY %3:vr | |
| // %x:vrnov0 = PseudoVADD_VV_M1_MASK %0:vrnov0, %1:vr, %2:vr, $0, ... | |
| // | |
| // * The only uses of vmv0 left behind are when used for inline asm with the vm | |
| // constraint. | |
| // | |
| //===---------------------------------------------------------------------===// | |
| #include "RISCV.h" | |
| #include "RISCVSubtarget.h" | |
| #ifndef NDEBUG | |
| #include "llvm/ADT/PostOrderIterator.h" | |
| #endif | |
| #include "llvm/CodeGen/MachineFunctionPass.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "riscv-vmv0-elimination" | |
| namespace { | |
| class RISCVVMV0Elimination : public MachineFunctionPass { | |
| public: | |
| static char ID; | |
| RISCVVMV0Elimination() : MachineFunctionPass(ID) {} | |
| bool runOnMachineFunction(MachineFunction &MF) override; | |
| void getAnalysisUsage(AnalysisUsage &AU) const override { | |
| AU.setPreservesCFG(); | |
| MachineFunctionPass::getAnalysisUsage(AU); | |
| } | |
| MachineFunctionProperties getRequiredProperties() const override { | |
| // TODO: We could move this closer to regalloc, out of SSA, which would | |
| // allow scheduling past mask operands. We would need to preserve live | |
| // intervals. | |
| return MachineFunctionProperties().setIsSSA(); | |
| } | |
| }; | |
| } // namespace | |
| char RISCVVMV0Elimination::ID = 0; | |
| INITIALIZE_PASS(RISCVVMV0Elimination, DEBUG_TYPE, "RISC-V VMV0 Elimination", | |
| false, false) | |
| FunctionPass *llvm::createRISCVVMV0EliminationPass() { | |
| return new RISCVVMV0Elimination(); | |
| } | |
| static bool isVMV0(const MCOperandInfo &MCOI) { | |
| return MCOI.RegClass == RISCV::VMV0RegClassID; | |
| } | |
| bool RISCVVMV0Elimination::runOnMachineFunction(MachineFunction &MF) { | |
| // Skip if the vector extension is not enabled. | |
| const RISCVSubtarget *ST = &MF.getSubtarget<RISCVSubtarget>(); | |
| if (!ST->hasVInstructions()) | |
| return false; | |
| MachineRegisterInfo &MRI = MF.getRegInfo(); | |
| const TargetInstrInfo *TII = ST->getInstrInfo(); | |
| #ifndef NDEBUG | |
| // Assert that we won't clobber any existing reads of v0 where we need to | |
| // insert copies. | |
| const TargetRegisterInfo *TRI = MRI.getTargetRegisterInfo(); | |
| ReversePostOrderTraversal<MachineBasicBlock *> RPOT(&*MF.begin()); | |
| for (MachineBasicBlock *MBB : RPOT) { | |
| bool V0Clobbered = false; | |
| for (MachineInstr &MI : *MBB) { | |
| assert(!(MI.readsRegister(RISCV::V0, TRI) && V0Clobbered) && | |
| "Inserting a copy to v0 would clobber a read"); | |
| if (MI.modifiesRegister(RISCV::V0, TRI)) | |
| V0Clobbered = false; | |
| if (any_of(MI.getDesc().operands(), isVMV0)) | |
| V0Clobbered = true; | |
| } | |
| assert(!(V0Clobbered && | |
| any_of(MBB->successors(), | |
| [](auto *Succ) { return Succ->isLiveIn(RISCV::V0); })) && | |
| "Clobbered a v0 used in a successor"); | |
| } | |
| #endif | |
| bool MadeChange = false; | |
| SmallVector<MachineInstr *> DeadCopies; | |
| // For any instruction with a vmv0 operand, replace it with a copy to v0. | |
| for (MachineBasicBlock &MBB : MF) { | |
| for (MachineInstr &MI : MBB) { | |
| assert(count_if(MI.getDesc().operands(), isVMV0) < 2 && | |
| "Expected only one or zero vmv0 operands"); | |
| for (auto [OpNo, MCOI] : enumerate(MI.getDesc().operands())) { | |
| if (isVMV0(MCOI)) { | |
| MachineOperand &MO = MI.getOperand(OpNo); | |
| Register Src = MO.getReg(); | |
| assert(MO.isUse() && MO.getSubReg() == RISCV::NoSubRegister && | |
| Src.isVirtual() && "vmv0 use in unexpected form"); | |
| // Peek through a single copy to match what isel does. | |
| if (MachineInstr *SrcMI = MRI.getVRegDef(Src); | |
| SrcMI->isCopy() && SrcMI->getOperand(1).getReg().isVirtual() && | |
| SrcMI->getOperand(1).getSubReg() == RISCV::NoSubRegister) { | |
| // Delete any dead copys to vmv0 to avoid allocating them. | |
| if (MRI.hasOneNonDBGUse(Src)) | |
| DeadCopies.push_back(SrcMI); | |
| Src = SrcMI->getOperand(1).getReg(); | |
| } | |
| BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(RISCV::COPY), RISCV::V0) | |
| .addReg(Src); | |
| MO.setReg(RISCV::V0); | |
| MadeChange = true; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| for (MachineInstr *MI : DeadCopies) | |
| MI->eraseFromParent(); | |
| if (!MadeChange) | |
| return false; | |
| // Now that any constraints requiring vmv0 are gone, eliminate any uses of | |
| // vmv0 by recomputing the reg class. | |
| // The only remaining uses should be around inline asm. | |
| for (MachineBasicBlock &MBB : MF) { | |
| for (MachineInstr &MI : MBB) { | |
| for (MachineOperand &MO : MI.uses()) { | |
| if (MO.isReg() && MO.getReg().isVirtual() && | |
| MRI.getRegClass(MO.getReg()) == &RISCV::VMV0RegClass) { | |
| MRI.recomputeRegClass(MO.getReg()); | |
| assert((MRI.getRegClass(MO.getReg()) != &RISCV::VMV0RegClass || | |
| MI.isInlineAsm() || | |
| MRI.getVRegDef(MO.getReg())->isInlineAsm()) && | |
| "Non-inline-asm use of vmv0 left behind"); | |
| } | |
| } | |
| } | |
| } | |
| return true; | |
| } |