46#define DEBUG_TYPE "si-insert-waitcnts"
50template <
typename EmitWaitcntFn>
51static void EmitExpandedWaitcnt(
unsigned Outstanding,
unsigned Target,
52 EmitWaitcntFn &&EmitWaitcnt) {
54 for (
unsigned I = Outstanding - 1;
I >
Target &&
I != ~0u; --
I)
74 TRACKINGID_RANGE_LEN = (1 << 16),
79 REGUNITS_END = REGUNITS_BEGIN + TRACKINGID_RANGE_LEN,
84 NUM_LDSDMA = TRACKINGID_RANGE_LEN,
85 LDSDMA_BEGIN = REGUNITS_END,
86 LDSDMA_END = LDSDMA_BEGIN + NUM_LDSDMA,
90static constexpr VMEMID toVMEMID(MCRegUnit RU) {
91 return static_cast<unsigned>(RU);
103 AMDGPU::S_WAIT_LOADCNT, AMDGPU::S_WAIT_DSCNT,
104 AMDGPU::S_WAIT_EXPCNT, AMDGPU::S_WAIT_STORECNT,
105 AMDGPU::S_WAIT_SAMPLECNT, AMDGPU::S_WAIT_BVHCNT,
106 AMDGPU::S_WAIT_KMCNT, AMDGPU::S_WAIT_XCNT,
107 AMDGPU::S_WAIT_ASYNCCNT, AMDGPU::S_WAIT_TENSORCNT};
112 switch (
MI.getOpcode()) {
113 case AMDGPU::ASYNCMARK:
114 case AMDGPU::WAIT_ASYNCMARK:
117 return MI.isMetaInstruction();
132class WaitcntBrackets;
140class WaitcntGenerator {
142 const GCNSubtarget &ST;
143 const SIInstrInfo &TII;
144 AMDGPU::IsaVersion IV;
147 bool ExpandWaitcntProfiling =
false;
148 const AMDGPU::HardwareLimits &Limits;
152 DenseSet<MachineInstr *> DeferredErasures;
155 WaitcntGenerator() =
delete;
156 WaitcntGenerator(
const WaitcntGenerator &) =
delete;
159 const AMDGPU::HardwareLimits &Limits)
160 : ST(MF.getSubtarget<GCNSubtarget>()), TII(*ST.getInstrInfo()),
164 ExpandWaitcntProfiling(
165 MF.
getFunction().hasFnAttribute(
"amdgpu-expand-waitcnt-profiling")),
170 bool isOptNone()
const {
return OptNone; }
186 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
187 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
190 bool eraseDeferredWaitcnts() {
192 for (MachineInstr *
MI : DeferredErasures) {
193 MI->eraseFromParent();
196 DeferredErasures.clear();
201 bool promoteSoftWaitCnt(MachineInstr *Waitcnt)
const;
206 virtual bool createNewWaitcnt(MachineBasicBlock &
Block,
208 AMDGPU::Waitcnt
Wait,
209 const WaitcntBrackets &ScoreBrackets) = 0;
216 assert(
E.size() == 1 &&
"Cannot handle a mask of events!");
218 if (getWaitEvents(
T) &
E)
229 virtual AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const = 0;
231 virtual ~WaitcntGenerator() =
default;
234class WaitcntGeneratorPreGFX12 final :
public WaitcntGenerator {
238 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_SAMPLER_READ_ACCESS |
239 HWEvents::VMEM_BVH_READ_ACCESS | HWEvents::VMEM_INV_ACCESS |
240 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
242 HWEvents::SMEM_ACCESS | HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS |
243 HWEvents::SQ_MESSAGE,
245 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
246 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
247 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
256 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_SAMPLER_READ_ACCESS |
257 HWEvents::VMEM_BVH_READ_ACCESS | HWEvents::VMEM_INV_ACCESS,
259 HWEvents::SMEM_ACCESS | HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS |
260 HWEvents::SQ_MESSAGE,
262 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
263 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
264 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
266 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
272 using WaitcntGenerator::WaitcntGenerator;
273 bool applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
274 MachineInstr &OldWaitcntInstr,
275 AMDGPU::Waitcnt &
Wait,
278 bool createNewWaitcnt(MachineBasicBlock &
Block,
280 AMDGPU::Waitcnt
Wait,
281 const WaitcntBrackets &ScoreBrackets)
override;
284 return ST.hasVscnt() ? WaitEventMaskForInstWithVscnt[
T]
285 : WaitEventMaskForInstWithoutVscnt[
T];
288 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
291class WaitcntGeneratorGFX12Plus final :
public WaitcntGenerator {
296 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_INV_ACCESS,
297 HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS,
298 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
299 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
300 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
302 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
303 HWEvents::VMEM_SAMPLER_READ_ACCESS,
304 HWEvents::VMEM_BVH_READ_ACCESS,
306 HWEvents::SMEM_ACCESS | HWEvents::SQ_MESSAGE | HWEvents::SCC_WRITE,
307 HWEvents::VMEM_GROUP | HWEvents::SMEM_GROUP,
308 HWEvents::ASYNC_ACCESS,
309 HWEvents::TENSOR_ACCESS,
310 HWEvents::VGPR_CSMACC_READ | HWEvents::VGPR_DPMACC_READ |
311 HWEvents::VGPR_TRANS_READ | HWEvents::VGPR_XDL_READ,
312 HWEvents::VGPR_CSMACC_WRITE | HWEvents::VGPR_DPMACC_WRITE |
313 HWEvents::VGPR_TRANS_WRITE | HWEvents::VGPR_XDL_WRITE,
314 HWEvents::VGPR_LDS_READ | HWEvents::VGPR_FLAT_READ |
315 HWEvents::VGPR_VMEM_READ};
318 WaitcntGeneratorGFX12Plus() =
delete;
321 const AMDGPU::HardwareLimits &Limits,
323 : WaitcntGenerator(MF, MaxCounter, Limits), IsExpertMode(IsExpertMode) {}
325 bool applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
326 MachineInstr &OldWaitcntInstr,
327 AMDGPU::Waitcnt &
Wait,
330 bool createNewWaitcnt(MachineBasicBlock &
Block,
332 AMDGPU::Waitcnt
Wait,
333 const WaitcntBrackets &ScoreBrackets)
override;
336 return WaitEventMaskForInstGFX12Plus[
T];
339 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
343struct PreheaderFlushFlags {
344 bool FlushVmCnt =
false;
345 bool FlushDsCnt =
false;
348class SIInsertWaitcnts {
349 DenseMap<const Value *, MachineBasicBlock *> SLoadAddresses;
350 DenseMap<MachineBasicBlock *, PreheaderFlushFlags> PreheadersToFlush;
351 MachineLoopInfo &MLI;
352 MachinePostDominatorTree &PDT;
357 std::unique_ptr<WaitcntBrackets> Incoming;
359 BlockInfo() =
default;
360 BlockInfo(BlockInfo &&) =
default;
361 BlockInfo &operator=(BlockInfo &&) =
default;
365 MapVector<MachineBasicBlock *, BlockInfo> BlockInfos;
369 std::unique_ptr<WaitcntGenerator> WCG;
372 DenseSet<MachineInstr *> CallInsts;
373 DenseSet<MachineInstr *> ReturnInsts;
378 DenseMap<MachineInstr *, bool> EndPgmInsts;
380 AMDGPU::HardwareLimits Limits;
383 const GCNSubtarget &ST;
384 const SIInstrInfo &TII;
385 const SIRegisterInfo &TRI;
386 const MachineRegisterInfo &MRI;
389 bool IsExpertMode =
false;
392 SIInsertWaitcnts(MachineLoopInfo &MLI, MachinePostDominatorTree &PDT,
394 : MLI(MLI), PDT(PDT), AA(AA), MF(MF), ST(MF.getSubtarget<GCNSubtarget>()),
395 TII(*ST.getInstrInfo()), TRI(TII.getRegisterInfo()),
396 MRI(MF.getRegInfo()),
397 TgSplit(ST.hasTgSplitSupport() &&
400 const AMDGPU::HardwareLimits &getLimits()
const {
return Limits; }
402 PreheaderFlushFlags getPreheaderFlushFlags(MachineLoop *
ML,
403 const WaitcntBrackets &Brackets);
404 PreheaderFlushFlags isPreheaderToFlush(MachineBasicBlock &
MBB,
405 const WaitcntBrackets &ScoreBrackets);
406 bool isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const;
407 bool isDSRead(
const MachineInstr &
MI)
const;
408 bool mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const;
411 bool isVmemAccess(
const MachineInstr &
MI)
const;
412 bool generateWaitcntInstBefore(MachineInstr &
MI,
413 WaitcntBrackets &ScoreBrackets,
414 MachineInstr *OldWaitcntInstr,
415 PreheaderFlushFlags FlushFlags);
416 bool generateWaitcnt(AMDGPU::Waitcnt
Wait,
418 MachineBasicBlock &
Block, WaitcntBrackets &ScoreBrackets,
419 MachineInstr *OldWaitcntInstr);
420 void updateEventWaitcntAfter(MachineInstr &Inst,
421 WaitcntBrackets *ScoreBrackets);
423 MachineBasicBlock *
Block)
const;
424 bool insertForcedWaitAfter(MachineInstr &Inst, MachineBasicBlock &
Block,
425 WaitcntBrackets &ScoreBrackets);
427 WaitcntBrackets &ScoreBrackets);
430 bool removeRedundantSoftXcnts(MachineBasicBlock &
Block);
432 bool ExpertMode)
const;
434 return WCG->getWaitEvents(
T);
437 return WCG->getCounterFromEvent(
E);
449class WaitcntBrackets {
451 WaitcntBrackets(
const SIInsertWaitcnts *Context) : Context(Context) {
452 assert(Context->TRI.getNumRegUnits() < REGUNITS_END);
457 unsigned NumUnusedVmem = 0, NumUnusedSGPRs = 0;
458 for (
auto &[ID, Val] : VMem) {
462 for (
auto &[ID, Val] : SGPRs) {
467 if (NumUnusedVmem || NumUnusedSGPRs) {
468 errs() <<
"WaitcntBracket had unused entries at destruction time: "
469 << NumUnusedVmem <<
" VMem and " << NumUnusedSGPRs
470 <<
" SGPR unused entries\n";
481 return ScoreUBs[
T] - ScoreLBs[
T];
485 return getVMemScore(ID,
T) > getScoreLB(
T);
503 return getScoreUB(
T) - getScoreLB(
T);
507 auto It = SGPRs.find(RU);
508 return It != SGPRs.end() ? It->second.get(
T) : 0;
512 auto It = VMem.find(TID);
513 return It != VMem.end() ? It->second.Scores[
T] : 0;
518 unsigned getNumOrderedXDLsAfterWrite(VMEMID TID)
const {
519 return std::min(OrderedXDLCount - VMem.at(TID).OrderedXDLCountAtWrite,
535 void simplifyWaitcnt(AMDGPU::Waitcnt &
Wait)
const {
538 void simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
539 AMDGPU::Waitcnt &UpdateWait)
const;
542 void simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
543 AMDGPU::Waitcnt &UpdateWait)
const;
544 void simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
545 AMDGPU::Waitcnt &UpdateWait)
const;
548 void simplifyVmemInvOnlyWait(AMDGPU::Waitcnt &
Wait)
const {
552 if (LoadEvents == HWEvents::VMEM_INV_ACCESS)
557 AMDGPU::Waitcnt &
Wait,
558 const MachineInstr &
MI)
const;
559 MCRegister determineVGPR16Dependency(
const MachineInstr &
MI,
561 MCRegister
Reg)
const;
563 AMDGPU::Waitcnt &
Wait)
const;
565 void tryClearSCCWriteEvent(MachineInstr *Inst);
567 void applyWaitcnt(
const AMDGPU::Waitcnt &
Wait);
573 HWEvents getPendingEvents()
const {
return PendingEvents; }
574 bool hasPendingEvent()
const {
return PendingEvents.
any(); }
575 bool hasPendingEvent(
HWEvents E)
const {
return PendingEvents.contains(
E); }
577 bool HasPending = (PendingEvents & Context->getWaitEvents(
T)).any();
579 "Expected pending events iff scoreboard is not empty");
584 HWEvents Events = PendingEvents & Context->getWaitEvents(
T);
586 return Events.
size() > 1;
589 bool hasPendingFlat()
const {
596 void setPendingFlat() {
601 bool hasPendingGDS()
const {
606 unsigned getPendingGDSWait()
const {
615 bool hasDifferentVGPRPendingEvents(MCRegister
Reg,
HWEvents E)
const {
616 for (MCRegUnit RU : regunits(
Reg)) {
617 auto It = VMem.find(toVMEMID(RU));
618 if (It != VMem.end() && (It->second.VGPRPendingEvents & ~
E).any())
624 void clearVGPRPendingEvents(MCRegister
Reg) {
625 for (MCRegUnit RU : regunits(
Reg)) {
626 if (
auto It = VMem.find(toVMEMID(RU)); It != VMem.end()) {
628 if (It->second.empty())
634 void setStateOnFunctionEntryOrReturn() {
640 ArrayRef<const MachineInstr *> getLDSDMAStores()
const {
644 bool hasPointSampleAccel(
const MachineInstr &
MI)
const;
645 bool hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
646 MCRegister RU)
const;
648 void print(raw_ostream &)
const;
653 void purgeEmptyTrackingData();
657 return Context->getLimits().get(
T);
667 using CounterValueArray = std::array<unsigned, AMDGPU::NUM_INST_CNTS>;
671 AMDGPU::Waitcnt &
Wait)
const;
672 void determineWaitForVAVDSTWrite(VMEMID ID, AMDGPU::Waitcnt &
Wait)
const;
674 static bool mergeScore(
const MergeInfo &M,
unsigned &Score,
675 unsigned OtherScore);
681 assert(
Reg != AMDGPU::SCC &&
"Shouldn't be used on SCC");
682 if (!Context->TRI.isInAllocatableClass(
Reg))
684 return Context->TRI.regunits(
Reg);
705 const SIRegisterInfo &
TRI = Context->TRI;
706 if (
Reg == AMDGPU::SCC) {
708 }
else if (
TRI.isVectorRegister(Context->MRI,
Reg)) {
709 for (MCRegUnit RU : regunits(
Reg)) {
710 auto &
Info = VMem[toVMEMID(RU)];
711 Info.Scores[
T] = Val;
713 Info.OrderedXDLCountAtWrite = OrderedXDLCount;
715 }
else if (
TRI.isSGPRReg(Context->MRI,
Reg)) {
716 for (MCRegUnit RU : regunits(
Reg))
717 SGPRs[RU].get(
T) = Val;
724 VMem[TID].Scores[
T] = Val;
727 void setScoreByOperand(
const MachineOperand &
Op,
730 const SIInsertWaitcnts *Context;
736 unsigned LastFlatDsCnt = 0;
737 unsigned LastFlatLoadCnt = 0;
739 unsigned LastGDS = 0;
742 unsigned OrderedXDLCount = 0;
759 CounterValueArray Scores{};
762 unsigned OrderedXDLCountAtWrite = 0;
776 unsigned ScoreDsKmCnt = 0;
777 unsigned ScoreXCnt = 0;
793 bool empty()
const {
return !ScoreDsKmCnt && !ScoreXCnt; }
796 DenseMap<VMEMID, VMEMInfo> VMem;
797 DenseMap<MCRegUnit, SGPRInfo> SGPRs;
800 unsigned SCCScore = 0;
802 const MachineInstr *PendingSCCWrite =
nullptr;
806 SmallVector<const MachineInstr *> LDSDMAStores;
816 static constexpr unsigned MaxAsyncMarks = 16;
820 std::array<CounterValueArray, AMDGPU::NumAsyncStages> AsyncScore{};
823SIInsertWaitcnts::BlockInfo::~BlockInfo() =
default;
828 SIInsertWaitcntsLegacy() : MachineFunctionPass(ID) {}
832 StringRef getPassName()
const override {
833 return "SI insert wait instructions";
836 void getAnalysisUsage(AnalysisUsage &AU)
const override {
839 AU.
addRequired<MachinePostDominatorTreeWrapperPass>();
851 setRegScore(
Op.getReg().asMCReg(), CntTy, Score);
859bool WaitcntBrackets::hasPointSampleAccel(
const MachineInstr &
MI)
const {
864 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
874bool WaitcntBrackets::hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
875 MCRegister
Reg)
const {
876 if (!hasPointSampleAccel(
MI))
879 return hasDifferentVGPRPendingEvents(
Reg, HWEvents::VMEM_READ_ACCESS);
882void WaitcntBrackets::updateByEvent(
HWEvents E, MachineInstr &Inst) {
883 assert(
E.size() == 1 &&
"Expected singular event!");
887 unsigned UB = getScoreUB(
T);
891 Context->ST.hasVOP3PX2IncrementsVaVdstTwice()) {
903 setScoreUB(
T, CurrScore);
908 if (
E == HWEvents::VGPR_XDL_WRITE &&
Context->TII.isVAVDSTOrderedXDL(Inst))
912 const MachineRegisterInfo &
MRI =
Context->MRI;
921 if (
const auto *AddrOp =
TII.getNamedOperand(Inst, AMDGPU::OpName::addr))
925 if (
const auto *Data0 =
926 TII.getNamedOperand(Inst, AMDGPU::OpName::data0))
928 if (
const auto *Data1 =
929 TII.getNamedOperand(Inst, AMDGPU::OpName::data1))
933 Inst.
getOpcode() != AMDGPU::DS_CONSUME &&
934 Inst.
getOpcode() != AMDGPU::DS_ORDERED_COUNT) {
935 for (
const MachineOperand &
Op : Inst.
all_uses()) {
936 if (
TRI.isVectorRegister(
MRI,
Op.getReg()))
940 }
else if (
TII.isFLAT(Inst)) {
942 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
945 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
948 }
else if (
TII.isMIMG(Inst)) {
952 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
955 }
else if (
TII.isMTBUF(Inst)) {
958 }
else if (
TII.isMUBUF(Inst)) {
962 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
965 }
else if (
TII.isLDSDIR(Inst)) {
967 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::vdst),
970 if (
TII.isEXP(Inst)) {
975 for (MachineOperand &DefMO : Inst.
all_defs()) {
976 if (
TRI.isVGPR(
MRI, DefMO.getReg())) {
981 for (
const MachineOperand &
Op : Inst.
all_uses()) {
982 if (
TRI.isVectorRegister(
MRI,
Op.getReg()))
988 E == HWEvents::SMEM_GROUP ? HWEvents::VMEM_GROUP : HWEvents::SMEM_GROUP;
989 if (PendingEvents.
contains(OtherEvent)) {
994 setScoreLB(
T, getScoreUB(
T) - 1);
995 PendingEvents -= OtherEvent;
997 for (
const MachineOperand &
Op : Inst.
all_uses())
998 setScoreByOperand(
Op,
T, CurrScore);
1003 for (
const MachineOperand &
Op : Inst.
operands()) {
1016 setScoreByOperand(
Op,
T, CurrScore);
1028 for (
const MachineOperand &
Op : Inst.
defs()) {
1031 if (!
TRI.isVectorRegister(
MRI,
Op.getReg()))
1033 if (updateVMCntOnly(Inst)) {
1042 if (hasPointSampleAccel(Inst))
1043 VGPRContext |= HWEvents::VMEM_READ_ACCESS;
1044 for (MCRegUnit RU : regunits(
Op.getReg().asMCReg()))
1045 VMem[toVMEMID(RU)].VGPRPendingEvents |= VGPRContext;
1048 setScoreByOperand(
Op,
T, CurrScore);
1051 (
TII.isDS(Inst) ||
TII.isNonAsyncLdsDmaWrite(Inst))) {
1060 if (!MemOp->isStore() ||
1065 auto AAI = MemOp->getAAInfo();
1071 if (!AAI || !AAI.Scope)
1073 for (
size_t I = 0,
E = LDSDMAStores.
size();
I !=
E && !Slot; ++
I) {
1074 for (
const auto *MemOp : LDSDMAStores[
I]->memoperands()) {
1075 if (MemOp->isStore() && AAI == MemOp->getAAInfo()) {
1090 setVMemScore(LDSDMA_BEGIN,
T, CurrScore);
1091 if (Slot && Slot < NUM_LDSDMA)
1092 setVMemScore(LDSDMA_BEGIN +
static_cast<VMEMID
>(Slot),
T, CurrScore);
1100 setRegScore(AMDGPU::SCC,
T, CurrScore);
1101 PendingSCCWrite = &Inst;
1106void WaitcntBrackets::recordAsyncMark(MachineInstr &Inst,
1116 LLVM_DEBUG(
dbgs() <<
"recordAsyncMark(stages=" << Stages <<
"):\n" << Inst);
1119 AsyncMarks[
SI].push_back(AsyncScore[SI]);
1121 dbgs() <<
" stage " << S <<
":\n";
1122 for (
const auto &Mark : AsyncMarks[SI]) {
1131void WaitcntBrackets::print(raw_ostream &OS)
const {
1135 unsigned SR = getScoreRange(
T);
1138 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM") <<
"_CNT("
1142 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM") <<
"_CNT("
1146 OS <<
" EXP_CNT(" << SR <<
"):";
1149 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS") <<
"_CNT("
1153 OS <<
" SAMPLE_CNT(" << SR <<
"):";
1156 OS <<
" BVH_CNT(" << SR <<
"):";
1159 OS <<
" KM_CNT(" << SR <<
"):";
1162 OS <<
" X_CNT(" << SR <<
"):";
1165 OS <<
" ASYNC_CNT(" << SR <<
"):";
1168 OS <<
" VA_VDST_RD(" << SR <<
"): ";
1171 OS <<
" VA_VDST_WR(" << SR <<
"): ";
1174 OS <<
" VM_VSRC(" << SR <<
"): ";
1177 OS <<
" UNKNOWN(" << SR <<
"):";
1183 unsigned LB = getScoreLB(
T);
1186 sort(SortedVMEMIDs);
1188 for (
auto ID : SortedVMEMIDs) {
1189 unsigned RegScore = VMem.at(ID).Scores[
T];
1192 unsigned RelScore = RegScore - LB - 1;
1193 if (ID < REGUNITS_END) {
1194 OS <<
' ' << RelScore <<
':'
1197 assert(ID >= LDSDMA_BEGIN && ID < LDSDMA_END &&
1198 "Unhandled/unexpected ID value!");
1199 OS <<
' ' << RelScore <<
":LDSDMA" <<
ID;
1204 if (isSmemCounter(
T)) {
1206 sort(SortedSMEMIDs);
1207 for (
auto ID : SortedSMEMIDs) {
1208 unsigned RegScore = SGPRs.at(ID).get(
T);
1211 unsigned RelScore = RegScore - LB - 1;
1212 OS <<
' ' << RelScore <<
':'
1218 OS <<
' ' << SCCScore <<
":scc";
1223 OS <<
"Pending Events: ";
1224 if (hasPendingEvent()) {
1225 OS << getPendingEvents();
1233 const CounterValueArray &Score = AsyncScore[
SI];
1234 OS <<
"Async score (stage " << S <<
"): ";
1241 OS <<
"Async marks (stage " << S <<
"): " << AsyncMarks[
SI].size() <<
'\n';
1243 for (
const auto &Mark : AsyncMarks[SI]) {
1245 unsigned MarkedScore = Mark[
T];
1248 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM")
1249 <<
"_CNT: " << MarkedScore;
1252 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM")
1253 <<
"_CNT: " << MarkedScore;
1256 OS <<
" EXP_CNT: " << MarkedScore;
1259 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS")
1260 <<
"_CNT: " << MarkedScore;
1263 OS <<
" SAMPLE_CNT: " << MarkedScore;
1266 OS <<
" BVH_CNT: " << MarkedScore;
1269 OS <<
" KM_CNT: " << MarkedScore;
1272 OS <<
" X_CNT: " << MarkedScore;
1275 OS <<
" ASYNC_CNT: " << MarkedScore;
1278 OS <<
" UNKNOWN: " << MarkedScore;
1290void WaitcntBrackets::simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
1291 AMDGPU::Waitcnt &UpdateWait)
const {
1299 simplifyXcnt(CheckWait, UpdateWait);
1302 simplifyVmVsrc(CheckWait, UpdateWait);
1307 unsigned &
Count)
const {
1311 if (
Count >= getScoreRange(
T))
1315void WaitcntBrackets::simplifyWaitcnt(AMDGPU::Waitcnt &
Wait,
1317 unsigned Cnt =
Wait.get(
T);
1318 simplifyWaitcnt(
T, Cnt);
1322void WaitcntBrackets::simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
1323 AMDGPU::Waitcnt &UpdateWait)
const {
1333 hasPendingEvent(HWEvents::SMEM_GROUP))
1339 hasPendingEvent(HWEvents::VMEM_GROUP) &&
1346void WaitcntBrackets::simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
1347 AMDGPU::Waitcnt &UpdateWait)
const {
1357 return Acc |
Context->getWaitEvents(
T);
1359 HWEvents PendingVmemEvents = PendingEvents & VmemEvents;
1361 unsigned CheckCount = CheckWait.
get(
T);
1363 (CheckCount == 0 || !counterOutOfOrder(
T)) &&
1364 (PendingVmemEvents & ~
Context->getWaitEvents(
T)) == 0)
1371void WaitcntBrackets::purgeEmptyTrackingData() {
1372 VMem.remove_if([](
const auto &
P) {
return P.second.empty(); });
1373 SGPRs.remove_if([](
const auto &
P) {
return P.second.empty(); });
1378 unsigned ScoreToWait)
const {
1379 const unsigned LB = getScoreLB(
T);
1380 const unsigned UB = getScoreUB(
T);
1383 if ((UB >= ScoreToWait) && (ScoreToWait > LB)) {
1385 !
Context->ST.hasFlatLgkmVMemCountInOrder()) {
1391 if (counterOutOfOrder(
T)) {
1399 return std::min(UB - ScoreToWait, getLimit(
T) - 1);
1405 unsigned ScoreToWait,
1406 AMDGPU::Waitcnt &
Wait)
const {
1407 Wait.add(
T, getWaitForScore(
T, ScoreToWait));
1410void WaitcntBrackets::determineWaitForVAVDSTWrite(VMEMID ID,
1411 AMDGPU::Waitcnt &
Wait)
const {
1421 std::max(
Count, getNumOrderedXDLsAfterWrite(ID)));
1429 AMDGPU::Waitcnt
Wait;
1433 dbgs() <<
"Need " <<
N <<
" async marks in stage " << S <<
". Found "
1434 << StageMarks.size() <<
":\n";
1435 for (
const auto &Mark : StageMarks) {
1441 unsigned StageN =
N;
1442 if (StageMarks.size() == MaxAsyncMarks) {
1448 dbgs() <<
"Possible truncation. Ensuring a non-trivial wait.\n");
1449 StageN = std::min(StageN, (
unsigned)MaxAsyncMarks - 1);
1452 if (StageMarks.size() <= StageN) {
1457 size_t MarkIndex = StageMarks.size() - StageN - 1;
1458 const auto &RequiredMark = StageMarks[MarkIndex];
1460 determineWaitForScore(
T, RequiredMark[
T],
Wait);
1467 dbgs() <<
"Removing " << (MarkIndex + 1)
1468 <<
" async marks after determining wait\n";
1470 StageMarks.erase(StageMarks.begin(), StageMarks.begin() + MarkIndex + 1);
1484MCRegister WaitcntBrackets::determineVGPR16Dependency(
const MachineInstr &
MI,
1486 MCRegister
Reg)
const {
1490 if (
Size != 16 || !
Context->ST.hasD16Writes32BitVgpr())
1500 AMDGPU::Waitcnt
Wait;
1501 for (MCRegUnit RU : regunits(OtherHalf)) {
1503 determineWaitForVAVDSTWrite(toVMEMID(RU),
Wait);
1505 determineWaitForScore(
T, getVMemScore(toVMEMID(RU),
T),
Wait);
1509 if (!
Wait.hasWait())
1519 HWEvents Events = MIEvents & OtherHalfEvents;
1520 if (Events.
size() > 1)
1527 AMDGPU::Waitcnt &
Wait,
1528 const MachineInstr &
MI)
const {
1529 if (
Reg == AMDGPU::SCC) {
1530 determineWaitForScore(
T, SCCScore,
Wait);
1534 Reg = determineVGPR16Dependency(
MI,
T,
Reg);
1535 for (MCRegUnit RU : regunits(
Reg)) {
1537 determineWaitForVAVDSTWrite(toVMEMID(RU),
Wait);
1539 determineWaitForScore(
1540 T, IsVGPR ? getVMemScore(toVMEMID(RU),
T) : getSGPRScore(RU,
T),
1549 AMDGPU::Waitcnt &
Wait)
const {
1550 assert(TID >= LDSDMA_BEGIN && TID < LDSDMA_END);
1551 determineWaitForScore(
T, getVMemScore(TID,
T),
Wait);
1554void WaitcntBrackets::tryClearSCCWriteEvent(MachineInstr *Inst) {
1557 if (PendingSCCWrite &&
1558 PendingSCCWrite->
getOpcode() == AMDGPU::S_BARRIER_SIGNAL_ISFIRST_IMM &&
1560 HWEvents SCC_WRITE_PendingEvent = HWEvents::SCC_WRITE;
1563 SCC_WRITE_PendingEvent) {
1567 PendingEvents -= SCC_WRITE_PendingEvent;
1568 PendingSCCWrite =
nullptr;
1572void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait) {
1581 applyWaitcnt(
T, Cnt);
1586 const unsigned UB = getScoreUB(
T);
1590 if (counterOutOfOrder(
T))
1592 setScoreLB(
T, std::max(getScoreLB(
T), UB -
Count));
1595 PendingEvents -=
Context->getWaitEvents(
T);
1599 hasPendingEvent(HWEvents::SMEM_GROUP)) {
1603 PendingEvents -= HWEvents::SMEM_GROUP;
1609 else if (
Count == 0)
1610 PendingEvents -= HWEvents::VMEM_GROUP;
1614void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait,
1616 unsigned Cnt =
Wait.get(
T);
1617 applyWaitcnt(
T, Cnt);
1624 if ((
T ==
Context->SmemAccessCounter &&
1625 hasPendingEvent(HWEvents::SMEM_ACCESS)) ||
1642 static constexpr HWEvents ExtendedImageEvents =
1643 HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
1644 if (!
Context->ST.hasExtendedWaitCounts() &&
1645 (Events & ExtendedImageEvents).any()) {
1646 Events -= ExtendedImageEvents;
1647 Events |= HWEvents::VMEM_READ_ACCESS;
1653 Events -= HWEvents::VMEM_INV_ACCESS;
1655 return Events.
size() > 1;
1658 return hasMixedPendingEvents(
T);
1668char SIInsertWaitcntsLegacy::ID = 0;
1673 return new SIInsertWaitcntsLegacy();
1678 int OpIdx = AMDGPU::getNamedOperandIdx(
MI.getOpcode(),
OpName);
1683 if (NewEnc == MO.
getImm())
1690bool WaitcntGenerator::promoteSoftWaitCnt(MachineInstr *Waitcnt)
const {
1704bool WaitcntGeneratorPreGFX12::applyPreexistingWaitcnt(
1705 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1707 assert(isNormalMode(MaxCounter));
1710 MachineInstr *WaitcntInstr =
nullptr;
1711 MachineInstr *WaitcntVsCntInstr =
nullptr;
1714 dbgs() <<
"PreGFX12::applyPreexistingWaitcnt at: ";
1716 dbgs() <<
"end of block\n";
1724 if (isNonWaitcntMetaInst(
II)) {
1730 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1734 if (Opcode == AMDGPU::S_WAITCNT) {
1735 unsigned IEnc =
static_cast<unsigned>(
II.getOperand(0).
getImm());
1738 ScoreBrackets.simplifyWaitcnt(OldWait);
1739 ScoreBrackets.simplifyVmemInvOnlyWait(OldWait);
1745 DeferredErasures.erase(&
II);
1746 II.eraseFromParent();
1748 }
else if (!
Wait.hasWaitExceptStoreCnt() && TrySimplify) {
1752 DeferredErasures.insert(&
II);
1755 DeferredErasures.erase(&
II);
1757 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1760 <<
"Before: " <<
Wait <<
'\n';);
1771 II.eraseFromParent();
1772 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1773 unsigned N =
static_cast<unsigned>(
II.getOperand(0).
getImm());
1775 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
1779 assert(Opcode == AMDGPU::S_WAITCNT_VSCNT);
1780 assert(
II.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
1782 unsigned OldVSCnt =
static_cast<unsigned>(
1783 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
1789 if (WaitcntVsCntInstr) {
1790 DeferredErasures.erase(&
II);
1791 II.eraseFromParent();
1793 }
else if (!
Wait.hasWaitStoreCnt() && TrySimplify) {
1795 DeferredErasures.insert(&
II);
1797 WaitcntVsCntInstr = &
II;
1798 DeferredErasures.erase(&
II);
1806 Modified |= promoteSoftWaitCnt(WaitcntInstr);
1815 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
1816 <<
"New Instr at block end: "
1817 << *WaitcntInstr <<
'\n'
1818 :
dbgs() <<
"applied pre-existing waitcnt\n"
1819 <<
"Old Instr: " << *It
1820 <<
"New Instr: " << *WaitcntInstr <<
'\n');
1823 if (WaitcntVsCntInstr) {
1827 Modified |= promoteSoftWaitCnt(WaitcntVsCntInstr);
1833 ?
dbgs() <<
"applied pre-existing waitcnt\n"
1834 <<
"New Instr at block end: " << *WaitcntVsCntInstr
1836 :
dbgs() <<
"applied pre-existing waitcnt\n"
1837 <<
"Old Instr: " << *It
1838 <<
"New Instr: " << *WaitcntVsCntInstr <<
'\n');
1846bool WaitcntGeneratorPreGFX12::createNewWaitcnt(
1848 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
1849 assert(isNormalMode(MaxCounter));
1856 if (
Wait.hasWaitExceptStoreCnt()) {
1858 if (ExpandWaitcntProfiling) {
1862 bool AnyOutOfOrder =
false;
1864 unsigned WaitCnt =
Wait.get(CT);
1865 if (WaitCnt != ~0u && ScoreBrackets.counterOutOfOrder(CT)) {
1866 AnyOutOfOrder =
true;
1871 if (AnyOutOfOrder) {
1879 unsigned WaitCnt =
Wait.get(CT);
1883 unsigned Outstanding =
1884 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
1885 EmitExpandedWaitcnt(Outstanding, WaitCnt, [&](
unsigned Count) {
1897 [[maybe_unused]]
auto SWaitInst =
1902 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1903 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1907 if (
Wait.hasWaitStoreCnt()) {
1913 unsigned Outstanding =
1916 EmitExpandedWaitcnt(
1918 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1919 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1924 [[maybe_unused]]
auto SWaitInst =
1926 .
addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1931 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1932 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1940WaitcntGeneratorPreGFX12::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1941 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt &&
ST.hasVscnt() ? 0 : ~0u);
1945WaitcntGeneratorGFX12Plus::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1946 unsigned ExpertVal = IsExpertMode ? 0 : ~0
u;
1947 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt ? 0 : ~0u, 0, 0, 0,
1949 ~0u , ExpertVal, ExpertVal, ExpertVal);
1956bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt(
1957 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1959 assert(!isNormalMode(MaxCounter));
1962 MachineInstr *CombinedLoadDsCntInstr =
nullptr;
1963 MachineInstr *CombinedStoreDsCntInstr =
nullptr;
1964 MachineInstr *WaitcntDepctrInstr =
nullptr;
1968 dbgs() <<
"GFX12Plus::applyPreexistingWaitcnt at: ";
1970 dbgs() <<
"end of block\n";
1976 AMDGPU::Waitcnt RequiredWait;
1981 if (isNonWaitcntMetaInst(
II)) {
1990 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1994 if (Opcode == AMDGPU::S_WAITCNT)
1997 if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) {
1998 unsigned OldEnc =
static_cast<unsigned>(
1999 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
2004 RequiredWait = RequiredWait.combined(OldWait);
2006 if (CombinedLoadDsCntInstr ==
nullptr) {
2007 CombinedLoadDsCntInstr = &
II;
2009 II.eraseFromParent();
2012 }
else if (Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT) {
2013 unsigned OldEnc =
static_cast<unsigned>(
2014 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
2019 RequiredWait = RequiredWait.combined(OldWait);
2021 if (CombinedStoreDsCntInstr ==
nullptr) {
2022 CombinedStoreDsCntInstr = &
II;
2024 II.eraseFromParent();
2027 }
else if (Opcode == AMDGPU::S_WAITCNT_DEPCTR) {
2028 unsigned OldEnc =
static_cast<unsigned>(
2029 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
2030 AMDGPU::Waitcnt OldWait;
2037 ScoreBrackets.simplifyWaitcnt(OldWait);
2039 if (WaitcntDepctrInstr ==
nullptr) {
2040 WaitcntDepctrInstr = &
II;
2048 unsigned Enc =
static_cast<unsigned>(
2049 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
2060 II.eraseFromParent();
2064 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
2067 II.eraseFromParent();
2069 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
2072 unsigned N =
static_cast<unsigned>(
II.getOperand(0).
getImm());
2073 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
2077 std::optional<AMDGPU::InstCounterType> CT =
2080 unsigned OldCnt =
static_cast<unsigned>(
2081 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
2083 Wait.add(CT.value(), OldCnt);
2085 RequiredWait.add(CT.value(), OldCnt);
2087 if (WaitInstrs[CT.value()] ==
nullptr) {
2088 WaitInstrs[CT.value()] = &
II;
2090 II.eraseFromParent();
2096 ScoreBrackets.simplifyWaitcnt(
Wait.combined(RequiredWait),
Wait);
2097 Wait =
Wait.combined(RequiredWait);
2099 if (CombinedLoadDsCntInstr) {
2115 AMDGPU::OpName::simm16, NewEnc);
2116 Modified |= promoteSoftWaitCnt(CombinedLoadDsCntInstr);
2122 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2123 <<
"New Instr at block end: "
2124 << *CombinedLoadDsCntInstr <<
'\n'
2125 :
dbgs() <<
"applied pre-existing waitcnt\n"
2126 <<
"Old Instr: " << *It <<
"New Instr: "
2127 << *CombinedLoadDsCntInstr <<
'\n');
2134 if (CombinedStoreDsCntInstr) {
2139 AMDGPU::OpName::simm16, NewEnc);
2140 Modified |= promoteSoftWaitCnt(CombinedStoreDsCntInstr);
2146 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2147 <<
"New Instr at block end: "
2148 << *CombinedStoreDsCntInstr <<
'\n'
2149 :
dbgs() <<
"applied pre-existing waitcnt\n"
2150 <<
"Old Instr: " << *It <<
"New Instr: "
2151 << *CombinedStoreDsCntInstr <<
'\n');
2181 for (MachineInstr **WI : WaitsToErase) {
2185 DeferredErasures.erase(*WI);
2186 (*WI)->eraseFromParent();
2193 if (!WaitInstrs[CT])
2196 unsigned NewCnt =
Wait.get(CT);
2197 if (NewCnt != ~0u) {
2199 AMDGPU::OpName::simm16, NewCnt);
2200 Modified |= promoteSoftWaitCnt(WaitInstrs[CT]);
2201 DeferredErasures.erase(WaitInstrs[CT]);
2203 ScoreBrackets.applyWaitcnt(CT, NewCnt);
2207 ?
dbgs() <<
"applied pre-existing waitcnt\n"
2208 <<
"New Instr at block end: " << *WaitInstrs[CT]
2210 :
dbgs() <<
"applied pre-existing waitcnt\n"
2211 <<
"Old Instr: " << *It
2212 <<
"New Instr: " << *WaitInstrs[CT] <<
'\n');
2215 DeferredErasures.insert(WaitInstrs[CT]);
2219 if (WaitcntDepctrInstr) {
2222 unsigned Enc =
static_cast<unsigned>(
2223 TII.getNamedOperand(*WaitcntDepctrInstr, AMDGPU::OpName::simm16)
2243 AMDGPU::OpName::simm16, Enc);
2245 <<
"New Instr at block end: "
2246 << *WaitcntDepctrInstr <<
'\n'
2247 :
dbgs() <<
"applyPreexistingWaitcnt\n"
2248 <<
"Old Instr: " << *It <<
"New Instr: "
2249 << *WaitcntDepctrInstr <<
'\n');
2260bool WaitcntGeneratorGFX12Plus::createNewWaitcnt(
2262 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
2263 assert(!isNormalMode(MaxCounter));
2270 if (ExpandWaitcntProfiling) {
2277 if (ScoreBrackets.counterOutOfOrder(CT)) {
2284 unsigned Outstanding =
2285 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
2286 EmitExpandedWaitcnt(Outstanding,
Count, [&](
unsigned Val) {
2298 MachineInstr *SWaitInst =
nullptr;
2322 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2323 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2335 [[maybe_unused]]
auto SWaitInst =
2342 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2343 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2346 if (
Wait.hasWaitDepctr()) {
2355 [[maybe_unused]]
auto SWaitInst =
2361 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2362 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2381bool SIInsertWaitcnts::generateWaitcntInstBefore(
2382 MachineInstr &
MI, WaitcntBrackets &ScoreBrackets,
2383 MachineInstr *OldWaitcntInstr, PreheaderFlushFlags FlushFlags) {
2388 AMDGPU::Waitcnt
Wait;
2389 const unsigned Opc =
MI.getOpcode();
2392 case AMDGPU::BUFFER_WBINVL1:
2393 case AMDGPU::BUFFER_WBINVL1_SC:
2394 case AMDGPU::BUFFER_WBINVL1_VOL:
2395 case AMDGPU::BUFFER_GL0_INV:
2396 case AMDGPU::BUFFER_GL1_INV: {
2404 case AMDGPU::SI_RETURN_TO_EPILOG:
2405 case AMDGPU::SI_RETURN:
2406 case AMDGPU::SI_WHOLE_WAVE_FUNC_RETURN:
2407 case AMDGPU::S_SETPC_B64_return: {
2412 AMDGPU::Waitcnt AllZeroWait =
2413 WCG->getAllZeroWaitcnt(
false);
2414 ScoreBrackets.simplifyVmemInvOnlyWait(AllZeroWait);
2418 case AMDGPU::S_ENDPGM:
2419 case AMDGPU::S_ENDPGM_SAVED: {
2430 !ScoreBrackets.hasPendingEvent(HWEvents::SCRATCH_WRITE_ACCESS);
2433 case AMDGPU::S_SENDMSG:
2434 case AMDGPU::S_SENDMSGHALT: {
2435 if (
ST.hasLegacyGeometry() &&
2450 if (
MI.modifiesRegister(AMDGPU::EXEC, &
TRI)) {
2453 if (ScoreBrackets.hasPendingEvent(HWEvents::EXP_GPR_LOCK) ||
2454 ScoreBrackets.hasPendingEvent(HWEvents::EXP_PARAM_ACCESS) ||
2455 ScoreBrackets.hasPendingEvent(HWEvents::EXP_POS_ACCESS) ||
2456 ScoreBrackets.hasPendingEvent(HWEvents::GDS_GPR_LOCK)) {
2463 if (
TII.isAlwaysGDS(
Opc) && ScoreBrackets.hasPendingGDS())
2471 Wait = AMDGPU::Waitcnt();
2473 const MachineOperand &CallAddrOp =
TII.getCalleeOperand(
MI);
2474 if (CallAddrOp.
isReg()) {
2475 ScoreBrackets.determineWaitForPhysReg(
2478 if (
const auto *RtnAddrOp =
2479 TII.getNamedOperand(
MI, AMDGPU::OpName::dst)) {
2480 ScoreBrackets.determineWaitForPhysReg(
2481 SmemAccessCounter, RtnAddrOp->getReg().asMCReg(),
Wait,
MI);
2484 }
else if (
Opc == AMDGPU::S_BARRIER_WAIT) {
2485 ScoreBrackets.tryClearSCCWriteEvent(&
MI);
2501 for (
const MachineMemOperand *Memop :
MI.memoperands()) {
2502 const Value *Ptr = Memop->getValue();
2503 if (Memop->isStore()) {
2504 if (
auto It = SLoadAddresses.
find(Ptr); It != SLoadAddresses.
end()) {
2505 Wait.add(SmemAccessCounter, 0);
2507 SLoadAddresses.
erase(It);
2510 unsigned AS = Memop->getAddrSpace();
2514 if (
TII.mayWriteLDSThroughDMA(
MI))
2518 VMEMID TID = LDSDMA_BEGIN;
2519 if (Ptr && Memop->getAAInfo()) {
2520 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2521 for (
size_t I = 0,
E = LDSDMAStores.size();
I !=
E; ++
I) {
2522 if (
MI.mayAlias(AA, *LDSDMAStores[
I],
true)) {
2523 size_t Slot =
I + 1;
2524 if (Slot >= NUM_LDSDMA) {
2532 ScoreBrackets.determineWaitForLDSDMA(
2539 if (Memop->isStore()) {
2545 for (
const MachineOperand &
Op :
MI.operands()) {
2550 if (
Op.isTied() &&
Op.isUse() &&
TII.doesNotReadTiedSource(
MI))
2553 MCRegister
Reg =
Op.getReg().asMCReg();
2555 const bool IsVGPR =
TRI.isVectorRegister(
MRI,
Op.getReg());
2562 if (
Op.isImplicit() &&
MI.mayLoadOrStore())
2580 if (
Op.isUse() || !updateVMCntOnly(
MI) ||
2581 ScoreBrackets.hasDifferentVGPRPendingEvents(
2583 ScoreBrackets.hasPointSamplePendingVmemTypes(
MI,
Reg) ||
2584 !
ST.hasVmemWriteVgprInOrder()) {
2591 ScoreBrackets.clearVGPRPendingEvents(
Reg);
2595 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2600 }
else if (
Op.getReg() == AMDGPU::SCC) {
2603 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter,
Reg,
Wait,
2607 if (
ST.hasWaitXcnt() &&
ST.isXNACKEnabled() &&
Op.isDef())
2626 if (
Opc == AMDGPU::S_BARRIER && !
ST.hasAutoWaitcntBeforeBarrier() &&
2627 !
ST.hasBackOffBarrier()) {
2628 Wait =
Wait.combined(WCG->getAllZeroWaitcnt(
true));
2635 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2640 ScoreBrackets.simplifyWaitcnt(
Wait);
2646 if (
TII.isVALU(
MI,
false)) {
2661 if (
ST.getCLOpts().waitcnt_forcezero && !
MI.isTerminator())
2662 Wait = WCG->getAllZeroWaitcnt(
false);
2666 if (!ForceEmitWaitcnt[
T])
2671 if (FlushFlags.FlushVmCnt) {
2677 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
2680 if (
ST.getCLOpts().waitcnt_load_forcezero &&
2684 return generateWaitcnt(
Wait,
MI.getIterator(), *
MI.getParent(), ScoreBrackets,
2688bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt
Wait,
2690 MachineBasicBlock &
Block,
2691 WaitcntBrackets &ScoreBrackets,
2692 MachineInstr *OldWaitcntInstr) {
2695 if (OldWaitcntInstr)
2699 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr,
Wait, It);
2704 MachineOperand *WaitExp =
TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2714 <<
"Update Instr: " << *It);
2717 if (WCG->createNewWaitcnt(
Block, It,
Wait, ScoreBrackets))
2722 ScoreBrackets.applyWaitcnt(
Wait);
2727bool SIInsertWaitcnts::isVmemAccess(
const MachineInstr &
MI)
const {
2728 return (
TII.isFLAT(
MI) &&
TII.mayAccessVMEMThroughFlat(
MI)) ||
2735 MachineBasicBlock *
Block)
const {
2736 auto BlockEnd =
Block->getParent()->end();
2737 auto BlockIter =
Block->getIterator();
2741 if (++BlockIter != BlockEnd) {
2742 It = BlockIter->instr_begin();
2749 if (!It->isMetaInstruction())
2757 return It->getOpcode() == AMDGPU::S_ENDPGM;
2761bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2762 MachineBasicBlock &
Block,
2763 WaitcntBrackets &ScoreBrackets) {
2764 AMDGPU::Waitcnt
Wait;
2765 bool NeedsEndPGMCheck =
false;
2773 NeedsEndPGMCheck =
true;
2776 ScoreBrackets.simplifyWaitcnt(
Wait);
2779 bool Result = generateWaitcnt(
Wait, SuccessorIt,
Block, ScoreBrackets,
2782 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &
Block)) {
2790void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2791 WaitcntBrackets *ScoreBrackets) {
2795 ScoreBrackets->updateByEvent(
E, Inst);
2797 if (
TII.isDS(Inst) &&
TII.usesLGKM_CNT(Inst)) {
2799 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2800 ScoreBrackets->setPendingGDS();
2802 }
else if (
TII.isFLAT(Inst)) {
2804 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2811 ScoreBrackets->setPendingFlat();
2813 }
else if (Inst.
isCall()) {
2816 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(
false));
2817 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2818 }
else if (
TII.isVINTERP(Inst)) {
2819 int64_t
Imm =
TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2829bool WaitcntBrackets::mergeScore(
const MergeInfo &M,
unsigned &Score,
2830 unsigned OtherScore) {
2831 unsigned MyShifted = Score <=
M.OldLB ? 0 : Score +
M.MyShift;
2832 unsigned OtherShifted =
2833 OtherScore <=
M.OtherLB ? 0 : OtherScore +
M.OtherShift;
2834 Score = std::max(MyShifted, OtherShifted);
2835 return OtherShifted > MyShifted;
2841 bool StrictDom =
false;
2844 LLVM_DEBUG(
dbgs() <<
"Merging async marks of stage " << Stage <<
" ...");
2846 if (StageMarks.empty() && OtherMarks.
empty()) {
2853 auto MaxSize = (unsigned)std::max(StageMarks.size(), OtherMarks.
size());
2854 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2857 if (StageMarks.size() > MaxSize)
2858 StageMarks.erase(StageMarks.begin(),
2859 StageMarks.begin() + (StageMarks.size() - MaxSize));
2865 constexpr CounterValueArray ZeroMark{};
2866 StageMarks.insert(StageMarks.begin(), MaxSize - StageMarks.size(), ZeroMark);
2869 dbgs() <<
"Before merge:\n";
2870 for (
const auto &Mark : StageMarks) {
2874 dbgs() <<
"Other marks:\n";
2875 for (
const auto &Mark : OtherMarks) {
2888 const size_t OtherSize = OtherMarks.size();
2889 const size_t OurSize = StageMarks.size();
2896 "StageMarks padded to MaxSize >= 1 (needs MaxAsyncMarks != 0)");
2899 const CounterValueArray &OtherMark =
2900 Idx <= OtherSize ? OtherMarks[OtherSize -
Idx] : ZeroMark;
2903 mergeScore(MergeInfos[
T], StageMarks[OurSize - Idx][
T], OtherMark[
T]);
2907 dbgs() <<
"After merge:\n";
2908 for (
const auto &Mark : StageMarks) {
2922bool WaitcntBrackets::merge(
const WaitcntBrackets &
Other) {
2923 bool StrictDom =
false;
2927 for (
auto K :
Other.VMem.keys())
2928 VMem.try_emplace(K);
2929 for (
auto K :
Other.SGPRs.keys())
2930 SGPRs.try_emplace(K);
2938 const HWEvents OldEvents = PendingEvents & EventsForT;
2939 const HWEvents OtherEvents =
Other.PendingEvents & EventsForT;
2940 if (!OldEvents.
contains(OtherEvents))
2942 PendingEvents |= OtherEvents;
2945 const unsigned MyPending = ScoreUBs[
T] - ScoreLBs[
T];
2946 const unsigned OtherPending =
Other.ScoreUBs[
T] -
Other.ScoreLBs[
T];
2947 const unsigned NewUB = ScoreLBs[
T] + std::max(MyPending, OtherPending);
2948 if (NewUB < ScoreLBs[
T])
2951 MergeInfo &
M = MergeInfos[
T];
2952 M.OldLB = ScoreLBs[
T];
2953 M.OtherLB =
Other.ScoreLBs[
T];
2954 M.MyShift = NewUB - ScoreUBs[
T];
2955 M.OtherShift = NewUB -
Other.ScoreUBs[
T];
2957 ScoreUBs[
T] = NewUB;
2960 StrictDom |= mergeScore(M, LastFlatLoadCnt,
Other.LastFlatLoadCnt);
2963 StrictDom |= mergeScore(M, LastFlatDsCnt,
Other.LastFlatDsCnt);
2964 StrictDom |= mergeScore(M, LastGDS,
Other.LastGDS);
2968 StrictDom |= mergeScore(M, SCCScore,
Other.SCCScore);
2969 if (
Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2970 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2971 PendingSCCWrite =
Other.PendingSCCWrite;
2972 }
else if (PendingSCCWrite !=
Other.PendingSCCWrite) {
2973 PendingSCCWrite =
nullptr;
2978 for (
auto &[RegID, Info] : VMem) {
2979 unsigned OtherScore =
Other.getVMemScore(RegID,
T);
2984 bool MyPending =
Info.Scores[
T] >
M.OldLB;
2985 bool OtherPending = OtherScore >
M.OtherLB;
2986 unsigned MyCount = MyPending ? getNumOrderedXDLsAfterWrite(RegID) : 0;
2987 unsigned OtherCount =
2988 OtherPending ?
Other.getNumOrderedXDLsAfterWrite(RegID) : 0;
2989 unsigned NewCount = MyPending && OtherPending
2990 ? std::min(MyCount, OtherCount)
2991 : (MyPending ? MyCount : OtherCount);
2992 StrictDom |= MyPending && NewCount < MyCount;
2993 Info.OrderedXDLCountAtWrite = OrderedXDLCount - NewCount;
2995 StrictDom |= mergeScore(M,
Info.Scores[
T], OtherScore);
2998 if (isSmemCounter(
T)) {
2999 for (
auto &[RegID, Info] : SGPRs) {
3000 auto It =
Other.SGPRs.find(RegID);
3001 unsigned OtherScore = (It !=
Other.SGPRs.end()) ? It->second.get(
T) : 0;
3002 StrictDom |= mergeScore(M,
Info.get(
T), OtherScore);
3007 for (
auto &[TID, Info] : VMem) {
3008 if (
auto It =
Other.VMem.find(TID); It !=
Other.VMem.end()) {
3010 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
3011 StrictDom |= NewVGPRContext !=
Info.VGPRPendingEvents;
3012 Info.VGPRPendingEvents = NewVGPRContext;
3018 StrictDom |= mergeAsyncMarks(S, MergeInfos,
Other.AsyncMarks[SI]);
3021 mergeScore(MergeInfos[
T], AsyncScore[SI][
T],
Other.AsyncScore[SI][
T]);
3024 purgeEmptyTrackingData();
3030 return Opcode == AMDGPU::S_WAITCNT ||
3033 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
3034 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
3035 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
3036 Opcode == AMDGPU::WAIT_ASYNCMARK ||
3040void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &
MBB,
3042 bool ExpertMode)
const {
3046 .
addImm(ExpertMode ? 2 : 0)
3064class VCCZWorkaround {
3065 const WaitcntBrackets &ScoreBrackets;
3066 const GCNSubtarget &
ST;
3067 const SIInstrInfo &
TII;
3068 const SIRegisterInfo &
TRI;
3069 bool VCCZCorruptionBug =
false;
3070 bool VCCZNotUpdatedByPartialWrites =
false;
3073 bool MustRecomputeVCCZ =
true;
3076 VCCZWorkaround(
const WaitcntBrackets &ScoreBrackets,
const GCNSubtarget &ST,
3077 const SIInstrInfo &
TII,
const SIRegisterInfo &
TRI)
3079 VCCZCorruptionBug =
ST.hasReadVCCZBug();
3080 VCCZNotUpdatedByPartialWrites = !
ST.partialVCCWritesUpdateVCCZ();
3087 bool tryRecomputeVCCZ(MachineInstr &
MI) {
3089 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
3099 MustRecomputeVCCZ |= VCCZCorruptionBug &&
TII.isSMRD(
MI);
3105 std::optional<bool> PartiallyWritesToVCCOpt;
3106 auto PartiallyWritesToVCC = [](MachineInstr &
MI) {
3107 return MI.definesRegister(AMDGPU::VCC_LO,
nullptr) ||
3108 MI.definesRegister(AMDGPU::VCC_HI,
nullptr);
3110 if (VCCZNotUpdatedByPartialWrites) {
3111 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3114 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
3120 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
3121 !VCCZCorruptionBug) {
3123 if (!PartiallyWritesToVCCOpt)
3124 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3125 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
3126 MI.definesRegister(AMDGPU::VCC,
nullptr);
3129 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
3130 *PartiallyWritesToVCCOpt);
3132 MustRecomputeVCCZ =
false;
3142 TII.get(
ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
3145 MustRecomputeVCCZ =
false;
3156 MachineBasicBlock &
Block,
3157 WaitcntBrackets &ScoreBrackets) {
3161 dbgs() <<
"*** Begin Block: ";
3163 ScoreBrackets.dump();
3165 VCCZWorkaround VCCZW(ScoreBrackets, ST,
TII,
TRI);
3168 MachineInstr *OldWaitcntInstr =
nullptr;
3171 ScoreBrackets.verify();
3174 Iter !=
E; ++Iter) {
3175 MachineInstr &Inst = *Iter;
3176 if (isNonWaitcntMetaInst(Inst))
3181 (IsExpertMode && Inst.
getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3182 if (!OldWaitcntInstr)
3183 OldWaitcntInstr = &Inst;
3187 PreheaderFlushFlags FlushFlags;
3188 if (
Block.getFirstTerminator() == Inst)
3189 FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3192 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3194 OldWaitcntInstr =
nullptr;
3196 if (Inst.
getOpcode() == AMDGPU::ASYNCMARK) {
3205 if (
TII.isSMRD(Inst)) {
3206 for (
const MachineMemOperand *Memop : Inst.
memoperands()) {
3209 if (!Memop->isInvariant()) {
3210 const Value *Ptr = Memop->getValue();
3216 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3220 Modified |= insertForcedWaitAfter(Inst,
Block, ScoreBrackets);
3224 ScoreBrackets.dump();
3229 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3231 ScoreBrackets.verify();
3236 AMDGPU::Waitcnt
Wait;
3237 if (
Block.getFirstTerminator() ==
Block.end()) {
3238 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3239 if (FlushFlags.FlushVmCnt) {
3247 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
3256 dbgs() <<
"*** End Block: ";
3258 ScoreBrackets.dump();
3264bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &
Block) {
3265 if (
Block.size() <= 1)
3273 MachineInstr *LastAtomicWithSoftXcnt =
nullptr;
3277 bool IsLDS =
TII.isDS(
MI) ||
3278 (
TII.isFLAT(
MI) &&
TII.mayAccessLDSThroughFlat(
MI, TgSplit));
3279 if (!IsLDS && (
MI.mayLoad() ^
MI.mayStore()))
3280 LastAtomicWithSoftXcnt =
nullptr;
3284 MachineInstr &PrevMI = *
MI.getPrevNode();
3286 if (PrevMI.
getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3289 if (LastAtomicWithSoftXcnt) {
3293 LastAtomicWithSoftXcnt = &
MI;
3301SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &
MBB,
3302 const WaitcntBrackets &ScoreBrackets) {
3310 return PreheaderFlushFlags();
3314 return PreheaderFlushFlags();
3316 if (
Loop->getLoopPreheader() == &
MBB) {
3317 Iterator->second = getPreheaderFlushFlags(
Loop, ScoreBrackets);
3321 return PreheaderFlushFlags();
3324bool SIInsertWaitcnts::isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const {
3326 return TII.mayAccessVMEMThroughFlat(
MI);
3330bool SIInsertWaitcnts::isDSRead(
const MachineInstr &
MI)
const {
3336bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const {
3365SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *
ML,
3366 const WaitcntBrackets &Brackets) {
3367 PreheaderFlushFlags
Flags;
3368 bool HasVMemLoad =
false;
3369 bool HasVMemStore =
false;
3370 bool UsesVgprVMEMLoadedOutside =
false;
3371 bool UsesVgprDSReadOutside =
false;
3372 bool VMemInvalidated =
false;
3376 bool TrackSimpleDSOpt =
ST.hasExtendedWaitCounts();
3377 DenseSet<MCRegUnit> VgprUse;
3378 DenseSet<MCRegUnit> VgprDefVMEM;
3379 DenseSet<MCRegUnit> VgprDefDS;
3385 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3386 unsigned DSReadPosition = 0;
3387 bool IsSingleBlock =
ML->getNumBlocks() == 1;
3388 bool TrackDSFlushPoint =
ST.hasExtendedWaitCounts() && IsSingleBlock;
3389 unsigned LastDSFlushPosition = 0;
3391 for (MachineBasicBlock *
MBB :
ML->blocks()) {
3392 for (MachineInstr &
MI : *
MBB) {
3393 if (isVMEMOrFlatVMEM(
MI)) {
3394 HasVMemLoad |=
MI.mayLoad();
3395 HasVMemStore |=
MI.mayStore();
3399 if (mayStoreIncrementingDSCNT(
MI)) {
3402 if (VMemInvalidated)
3404 TrackSimpleDSOpt =
false;
3405 TrackDSFlushPoint =
false;
3407 bool IsDSRead = isDSRead(
MI);
3412 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3413 if (!TrackDSFlushPoint)
3415 if (
auto It = LastDSReadPositionMap.
find(RU);
3416 It != LastDSReadPositionMap.
end()) {
3420 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3424 for (
const MachineOperand &
Op :
MI.all_uses()) {
3425 if (
Op.isDebug() || !
TRI.isVectorRegister(
MRI,
Op.getReg()))
3428 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3432 VMemInvalidated =
true;
3436 TrackSimpleDSOpt =
false;
3439 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3443 updateDSReadFlushTracking(RU);
3448 VMEMID
ID = toVMEMID(RU);
3452 UsesVgprVMEMLoadedOutside =
true;
3457 UsesVgprDSReadOutside =
true;
3462 if (isVMEMOrFlatVMEM(
MI) &&
MI.mayLoad()) {
3463 for (
const MachineOperand &
Op :
MI.all_defs()) {
3464 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3468 VMemInvalidated =
true;
3473 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3484 if (IsDSRead || TrackDSFlushPoint) {
3485 for (
const MachineOperand &
Op :
MI.all_defs()) {
3486 if (!
TRI.isVectorRegister(
MRI,
Op.getReg()))
3488 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3491 updateDSReadFlushTracking(RU);
3494 if (TrackDSFlushPoint)
3495 LastDSReadPositionMap[RU] = DSReadPosition;
3504 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3505 ((!
ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3506 (HasVMemLoad &&
ST.hasVmemWriteVgprInOrder())))
3507 Flags.FlushVmCnt =
true;
3513 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3516 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3517 bool DSFlushPointPrefetch =
3518 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3520 if (SimpleDSOpt || DSFlushPointPrefetch)
3521 Flags.FlushDsCnt =
true;
3526bool SIInsertWaitcntsLegacy::runOnMachineFunction(
MachineFunction &MF) {
3527 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3529 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3531 if (
auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3532 AA = &AAR->getAAResults();
3534 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3546 if (!SIInsertWaitcnts(MLI, PDT,
AA, MF).
run())
3551 .preserve<AAManager>();
3554bool SIInsertWaitcnts::run() {
3562 if (ST.hasExtendedWaitCounts()) {
3567 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3572 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3576 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3580 MachineBasicBlock &EntryBB = MF.
front();
3591 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3594 if (
ST.hasExtendedWaitCounts()) {
3603 if (!
ST.hasImageInsts() &&
3612 TII.get(instrsForExtendedCounterTypes[CT]))
3625 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(
this);
3626 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3627 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3634 for (
auto *
MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3637 std::unique_ptr<WaitcntBrackets> Brackets;
3642 for (
auto BII = BlockInfos.
begin(), BIE = BlockInfos.
end(); BII != BIE;
3644 MachineBasicBlock *
MBB = BII->first;
3645 BlockInfo &BI = BII->second;
3651 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3653 *Brackets = *BI.Incoming;
3656 Brackets = std::make_unique<WaitcntBrackets>(
this);
3661 Brackets->~WaitcntBrackets();
3662 new (Brackets.get()) WaitcntBrackets(
this);
3666 if (
ST.hasWaitXcnt() &&
ST.isXNACKEnabled())
3668 Modified |= insertWaitcntInBlock(MF, *
MBB, *Brackets);
3671 if (Brackets->hasPendingEvent()) {
3672 BlockInfo *MoveBracketsToSucc =
nullptr;
3674 auto *SuccBII = BlockInfos.
find(Succ);
3675 BlockInfo &SuccBI = SuccBII->second;
3676 if (!SuccBI.Incoming) {
3677 SuccBI.Dirty =
true;
3678 if (SuccBII <= BII) {
3682 if (!MoveBracketsToSucc) {
3683 MoveBracketsToSucc = &SuccBI;
3685 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3689 dbgs() <<
"Try to merge ";
3695 if (SuccBI.Incoming->merge(*Brackets)) {
3696 SuccBI.Dirty =
true;
3697 if (SuccBII <= BII) {
3704 if (MoveBracketsToSucc)
3705 MoveBracketsToSucc->Incoming = std::move(Brackets);
3710 Modified |= WCG->eraseDeferredWaitcnts();
3712 if (
ST.hasScalarStores()) {
3713 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3714 bool HaveScalarStores =
false;
3716 for (MachineBasicBlock &
MBB : MF) {
3717 for (MachineInstr &
MI :
MBB) {
3718 if (!HaveScalarStores &&
TII.isScalarStore(
MI))
3719 HaveScalarStores =
true;
3721 if (
MI.getOpcode() == AMDGPU::S_ENDPGM ||
3722 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3727 if (HaveScalarStores) {
3736 for (MachineBasicBlock *
MBB : EndPgmBlocks) {
3737 bool SeenDCacheWB =
false;
3741 if (
I->getOpcode() == AMDGPU::S_DCACHE_WB)
3742 SeenDCacheWB =
true;
3743 else if (
TII.isScalarStore(*
I))
3744 SeenDCacheWB =
false;
3747 if ((
I->getOpcode() == AMDGPU::S_ENDPGM ||
3748 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3764 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3766 setSchedulingMode(EntryBB,
I,
true);
3768 for (MachineInstr *
MI : CallInsts) {
3770 setSchedulingMode(
MBB,
MI,
false);
3771 setSchedulingMode(
MBB, std::next(
MI->getIterator()),
true);
3774 for (MachineInstr *
MI : ReturnInsts)
3775 setSchedulingMode(*
MI->getParent(),
MI,
false);
3786 for (
auto [
MI,
_] : EndPgmInsts) {
3788 TII.get(AMDGPU::S_ALLOC_VGPR))
3792 }
else if (!WCG->isOptNone() &&
3793 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3794 (MF.getFrameInfo().hasCalls() ||
3795 ST.getOccupancyWithNumVGPRs(
3796 TRI.getNumUsedPhysRegs(
MRI, AMDGPU::VGPR_32RegClass),
3797 false) <
ST.getMaxWavesPerEU())) {
3798 for (
auto [
MI, Flag] : EndPgmInsts) {
3800 if (
ST.requiresNopBeforeDeallocVGPRs()) {
3802 TII.get(AMDGPU::S_NOP))
3806 TII.get(AMDGPU::S_SENDMSG))
3816 if (
ST.hasWaitXcnt() &&
ST.isXNACKEnabled()) {
3823 TII.get(AMDGPU::S_SETREG_IMM32_B32))
3829 if (
ST.hasRequiresInitialUnclausedVmem()) {
3835 AMDGPU::SGPR64_SGPR65)
3839 TII.get(AMDGPU::GLOBAL_PREFETCH_B8_SADDR))
3840 .
addReg(AMDGPU::SGPR64_SGPR65)
3841 .
addReg(AMDGPU::VGPR0, RegState::Undef)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Shared AMDGPU asyncmark stage definitions.
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
AMD GCN specific subclass of TargetSubtarget.
const HexagonInstrInfo * TII
static bool isOptNone(const MachineFunction &MF)
static LoopDeletionResult merge(LoopDeletionResult A, LoopDeletionResult B)
Register const TargetRegisterInfo * TRI
Promote Memory to Register
static bool isReg(const MCInst &MI, unsigned OpNo)
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS_DEPENDENCY(depName)
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
MachineRegisterInfo & MRI
static bool updateOperandIfDifferent(MachineInstr &MI, AMDGPU::OpName OpName, unsigned NewEnc)
static bool isWaitInstr(MachineInstr &Inst)
AMDGPU::HWEvents HWEvents
static Function * getFunction(FunctionType *Ty, const Twine &Name, Module *M)
static const uint32_t IV[8]
A manager for alias analyses.
bool isEntryFunction() const
Bit mask of hardware events.
constexpr unsigned size() const
constexpr bool contains(HWEvents Other) const
constexpr bool any() const
unsigned get(InstCounterType T) const
void set(InstCounterType T, unsigned Val)
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
AnalysisUsage & addUsedIfAvailable()
Add the specified Pass class to the set of analyses used by this pass.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
size_t size() const
Get the array size.
bool empty() const
Check if the array is empty.
Represents analyses that only rely on functions' control flow.
iterator find(const_arg_type_t< KeyT > Val)
bool erase(const KeyT &Val)
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
bool dominates(const DomTreeNodeBase< NodeT > *A, const DomTreeNodeBase< NodeT > *B) const
dominates - Returns true iff A dominates B.
FunctionPass class - This class is used to implement most global optimizations.
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
LLVM_ABI const MachineBasicBlock * getSingleSuccessor() const
Return the successor of this block if it has a single successor.
LLVM_ABI DebugLoc findDebugLoc(instr_iterator MBBI)
Find the next valid DebugLoc starting at MBBI, skipping any debug instructions.
Instructions::iterator instr_iterator
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
iterator_range< succ_iterator > successors()
LLVM_ABI void printName(raw_ostream &os, unsigned printNameFlags=PrintNameIr, ModuleSlotTracker *moduleSlotTracker=nullptr) const
Print the basic block's name as:
MachineInstrBundleIterator< MachineInstr > iterator
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
Function & getFunction()
Return the LLVM function that this machine code represents.
Ty * getInfo()
getInfo - Keep track of various per-function pieces of information for backends that would like to do...
const MachineBasicBlock & front() const
const MachineInstrBuilder & addReg(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
Representation of each machine instruction.
mop_range defs()
Returns all explicit operands that are register definitions.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
bool mayLoadOrStore(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly read or modify memory.
const MachineBasicBlock * getParent() const
filtered_mop_range all_defs()
Returns an iterator range over all operands that are (explicit or implicit) register defs.
bool isCall(QueryType Type=AnyInBundle) const
LLVM_ABI void setDesc(const MCInstrDesc &TID)
Replace the instruction descriptor (thus opcode) of the current instruction with a new one.
ArrayRef< MachineMemOperand * > memoperands() const
Access to memory operands of the instruction.
LLVM_ABI void print(raw_ostream &OS, bool IsStandalone=true, bool SkipOpers=false, bool SkipDebugLoc=false, bool AddNewLine=true, const TargetInstrInfo *TII=nullptr) const
Print this MI to OS.
bool mayStore(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly modify memory.
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
filtered_mop_range all_uses()
Returns an iterator range over all operands that are (explicit or implicit) register uses.
const MachineOperand & getOperand(unsigned i) const
LLVM_ABI MachineInstrBundleIterator< MachineInstr > eraseFromParent()
Unlink 'this' from the containing basic block and delete it.
Analysis pass that exposes the MachineLoopInfo for a machine function.
MachineOperand class - Representation of each machine instruction operand.
void setImm(int64_t immVal)
bool isReg() const
isReg - Tests if this is a MO_Register operand.
Register getReg() const
getReg - Returns the register number.
iterator find(const KeyT &Key)
std::pair< iterator, bool > try_emplace(const KeyT &Key, Ts &&...Args)
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
MCRegister asMCReg() const
Utility to check-convert this value to a MCRegister.
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
static bool isCBranchVCCZRead(const MachineInstr &MI)
static bool isDS(const MachineInstr &MI)
static bool isVMEM(const MachineInstr &MI)
static bool isFLATScratch(const MachineInstr &MI)
static bool isXcntDrain(const MachineInstr &MI)
True if MI implicitly drains XCNT.
static bool isGWS(const MachineInstr &MI)
static bool isFLATGlobal(const MachineInstr &MI)
static bool isAtomicRet(const MachineInstr &MI)
static unsigned getNonSoftWaitcntOpcode(unsigned Opcode)
static bool isVINTERP(const MachineInstr &MI)
static bool isSBarrierSCCWrite(unsigned Opcode)
static bool isMIMG(const MachineInstr &MI)
static bool isFLAT(const MachineInstr &MI)
static bool isLDSDMA(const MachineInstr &MI)
This class keeps track of the SPI_SP_INPUT_ADDR config register, which tells the hardware which inter...
bool isDynamicVGPREnabled() const
void push_back(const T &Elt)
Target - Wrapper for Target specific information.
std::pair< iterator, bool > insert(const ValueT &V)
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
self_iterator getIterator()
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Abstract Attribute helper functions.
@ LOCAL_ADDRESS
Address space for local memory.
@ FLAT_ADDRESS
Address space for flat memory.
unsigned encodeFieldVaVdst(unsigned Encoded, unsigned VaVdst)
unsigned encodeFieldVmVsrc(unsigned Encoded, unsigned VmVsrc)
unsigned decodeFieldVaVdst(unsigned Encoded)
int getDefaultDepCtrEncoding(const MCSubtargetInfo &STI)
unsigned decodeFieldVmVsrc(unsigned Encoded)
@ ID_DEALLOC_VGPRS_GFX11Plus
LLVM_READONLY const MIMGInfo * getMIMGInfo(unsigned Opc)
constexpr AsyncStages asyncStagesFromMaskOperand(int64_t Mask)
bool isHi16Reg(MCRegister Reg, const MCRegisterInfo &MRI)
iota_range< InstCounterType > inst_counter_types(InstCounterType MaxCounter)
unsigned encodeLoadcntDscnt(const IsaVersion &Version, const Waitcnt &Decoded)
bool getHasMatrixScale(unsigned Opc)
AsyncStages
Bit mask of the async stages tracked by the asyncmark / wait_asyncmark intrinsics.
LLVM_ABI IsaVersion getIsaVersion(StringRef GPU)
Waitcnt decodeWaitcnt(const IsaVersion &Version, unsigned Encoded)
unsigned encodeWaitcnt(const IsaVersion &Version, const Waitcnt &Decoded)
bool isTgSplitEnabled(const Function &F)
bool isExpertSchedulingMode(const GCNSubtarget &ST, const Function &F)
Return whether expert scheduling mode is available and enabled for F.
HWEvents getSimplifiedVMEMEventsFor(const MachineInstr &Inst, const SIInstrInfo &TII)
Waitcnt decodeStorecntDscnt(const IsaVersion &Version, unsigned StorecntDscnt)
auto asyncStages(AsyncStages Mask)
unsigned getAsyncStageIndex(AsyncStages Stage)
std::optional< AMDGPU::InstCounterType > counterTypeForInstr(unsigned Opcode)
Determine if MI is a gfx12+ single-counter S_WAIT_*CNT instruction, and if so, which counter it is wa...
constexpr unsigned NumAsyncStages
The number of stages, which is also the number of bits in a stage mask.
HWEvents getEventsFor(const MachineInstr &Inst, const GCNSubtarget &ST, bool IsExpertMode, bool TgSplit)
Waitcnt decodeLoadcntDscnt(const IsaVersion &Version, unsigned LoadcntDscnt)
unsigned encodeStorecntDscnt(const IsaVersion &Version, const Waitcnt &Decoded)
bool getMUBUFIsBufferInv(unsigned Opc)
LLVM_READONLY const MIMGBaseOpcodeInfo * getMIMGBaseOpcodeInfo(unsigned BaseOpcode)
constexpr bool any(E Val)
constexpr bool isMaybeAtomic(const T &...O)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
This is an optimization pass for GlobalISel generic memory operations.
IterT next_nodbg(IterT It, IterT End, bool SkipPseudoOp=true)
Increment It, then continue incrementing it while it points to a debug instruction.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
void dump(const SparseBitVector< ElementSize > &LHS, raw_ostream &out)
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Printable print(const GCNRegPressure &RP, const GCNSubtarget *ST=nullptr, unsigned DynamicVGPRBlockSize=0)
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
constexpr auto seq_inclusive(T Begin, T End)
Iterate over an integral type from Begin to End inclusive.
static StringRef getCPU(StringRef CPU)
Processes a CPU name.
auto accumulate(R &&Range, E &&Init)
Wrapper for std::accumulate.
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
void interleaveComma(const Container &c, StreamT &os, UnaryFunctor each_fn)
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
LLVM_ABI Printable printRegUnit(MCRegUnit Unit, const TargetRegisterInfo *TRI)
Create Printable object to print register units on a raw_ostream.
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
constexpr auto equal_to(T &&Arg)
Functor variant of std::equal_to that can be used as a UnaryPredicate in functional algorithms like a...
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
char & SIInsertWaitcntsID
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
void sort(IteratorTy Start, IteratorTy End)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
CodeGenOptLevel
Code generation optimization level.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
iterator_range(Container &&) -> iterator_range< llvm::detail::IterOfRange< Container > >
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
@ Increment
Incrementally increasing token ID.
FunctionPass * createSIInsertWaitcntsPass()
AAResults AliasAnalysis
Temporary typedef for legacy code that uses a generic AliasAnalysis pointer or reference.
MCRegisterClass TargetRegisterClass
static constexpr ValueType Default
static constexpr uint64_t encode(Fields... Values)
Represents the hardware counter limits for different wait count types.
Instruction set architecture version.