//===- DetectDeadLanes.cpp - SubRegister Lane Usage Analysis --*- C++ -*---===// // // 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 // //===----------------------------------------------------------------------===// // /// \file /// Analysis that tracks defined/used subregister lanes across COPY instructions /// and instructions that get lowered to a COPY (PHI, REG_SEQUENCE, /// INSERT_SUBREG, EXTRACT_SUBREG). /// The information is used to detect dead definitions and the usage of /// (completely) undefined values and mark the operands as such. /// This pass is necessary because the dead/undef status is not obvious anymore /// when subregisters are involved. /// /// Example: /// %0 = some definition /// %1 = IMPLICIT_DEF /// %2 = REG_SEQUENCE %0, sub0, %1, sub1 /// %3 = EXTRACT_SUBREG %2, sub1 /// = use %3 /// The %0 definition is dead and %3 contains an undefined value. // //===----------------------------------------------------------------------===// #include "llvm/CodeGen/DetectDeadLanes.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; #define DEBUG_TYPE "detect-dead-lanes" DeadLaneDetector::DeadLaneDetector(const MachineRegisterInfo *MRI, const TargetRegisterInfo *TRI) : MRI(MRI), TRI(TRI) { unsigned NumVirtRegs = MRI->getNumVirtRegs(); VRegInfos = std::unique_ptr(new VRegInfo[NumVirtRegs]); WorklistMembers.resize(NumVirtRegs); DefinedByCopy.resize(NumVirtRegs); } /// Returns true if \p MI will get lowered to a series of COPY instructions. /// We call this a COPY-like instruction. static bool lowersToCopies(const MachineInstr &MI) { // Note: We could support instructions with MCInstrDesc::isRegSequenceLike(), // isExtractSubRegLike(), isInsertSubregLike() in the future even though they // are not lowered to a COPY. switch (MI.getOpcode()) { case TargetOpcode::COPY: case TargetOpcode::PHI: case TargetOpcode::INSERT_SUBREG: case TargetOpcode::REG_SEQUENCE: case TargetOpcode::EXTRACT_SUBREG: return true; } return false; } static bool isCrossCopy(const MachineRegisterInfo &MRI, const MachineInstr &MI, const TargetRegisterClass *DstRC, const MachineOperand &MO) { assert(lowersToCopies(MI)); Register SrcReg = MO.getReg(); const TargetRegisterClass *SrcRC = MRI.getRegClass(SrcReg); if (DstRC == SrcRC) return false; unsigned SrcSubIdx = MO.getSubReg(); const TargetRegisterInfo &TRI = *MRI.getTargetRegisterInfo(); unsigned DstSubIdx = 0; switch (MI.getOpcode()) { case TargetOpcode::INSERT_SUBREG: if (MO.getOperandNo() == 2) DstSubIdx = MI.getOperand(3).getImm(); break; case TargetOpcode::REG_SEQUENCE: { unsigned OpNum = MO.getOperandNo(); DstSubIdx = MI.getOperand(OpNum+1).getImm(); break; } case TargetOpcode::EXTRACT_SUBREG: { unsigned SubReg = MI.getOperand(2).getImm(); SrcSubIdx = TRI.composeSubRegIndices(SubReg, SrcSubIdx); } } unsigned PreA, PreB; // Unused. if (SrcSubIdx && DstSubIdx) return !TRI.getCommonSuperRegClass(SrcRC, SrcSubIdx, DstRC, DstSubIdx, PreA, PreB); if (SrcSubIdx) return !TRI.getMatchingSuperRegClass(SrcRC, DstRC, SrcSubIdx); if (DstSubIdx) return !TRI.getMatchingSuperRegClass(DstRC, SrcRC, DstSubIdx); return !TRI.getCommonSubClass(SrcRC, DstRC); } void DeadLaneDetector::addUsedLanesOnOperand(const MachineOperand &MO, LaneBitmask UsedLanes) { if (!MO.readsReg()) return; Register MOReg = MO.getReg(); if (!MOReg.isVirtual()) return; unsigned MOSubReg = MO.getSubReg(); if (MOSubReg != 0) UsedLanes = TRI->composeSubRegIndexLaneMask(MOSubReg, UsedLanes); UsedLanes &= MRI->getMaxLaneMaskForVReg(MOReg); unsigned MORegIdx = Register::virtReg2Index(MOReg); DeadLaneDetector::VRegInfo &MORegInfo = VRegInfos[MORegIdx]; LaneBitmask PrevUsedLanes = MORegInfo.UsedLanes; // Any change at all? if ((UsedLanes & ~PrevUsedLanes).none()) return; // Set UsedLanes and remember instruction for further propagation. MORegInfo.UsedLanes = PrevUsedLanes | UsedLanes; if (DefinedByCopy.test(MORegIdx)) PutInWorklist(MORegIdx); } void DeadLaneDetector::transferUsedLanesStep(const MachineInstr &MI, LaneBitmask UsedLanes) { for (const MachineOperand &MO : MI.uses()) { if (!MO.isReg() || !MO.getReg().isVirtual()) continue; LaneBitmask UsedOnMO = transferUsedLanes(MI, UsedLanes, MO); addUsedLanesOnOperand(MO, UsedOnMO); } } LaneBitmask DeadLaneDetector::transferUsedLanes(const MachineInstr &MI, LaneBitmask UsedLanes, const MachineOperand &MO) const { unsigned OpNum = MO.getOperandNo(); assert(lowersToCopies(MI) && DefinedByCopy[Register::virtReg2Index(MI.getOperand(0).getReg())]); switch (MI.getOpcode()) { case TargetOpcode::COPY: case TargetOpcode::PHI: return UsedLanes; case TargetOpcode::REG_SEQUENCE: { assert(OpNum % 2 == 1); unsigned SubIdx = MI.getOperand(OpNum + 1).getImm(); return TRI->reverseComposeSubRegIndexLaneMask(SubIdx, UsedLanes); } case TargetOpcode::INSERT_SUBREG: { unsigned SubIdx = MI.getOperand(3).getImm(); LaneBitmask MO2UsedLanes = TRI->reverseComposeSubRegIndexLaneMask(SubIdx, UsedLanes); if (OpNum == 2) return MO2UsedLanes; const MachineOperand &Def = MI.getOperand(0); Register DefReg = Def.getReg(); const TargetRegisterClass *RC = MRI->getRegClass(DefReg); LaneBitmask MO1UsedLanes; if (RC->CoveredBySubRegs) MO1UsedLanes = UsedLanes & ~TRI->getSubRegIndexLaneMask(SubIdx); else MO1UsedLanes = RC->LaneMask; assert(OpNum == 1); return MO1UsedLanes; } case TargetOpcode::EXTRACT_SUBREG: { assert(OpNum == 1); unsigned SubIdx = MI.getOperand(2).getImm(); return TRI->composeSubRegIndexLaneMask(SubIdx, UsedLanes); } default: llvm_unreachable("function must be called with COPY-like instruction"); } } void DeadLaneDetector::transferDefinedLanesStep(const MachineOperand &Use, LaneBitmask DefinedLanes) { if (!Use.readsReg()) return; // Check whether the operand writes a vreg and is part of a COPY-like // instruction. const MachineInstr &MI = *Use.getParent(); if (MI.getDesc().getNumDefs() != 1) return; // FIXME: PATCHPOINT instructions announce a Def that does not always exist, // they really need to be modeled differently! if (MI.getOpcode() == TargetOpcode::PATCHPOINT) return; const MachineOperand &Def = *MI.defs().begin(); Register DefReg = Def.getReg(); if (!DefReg.isVirtual()) return; unsigned DefRegIdx = Register::virtReg2Index(DefReg); if (!DefinedByCopy.test(DefRegIdx)) return; unsigned OpNum = Use.getOperandNo(); DefinedLanes = TRI->reverseComposeSubRegIndexLaneMask(Use.getSubReg(), DefinedLanes); DefinedLanes = transferDefinedLanes(Def, OpNum, DefinedLanes); VRegInfo &RegInfo = VRegInfos[DefRegIdx]; LaneBitmask PrevDefinedLanes = RegInfo.DefinedLanes; // Any change at all? if ((DefinedLanes & ~PrevDefinedLanes).none()) return; RegInfo.DefinedLanes = PrevDefinedLanes | DefinedLanes; PutInWorklist(DefRegIdx); } LaneBitmask DeadLaneDetector::transferDefinedLanes( const MachineOperand &Def, unsigned OpNum, LaneBitmask DefinedLanes) const { const MachineInstr &MI = *Def.getParent(); // Translate DefinedLanes if necessary. switch (MI.getOpcode()) { case TargetOpcode::REG_SEQUENCE: { unsigned SubIdx = MI.getOperand(OpNum + 1).getImm(); DefinedLanes = TRI->composeSubRegIndexLaneMask(SubIdx, DefinedLanes); DefinedLanes &= TRI->getSubRegIndexLaneMask(SubIdx); break; } case TargetOpcode::INSERT_SUBREG: { unsigned SubIdx = MI.getOperand(3).getImm(); if (OpNum == 2) { DefinedLanes = TRI->composeSubRegIndexLaneMask(SubIdx, DefinedLanes); DefinedLanes &= TRI->getSubRegIndexLaneMask(SubIdx); } else { assert(OpNum == 1 && "INSERT_SUBREG must have two operands"); // Ignore lanes defined by operand 2. DefinedLanes &= ~TRI->getSubRegIndexLaneMask(SubIdx); } break; } case TargetOpcode::EXTRACT_SUBREG: { unsigned SubIdx = MI.getOperand(2).getImm(); assert(OpNum == 1 && "EXTRACT_SUBREG must have one register operand only"); DefinedLanes = TRI->reverseComposeSubRegIndexLaneMask(SubIdx, DefinedLanes); break; } case TargetOpcode::COPY: case TargetOpcode::PHI: break; default: llvm_unreachable("function must be called with COPY-like instruction"); } assert(Def.getSubReg() == 0 && "Should not have subregister defs in machine SSA phase"); DefinedLanes &= MRI->getMaxLaneMaskForVReg(Def.getReg()); return DefinedLanes; } LaneBitmask DeadLaneDetector::determineInitialDefinedLanes(unsigned Reg) { // Live-In or unused registers have no definition but are considered fully // defined. if (!MRI->hasOneDef(Reg)) return LaneBitmask::getAll(); const MachineOperand &Def = *MRI->def_begin(Reg); const MachineInstr &DefMI = *Def.getParent(); if (lowersToCopies(DefMI)) { // Start optimisatically with no used or defined lanes for copy // instructions. The following dataflow analysis will add more bits. unsigned RegIdx = Register::virtReg2Index(Reg); DefinedByCopy.set(RegIdx); PutInWorklist(RegIdx); if (Def.isDead()) return LaneBitmask::getNone(); // COPY/PHI can copy across unrelated register classes (example: float/int) // with incompatible subregister structure. Do not include these in the // dataflow analysis since we cannot transfer lanemasks in a meaningful way. const TargetRegisterClass *DefRC = MRI->getRegClass(Reg); // Determine initially DefinedLanes. LaneBitmask DefinedLanes; for (const MachineOperand &MO : DefMI.uses()) { if (!MO.isReg() || !MO.readsReg()) continue; Register MOReg = MO.getReg(); if (!MOReg) continue; LaneBitmask MODefinedLanes; if (MOReg.isPhysical()) { MODefinedLanes = LaneBitmask::getAll(); } else if (isCrossCopy(*MRI, DefMI, DefRC, MO)) { MODefinedLanes = LaneBitmask::getAll(); } else { assert(MOReg.isVirtual()); if (MRI->hasOneDef(MOReg)) { const MachineOperand &MODef = *MRI->def_begin(MOReg); const MachineInstr &MODefMI = *MODef.getParent(); // Bits from copy-like operations will be added later. if (lowersToCopies(MODefMI) || MODefMI.isImplicitDef()) continue; } unsigned MOSubReg = MO.getSubReg(); MODefinedLanes = MRI->getMaxLaneMaskForVReg(MOReg); MODefinedLanes = TRI->reverseComposeSubRegIndexLaneMask( MOSubReg, MODefinedLanes); } unsigned OpNum = MO.getOperandNo(); DefinedLanes |= transferDefinedLanes(Def, OpNum, MODefinedLanes); } return DefinedLanes; } if (DefMI.isImplicitDef() || Def.isDead()) return LaneBitmask::getNone(); assert(Def.getSubReg() == 0 && "Should not have subregister defs in machine SSA phase"); return MRI->getMaxLaneMaskForVReg(Reg); } LaneBitmask DeadLaneDetector::determineInitialUsedLanes(unsigned Reg) { LaneBitmask UsedLanes = LaneBitmask::getNone(); for (const MachineOperand &MO : MRI->use_nodbg_operands(Reg)) { if (!MO.readsReg()) continue; const MachineInstr &UseMI = *MO.getParent(); if (UseMI.isKill()) continue; unsigned SubReg = MO.getSubReg(); if (lowersToCopies(UseMI)) { assert(UseMI.getDesc().getNumDefs() == 1); const MachineOperand &Def = *UseMI.defs().begin(); Register DefReg = Def.getReg(); // The used lanes of COPY-like instruction operands are determined by the // following dataflow analysis. if (DefReg.isVirtual()) { // But ignore copies across incompatible register classes. bool CrossCopy = false; if (lowersToCopies(UseMI)) { const TargetRegisterClass *DstRC = MRI->getRegClass(DefReg); CrossCopy = isCrossCopy(*MRI, UseMI, DstRC, MO); if (CrossCopy) LLVM_DEBUG(dbgs() << "Copy across incompatible classes: " << UseMI); } if (!CrossCopy) continue; } } // Shortcut: All lanes are used. if (SubReg == 0) return MRI->getMaxLaneMaskForVReg(Reg); UsedLanes |= TRI->getSubRegIndexLaneMask(SubReg); } return UsedLanes; } namespace { class DetectDeadLanes : public MachineFunctionPass { public: bool runOnMachineFunction(MachineFunction &MF) override; static char ID; DetectDeadLanes() : MachineFunctionPass(ID) {} StringRef getPassName() const override { return "Detect Dead Lanes"; } void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesCFG(); MachineFunctionPass::getAnalysisUsage(AU); } private: /// update the operand status. /// The first return value shows whether MF been changed. /// The second return value indicates we need to call /// DeadLaneDetector::computeSubRegisterLaneBitInfo and this function again /// to propagate changes. std::pair modifySubRegisterOperandStatus(const DeadLaneDetector &DLD, MachineFunction &MF); bool isUndefRegAtInput(const MachineOperand &MO, const DeadLaneDetector::VRegInfo &RegInfo) const; bool isUndefInput(const DeadLaneDetector &DLD, const MachineOperand &MO, bool *CrossCopy) const; const MachineRegisterInfo *MRI = nullptr; const TargetRegisterInfo *TRI = nullptr; }; } // end anonymous namespace char DetectDeadLanes::ID = 0; char &llvm::DetectDeadLanesID = DetectDeadLanes::ID; INITIALIZE_PASS(DetectDeadLanes, DEBUG_TYPE, "Detect Dead Lanes", false, false) bool DetectDeadLanes::isUndefRegAtInput( const MachineOperand &MO, const DeadLaneDetector::VRegInfo &RegInfo) const { unsigned SubReg = MO.getSubReg(); LaneBitmask Mask = TRI->getSubRegIndexLaneMask(SubReg); return (RegInfo.DefinedLanes & RegInfo.UsedLanes & Mask).none(); } bool DetectDeadLanes::isUndefInput(const DeadLaneDetector &DLD, const MachineOperand &MO, bool *CrossCopy) const { if (!MO.isUse()) return false; const MachineInstr &MI = *MO.getParent(); if (!lowersToCopies(MI)) return false; const MachineOperand &Def = MI.getOperand(0); Register DefReg = Def.getReg(); if (!DefReg.isVirtual()) return false; unsigned DefRegIdx = Register::virtReg2Index(DefReg); if (!DLD.isDefinedByCopy(DefRegIdx)) return false; const DeadLaneDetector::VRegInfo &DefRegInfo = DLD.getVRegInfo(DefRegIdx); LaneBitmask UsedLanes = DLD.transferUsedLanes(MI, DefRegInfo.UsedLanes, MO); if (UsedLanes.any()) return false; Register MOReg = MO.getReg(); if (MOReg.isVirtual()) { const TargetRegisterClass *DstRC = MRI->getRegClass(DefReg); *CrossCopy = isCrossCopy(*MRI, MI, DstRC, MO); } return true; } void DeadLaneDetector::computeSubRegisterLaneBitInfo() { // First pass: Populate defs/uses of vregs with initial values unsigned NumVirtRegs = MRI->getNumVirtRegs(); for (unsigned RegIdx = 0; RegIdx < NumVirtRegs; ++RegIdx) { Register Reg = Register::index2VirtReg(RegIdx); // Determine used/defined lanes and add copy instructions to worklist. VRegInfo &Info = VRegInfos[RegIdx]; Info.DefinedLanes = determineInitialDefinedLanes(Reg); Info.UsedLanes = determineInitialUsedLanes(Reg); } // Iterate as long as defined lanes/used lanes keep changing. while (!Worklist.empty()) { unsigned RegIdx = Worklist.front(); Worklist.pop_front(); WorklistMembers.reset(RegIdx); VRegInfo &Info = VRegInfos[RegIdx]; Register Reg = Register::index2VirtReg(RegIdx); // Transfer UsedLanes to operands of DefMI (backwards dataflow). MachineOperand &Def = *MRI->def_begin(Reg); const MachineInstr &MI = *Def.getParent(); transferUsedLanesStep(MI, Info.UsedLanes); // Transfer DefinedLanes to users of Reg (forward dataflow). for (const MachineOperand &MO : MRI->use_nodbg_operands(Reg)) transferDefinedLanesStep(MO, Info.DefinedLanes); } LLVM_DEBUG({ dbgs() << "Defined/Used lanes:\n"; for (unsigned RegIdx = 0; RegIdx < NumVirtRegs; ++RegIdx) { Register Reg = Register::index2VirtReg(RegIdx); const VRegInfo &Info = VRegInfos[RegIdx]; dbgs() << printReg(Reg, nullptr) << " Used: " << PrintLaneMask(Info.UsedLanes) << " Def: " << PrintLaneMask(Info.DefinedLanes) << '\n'; } dbgs() << "\n"; }); } std::pair DetectDeadLanes::modifySubRegisterOperandStatus(const DeadLaneDetector &DLD, MachineFunction &MF) { bool Changed = false; bool Again = false; // Mark operands as dead/unused. for (MachineBasicBlock &MBB : MF) { for (MachineInstr &MI : MBB) { for (MachineOperand &MO : MI.operands()) { if (!MO.isReg()) continue; Register Reg = MO.getReg(); if (!Reg.isVirtual()) continue; unsigned RegIdx = Register::virtReg2Index(Reg); const DeadLaneDetector::VRegInfo &RegInfo = DLD.getVRegInfo(RegIdx); if (MO.isDef() && !MO.isDead() && RegInfo.UsedLanes.none()) { LLVM_DEBUG(dbgs() << "Marking operand '" << MO << "' as dead in " << MI); MO.setIsDead(); Changed = true; } if (MO.readsReg()) { bool CrossCopy = false; if (isUndefRegAtInput(MO, RegInfo)) { LLVM_DEBUG(dbgs() << "Marking operand '" << MO << "' as undef in " << MI); MO.setIsUndef(); Changed = true; } else if (isUndefInput(DLD, MO, &CrossCopy)) { LLVM_DEBUG(dbgs() << "Marking operand '" << MO << "' as undef in " << MI); MO.setIsUndef(); Changed = true; if (CrossCopy) Again = true; } } } } } return std::make_pair(Changed, Again); } bool DetectDeadLanes::runOnMachineFunction(MachineFunction &MF) { // Don't bother if we won't track subregister liveness later. This pass is // required for correctness if subregister liveness is enabled because the // register coalescer cannot deal with hidden dead defs. However without // subregister liveness enabled, the expected benefits of this pass are small // so we safe the compile time. MRI = &MF.getRegInfo(); if (!MRI->subRegLivenessEnabled()) { LLVM_DEBUG(dbgs() << "Skipping Detect dead lanes pass\n"); return false; } TRI = MRI->getTargetRegisterInfo(); DeadLaneDetector DLD(MRI, TRI); bool Changed = false; bool Again; do { DLD.computeSubRegisterLaneBitInfo(); bool LocalChanged; std::tie(LocalChanged, Again) = modifySubRegisterOperandStatus(DLD, MF); Changed |= LocalChanged; } while (Again); return Changed; }