|
| 1 | +//===- RISCVRVVInitUndef.cpp - Initialize undef vector value to pseudo ----===// |
| 2 | +// |
| 3 | +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | +// See https://llvm.org/LICENSE.txt for license information. |
| 5 | +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | +// |
| 7 | +//===----------------------------------------------------------------------===// |
| 8 | +// |
| 9 | +// This file implements a function pass that initializes undef vector value to |
| 10 | +// temporary pseudo instruction and remove it in expandpseudo pass to prevent |
| 11 | +// register allocation resulting in a constraint violated result for vector |
| 12 | +// instruction. |
| 13 | +// |
| 14 | +// RISC-V vector instruction has register overlapping constraint for certain |
| 15 | +// instructions, and will cause illegal instruction trap if violated, we use |
| 16 | +// early clobber to model this constraint, but it can't prevent register |
| 17 | +// allocator allocated same or overlapped if the input register is undef value, |
| 18 | +// so convert IMPLICIT_DEF to temporary pseudo instruction and remove it later |
| 19 | +// could prevent that happen, it's not best way to resolve this, and it might |
| 20 | +// change the order of program or increase the register pressure, so ideally we |
| 21 | +// should model the constraint right, but before we model the constraint right, |
| 22 | +// it's the only way to prevent that happen. |
| 23 | +// |
| 24 | +// When we enable the subregister liveness option, it will also trigger same |
| 25 | +// issue due to the partial of register is undef. If we pseudoinit the whole |
| 26 | +// register, then it will generate redundant COPY instruction. Currently, it |
| 27 | +// will generate INSERT_SUBREG to make sure the whole register is occupied |
| 28 | +// when program encounter operation that has early-clobber constraint. |
| 29 | +// |
| 30 | +// |
| 31 | +// See also: https://github.com/llvm/llvm-project/issues/50157 |
| 32 | +// |
| 33 | +//===----------------------------------------------------------------------===// |
| 34 | + |
| 35 | +#include "RISCV.h" |
| 36 | +#include "RISCVSubtarget.h" |
| 37 | +#include "llvm/CodeGen/DetectDeadLanes.h" |
| 38 | +#include "llvm/CodeGen/MachineFunctionPass.h" |
| 39 | +using namespace llvm; |
| 40 | + |
| 41 | +#define DEBUG_TYPE "riscv-init-undef" |
| 42 | +#define RISCV_INIT_UNDEF_NAME "RISCV init undef pass" |
| 43 | + |
| 44 | +namespace { |
| 45 | + |
| 46 | +class RISCVInitUndef : public MachineFunctionPass { |
| 47 | + const TargetInstrInfo *TII; |
| 48 | + MachineRegisterInfo *MRI; |
| 49 | + const RISCVSubtarget *ST; |
| 50 | + const TargetRegisterInfo *TRI; |
| 51 | + |
| 52 | +public: |
| 53 | + static char ID; |
| 54 | + |
| 55 | + RISCVInitUndef() : MachineFunctionPass(ID) { |
| 56 | + initializeRISCVInitUndefPass(*PassRegistry::getPassRegistry()); |
| 57 | + } |
| 58 | + bool runOnMachineFunction(MachineFunction &MF) override; |
| 59 | + |
| 60 | + void getAnalysisUsage(AnalysisUsage &AU) const override { |
| 61 | + AU.setPreservesCFG(); |
| 62 | + MachineFunctionPass::getAnalysisUsage(AU); |
| 63 | + } |
| 64 | + |
| 65 | + StringRef getPassName() const override { return RISCV_INIT_UNDEF_NAME; } |
| 66 | + |
| 67 | +private: |
| 68 | + bool processBasicBlock(MachineFunction &MF, MachineBasicBlock &MBB, |
| 69 | + const DeadLaneDetector &DLD); |
| 70 | + bool handleImplicitDef(MachineBasicBlock &MBB, |
| 71 | + MachineBasicBlock::iterator &Inst); |
| 72 | + bool isVectorRegClass(const Register R); |
| 73 | + const TargetRegisterClass * |
| 74 | + getVRLargestSuperClass(const TargetRegisterClass *RC) const; |
| 75 | + bool handleSubReg(MachineFunction &MF, MachineInstr &MI, |
| 76 | + const DeadLaneDetector &DLD); |
| 77 | +}; |
| 78 | + |
| 79 | +} // end anonymous namespace |
| 80 | + |
| 81 | +char RISCVInitUndef::ID = 0; |
| 82 | +INITIALIZE_PASS(RISCVInitUndef, DEBUG_TYPE, RISCV_INIT_UNDEF_NAME, false, false) |
| 83 | +char &llvm::RISCVInitUndefID = RISCVInitUndef::ID; |
| 84 | + |
| 85 | +const TargetRegisterClass * |
| 86 | +RISCVInitUndef::getVRLargestSuperClass(const TargetRegisterClass *RC) const { |
| 87 | + if (RISCV::VRM8RegClass.hasSubClassEq(RC)) |
| 88 | + return &RISCV::VRM8RegClass; |
| 89 | + if (RISCV::VRM4RegClass.hasSubClassEq(RC)) |
| 90 | + return &RISCV::VRM4RegClass; |
| 91 | + if (RISCV::VRM2RegClass.hasSubClassEq(RC)) |
| 92 | + return &RISCV::VRM2RegClass; |
| 93 | + if (RISCV::VRRegClass.hasSubClassEq(RC)) |
| 94 | + return &RISCV::VRRegClass; |
| 95 | + return RC; |
| 96 | +} |
| 97 | + |
| 98 | +bool RISCVInitUndef::isVectorRegClass(const Register R) { |
| 99 | + const TargetRegisterClass *RC = MRI->getRegClass(R); |
| 100 | + return RISCV::VRRegClass.hasSubClassEq(RC) || |
| 101 | + RISCV::VRM2RegClass.hasSubClassEq(RC) || |
| 102 | + RISCV::VRM4RegClass.hasSubClassEq(RC) || |
| 103 | + RISCV::VRM8RegClass.hasSubClassEq(RC); |
| 104 | +} |
| 105 | + |
| 106 | +static unsigned getUndefInitOpcode(unsigned RegClassID) { |
| 107 | + switch (RegClassID) { |
| 108 | + case RISCV::VRRegClassID: |
| 109 | + return RISCV::PseudoRVVInitUndefM1; |
| 110 | + case RISCV::VRM2RegClassID: |
| 111 | + return RISCV::PseudoRVVInitUndefM2; |
| 112 | + case RISCV::VRM4RegClassID: |
| 113 | + return RISCV::PseudoRVVInitUndefM4; |
| 114 | + case RISCV::VRM8RegClassID: |
| 115 | + return RISCV::PseudoRVVInitUndefM8; |
| 116 | + default: |
| 117 | + llvm_unreachable("Unexpected register class."); |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +bool RISCVInitUndef::handleImplicitDef(MachineBasicBlock &MBB, |
| 122 | + MachineBasicBlock::iterator &Inst) { |
| 123 | + const TargetRegisterInfo &TRI = |
| 124 | + *MBB.getParent()->getSubtarget().getRegisterInfo(); |
| 125 | + |
| 126 | + assert(Inst->getOpcode() == TargetOpcode::IMPLICIT_DEF); |
| 127 | + |
| 128 | + Register Reg = Inst->getOperand(0).getReg(); |
| 129 | + if (!Reg.isVirtual()) |
| 130 | + return false; |
| 131 | + |
| 132 | + bool NeedPseudoInit = false; |
| 133 | + SmallVector<MachineOperand *, 1> UseMOs; |
| 134 | + for (MachineOperand &MO : MRI->use_nodbg_operands(Reg)) { |
| 135 | + MachineInstr *UserMI = MO.getParent(); |
| 136 | + |
| 137 | + bool HasEarlyClobber = false; |
| 138 | + bool TiedToDef = false; |
| 139 | + for (MachineOperand &UserMO : UserMI->operands()) { |
| 140 | + if (!UserMO.isReg()) |
| 141 | + continue; |
| 142 | + if (UserMO.isEarlyClobber()) |
| 143 | + HasEarlyClobber = true; |
| 144 | + if (UserMO.isUse() && UserMO.isTied() && |
| 145 | + TRI.regsOverlap(UserMO.getReg(), Reg)) |
| 146 | + TiedToDef = true; |
| 147 | + } |
| 148 | + if (HasEarlyClobber && !TiedToDef) { |
| 149 | + NeedPseudoInit = true; |
| 150 | + UseMOs.push_back(&MO); |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + if (!NeedPseudoInit) |
| 155 | + return false; |
| 156 | + |
| 157 | + LLVM_DEBUG( |
| 158 | + dbgs() << "Emitting PseudoRVVInitUndef for implicit vector register " |
| 159 | + << Reg << '\n'); |
| 160 | + |
| 161 | + unsigned RegClassID = getVRLargestSuperClass(MRI->getRegClass(Reg))->getID(); |
| 162 | + unsigned Opcode = getUndefInitOpcode(RegClassID); |
| 163 | + |
| 164 | + BuildMI(MBB, Inst, Inst->getDebugLoc(), TII->get(Opcode), Reg); |
| 165 | + |
| 166 | + Inst = MBB.erase(Inst); |
| 167 | + |
| 168 | + for (auto MO : UseMOs) |
| 169 | + MO->setIsUndef(false); |
| 170 | + |
| 171 | + return true; |
| 172 | +} |
| 173 | + |
| 174 | +static bool isEarlyClobberMI(MachineInstr &MI) { |
| 175 | + return llvm::any_of(MI.defs(), [](const MachineOperand &DefMO) { |
| 176 | + return DefMO.isReg() && DefMO.isEarlyClobber(); |
| 177 | + }); |
| 178 | +} |
| 179 | + |
| 180 | +bool RISCVInitUndef::handleSubReg(MachineFunction &MF, MachineInstr &MI, |
| 181 | + const DeadLaneDetector &DLD) { |
| 182 | + bool Changed = false; |
| 183 | + |
| 184 | + for (MachineOperand &UseMO : MI.uses()) { |
| 185 | + if (!UseMO.isReg()) |
| 186 | + continue; |
| 187 | + if (!UseMO.getReg().isVirtual()) |
| 188 | + continue; |
| 189 | + |
| 190 | + Register Reg = UseMO.getReg(); |
| 191 | + DeadLaneDetector::VRegInfo Info = |
| 192 | + DLD.getVRegInfo(Register::virtReg2Index(Reg)); |
| 193 | + |
| 194 | + if (Info.UsedLanes == Info.DefinedLanes) |
| 195 | + continue; |
| 196 | + |
| 197 | + const TargetRegisterClass *TargetRegClass = |
| 198 | + getVRLargestSuperClass(MRI->getRegClass(Reg)); |
| 199 | + |
| 200 | + LaneBitmask NeedDef = Info.UsedLanes & ~Info.DefinedLanes; |
| 201 | + |
| 202 | + LLVM_DEBUG({ |
| 203 | + dbgs() << "Instruction has undef subregister.\n"; |
| 204 | + dbgs() << printReg(Reg, nullptr) |
| 205 | + << " Used: " << PrintLaneMask(Info.UsedLanes) |
| 206 | + << " Def: " << PrintLaneMask(Info.DefinedLanes) |
| 207 | + << " Need Def: " << PrintLaneMask(NeedDef) << "\n"; |
| 208 | + }); |
| 209 | + |
| 210 | + SmallVector<unsigned> SubRegIndexNeedInsert; |
| 211 | + TRI->getCoveringSubRegIndexes(*MRI, TargetRegClass, NeedDef, |
| 212 | + SubRegIndexNeedInsert); |
| 213 | + |
| 214 | + Register LatestReg = Reg; |
| 215 | + for (auto ind : SubRegIndexNeedInsert) { |
| 216 | + Changed = true; |
| 217 | + const TargetRegisterClass *SubRegClass = |
| 218 | + getVRLargestSuperClass(TRI->getSubRegisterClass(TargetRegClass, ind)); |
| 219 | + Register TmpInitSubReg = MRI->createVirtualRegister(SubRegClass); |
| 220 | + BuildMI(*MI.getParent(), &MI, MI.getDebugLoc(), |
| 221 | + TII->get(getUndefInitOpcode(SubRegClass->getID())), |
| 222 | + TmpInitSubReg); |
| 223 | + Register NewReg = MRI->createVirtualRegister(TargetRegClass); |
| 224 | + BuildMI(*MI.getParent(), &MI, MI.getDebugLoc(), |
| 225 | + TII->get(TargetOpcode::INSERT_SUBREG), NewReg) |
| 226 | + .addReg(LatestReg) |
| 227 | + .addReg(TmpInitSubReg) |
| 228 | + .addImm(ind); |
| 229 | + LatestReg = NewReg; |
| 230 | + } |
| 231 | + |
| 232 | + UseMO.setReg(LatestReg); |
| 233 | + } |
| 234 | + |
| 235 | + return Changed; |
| 236 | +} |
| 237 | + |
| 238 | +bool RISCVInitUndef::processBasicBlock(MachineFunction &MF, |
| 239 | + MachineBasicBlock &MBB, |
| 240 | + const DeadLaneDetector &DLD) { |
| 241 | + bool Changed = false; |
| 242 | + for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) { |
| 243 | + MachineInstr &MI = *I; |
| 244 | + if (ST->enableSubRegLiveness() && isEarlyClobberMI(MI)) |
| 245 | + Changed |= handleSubReg(MF, MI, DLD); |
| 246 | + if (MI.isImplicitDef()) { |
| 247 | + auto DstReg = MI.getOperand(0).getReg(); |
| 248 | + if (isVectorRegClass(DstReg)) |
| 249 | + Changed |= handleImplicitDef(MBB, I); |
| 250 | + } |
| 251 | + } |
| 252 | + return Changed; |
| 253 | +} |
| 254 | + |
| 255 | +bool RISCVInitUndef::runOnMachineFunction(MachineFunction &MF) { |
| 256 | + ST = &MF.getSubtarget<RISCVSubtarget>(); |
| 257 | + if (!ST->hasVInstructions()) |
| 258 | + return false; |
| 259 | + |
| 260 | + MRI = &MF.getRegInfo(); |
| 261 | + TII = ST->getInstrInfo(); |
| 262 | + TRI = MRI->getTargetRegisterInfo(); |
| 263 | + |
| 264 | + bool Changed = false; |
| 265 | + DeadLaneDetector DLD(MRI, TRI); |
| 266 | + DLD.computeSubRegisterLaneBitInfo(); |
| 267 | + |
| 268 | + for (MachineBasicBlock &BB : MF) |
| 269 | + Changed |= processBasicBlock(MF, BB, DLD); |
| 270 | + |
| 271 | + return Changed; |
| 272 | +} |
| 273 | + |
| 274 | +FunctionPass *llvm::createRISCVInitUndefPass() { return new RISCVInitUndef(); } |
0 commit comments