diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2021-07-29 20:15:26 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2021-07-29 20:15:26 +0000 |
| commit | 344a3780b2e33f6ca763666c380202b18aab72a3 (patch) | |
| tree | f0b203ee6eb71d7fdd792373e3c81eb18d6934dd /llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp | |
| parent | b60736ec1405bb0a8dd40989f67ef4c93da068ab (diff) | |
vendor/llvm-project/llvmorg-13-init-16847-g88e66fa60ae5vendor/llvm-project/llvmorg-12.0.1-rc2-0-ge7dac564cd0evendor/llvm-project/llvmorg-12.0.1-0-gfed41342a82f
Diffstat (limited to 'llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp')
| -rw-r--r-- | llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp | 822 |
1 files changed, 624 insertions, 198 deletions
diff --git a/llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp b/llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp index ed1dc77bd545..bc2fb1e9770c 100644 --- a/llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp +++ b/llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp @@ -23,6 +23,9 @@ using namespace llvm; // Hazard Recoginizer Implementation //===----------------------------------------------------------------------===// +static bool shouldRunLdsBranchVmemWARHazardFixup(const MachineFunction &MF, + const GCNSubtarget &ST); + GCNHazardRecognizer::GCNHazardRecognizer(const MachineFunction &MF) : IsHazardRecognizerMode(false), CurrCycleInstr(nullptr), @@ -32,8 +35,9 @@ GCNHazardRecognizer::GCNHazardRecognizer(const MachineFunction &MF) : TRI(TII.getRegisterInfo()), ClauseUses(TRI.getNumRegUnits()), ClauseDefs(TRI.getNumRegUnits()) { - MaxLookAhead = MF.getRegInfo().isPhysRegUsed(AMDGPU::AGPR0) ? 18 : 5; + MaxLookAhead = MF.getRegInfo().isPhysRegUsed(AMDGPU::AGPR0) ? 19 : 5; TSchedModel.init(&ST); + RunLdsBranchVmemWARHazardFixup = shouldRunLdsBranchVmemWARHazardFixup(MF, ST); } void GCNHazardRecognizer::Reset() { @@ -87,6 +91,25 @@ static bool isSMovRel(unsigned Opcode) { } } +static bool isDGEMM(unsigned Opcode) { + return Opcode == AMDGPU::V_MFMA_F64_4X4X4F64_e64 || + Opcode == AMDGPU::V_MFMA_F64_4X4X4F64_vgprcd_e64 || + Opcode == AMDGPU::V_MFMA_F64_16X16X4F64_e64 || + Opcode == AMDGPU::V_MFMA_F64_16X16X4F64_vgprcd_e64; +} + +static bool isXDL(const GCNSubtarget &ST, const MachineInstr &MI) { + unsigned Opcode = MI.getOpcode(); + + if (!SIInstrInfo::isMAI(MI) || + isDGEMM(Opcode) || + Opcode == AMDGPU::V_ACCVGPR_WRITE_B32_e64 || + Opcode == AMDGPU::V_ACCVGPR_READ_B32_e64) + return false; + + return true; +} + static bool isSendMsgTraceDataOrGDS(const SIInstrInfo &TII, const MachineInstr &MI) { if (TII.isAlwaysGDS(MI.getOpcode())) @@ -138,12 +161,6 @@ GCNHazardRecognizer::getHazardType(SUnit *SU, int Stalls) { if (SIInstrInfo::isSMRD(*MI) && checkSMRDHazards(MI) > 0) return HazardType; - // FIXME: Should flat be considered vmem? - if ((SIInstrInfo::isVMEM(*MI) || - SIInstrInfo::isFLAT(*MI)) - && checkVMEMHazards(MI) > 0) - return HazardType; - if (ST.hasNSAtoVMEMBug() && checkNSAtoVMEMHazard(MI) > 0) return HazardType; @@ -153,6 +170,12 @@ GCNHazardRecognizer::getHazardType(SUnit *SU, int Stalls) { if (ST.hasNoDataDepHazard()) return NoHazard; + // FIXME: Should flat be considered vmem? + if ((SIInstrInfo::isVMEM(*MI) || + SIInstrInfo::isFLAT(*MI)) + && checkVMEMHazards(MI) > 0) + return HazardType; + if (SIInstrInfo::isVALU(*MI) && checkVALUHazards(MI) > 0) return HazardType; @@ -165,6 +188,11 @@ GCNHazardRecognizer::getHazardType(SUnit *SU, int Stalls) { if (isRWLane(MI->getOpcode()) && checkRWLaneHazards(MI) > 0) return HazardType; + if ((SIInstrInfo::isVALU(*MI) || SIInstrInfo::isVMEM(*MI) || + SIInstrInfo::isFLAT(*MI) || SIInstrInfo::isDS(*MI) || + SIInstrInfo::isEXP(*MI)) && checkMAIVALUHazards(MI) > 0) + return HazardType; + if (isSGetReg(MI->getOpcode()) && checkGetRegHazards(MI) > 0) return HazardType; @@ -251,9 +279,6 @@ unsigned GCNHazardRecognizer::PreEmitNoopsCommon(MachineInstr *MI) { if (SIInstrInfo::isSMRD(*MI)) return std::max(WaitStates, checkSMRDHazards(MI)); - if (SIInstrInfo::isVMEM(*MI) || SIInstrInfo::isFLAT(*MI)) - WaitStates = std::max(WaitStates, checkVMEMHazards(MI)); - if (ST.hasNSAtoVMEMBug()) WaitStates = std::max(WaitStates, checkNSAtoVMEMHazard(MI)); @@ -262,6 +287,9 @@ unsigned GCNHazardRecognizer::PreEmitNoopsCommon(MachineInstr *MI) { if (ST.hasNoDataDepHazard()) return WaitStates; + if (SIInstrInfo::isVMEM(*MI) || SIInstrInfo::isFLAT(*MI)) + WaitStates = std::max(WaitStates, checkVMEMHazards(MI)); + if (SIInstrInfo::isVALU(*MI)) WaitStates = std::max(WaitStates, checkVALUHazards(MI)); @@ -274,6 +302,11 @@ unsigned GCNHazardRecognizer::PreEmitNoopsCommon(MachineInstr *MI) { if (isRWLane(MI->getOpcode())) WaitStates = std::max(WaitStates, checkRWLaneHazards(MI)); + if ((SIInstrInfo::isVALU(*MI) || SIInstrInfo::isVMEM(*MI) || + SIInstrInfo::isFLAT(*MI) || SIInstrInfo::isDS(*MI) || + SIInstrInfo::isEXP(*MI)) && checkMAIVALUHazards(MI) > 0) + WaitStates = std::max(WaitStates, checkMAIVALUHazards(MI)); + if (MI->isInlineAsm()) return std::max(WaitStates, checkInlineAsmHazards(MI)); @@ -319,8 +352,7 @@ void GCNHazardRecognizer::AdvanceCycle() { // Do not track non-instructions which do not affect the wait states. // If included, these instructions can lead to buffer overflow such that // detectable hazards are missed. - if (CurrCycleInstr->isImplicitDef() || CurrCycleInstr->isDebugInstr() || - CurrCycleInstr->isKill()) { + if (CurrCycleInstr->isMetaInstruction()) { CurrCycleInstr = nullptr; return; } @@ -359,23 +391,22 @@ void GCNHazardRecognizer::RecedeCycle() { // Helper Functions //===----------------------------------------------------------------------===// -typedef function_ref<bool(MachineInstr *, int WaitStates)> IsExpiredFn; +typedef function_ref<bool(const MachineInstr &, int WaitStates)> IsExpiredFn; // Returns a minimum wait states since \p I walking all predecessors. // Only scans until \p IsExpired does not return true. // Can only be run in a hazard recognizer mode. static int getWaitStatesSince(GCNHazardRecognizer::IsHazardFn IsHazard, - MachineBasicBlock *MBB, - MachineBasicBlock::reverse_instr_iterator I, - int WaitStates, - IsExpiredFn IsExpired, + const MachineBasicBlock *MBB, + MachineBasicBlock::const_reverse_instr_iterator I, + int WaitStates, IsExpiredFn IsExpired, DenseSet<const MachineBasicBlock *> &Visited) { for (auto E = MBB->instr_rend(); I != E; ++I) { // Don't add WaitStates for parent BUNDLE instructions. if (I->isBundle()) continue; - if (IsHazard(&*I)) + if (IsHazard(*I)) return WaitStates; if (I->isInlineAsm() || I->isMetaInstruction()) @@ -383,12 +414,11 @@ static int getWaitStatesSince(GCNHazardRecognizer::IsHazardFn IsHazard, WaitStates += SIInstrInfo::getNumWaitStates(*I); - if (IsExpired(&*I, WaitStates)) + if (IsExpired(*I, WaitStates)) return std::numeric_limits<int>::max(); } - int MinWaitStates = WaitStates; - bool Found = false; + int MinWaitStates = std::numeric_limits<int>::max(); for (MachineBasicBlock *Pred : MBB->predecessors()) { if (!Visited.insert(Pred).second) continue; @@ -396,25 +426,14 @@ static int getWaitStatesSince(GCNHazardRecognizer::IsHazardFn IsHazard, int W = getWaitStatesSince(IsHazard, Pred, Pred->instr_rbegin(), WaitStates, IsExpired, Visited); - if (W == std::numeric_limits<int>::max()) - continue; - - MinWaitStates = Found ? std::min(MinWaitStates, W) : W; - if (IsExpired(nullptr, MinWaitStates)) - return MinWaitStates; - - Found = true; + MinWaitStates = std::min(MinWaitStates, W); } - if (Found) - return MinWaitStates; - - return std::numeric_limits<int>::max(); + return MinWaitStates; } static int getWaitStatesSince(GCNHazardRecognizer::IsHazardFn IsHazard, - MachineInstr *MI, - IsExpiredFn IsExpired) { + const MachineInstr *MI, IsExpiredFn IsExpired) { DenseSet<const MachineBasicBlock *> Visited; return getWaitStatesSince(IsHazard, MI->getParent(), std::next(MI->getReverseIterator()), @@ -423,7 +442,7 @@ static int getWaitStatesSince(GCNHazardRecognizer::IsHazardFn IsHazard, int GCNHazardRecognizer::getWaitStatesSince(IsHazardFn IsHazard, int Limit) { if (IsHazardRecognizerMode) { - auto IsExpiredFn = [Limit] (MachineInstr *, int WaitStates) { + auto IsExpiredFn = [Limit](const MachineInstr &, int WaitStates) { return WaitStates >= Limit; }; return ::getWaitStatesSince(IsHazard, CurrCycleInstr, IsExpiredFn); @@ -432,7 +451,7 @@ int GCNHazardRecognizer::getWaitStatesSince(IsHazardFn IsHazard, int Limit) { int WaitStates = 0; for (MachineInstr *MI : EmittedInstrs) { if (MI) { - if (IsHazard(MI)) + if (IsHazard(*MI)) return WaitStates; if (MI->isInlineAsm()) @@ -451,8 +470,8 @@ int GCNHazardRecognizer::getWaitStatesSinceDef(unsigned Reg, int Limit) { const SIRegisterInfo *TRI = ST.getRegisterInfo(); - auto IsHazardFn = [IsHazardDef, TRI, Reg] (MachineInstr *MI) { - return IsHazardDef(MI) && MI->modifiesRegister(Reg, TRI); + auto IsHazardFn = [IsHazardDef, TRI, Reg](const MachineInstr &MI) { + return IsHazardDef(MI) && MI.modifiesRegister(Reg, TRI); }; return getWaitStatesSince(IsHazardFn, Limit); @@ -460,8 +479,8 @@ int GCNHazardRecognizer::getWaitStatesSinceDef(unsigned Reg, int GCNHazardRecognizer::getWaitStatesSinceSetReg(IsHazardFn IsHazard, int Limit) { - auto IsHazardFn = [IsHazard] (MachineInstr *MI) { - return isSSetReg(MI->getOpcode()) && IsHazard(MI); + auto IsHazardFn = [IsHazard](const MachineInstr &MI) { + return isSSetReg(MI.getOpcode()) && IsHazard(MI); }; return getWaitStatesSince(IsHazardFn, Limit); @@ -560,8 +579,12 @@ int GCNHazardRecognizer::checkSMRDHazards(MachineInstr *SMRD) { // A read of an SGPR by SMRD instruction requires 4 wait states when the // SGPR was written by a VALU instruction. int SmrdSgprWaitStates = 4; - auto IsHazardDefFn = [this] (MachineInstr *MI) { return TII.isVALU(*MI); }; - auto IsBufferHazardDefFn = [this] (MachineInstr *MI) { return TII.isSALU(*MI); }; + auto IsHazardDefFn = [this](const MachineInstr &MI) { + return TII.isVALU(MI); + }; + auto IsBufferHazardDefFn = [this](const MachineInstr &MI) { + return TII.isSALU(MI); + }; bool IsBufferSMRD = TII.isBufferSMRD(*SMRD); @@ -601,9 +624,11 @@ int GCNHazardRecognizer::checkVMEMHazards(MachineInstr* VMEM) { // A read of an SGPR by a VMEM instruction requires 5 wait states when the // SGPR was written by a VALU Instruction. const int VmemSgprWaitStates = 5; - auto IsHazardDefFn = [this] (MachineInstr *MI) { return TII.isVALU(*MI); }; + auto IsHazardDefFn = [this](const MachineInstr &MI) { + return TII.isVALU(MI); + }; for (const MachineOperand &Use : VMEM->uses()) { - if (!Use.isReg() || TRI.isVGPR(MF.getRegInfo(), Use.getReg())) + if (!Use.isReg() || TRI.isVectorRegister(MF.getRegInfo(), Use.getReg())) continue; int WaitStatesNeededForUse = @@ -622,15 +647,18 @@ int GCNHazardRecognizer::checkDPPHazards(MachineInstr *DPP) { int DppVgprWaitStates = 2; int DppExecWaitStates = 5; int WaitStatesNeeded = 0; - auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); }; + auto IsHazardDefFn = [TII](const MachineInstr &MI) { + return TII->isVALU(MI); + }; for (const MachineOperand &Use : DPP->uses()) { if (!Use.isReg() || !TRI->isVGPR(MF.getRegInfo(), Use.getReg())) continue; int WaitStatesNeededForUse = - DppVgprWaitStates - getWaitStatesSinceDef(Use.getReg(), - [](MachineInstr *) { return true; }, - DppVgprWaitStates); + DppVgprWaitStates - getWaitStatesSinceDef( + Use.getReg(), + [](const MachineInstr &) { return true; }, + DppVgprWaitStates); WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); } @@ -648,7 +676,9 @@ int GCNHazardRecognizer::checkDivFMasHazards(MachineInstr *DivFMas) { // v_div_fmas requires 4 wait states after a write to vcc from a VALU // instruction. const int DivFMasWaitStates = 4; - auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); }; + auto IsHazardDefFn = [TII](const MachineInstr &MI) { + return TII->isVALU(MI); + }; int WaitStatesNeeded = getWaitStatesSinceDef(AMDGPU::VCC, IsHazardDefFn, DivFMasWaitStates); @@ -660,8 +690,8 @@ int GCNHazardRecognizer::checkGetRegHazards(MachineInstr *GetRegInstr) { unsigned GetRegHWReg = getHWReg(TII, *GetRegInstr); const int GetRegWaitStates = 2; - auto IsHazardFn = [TII, GetRegHWReg] (MachineInstr *MI) { - return GetRegHWReg == getHWReg(TII, *MI); + auto IsHazardFn = [TII, GetRegHWReg](const MachineInstr &MI) { + return GetRegHWReg == getHWReg(TII, MI); }; int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn, GetRegWaitStates); @@ -673,8 +703,8 @@ int GCNHazardRecognizer::checkSetRegHazards(MachineInstr *SetRegInstr) { unsigned HWReg = getHWReg(TII, *SetRegInstr); const int SetRegWaitStates = ST.getSetRegWaitStates(); - auto IsHazardFn = [TII, HWReg] (MachineInstr *MI) { - return HWReg == getHWReg(TII, *MI); + auto IsHazardFn = [TII, HWReg](const MachineInstr &MI) { + return HWReg == getHWReg(TII, MI); }; int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn, SetRegWaitStates); return SetRegWaitStates - WaitStatesNeeded; @@ -739,13 +769,13 @@ GCNHazardRecognizer::checkVALUHazardsHelper(const MachineOperand &Def, const int VALUWaitStates = 1; int WaitStatesNeeded = 0; - if (!TRI->isVGPR(MRI, Def.getReg())) + if (!TRI->isVectorRegister(MRI, Def.getReg())) return WaitStatesNeeded; Register Reg = Def.getReg(); - auto IsHazardFn = [this, Reg, TRI] (MachineInstr *MI) { - int DataIdx = createsVALUHazard(*MI); + auto IsHazardFn = [this, Reg, TRI](const MachineInstr &MI) { + int DataIdx = createsVALUHazard(MI); return DataIdx >= 0 && - TRI->regsOverlap(MI->getOperand(DataIdx).getReg(), Reg); + TRI->regsOverlap(MI.getOperand(DataIdx).getReg(), Reg); }; int WaitStatesNeededForDef = VALUWaitStates - getWaitStatesSince(IsHazardFn, VALUWaitStates); @@ -808,9 +838,7 @@ int GCNHazardRecognizer::checkRWLaneHazards(MachineInstr *RWLane) { return 0; Register LaneSelectReg = LaneSelectOp->getReg(); - auto IsHazardFn = [TII] (MachineInstr *MI) { - return TII->isVALU(*MI); - }; + auto IsHazardFn = [TII](const MachineInstr &MI) { return TII->isVALU(MI); }; const int RWLaneWaitStates = 4; int WaitStatesSince = getWaitStatesSinceDef(LaneSelectReg, IsHazardFn, @@ -826,8 +854,8 @@ int GCNHazardRecognizer::checkRFEHazards(MachineInstr *RFE) { const int RFEWaitStates = 1; - auto IsHazardFn = [TII] (MachineInstr *MI) { - return getHWReg(TII, *MI) == AMDGPU::Hwreg::ID_TRAPSTS; + auto IsHazardFn = [TII](const MachineInstr &MI) { + return getHWReg(TII, MI) == AMDGPU::Hwreg::ID_TRAPSTS; }; int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn, RFEWaitStates); return RFEWaitStates - WaitStatesNeeded; @@ -836,9 +864,7 @@ int GCNHazardRecognizer::checkRFEHazards(MachineInstr *RFE) { int GCNHazardRecognizer::checkReadM0Hazards(MachineInstr *MI) { const SIInstrInfo *TII = ST.getInstrInfo(); const int SMovRelWaitStates = 1; - auto IsHazardFn = [TII] (MachineInstr *MI) { - return TII->isSALU(*MI); - }; + auto IsHazardFn = [TII](const MachineInstr &MI) { return TII->isSALU(MI); }; return SMovRelWaitStates - getWaitStatesSinceDef(AMDGPU::M0, IsHazardFn, SMovRelWaitStates); } @@ -856,18 +882,12 @@ bool GCNHazardRecognizer::fixVcmpxPermlaneHazards(MachineInstr *MI) { return false; const SIInstrInfo *TII = ST.getInstrInfo(); - auto IsHazardFn = [TII] (MachineInstr *MI) { - return TII->isVOPC(*MI); - }; + auto IsHazardFn = [TII](const MachineInstr &MI) { return TII->isVOPC(MI); }; - auto IsExpiredFn = [] (MachineInstr *MI, int) { - if (!MI) - return false; - unsigned Opc = MI->getOpcode(); - return SIInstrInfo::isVALU(*MI) && - Opc != AMDGPU::V_NOP_e32 && - Opc != AMDGPU::V_NOP_e64 && - Opc != AMDGPU::V_NOP_sdwa; + auto IsExpiredFn = [](const MachineInstr &MI, int) { + unsigned Opc = MI.getOpcode(); + return SIInstrInfo::isVALU(MI) && Opc != AMDGPU::V_NOP_e32 && + Opc != AMDGPU::V_NOP_e64 && Opc != AMDGPU::V_NOP_sdwa; }; if (::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn) == @@ -900,13 +920,14 @@ bool GCNHazardRecognizer::fixVMEMtoScalarWriteHazards(MachineInstr *MI) { const SIRegisterInfo *TRI = ST.getRegisterInfo(); - auto IsHazardFn = [TRI, MI] (MachineInstr *I) { - if (!SIInstrInfo::isVMEM(*I) && !SIInstrInfo::isDS(*I) && - !SIInstrInfo::isFLAT(*I)) + auto IsHazardFn = [TRI, MI](const MachineInstr &I) { + if (!SIInstrInfo::isVMEM(I) && !SIInstrInfo::isDS(I) && + !SIInstrInfo::isFLAT(I)) return false; for (const MachineOperand &Def : MI->defs()) { - MachineOperand *Op = I->findRegisterUseOperand(Def.getReg(), false, TRI); + const MachineOperand *Op = + I.findRegisterUseOperand(Def.getReg(), false, TRI); if (!Op) continue; return true; @@ -914,12 +935,12 @@ bool GCNHazardRecognizer::fixVMEMtoScalarWriteHazards(MachineInstr *MI) { return false; }; - auto IsExpiredFn = [](MachineInstr *MI, int) { - return MI && (SIInstrInfo::isVALU(*MI) || - (MI->getOpcode() == AMDGPU::S_WAITCNT && - !MI->getOperand(0).getImm()) || - (MI->getOpcode() == AMDGPU::S_WAITCNT_DEPCTR && - MI->getOperand(0).getImm() == 0xffe3)); + auto IsExpiredFn = [](const MachineInstr &MI, int) { + return SIInstrInfo::isVALU(MI) || + (MI.getOpcode() == AMDGPU::S_WAITCNT && + !MI.getOperand(0).getImm()) || + (MI.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR && + MI.getOperand(0).getImm() == 0xffe3); }; if (::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn) == @@ -968,43 +989,41 @@ bool GCNHazardRecognizer::fixSMEMtoVectorWriteHazards(MachineInstr *MI) { return false; const Register SDSTReg = SDST->getReg(); - auto IsHazardFn = [SDSTReg, TRI] (MachineInstr *I) { - return SIInstrInfo::isSMRD(*I) && I->readsRegister(SDSTReg, TRI); + auto IsHazardFn = [SDSTReg, TRI](const MachineInstr &I) { + return SIInstrInfo::isSMRD(I) && I.readsRegister(SDSTReg, TRI); }; - auto IsExpiredFn = [TII, IV] (MachineInstr *MI, int) { - if (MI) { - if (TII->isSALU(*MI)) { - switch (MI->getOpcode()) { - case AMDGPU::S_SETVSKIP: - case AMDGPU::S_VERSION: - case AMDGPU::S_WAITCNT_VSCNT: - case AMDGPU::S_WAITCNT_VMCNT: - case AMDGPU::S_WAITCNT_EXPCNT: - // These instructions cannot not mitigate the hazard. + auto IsExpiredFn = [TII, IV](const MachineInstr &MI, int) { + if (TII->isSALU(MI)) { + switch (MI.getOpcode()) { + case AMDGPU::S_SETVSKIP: + case AMDGPU::S_VERSION: + case AMDGPU::S_WAITCNT_VSCNT: + case AMDGPU::S_WAITCNT_VMCNT: + case AMDGPU::S_WAITCNT_EXPCNT: + // These instructions cannot not mitigate the hazard. + return false; + case AMDGPU::S_WAITCNT_LGKMCNT: + // Reducing lgkmcnt count to 0 always mitigates the hazard. + return (MI.getOperand(1).getImm() == 0) && + (MI.getOperand(0).getReg() == AMDGPU::SGPR_NULL); + case AMDGPU::S_WAITCNT: { + const int64_t Imm = MI.getOperand(0).getImm(); + AMDGPU::Waitcnt Decoded = AMDGPU::decodeWaitcnt(IV, Imm); + return (Decoded.LgkmCnt == 0); + } + default: + // SOPP instructions cannot mitigate the hazard. + if (TII->isSOPP(MI)) return false; - case AMDGPU::S_WAITCNT_LGKMCNT: - // Reducing lgkmcnt count to 0 always mitigates the hazard. - return (MI->getOperand(1).getImm() == 0) && - (MI->getOperand(0).getReg() == AMDGPU::SGPR_NULL); - case AMDGPU::S_WAITCNT: { - const int64_t Imm = MI->getOperand(0).getImm(); - AMDGPU::Waitcnt Decoded = AMDGPU::decodeWaitcnt(IV, Imm); - return (Decoded.LgkmCnt == 0); - } - default: - // SOPP instructions cannot mitigate the hazard. - if (TII->isSOPP(*MI)) - return false; - // At this point the SALU can be assumed to mitigate the hazard - // because either: - // (a) it is independent of the at risk SMEM (breaking chain), - // or - // (b) it is dependent on the SMEM, in which case an appropriate - // s_waitcnt lgkmcnt _must_ exist between it and the at risk - // SMEM instruction. - return true; - } + // At this point the SALU can be assumed to mitigate the hazard + // because either: + // (a) it is independent of the at risk SMEM (breaking chain), + // or + // (b) it is dependent on the SMEM, in which case an appropriate + // s_waitcnt lgkmcnt _must_ exist between it and the at risk + // SMEM instruction. + return true; } } return false; @@ -1028,25 +1047,23 @@ bool GCNHazardRecognizer::fixVcmpxExecWARHazard(MachineInstr *MI) { if (!MI->modifiesRegister(AMDGPU::EXEC, TRI)) return false; - auto IsHazardFn = [TRI] (MachineInstr *I) { - if (SIInstrInfo::isVALU(*I)) + auto IsHazardFn = [TRI](const MachineInstr &I) { + if (SIInstrInfo::isVALU(I)) return false; - return I->readsRegister(AMDGPU::EXEC, TRI); + return I.readsRegister(AMDGPU::EXEC, TRI); }; const SIInstrInfo *TII = ST.getInstrInfo(); - auto IsExpiredFn = [TII, TRI] (MachineInstr *MI, int) { - if (!MI) - return false; - if (SIInstrInfo::isVALU(*MI)) { - if (TII->getNamedOperand(*MI, AMDGPU::OpName::sdst)) + auto IsExpiredFn = [TII, TRI](const MachineInstr &MI, int) { + if (SIInstrInfo::isVALU(MI)) { + if (TII->getNamedOperand(MI, AMDGPU::OpName::sdst)) return true; - for (auto MO : MI->implicit_operands()) + for (auto MO : MI.implicit_operands()) if (MO.isDef() && TRI->isSGPRClass(TRI->getPhysRegClass(MO.getReg()))) return true; } - if (MI->getOpcode() == AMDGPU::S_WAITCNT_DEPCTR && - (MI->getOperand(0).getImm() & 0xfffe) == 0xfffe) + if (MI.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR && + (MI.getOperand(0).getImm() & 0xfffe) == 0xfffe) return true; return false; }; @@ -1061,52 +1078,71 @@ bool GCNHazardRecognizer::fixVcmpxExecWARHazard(MachineInstr *MI) { return true; } -bool GCNHazardRecognizer::fixLdsBranchVmemWARHazard(MachineInstr *MI) { +static bool shouldRunLdsBranchVmemWARHazardFixup(const MachineFunction &MF, + const GCNSubtarget &ST) { if (!ST.hasLdsBranchVmemWARHazard()) return false; - auto IsHazardInst = [] (const MachineInstr *MI) { - if (SIInstrInfo::isDS(*MI)) + // Check if the necessary condition for the hazard is met: both LDS and VMEM + // instructions need to appear in the same function. + bool HasLds = false; + bool HasVmem = false; + for (auto &MBB : MF) { + for (auto &MI : MBB) { + HasLds |= SIInstrInfo::isDS(MI); + HasVmem |= + SIInstrInfo::isVMEM(MI) || SIInstrInfo::isSegmentSpecificFLAT(MI); + if (HasLds && HasVmem) + return true; + } + } + return false; +} + +bool GCNHazardRecognizer::fixLdsBranchVmemWARHazard(MachineInstr *MI) { + if (!RunLdsBranchVmemWARHazardFixup) + return false; + + assert(ST.hasLdsBranchVmemWARHazard()); + + auto IsHazardInst = [](const MachineInstr &MI) { + if (SIInstrInfo::isDS(MI)) return 1; - if (SIInstrInfo::isVMEM(*MI) || SIInstrInfo::isSegmentSpecificFLAT(*MI)) + if (SIInstrInfo::isVMEM(MI) || SIInstrInfo::isSegmentSpecificFLAT(MI)) return 2; return 0; }; - auto InstType = IsHazardInst(MI); + auto InstType = IsHazardInst(*MI); if (!InstType) return false; - auto IsExpiredFn = [&IsHazardInst] (MachineInstr *I, int) { - return I && (IsHazardInst(I) || - (I->getOpcode() == AMDGPU::S_WAITCNT_VSCNT && - I->getOperand(0).getReg() == AMDGPU::SGPR_NULL && - !I->getOperand(1).getImm())); + auto IsExpiredFn = [&IsHazardInst](const MachineInstr &I, int) { + return IsHazardInst(I) || (I.getOpcode() == AMDGPU::S_WAITCNT_VSCNT && + I.getOperand(0).getReg() == AMDGPU::SGPR_NULL && + !I.getOperand(1).getImm()); }; - auto IsHazardFn = [InstType, &IsHazardInst] (MachineInstr *I) { - if (!I->isBranch()) + auto IsHazardFn = [InstType, &IsHazardInst](const MachineInstr &I) { + if (!I.isBranch()) return false; - auto IsHazardFn = [InstType, IsHazardInst] (MachineInstr *I) { + auto IsHazardFn = [InstType, IsHazardInst](const MachineInstr &I) { auto InstType2 = IsHazardInst(I); return InstType2 && InstType != InstType2; }; - auto IsExpiredFn = [InstType, &IsHazardInst] (MachineInstr *I, int) { - if (!I) - return false; - + auto IsExpiredFn = [InstType, &IsHazardInst](const MachineInstr &I, int) { auto InstType2 = IsHazardInst(I); if (InstType == InstType2) return true; - return I->getOpcode() == AMDGPU::S_WAITCNT_VSCNT && - I->getOperand(0).getReg() == AMDGPU::SGPR_NULL && - !I->getOperand(1).getImm(); + return I.getOpcode() == AMDGPU::S_WAITCNT_VSCNT && + I.getOperand(0).getReg() == AMDGPU::SGPR_NULL && + !I.getOperand(1).getImm(); }; - return ::getWaitStatesSince(IsHazardFn, I, IsExpiredFn) != + return ::getWaitStatesSince(IsHazardFn, &I, IsExpiredFn) != std::numeric_limits<int>::max(); }; @@ -1137,12 +1173,12 @@ int GCNHazardRecognizer::checkNSAtoVMEMHazard(MachineInstr *MI) { if (!Offset || (Offset->getImm() & 6) == 0) return 0; - auto IsHazardFn = [TII] (MachineInstr *I) { - if (!SIInstrInfo::isMIMG(*I)) + auto IsHazardFn = [TII](const MachineInstr &I) { + if (!SIInstrInfo::isMIMG(I)) return false; - const AMDGPU::MIMGInfo *Info = AMDGPU::getMIMGInfo(I->getOpcode()); + const AMDGPU::MIMGInfo *Info = AMDGPU::getMIMGInfo(I.getOpcode()); return Info->MIMGEncoding == AMDGPU::MIMGEncGfx10NSA && - TII->getInstSizeInBytes(*I) >= 16; + TII->getInstSizeInBytes(I) >= 16; }; return NSAtoVMEMWaitStates - getWaitStatesSince(IsHazardFn, 1); @@ -1154,17 +1190,17 @@ int GCNHazardRecognizer::checkFPAtomicToDenormModeHazard(MachineInstr *MI) { if (MI->getOpcode() != AMDGPU::S_DENORM_MODE) return 0; - auto IsHazardFn = [] (MachineInstr *I) { - if (!SIInstrInfo::isVMEM(*I) && !SIInstrInfo::isFLAT(*I)) + auto IsHazardFn = [](const MachineInstr &I) { + if (!SIInstrInfo::isVMEM(I) && !SIInstrInfo::isFLAT(I)) return false; - return SIInstrInfo::isFPAtomic(*I); + return SIInstrInfo::isFPAtomic(I); }; - auto IsExpiredFn = [] (MachineInstr *MI, int WaitStates) { - if (WaitStates >= 3 || SIInstrInfo::isVALU(*MI)) + auto IsExpiredFn = [](const MachineInstr &MI, int WaitStates) { + if (WaitStates >= 3 || SIInstrInfo::isVALU(MI)) return true; - switch (MI->getOpcode()) { + switch (MI.getOpcode()) { case AMDGPU::S_WAITCNT: case AMDGPU::S_WAITCNT_VSCNT: case AMDGPU::S_WAITCNT_VMCNT: @@ -1179,7 +1215,6 @@ int GCNHazardRecognizer::checkFPAtomicToDenormModeHazard(MachineInstr *MI) { return false; }; - return FPAtomicToDenormModeWaitStates - ::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn); } @@ -1187,11 +1222,15 @@ int GCNHazardRecognizer::checkFPAtomicToDenormModeHazard(MachineInstr *MI) { int GCNHazardRecognizer::checkMAIHazards(MachineInstr *MI) { assert(SIInstrInfo::isMAI(*MI)); + return ST.hasGFX90AInsts() ? checkMAIHazards90A(MI) : checkMAIHazards908(MI); +} + +int GCNHazardRecognizer::checkMAIHazards908(MachineInstr *MI) { int WaitStatesNeeded = 0; unsigned Opc = MI->getOpcode(); - auto IsVALUFn = [] (MachineInstr *MI) { - return SIInstrInfo::isVALU(*MI); + auto IsVALUFn = [](const MachineInstr &MI) { + return SIInstrInfo::isVALU(MI); }; if (Opc != AMDGPU::V_ACCVGPR_READ_B32_e64) { // MFMA or v_accvgpr_write @@ -1220,10 +1259,10 @@ int GCNHazardRecognizer::checkMAIHazards(MachineInstr *MI) { } } - auto IsMFMAFn = [] (MachineInstr *MI) { - return SIInstrInfo::isMAI(*MI) && - MI->getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64 && - MI->getOpcode() != AMDGPU::V_ACCVGPR_READ_B32_e64; + auto IsMFMAFn = [](const MachineInstr &MI) { + return SIInstrInfo::isMAI(MI) && + MI.getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64 && + MI.getOpcode() != AMDGPU::V_ACCVGPR_READ_B32_e64; }; for (const MachineOperand &Op : MI->explicit_operands()) { @@ -1245,15 +1284,15 @@ int GCNHazardRecognizer::checkMAIHazards(MachineInstr *MI) { Register Reg = Op.getReg(); unsigned HazardDefLatency = 0; - auto IsOverlappedMFMAFn = [Reg, &IsMFMAFn, &HazardDefLatency, this] - (MachineInstr *MI) { + auto IsOverlappedMFMAFn = [Reg, &IsMFMAFn, &HazardDefLatency, + this](const MachineInstr &MI) { if (!IsMFMAFn(MI)) return false; - Register DstReg = MI->getOperand(0).getReg(); + Register DstReg = MI.getOperand(0).getReg(); if (DstReg == Reg) return false; - HazardDefLatency = std::max(HazardDefLatency, - TSchedModel.computeInstrLatency(MI)); + HazardDefLatency = + std::max(HazardDefLatency, TSchedModel.computeInstrLatency(&MI)); return TRI.regsOverlap(DstReg, Reg); }; @@ -1292,10 +1331,10 @@ int GCNHazardRecognizer::checkMAIHazards(MachineInstr *MI) { if (WaitStatesNeeded == MaxWaitStates) return WaitStatesNeeded; // Early exit. - auto IsAccVgprWriteFn = [Reg, this] (MachineInstr *MI) { - if (MI->getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64) + auto IsAccVgprWriteFn = [Reg, this](const MachineInstr &MI) { + if (MI.getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64) return false; - Register DstReg = MI->getOperand(0).getReg(); + Register DstReg = MI.getOperand(0).getReg(); return TRI.regsOverlap(Reg, DstReg); }; @@ -1324,13 +1363,13 @@ int GCNHazardRecognizer::checkMAIHazards(MachineInstr *MI) { Register DstReg = MI->getOperand(0).getReg(); unsigned HazardDefLatency = 0; - auto IsSrcCMFMAFn = [DstReg, &IsMFMAFn, &HazardDefLatency, this] - (MachineInstr *MI) { + auto IsSrcCMFMAFn = [DstReg, &IsMFMAFn, &HazardDefLatency, + this](const MachineInstr &MI) { if (!IsMFMAFn(MI)) return false; - Register Reg = TII.getNamedOperand(*MI, AMDGPU::OpName::src2)->getReg(); - HazardDefLatency = std::max(HazardDefLatency, - TSchedModel.computeInstrLatency(MI)); + Register Reg = TII.getNamedOperand(MI, AMDGPU::OpName::src2)->getReg(); + HazardDefLatency = + std::max(HazardDefLatency, TSchedModel.computeInstrLatency(&MI)); return TRI.regsOverlap(Reg, DstReg); }; @@ -1353,14 +1392,171 @@ int GCNHazardRecognizer::checkMAIHazards(MachineInstr *MI) { return WaitStatesNeeded; } +int GCNHazardRecognizer::checkMAIHazards90A(MachineInstr *MI) { + int WaitStatesNeeded = 0; + unsigned Opc = MI->getOpcode(); + + auto IsMFMAFn = [](const MachineInstr &MI) { + return SIInstrInfo::isMAI(MI) && + MI.getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64 && + MI.getOpcode() != AMDGPU::V_ACCVGPR_READ_B32_e64; + }; + + auto IsLegacyVALUFn = [&IsMFMAFn](const MachineInstr &MI) { + return SIInstrInfo::isVALU(MI) && !IsMFMAFn(MI); + }; + + auto IsLegacyVALUNotDotFn = [&IsMFMAFn](const MachineInstr &MI) { + return SIInstrInfo::isVALU(MI) && !IsMFMAFn(MI) && !SIInstrInfo::isDOT(MI); + }; + + if (!IsMFMAFn(*MI)) + return WaitStatesNeeded; + + const int VALUWritesExecWaitStates = 4; + int WaitStatesNeededForUse = VALUWritesExecWaitStates - + getWaitStatesSinceDef(AMDGPU::EXEC, IsLegacyVALUFn, + VALUWritesExecWaitStates); + WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); + + int SrcCIdx = AMDGPU::getNamedOperandIdx(Opc, AMDGPU::OpName::src2); + + // Loop for both DGEMM and S/HGEMM 2nd instruction. + for (const MachineOperand &Use : MI->explicit_uses()) { + const int LegacyVALUNotDotWritesVGPRWaitStates = 2; + const int SMFMA4x4WritesVGPROverlappedSMFMASrcCWaitStates = 2; + const int SMFMA16x16WritesVGPROverlappedSMFMASrcCWaitStates = 8; + const int SMFMA32x32WritesVGPROverlappedSMFMASrcCWaitStates = 16; + const int SMFMA4x4WritesVGPROverlappedDMFMASrcCWaitStates = 3; + const int SMFMA16x16WritesVGPROverlappedDMFMASrcCWaitStates = 9; + const int SMFMA32x32WritesVGPROverlappedDMFMASrcCWaitStates = 17; + const int DMFMA16x16WritesVGPROverlappedSrcCWaitStates = 9; + const int DMFMA4x4WritesVGPROverlappedSrcCWaitStates = 4; + const int SMFMA4x4WritesVGPROverlappedSrcABWaitStates = 5; + const int SMFMA16x16WritesVGPROverlappedSrcABWaitStates = 11; + const int SMFMA32x32WritesVGPROverlappedSrcABWaitStates = 19; + const int DMFMA4x4WritesVGPROverlappedMFMASrcABWaitStates = 6; + const int DMFMA16x16WritesVGPROverlappedMFMASrcABWaitStates = 11; + const int DMFMA4x4WritesVGPRFullSrcCWaitStates = 4; + const int MaxWaitStates = 19; + + if (!Use.isReg()) + continue; + unsigned Reg = Use.getReg(); + bool FullReg; + const MachineInstr *MI1; + + auto IsOverlappedDGEMMorXDLFn = [Reg, &IsMFMAFn, &FullReg, &MI1, + this](const MachineInstr &MI) { + if (!IsMFMAFn(MI)) + return false; + if (!isDGEMM(MI.getOpcode()) && !isXDL(ST, MI)) + return false; + Register DstReg = MI.getOperand(0).getReg(); + FullReg = (DstReg == Reg); + MI1 = &MI; + return TRI.regsOverlap(DstReg, Reg); + }; + + WaitStatesNeededForUse = LegacyVALUNotDotWritesVGPRWaitStates - + getWaitStatesSinceDef(Reg, IsLegacyVALUNotDotFn, MaxWaitStates); + WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); + + int NumWaitStates = getWaitStatesSinceDef(Reg, IsOverlappedDGEMMorXDLFn, + MaxWaitStates); + if (NumWaitStates == std::numeric_limits<int>::max()) + continue; + + int OpNo = MI->getOperandNo(&Use); + unsigned Opc1 = MI1->getOpcode(); + int NeedWaitStates = 0; + if (OpNo == SrcCIdx) { + if (!isDGEMM(Opc) && isDGEMM(Opc1)) { + NeedWaitStates = 0; + } else if (FullReg) { + if ((Opc == AMDGPU::V_MFMA_F64_4X4X4F64_e64 || + Opc == AMDGPU::V_MFMA_F64_4X4X4F64_vgprcd_e64) && + (Opc1 == AMDGPU::V_MFMA_F64_4X4X4F64_e64 || + Opc1 == AMDGPU::V_MFMA_F64_4X4X4F64_vgprcd_e64)) + NeedWaitStates = DMFMA4x4WritesVGPRFullSrcCWaitStates; + } else { + switch (Opc1) { + case AMDGPU::V_MFMA_F64_16X16X4F64_e64: + case AMDGPU::V_MFMA_F64_16X16X4F64_vgprcd_e64: + if (!isXDL(ST, *MI)) + NeedWaitStates = DMFMA16x16WritesVGPROverlappedSrcCWaitStates; + break; + case AMDGPU::V_MFMA_F64_4X4X4F64_e64: + case AMDGPU::V_MFMA_F64_4X4X4F64_vgprcd_e64: + if (!isXDL(ST, *MI)) + NeedWaitStates = DMFMA4x4WritesVGPROverlappedSrcCWaitStates; + break; + default: + switch (TSchedModel.computeInstrLatency(MI1)) { + case 2: + NeedWaitStates = isDGEMM(Opc) + ? SMFMA4x4WritesVGPROverlappedDMFMASrcCWaitStates + : SMFMA4x4WritesVGPROverlappedSMFMASrcCWaitStates; + break; + case 8: + NeedWaitStates = isDGEMM(Opc) + ? SMFMA16x16WritesVGPROverlappedDMFMASrcCWaitStates + : SMFMA16x16WritesVGPROverlappedSMFMASrcCWaitStates; + break; + case 16: LLVM_FALLTHROUGH; + default: + NeedWaitStates = isDGEMM(Opc) + ? SMFMA32x32WritesVGPROverlappedDMFMASrcCWaitStates + : SMFMA32x32WritesVGPROverlappedSMFMASrcCWaitStates; + } + } + } + } else { + switch (Opc1) { + case AMDGPU::V_MFMA_F64_16X16X4F64_e64: + case AMDGPU::V_MFMA_F64_16X16X4F64_vgprcd_e64: + NeedWaitStates = DMFMA16x16WritesVGPROverlappedMFMASrcABWaitStates; + break; + case AMDGPU::V_MFMA_F64_4X4X4F64_e64: + case AMDGPU::V_MFMA_F64_4X4X4F64_vgprcd_e64: + NeedWaitStates = DMFMA4x4WritesVGPROverlappedMFMASrcABWaitStates; + break; + default: + switch (TSchedModel.computeInstrLatency(MI1)) { + case 2: + NeedWaitStates = SMFMA4x4WritesVGPROverlappedSrcABWaitStates; + break; + case 8: + NeedWaitStates = SMFMA16x16WritesVGPROverlappedSrcABWaitStates; + break; + case 16: LLVM_FALLTHROUGH; + default: + NeedWaitStates = SMFMA32x32WritesVGPROverlappedSrcABWaitStates; + } + } + } + if (WaitStatesNeeded >= NeedWaitStates) + continue; + + WaitStatesNeededForUse = NeedWaitStates - NumWaitStates; + WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); + + if (WaitStatesNeeded == MaxWaitStates) + break; + } + + return WaitStatesNeeded; +} + int GCNHazardRecognizer::checkMAILdStHazards(MachineInstr *MI) { - if (!ST.hasMAIInsts()) + // On gfx90a+ releveant hazards are checked in checkMAIVALUHazards() + if (!ST.hasMAIInsts() || ST.hasGFX90AInsts()) return 0; int WaitStatesNeeded = 0; - auto IsAccVgprReadFn = [] (MachineInstr *MI) { - return MI->getOpcode() == AMDGPU::V_ACCVGPR_READ_B32_e64; + auto IsAccVgprReadFn = [](const MachineInstr &MI) { + return MI.getOpcode() == AMDGPU::V_ACCVGPR_READ_B32_e64; }; for (const MachineOperand &Op : MI->explicit_uses()) { @@ -1380,12 +1576,12 @@ int GCNHazardRecognizer::checkMAILdStHazards(MachineInstr *MI) { if (WaitStatesNeeded == MaxWaitStates) return WaitStatesNeeded; // Early exit. - auto IsVALUAccVgprRdWrCheckFn = [Reg, this](MachineInstr *MI) { - if (MI->getOpcode() != AMDGPU::V_ACCVGPR_READ_B32_e64 && - MI->getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64) + auto IsVALUAccVgprRdWrCheckFn = [Reg, this](const MachineInstr &MI) { + if (MI.getOpcode() != AMDGPU::V_ACCVGPR_READ_B32_e64 && + MI.getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64) return false; - auto IsVALUFn = [] (MachineInstr *MI) { - return SIInstrInfo::isVALU(*MI) && !SIInstrInfo::isMAI(*MI); + auto IsVALUFn = [](const MachineInstr &MI) { + return SIInstrInfo::isVALU(MI) && !SIInstrInfo::isMAI(MI); }; return getWaitStatesSinceDef(Reg, IsVALUFn, 2 /*MaxWaitStates*/) < std::numeric_limits<int>::max(); @@ -1399,22 +1595,252 @@ int GCNHazardRecognizer::checkMAILdStHazards(MachineInstr *MI) { return WaitStatesNeeded; } +int GCNHazardRecognizer::checkMAIVALUHazards(MachineInstr *MI) { + if (!ST.hasGFX90AInsts()) + return 0; + + auto IsMFMAFn = [](const MachineInstr &MI) -> bool { + return SIInstrInfo::isMAI(MI) && + MI.getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64 && + MI.getOpcode() != AMDGPU::V_ACCVGPR_READ_B32_e64; + }; + + auto IsDGEMMFn = [](const MachineInstr &MI) -> bool { + return isDGEMM(MI.getOpcode()); + }; + + // This is checked in checkMAIHazards90A() + if (IsMFMAFn(*MI)) + return 0; + + int WaitStatesNeeded = 0; + + bool IsMemOrExport = SIInstrInfo::isVMEM(*MI) || + SIInstrInfo::isFLAT(*MI) || + SIInstrInfo::isDS(*MI) || + SIInstrInfo::isEXP(*MI); + bool IsVALU = SIInstrInfo::isVALU(*MI); + + const MachineInstr *MFMA = nullptr; + unsigned Reg; + auto IsDGEMMorXDLWriteFn = [&Reg, &IsMFMAFn, &MFMA, + this](const MachineInstr &MI) { + if (!IsMFMAFn(MI) || !TRI.regsOverlap(MI.getOperand(0).getReg(), Reg)) + return false; + if (!isDGEMM(MI.getOpcode()) && !isXDL(ST, MI)) + return false; + MFMA = &MI; + return true; + }; + + const MachineInstr *DOT = nullptr; + auto IsDotWriteFn = [&Reg, &DOT, this](const MachineInstr &MI) { + if (!SIInstrInfo::isDOT(MI) || + !TRI.regsOverlap(MI.getOperand(0).getReg(), Reg)) + return false; + DOT = &MI; + return true; + }; + + int SrcCIdx = AMDGPU::getNamedOperandIdx(MI->getOpcode(), + AMDGPU::OpName::src2); + + if (IsMemOrExport || IsVALU) { + const int SMFMA4x4WriteVgprVALUMemExpReadWaitStates = 5; + const int SMFMA16x16WriteVgprVALUMemExpReadWaitStates = 11; + const int SMFMA32x32WriteVgprVALUMemExpReadWaitStates = 19; + const int DMFMA4x4WriteVgprMemExpReadWaitStates = 9; + const int DMFMA16x16WriteVgprMemExpReadWaitStates = 18; + const int DMFMA4x4WriteVgprVALUReadWaitStates = 6; + const int DMFMA16x16WriteVgprVALUReadWaitStates = 11; + const int DotWriteSameDotReadSrcAB = 3; + const int DotWriteDifferentVALURead = 3; + const int MaxWaitStates = 19; + + for (const MachineOperand &Use : MI->explicit_uses()) { + if (!Use.isReg()) + continue; + Reg = Use.getReg(); + + DOT = nullptr; + int WaitStatesSinceDef = getWaitStatesSinceDef(Reg, IsDotWriteFn, + MaxWaitStates); + if (DOT) { + int NeedWaitStates = 0; + if (DOT->getOpcode() == MI->getOpcode()) { + if (&Use - &MI->getOperand(0) != SrcCIdx) + NeedWaitStates = DotWriteSameDotReadSrcAB; + } else { + NeedWaitStates = DotWriteDifferentVALURead; + } + + int WaitStatesNeededForUse = NeedWaitStates - WaitStatesSinceDef; + WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); + } + + MFMA = nullptr; + WaitStatesSinceDef = getWaitStatesSinceDef(Reg, IsDGEMMorXDLWriteFn, + MaxWaitStates); + if (!MFMA) + continue; + + unsigned HazardDefLatency = TSchedModel.computeInstrLatency(MFMA); + int NeedWaitStates = MaxWaitStates; + switch (HazardDefLatency) { + case 2: + NeedWaitStates = SMFMA4x4WriteVgprVALUMemExpReadWaitStates; + break; + case 4: + assert(isDGEMM(MFMA->getOpcode())); + NeedWaitStates = + IsMemOrExport ? DMFMA4x4WriteVgprMemExpReadWaitStates + : DMFMA4x4WriteVgprVALUReadWaitStates; + break; + case 8: + NeedWaitStates = SMFMA16x16WriteVgprVALUMemExpReadWaitStates; + break; + case 16: LLVM_FALLTHROUGH; + default: + NeedWaitStates = + isDGEMM(MFMA->getOpcode()) + ? IsMemOrExport ? DMFMA16x16WriteVgprMemExpReadWaitStates + : DMFMA16x16WriteVgprVALUReadWaitStates + : SMFMA32x32WriteVgprVALUMemExpReadWaitStates; + break; + } + + int WaitStatesNeededForUse = NeedWaitStates - WaitStatesSinceDef; + WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); + + if (WaitStatesNeeded == MaxWaitStates) + break; + } + } + + unsigned Opc = MI->getOpcode(); + const int DMFMAToFMA64WaitStates = 2; + if ((Opc == AMDGPU::V_FMA_F64_e64 || + Opc == AMDGPU::V_FMAC_F64_e32 || Opc == AMDGPU::V_FMAC_F64_e64 || + Opc == AMDGPU::V_FMAC_F64_dpp) && + WaitStatesNeeded < DMFMAToFMA64WaitStates) { + int WaitStatesNeededForUse = DMFMAToFMA64WaitStates - + getWaitStatesSince(IsDGEMMFn, DMFMAToFMA64WaitStates); + WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); + } + + if (!IsVALU && !IsMemOrExport) + return WaitStatesNeeded; + + for (const MachineOperand &Def : MI->defs()) { + const int SMFMA4x4WriteVgprVALUWawWaitStates = 5; + const int SMFMA16x16WriteVgprVALUWawWaitStates = 11; + const int SMFMA32x32WriteVgprVALUWawWaitStates = 19; + const int SMFMA4x4ReadVgprVALUWarWaitStates = 1; + const int SMFMA16x16ReadVgprVALUWarWaitStates = 7; + const int SMFMA32x32ReadVgprVALUWarWaitStates = 15; + const int DMFMA4x4WriteVgprVALUWriteWaitStates = 6; + const int DMFMA16x16WriteVgprVALUWriteWaitStates = 11; + const int DotWriteDifferentVALUWrite = 3; + const int MaxWaitStates = 19; + const int MaxWarWaitStates = 15; + + Reg = Def.getReg(); + + DOT = nullptr; + int WaitStatesSinceDef = getWaitStatesSinceDef(Reg, IsDotWriteFn, + MaxWaitStates); + if (DOT && DOT->getOpcode() != MI->getOpcode()) + WaitStatesNeeded = std::max(WaitStatesNeeded, DotWriteDifferentVALUWrite - + WaitStatesSinceDef); + + MFMA = nullptr; + WaitStatesSinceDef = getWaitStatesSinceDef(Reg, IsDGEMMorXDLWriteFn, + MaxWaitStates); + if (MFMA) { + int NeedWaitStates = MaxWaitStates; + switch (TSchedModel.computeInstrLatency(MFMA)) { + case 2: + NeedWaitStates = SMFMA4x4WriteVgprVALUWawWaitStates; + break; + case 4: + assert(isDGEMM(MFMA->getOpcode())); + NeedWaitStates = DMFMA4x4WriteVgprVALUWriteWaitStates; + break; + case 8: + NeedWaitStates = SMFMA16x16WriteVgprVALUWawWaitStates; + break; + case 16: LLVM_FALLTHROUGH; + default: + NeedWaitStates = isDGEMM(MFMA->getOpcode()) + ? DMFMA16x16WriteVgprVALUWriteWaitStates + : SMFMA32x32WriteVgprVALUWawWaitStates; + break; + } + + int WaitStatesNeededForUse = NeedWaitStates - WaitStatesSinceDef; + WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); + + if (WaitStatesNeeded == MaxWaitStates) + break; + } + + auto IsSMFMAReadAsCFn = [&Reg, &IsMFMAFn, &MFMA, + this](const MachineInstr &MI) { + if (!IsMFMAFn(MI) || isDGEMM(MI.getOpcode()) || + !MI.readsRegister(Reg, &TRI)) + return false; + + const MachineOperand *SrcC = + TII.getNamedOperand(MI, AMDGPU::OpName::src2); + assert(SrcC); + if (!SrcC->isReg() || !TRI.regsOverlap(SrcC->getReg(), Reg)) + return false; + + MFMA = &MI; + return true; + }; + + MFMA = nullptr; + int WaitStatesSinceUse = getWaitStatesSince(IsSMFMAReadAsCFn, + MaxWarWaitStates); + if (!MFMA) + continue; + + unsigned HazardDefLatency = TSchedModel.computeInstrLatency(MFMA); + int NeedWaitStates = MaxWaitStates; + switch (HazardDefLatency) { + case 2: NeedWaitStates = SMFMA4x4ReadVgprVALUWarWaitStates; + break; + case 8: NeedWaitStates = SMFMA16x16ReadVgprVALUWarWaitStates; + break; + case 16: LLVM_FALLTHROUGH; + default: NeedWaitStates = SMFMA32x32ReadVgprVALUWarWaitStates; + break; + } + + int WaitStatesNeededForUse = NeedWaitStates - WaitStatesSinceUse; + WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); + } + + return WaitStatesNeeded; +} + bool GCNHazardRecognizer::ShouldPreferAnother(SUnit *SU) { if (!SU->isInstr()) return false; - MachineInstr *MAI = nullptr; - auto IsMFMAFn = [&MAI] (MachineInstr *MI) { + const MachineInstr *MAI = nullptr; + auto IsMFMAFn = [&MAI](const MachineInstr &MI) { MAI = nullptr; - if (SIInstrInfo::isMAI(*MI) && - MI->getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64 && - MI->getOpcode() != AMDGPU::V_ACCVGPR_READ_B32_e64) - MAI = MI; + if (SIInstrInfo::isMAI(MI) && + MI.getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64 && + MI.getOpcode() != AMDGPU::V_ACCVGPR_READ_B32_e64) + MAI = &MI; return MAI != nullptr; }; MachineInstr *MI = SU->getInstr(); - if (IsMFMAFn(MI)) { + if (IsMFMAFn(*MI)) { int W = getWaitStatesSince(IsMFMAFn, 16); if (MAI) return W < (int)TSchedModel.computeInstrLatency(MAI); |
