1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
//===- 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;
}
|