45#define DEBUG_TYPE "si-insert-waitcnts"
49 cl::desc(
"Force all waitcnt instrs to be emitted as "
50 "s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)"),
54 "amdgpu-waitcnt-load-forcezero",
55 cl::desc(
"Force all waitcnt load counters to wait until 0"),
60template <
typename EmitWaitcntFn>
61static void EmitExpandedWaitcnt(
unsigned Outstanding,
unsigned Target,
62 EmitWaitcntFn &&EmitWaitcnt) {
64 for (
unsigned I = Outstanding - 1;
I >
Target &&
I != ~0u; --
I)
84 TRACKINGID_RANGE_LEN = (1 << 16),
89 REGUNITS_END = REGUNITS_BEGIN + TRACKINGID_RANGE_LEN,
94 NUM_LDSDMA = TRACKINGID_RANGE_LEN,
95 LDSDMA_BEGIN = REGUNITS_END,
96 LDSDMA_END = LDSDMA_BEGIN + NUM_LDSDMA,
100static constexpr VMEMID toVMEMID(MCRegUnit RU) {
101 return static_cast<unsigned>(RU);
113 AMDGPU::S_WAIT_LOADCNT, AMDGPU::S_WAIT_DSCNT,
114 AMDGPU::S_WAIT_EXPCNT, AMDGPU::S_WAIT_STORECNT,
115 AMDGPU::S_WAIT_SAMPLECNT, AMDGPU::S_WAIT_BVHCNT,
116 AMDGPU::S_WAIT_KMCNT, AMDGPU::S_WAIT_XCNT,
117 AMDGPU::S_WAIT_ASYNCCNT, AMDGPU::S_WAIT_TENSORCNT};
122 switch (
MI.getOpcode()) {
123 case AMDGPU::ASYNCMARK:
124 case AMDGPU::WAIT_ASYNCMARK:
127 return MI.isMetaInstruction();
142class WaitcntBrackets;
150class WaitcntGenerator {
152 const GCNSubtarget &ST;
153 const SIInstrInfo &TII;
154 AMDGPU::IsaVersion IV;
157 bool ExpandWaitcntProfiling =
false;
158 const AMDGPU::HardwareLimits &Limits;
162 DenseSet<MachineInstr *> DeferredErasures;
165 WaitcntGenerator() =
delete;
166 WaitcntGenerator(
const WaitcntGenerator &) =
delete;
169 const AMDGPU::HardwareLimits &Limits)
170 : ST(MF.getSubtarget<GCNSubtarget>()), TII(*ST.getInstrInfo()),
174 ExpandWaitcntProfiling(
175 MF.
getFunction().hasFnAttribute(
"amdgpu-expand-waitcnt-profiling")),
180 bool isOptNone()
const {
return OptNone; }
196 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
197 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
200 bool eraseDeferredWaitcnts() {
202 for (MachineInstr *
MI : DeferredErasures) {
203 MI->eraseFromParent();
206 DeferredErasures.clear();
211 bool promoteSoftWaitCnt(MachineInstr *Waitcnt)
const;
216 virtual bool createNewWaitcnt(MachineBasicBlock &
Block,
218 AMDGPU::Waitcnt
Wait,
219 const WaitcntBrackets &ScoreBrackets) = 0;
226 assert(
E.size() == 1 &&
"Cannot handle a mask of events!");
228 if (getWaitEvents(
T) &
E)
239 virtual AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const = 0;
241 virtual ~WaitcntGenerator() =
default;
244class WaitcntGeneratorPreGFX12 final :
public WaitcntGenerator {
247 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_SAMPLER_READ_ACCESS |
248 HWEvents::VMEM_BVH_READ_ACCESS | HWEvents::VMEM_INV_ACCESS,
249 HWEvents::SMEM_ACCESS | HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS |
250 HWEvents::SQ_MESSAGE,
251 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
252 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
253 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
254 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
266 using WaitcntGenerator::WaitcntGenerator;
267 bool applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
268 MachineInstr &OldWaitcntInstr,
269 AMDGPU::Waitcnt &
Wait,
272 bool createNewWaitcnt(MachineBasicBlock &
Block,
274 AMDGPU::Waitcnt
Wait,
275 const WaitcntBrackets &ScoreBrackets)
override;
278 HWEvents EVs = WaitEventMaskForInstPreGFX12[
T];
284 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
287class WaitcntGeneratorGFX12Plus final :
public WaitcntGenerator {
292 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_INV_ACCESS,
293 HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS,
294 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
295 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
296 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
298 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
299 HWEvents::VMEM_SAMPLER_READ_ACCESS,
300 HWEvents::VMEM_BVH_READ_ACCESS,
302 HWEvents::SMEM_ACCESS | HWEvents::SQ_MESSAGE | HWEvents::SCC_WRITE,
303 HWEvents::VMEM_GROUP | HWEvents::SMEM_GROUP,
304 HWEvents::ASYNC_ACCESS,
305 HWEvents::TENSOR_ACCESS,
306 HWEvents::VGPR_CSMACC_READ | HWEvents::VGPR_DPMACC_READ |
307 HWEvents::VGPR_TRANS_READ | HWEvents::VGPR_XDL_READ,
308 HWEvents::VGPR_CSMACC_WRITE | HWEvents::VGPR_DPMACC_WRITE |
309 HWEvents::VGPR_TRANS_WRITE | HWEvents::VGPR_XDL_WRITE,
310 HWEvents::VGPR_LDS_READ | HWEvents::VGPR_FLAT_READ |
311 HWEvents::VGPR_VMEM_READ};
314 WaitcntGeneratorGFX12Plus() =
delete;
317 const AMDGPU::HardwareLimits &Limits,
319 : WaitcntGenerator(MF, MaxCounter, Limits), IsExpertMode(IsExpertMode) {}
321 bool applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
322 MachineInstr &OldWaitcntInstr,
323 AMDGPU::Waitcnt &
Wait,
326 bool createNewWaitcnt(MachineBasicBlock &
Block,
328 AMDGPU::Waitcnt
Wait,
329 const WaitcntBrackets &ScoreBrackets)
override;
332 return WaitEventMaskForInstGFX12Plus[
T];
335 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
339struct PreheaderFlushFlags {
340 bool FlushVmCnt =
false;
341 bool FlushDsCnt =
false;
344class SIInsertWaitcnts {
345 DenseMap<const Value *, MachineBasicBlock *> SLoadAddresses;
346 DenseMap<MachineBasicBlock *, PreheaderFlushFlags> PreheadersToFlush;
347 MachineLoopInfo &MLI;
348 MachinePostDominatorTree &PDT;
353 std::unique_ptr<WaitcntBrackets> Incoming;
355 BlockInfo() =
default;
356 BlockInfo(BlockInfo &&) =
default;
357 BlockInfo &operator=(BlockInfo &&) =
default;
361 MapVector<MachineBasicBlock *, BlockInfo> BlockInfos;
365 std::unique_ptr<WaitcntGenerator> WCG;
368 DenseSet<MachineInstr *> CallInsts;
369 DenseSet<MachineInstr *> ReturnInsts;
374 DenseMap<MachineInstr *, bool> EndPgmInsts;
376 AMDGPU::HardwareLimits Limits;
379 const GCNSubtarget &ST;
380 const SIInstrInfo &TII;
381 const SIRegisterInfo &TRI;
382 const MachineRegisterInfo &MRI;
385 bool IsExpertMode =
false;
388 SIInsertWaitcnts(MachineLoopInfo &MLI, MachinePostDominatorTree &PDT,
390 : MLI(MLI), PDT(PDT), AA(AA), MF(MF), ST(MF.getSubtarget<GCNSubtarget>()),
391 TII(*ST.getInstrInfo()), TRI(TII.getRegisterInfo()),
392 MRI(MF.getRegInfo()),
393 TgSplit(ST.hasTgSplitSupport() &&
396 const AMDGPU::HardwareLimits &getLimits()
const {
return Limits; }
398 PreheaderFlushFlags getPreheaderFlushFlags(MachineLoop *
ML,
399 const WaitcntBrackets &Brackets);
400 PreheaderFlushFlags isPreheaderToFlush(MachineBasicBlock &
MBB,
401 const WaitcntBrackets &ScoreBrackets);
402 bool isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const;
403 bool isDSRead(
const MachineInstr &
MI)
const;
404 bool mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const;
407 bool isAsync(
const MachineInstr &
MI)
const {
412 const MachineOperand *
Async =
413 TII.getNamedOperand(
MI, AMDGPU::OpName::IsAsync);
417 bool isNonAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
421 bool isAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
425 bool shouldUpdateAsyncMark(
const MachineInstr &
MI,
429 if (!isAsyncLdsDmaWrite(
MI))
436 bool isVmemAccess(
const MachineInstr &
MI)
const;
437 bool generateWaitcntInstBefore(MachineInstr &
MI,
438 WaitcntBrackets &ScoreBrackets,
439 MachineInstr *OldWaitcntInstr,
440 PreheaderFlushFlags FlushFlags);
441 bool generateWaitcnt(AMDGPU::Waitcnt
Wait,
443 MachineBasicBlock &
Block, WaitcntBrackets &ScoreBrackets,
444 MachineInstr *OldWaitcntInstr);
445 void updateEventWaitcntAfter(MachineInstr &Inst,
446 WaitcntBrackets *ScoreBrackets);
448 MachineBasicBlock *
Block)
const;
449 bool insertForcedWaitAfter(MachineInstr &Inst, MachineBasicBlock &
Block,
450 WaitcntBrackets &ScoreBrackets);
452 WaitcntBrackets &ScoreBrackets);
455 bool removeRedundantSoftXcnts(MachineBasicBlock &
Block);
457 bool ExpertMode)
const;
459 return WCG->getWaitEvents(
T);
462 return WCG->getCounterFromEvent(
E);
474class WaitcntBrackets {
476 WaitcntBrackets(
const SIInsertWaitcnts *Context) : Context(Context) {
477 assert(Context->TRI.getNumRegUnits() < REGUNITS_END);
482 unsigned NumUnusedVmem = 0, NumUnusedSGPRs = 0;
483 for (
auto &[ID, Val] : VMem) {
487 for (
auto &[ID, Val] : SGPRs) {
492 if (NumUnusedVmem || NumUnusedSGPRs) {
493 errs() <<
"WaitcntBracket had unused entries at destruction time: "
494 << NumUnusedVmem <<
" VMem and " << NumUnusedSGPRs
495 <<
" SGPR unused entries\n";
506 return ScoreUBs[
T] - ScoreLBs[
T];
510 return getVMemScore(ID,
T) > getScoreLB(
T);
528 return getScoreUB(
T) - getScoreLB(
T);
532 auto It = SGPRs.find(RU);
533 return It != SGPRs.end() ? It->second.get(
T) : 0;
537 auto It = VMem.find(TID);
538 return It != VMem.end() ? It->second.Scores[
T] : 0;
553 void simplifyWaitcnt(AMDGPU::Waitcnt &
Wait)
const {
556 void simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
557 AMDGPU::Waitcnt &UpdateWait)
const;
560 void simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
561 AMDGPU::Waitcnt &UpdateWait)
const;
562 void simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
563 AMDGPU::Waitcnt &UpdateWait)
const;
566 void simplifyVmemInvOnlyWait(AMDGPU::Waitcnt &
Wait)
const {
571 if (LoadEvents == HWEvents::VMEM_INV_ACCESS)
576 AMDGPU::Waitcnt &
Wait,
577 const MachineInstr &
MI)
const;
578 MCPhysReg determineVGPR16Dependency(
const MachineInstr &
MI,
582 AMDGPU::Waitcnt &
Wait)
const;
583 AMDGPU::Waitcnt determineAsyncWait(
unsigned N);
584 void tryClearSCCWriteEvent(MachineInstr *Inst);
586 void applyWaitcnt(
const AMDGPU::Waitcnt &
Wait);
590 void recordAsyncMark(MachineInstr &
MI);
592 HWEvents getPendingEvents()
const {
return PendingEvents; }
593 bool hasPendingEvent()
const {
return PendingEvents.
any(); }
594 bool hasPendingEvent(
HWEvents E)
const {
return PendingEvents.contains(
E); }
596 bool HasPending = (PendingEvents & Context->getWaitEvents(
T)).any();
598 "Expected pending events iff scoreboard is not empty");
603 HWEvents Events = PendingEvents & Context->getWaitEvents(
T);
605 return Events.
size() > 1;
608 bool hasPendingFlat()
const {
615 void setPendingFlat() {
620 bool hasPendingGDS()
const {
625 unsigned getPendingGDSWait()
const {
635 for (MCRegUnit RU : regunits(
Reg)) {
636 auto It = VMem.find(toVMEMID(RU));
637 if (It != VMem.end() && (It->second.VGPRPendingEvents & ~
E).any())
644 for (MCRegUnit RU : regunits(
Reg)) {
645 if (
auto It = VMem.find(toVMEMID(RU)); It != VMem.end()) {
647 if (It->second.empty())
653 void setStateOnFunctionEntryOrReturn() {
659 ArrayRef<const MachineInstr *> getLDSDMAStores()
const {
663 bool hasPointSampleAccel(
const MachineInstr &
MI)
const;
664 bool hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
667 void print(raw_ostream &)
const;
672 void purgeEmptyTrackingData();
676 return Context->getLimits().get(
T);
686 using CounterValueArray = std::array<unsigned, AMDGPU::NUM_INST_CNTS>;
689 AMDGPU::Waitcnt &
Wait)
const;
691 static bool mergeScore(
const MergeInfo &M,
unsigned &Score,
692 unsigned OtherScore);
697 assert(
Reg != AMDGPU::SCC &&
"Shouldn't be used on SCC");
698 if (!Context->TRI.isInAllocatableClass(
Reg))
700 return Context->TRI.regunits(
Reg);
721 const SIRegisterInfo &
TRI = Context->TRI;
722 if (
Reg == AMDGPU::SCC) {
724 }
else if (
TRI.isVectorRegister(Context->MRI,
Reg)) {
725 for (MCRegUnit RU : regunits(
Reg))
726 VMem[toVMEMID(RU)].Scores[
T] = Val;
727 }
else if (
TRI.isSGPRReg(Context->MRI,
Reg)) {
728 for (MCRegUnit RU : regunits(
Reg))
729 SGPRs[RU].get(
T) = Val;
736 VMem[TID].Scores[
T] = Val;
739 void setScoreByOperand(
const MachineOperand &
Op,
742 const SIInsertWaitcnts *Context;
748 unsigned LastFlatDsCnt = 0;
749 unsigned LastFlatLoadCnt = 0;
751 unsigned LastGDS = 0;
768 CounterValueArray Scores{};
782 unsigned ScoreDsKmCnt = 0;
783 unsigned ScoreXCnt = 0;
799 bool empty()
const {
return !ScoreDsKmCnt && !ScoreXCnt; }
802 DenseMap<VMEMID, VMEMInfo> VMem;
803 DenseMap<MCRegUnit, SGPRInfo> SGPRs;
806 unsigned SCCScore = 0;
808 const MachineInstr *PendingSCCWrite =
nullptr;
812 SmallVector<const MachineInstr *> LDSDMAStores;
821 static constexpr unsigned MaxAsyncMarks = 16;
825 CounterValueArray AsyncScore{};
828SIInsertWaitcnts::BlockInfo::~BlockInfo() =
default;
833 SIInsertWaitcntsLegacy() : MachineFunctionPass(ID) {}
837 StringRef getPassName()
const override {
838 return "SI insert wait instructions";
841 void getAnalysisUsage(AnalysisUsage &AU)
const override {
844 AU.
addRequired<MachinePostDominatorTreeWrapperPass>();
856 setRegScore(
Op.getReg().asMCReg(), CntTy, Score);
864bool WaitcntBrackets::hasPointSampleAccel(
const MachineInstr &
MI)
const {
869 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
879bool WaitcntBrackets::hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
881 if (!hasPointSampleAccel(
MI))
884 return hasDifferentVGPRPendingEvents(
Reg, HWEvents::VMEM_READ_ACCESS);
887void WaitcntBrackets::updateByEvent(
HWEvents E, MachineInstr &Inst) {
888 assert(
E.size() == 1 &&
"Expected singular event!");
892 unsigned UB = getScoreUB(
T);
896 Context->ST.hasVOP3PX2IncrementsVaVdstTwice()) {
908 setScoreUB(
T, CurrScore);
911 const MachineRegisterInfo &MRI =
Context->MRI;
920 if (
const auto *AddrOp =
TII.getNamedOperand(Inst, AMDGPU::OpName::addr))
924 if (
const auto *Data0 =
925 TII.getNamedOperand(Inst, AMDGPU::OpName::data0))
927 if (
const auto *Data1 =
928 TII.getNamedOperand(Inst, AMDGPU::OpName::data1))
932 Inst.
getOpcode() != AMDGPU::DS_CONSUME &&
933 Inst.
getOpcode() != AMDGPU::DS_ORDERED_COUNT) {
934 for (
const MachineOperand &
Op : Inst.
all_uses()) {
935 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
939 }
else if (
TII.isFLAT(Inst)) {
941 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
944 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
947 }
else if (
TII.isMIMG(Inst)) {
951 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
954 }
else if (
TII.isMTBUF(Inst)) {
957 }
else if (
TII.isMUBUF(Inst)) {
961 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
964 }
else if (
TII.isLDSDIR(Inst)) {
966 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::vdst),
969 if (
TII.isEXP(Inst)) {
974 for (MachineOperand &DefMO : Inst.
all_defs()) {
975 if (
TRI.isVGPR(MRI, DefMO.getReg())) {
980 for (
const MachineOperand &
Op : Inst.
all_uses()) {
981 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
987 E == HWEvents::SMEM_GROUP ? HWEvents::VMEM_GROUP : HWEvents::SMEM_GROUP;
988 if (PendingEvents.
contains(OtherEvent)) {
993 setScoreLB(
T, getScoreUB(
T) - 1);
994 PendingEvents -= OtherEvent;
996 for (
const MachineOperand &
Op : Inst.
all_uses())
997 setScoreByOperand(
Op,
T, CurrScore);
1002 for (
const MachineOperand &
Op : Inst.
operands()) {
1015 setScoreByOperand(
Op,
T, CurrScore);
1027 for (
const MachineOperand &
Op : Inst.
defs()) {
1030 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
1032 if (updateVMCntOnly(Inst)) {
1041 if (hasPointSampleAccel(Inst))
1042 VGPRContext |= HWEvents::VMEM_READ_ACCESS;
1043 for (MCRegUnit RU : regunits(
Op.getReg().asMCReg()))
1044 VMem[toVMEMID(RU)].VGPRPendingEvents |= VGPRContext;
1047 setScoreByOperand(
Op,
T, CurrScore);
1050 (
TII.isDS(Inst) ||
Context->isNonAsyncLdsDmaWrite(Inst))) {
1059 if (!MemOp->isStore() ||
1064 auto AAI = MemOp->getAAInfo();
1070 if (!AAI || !AAI.Scope)
1072 for (
unsigned I = 0,
E = LDSDMAStores.
size();
I !=
E && !Slot; ++
I) {
1073 for (
const auto *MemOp : LDSDMAStores[
I]->memoperands()) {
1074 if (MemOp->isStore() && AAI == MemOp->getAAInfo()) {
1089 setVMemScore(LDSDMA_BEGIN,
T, CurrScore);
1090 if (Slot && Slot < NUM_LDSDMA)
1091 setVMemScore(LDSDMA_BEGIN + Slot,
T, CurrScore);
1094 if (
Context->shouldUpdateAsyncMark(Inst,
T)) {
1095 AsyncScore[
T] = CurrScore;
1099 setRegScore(AMDGPU::SCC,
T, CurrScore);
1100 PendingSCCWrite = &Inst;
1105void WaitcntBrackets::recordAsyncMark(MachineInstr &Inst) {
1111 AsyncMarks.push_back(AsyncScore);
1113 dbgs() <<
"recordAsyncMark:\n" << Inst;
1114 for (
const auto &Mark : AsyncMarks) {
1121void WaitcntBrackets::print(raw_ostream &OS)
const {
1125 unsigned SR = getScoreRange(
T);
1128 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM") <<
"_CNT("
1132 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM") <<
"_CNT("
1136 OS <<
" EXP_CNT(" << SR <<
"):";
1139 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS") <<
"_CNT("
1143 OS <<
" SAMPLE_CNT(" << SR <<
"):";
1146 OS <<
" BVH_CNT(" << SR <<
"):";
1149 OS <<
" KM_CNT(" << SR <<
"):";
1152 OS <<
" X_CNT(" << SR <<
"):";
1155 OS <<
" ASYNC_CNT(" << SR <<
"):";
1158 OS <<
" VA_VDST_RD(" << SR <<
"): ";
1161 OS <<
" VA_VDST_WR(" << SR <<
"): ";
1164 OS <<
" VM_VSRC(" << SR <<
"): ";
1167 OS <<
" UNKNOWN(" << SR <<
"):";
1173 unsigned LB = getScoreLB(
T);
1176 sort(SortedVMEMIDs);
1178 for (
auto ID : SortedVMEMIDs) {
1179 unsigned RegScore = VMem.at(ID).Scores[
T];
1182 unsigned RelScore = RegScore - LB - 1;
1183 if (ID < REGUNITS_END) {
1184 OS <<
' ' << RelScore <<
':'
1187 assert(ID >= LDSDMA_BEGIN && ID < LDSDMA_END &&
1188 "Unhandled/unexpected ID value!");
1189 OS <<
' ' << RelScore <<
":LDSDMA" <<
ID;
1194 if (isSmemCounter(
T)) {
1196 sort(SortedSMEMIDs);
1197 for (
auto ID : SortedSMEMIDs) {
1198 unsigned RegScore = SGPRs.at(ID).get(
T);
1201 unsigned RelScore = RegScore - LB - 1;
1202 OS <<
' ' << RelScore <<
':'
1208 OS <<
' ' << SCCScore <<
":scc";
1213 OS <<
"Pending Events: ";
1214 if (hasPendingEvent()) {
1215 OS << getPendingEvents();
1221 OS <<
"Async score: ";
1222 if (AsyncScore.empty())
1228 OS <<
"Async marks: " << AsyncMarks.size() <<
'\n';
1230 for (
const auto &Mark : AsyncMarks) {
1232 unsigned MarkedScore = Mark[
T];
1235 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM")
1236 <<
"_CNT: " << MarkedScore;
1239 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM")
1240 <<
"_CNT: " << MarkedScore;
1243 OS <<
" EXP_CNT: " << MarkedScore;
1246 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS")
1247 <<
"_CNT: " << MarkedScore;
1250 OS <<
" SAMPLE_CNT: " << MarkedScore;
1253 OS <<
" BVH_CNT: " << MarkedScore;
1256 OS <<
" KM_CNT: " << MarkedScore;
1259 OS <<
" X_CNT: " << MarkedScore;
1262 OS <<
" ASYNC_CNT: " << MarkedScore;
1265 OS <<
" UNKNOWN: " << MarkedScore;
1276void WaitcntBrackets::simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
1277 AMDGPU::Waitcnt &UpdateWait)
const {
1285 simplifyXcnt(CheckWait, UpdateWait);
1288 simplifyVmVsrc(CheckWait, UpdateWait);
1293 unsigned &
Count)
const {
1297 if (
Count >= getScoreRange(
T))
1301void WaitcntBrackets::simplifyWaitcnt(AMDGPU::Waitcnt &
Wait,
1303 unsigned Cnt =
Wait.get(
T);
1304 simplifyWaitcnt(
T, Cnt);
1308void WaitcntBrackets::simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
1309 AMDGPU::Waitcnt &UpdateWait)
const {
1319 hasPendingEvent(HWEvents::SMEM_GROUP))
1325 hasPendingEvent(HWEvents::VMEM_GROUP) &&
1332void WaitcntBrackets::simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
1333 AMDGPU::Waitcnt &UpdateWait)
const {
1343 return Acc |
Context->getWaitEvents(
T);
1345 HWEvents PendingVmemEvents = PendingEvents & VmemEvents;
1347 unsigned CheckCount = CheckWait.
get(
T);
1349 (CheckCount == 0 || !counterOutOfOrder(
T)) &&
1350 (PendingVmemEvents & ~
Context->getWaitEvents(
T)) == 0)
1357void WaitcntBrackets::purgeEmptyTrackingData() {
1358 VMem.remove_if([](
const auto &
P) {
return P.second.empty(); });
1359 SGPRs.remove_if([](
const auto &
P) {
return P.second.empty(); });
1363 unsigned ScoreToWait,
1364 AMDGPU::Waitcnt &
Wait)
const {
1365 const unsigned LB = getScoreLB(
T);
1366 const unsigned UB = getScoreUB(
T);
1369 if ((UB >= ScoreToWait) && (ScoreToWait > LB)) {
1371 !
Context->ST.hasFlatLgkmVMemCountInOrder()) {
1376 }
else if (counterOutOfOrder(
T)) {
1384 unsigned NeededWait = std::min(UB - ScoreToWait, getLimit(
T) - 1);
1385 Wait.add(
T, NeededWait);
1390AMDGPU::Waitcnt WaitcntBrackets::determineAsyncWait(
unsigned N) {
1392 dbgs() <<
"Need " <<
N <<
" async marks. Found " << AsyncMarks.size()
1394 for (
const auto &Mark : AsyncMarks) {
1400 if (AsyncMarks.size() == MaxAsyncMarks) {
1405 LLVM_DEBUG(
dbgs() <<
"Possible truncation. Ensuring a non-trivial wait.\n");
1406 N = std::min(
N, (
unsigned)MaxAsyncMarks - 1);
1409 AMDGPU::Waitcnt
Wait;
1410 if (AsyncMarks.size() <=
N) {
1415 size_t MarkIndex = AsyncMarks.size() -
N - 1;
1416 const auto &RequiredMark = AsyncMarks[MarkIndex];
1418 determineWaitForScore(
T, RequiredMark[
T],
Wait);
1424 dbgs() <<
"Removing " << (MarkIndex + 1)
1425 <<
" async marks after determining wait\n";
1427 AsyncMarks.erase(AsyncMarks.begin(), AsyncMarks.begin() + MarkIndex + 1);
1440MCPhysReg WaitcntBrackets::determineVGPR16Dependency(
const MachineInstr &
MI,
1444 unsigned Size =
Context->TRI.getRegSizeInBits(*RC);
1446 if (
Size != 16 || !
Context->ST.hasD16Writes32BitVgpr())
1456 AMDGPU::Waitcnt
Wait;
1457 for (MCRegUnit RU : regunits(OtherHalf))
1458 determineWaitForScore(
T, getVMemScore(toVMEMID(RU),
T),
Wait);
1461 if (!
Wait.hasWait())
1471 HWEvents Events = MIEvents & OtherHalfEvents;
1472 if (Events.
size() > 1)
1479 AMDGPU::Waitcnt &
Wait,
1480 const MachineInstr &
MI)
const {
1481 if (
Reg == AMDGPU::SCC) {
1482 determineWaitForScore(
T, SCCScore,
Wait);
1486 Reg = determineVGPR16Dependency(
MI,
T,
Reg);
1487 for (MCRegUnit RU : regunits(
Reg))
1488 determineWaitForScore(
1489 T, IsVGPR ? getVMemScore(toVMEMID(RU),
T) : getSGPRScore(RU,
T),
1496 AMDGPU::Waitcnt &
Wait)
const {
1497 assert(TID >= LDSDMA_BEGIN && TID < LDSDMA_END);
1498 determineWaitForScore(
T, getVMemScore(TID,
T),
Wait);
1501void WaitcntBrackets::tryClearSCCWriteEvent(MachineInstr *Inst) {
1504 if (PendingSCCWrite &&
1505 PendingSCCWrite->
getOpcode() == AMDGPU::S_BARRIER_SIGNAL_ISFIRST_IMM &&
1507 HWEvents SCC_WRITE_PendingEvent = HWEvents::SCC_WRITE;
1510 SCC_WRITE_PendingEvent) {
1514 PendingEvents -= SCC_WRITE_PendingEvent;
1515 PendingSCCWrite =
nullptr;
1519void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait) {
1528 applyWaitcnt(
T, Cnt);
1533 const unsigned UB = getScoreUB(
T);
1537 if (counterOutOfOrder(
T))
1539 setScoreLB(
T, std::max(getScoreLB(
T), UB -
Count));
1542 PendingEvents -=
Context->getWaitEvents(
T);
1546 hasPendingEvent(HWEvents::SMEM_GROUP)) {
1550 PendingEvents -= HWEvents::SMEM_GROUP;
1556 else if (
Count == 0)
1557 PendingEvents -= HWEvents::VMEM_GROUP;
1561void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait,
1563 unsigned Cnt =
Wait.get(
T);
1564 applyWaitcnt(
T, Cnt);
1571 if ((
T ==
Context->SmemAccessCounter &&
1572 hasPendingEvent(HWEvents::SMEM_ACCESS)) ||
1589 static constexpr HWEvents ExtendedImageEvents =
1590 HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
1591 if (!
Context->ST.hasExtendedWaitCounts() &&
1592 (Events & ExtendedImageEvents).any()) {
1593 Events -= ExtendedImageEvents;
1594 Events |= HWEvents::VMEM_READ_ACCESS;
1600 Events -= HWEvents::VMEM_INV_ACCESS;
1602 return Events.
size() > 1;
1605 return hasMixedPendingEvents(
T);
1615char SIInsertWaitcntsLegacy::ID = 0;
1620 return new SIInsertWaitcntsLegacy();
1625 int OpIdx = AMDGPU::getNamedOperandIdx(
MI.getOpcode(),
OpName);
1630 if (NewEnc == MO.
getImm())
1637bool WaitcntGenerator::promoteSoftWaitCnt(MachineInstr *Waitcnt)
const {
1651bool WaitcntGeneratorPreGFX12::applyPreexistingWaitcnt(
1652 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1654 assert(isNormalMode(MaxCounter));
1657 MachineInstr *WaitcntInstr =
nullptr;
1658 MachineInstr *WaitcntVsCntInstr =
nullptr;
1661 dbgs() <<
"PreGFX12::applyPreexistingWaitcnt at: ";
1663 dbgs() <<
"end of block\n";
1671 if (isNonWaitcntMetaInst(
II)) {
1677 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1681 if (Opcode == AMDGPU::S_WAITCNT) {
1682 unsigned IEnc =
II.getOperand(0).getImm();
1685 ScoreBrackets.simplifyWaitcnt(OldWait);
1686 ScoreBrackets.simplifyVmemInvOnlyWait(OldWait);
1692 DeferredErasures.erase(&
II);
1693 II.eraseFromParent();
1695 }
else if (!
Wait.hasWaitExceptStoreCnt() && TrySimplify) {
1699 DeferredErasures.insert(&
II);
1702 DeferredErasures.erase(&
II);
1704 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1707 <<
"Before: " <<
Wait <<
'\n';);
1718 II.eraseFromParent();
1719 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1720 unsigned N =
II.getOperand(0).getImm();
1722 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1725 assert(Opcode == AMDGPU::S_WAITCNT_VSCNT);
1726 assert(
II.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
1729 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1735 if (WaitcntVsCntInstr) {
1736 DeferredErasures.erase(&
II);
1737 II.eraseFromParent();
1739 }
else if (!
Wait.hasWaitStoreCnt() && TrySimplify) {
1741 DeferredErasures.insert(&
II);
1743 WaitcntVsCntInstr = &
II;
1744 DeferredErasures.erase(&
II);
1752 Modified |= promoteSoftWaitCnt(WaitcntInstr);
1761 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
1762 <<
"New Instr at block end: "
1763 << *WaitcntInstr <<
'\n'
1764 :
dbgs() <<
"applied pre-existing waitcnt\n"
1765 <<
"Old Instr: " << *It
1766 <<
"New Instr: " << *WaitcntInstr <<
'\n');
1769 if (WaitcntVsCntInstr) {
1773 Modified |= promoteSoftWaitCnt(WaitcntVsCntInstr);
1779 ?
dbgs() <<
"applied pre-existing waitcnt\n"
1780 <<
"New Instr at block end: " << *WaitcntVsCntInstr
1782 :
dbgs() <<
"applied pre-existing waitcnt\n"
1783 <<
"Old Instr: " << *It
1784 <<
"New Instr: " << *WaitcntVsCntInstr <<
'\n');
1792bool WaitcntGeneratorPreGFX12::createNewWaitcnt(
1794 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
1795 assert(isNormalMode(MaxCounter));
1802 if (
Wait.hasWaitExceptStoreCnt()) {
1804 if (ExpandWaitcntProfiling) {
1808 bool AnyOutOfOrder =
false;
1810 unsigned WaitCnt =
Wait.get(CT);
1811 if (WaitCnt != ~0u && ScoreBrackets.counterOutOfOrder(CT)) {
1812 AnyOutOfOrder =
true;
1817 if (AnyOutOfOrder) {
1825 unsigned WaitCnt =
Wait.get(CT);
1829 unsigned Outstanding =
1830 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
1831 EmitExpandedWaitcnt(Outstanding, WaitCnt, [&](
unsigned Count) {
1843 [[maybe_unused]]
auto SWaitInst =
1848 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1849 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1853 if (
Wait.hasWaitStoreCnt()) {
1859 unsigned Outstanding =
1862 EmitExpandedWaitcnt(
1864 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1865 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1870 [[maybe_unused]]
auto SWaitInst =
1872 .
addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1877 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1878 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1886WaitcntGeneratorPreGFX12::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1887 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt &&
ST.hasVscnt() ? 0 : ~0u);
1891WaitcntGeneratorGFX12Plus::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1892 unsigned ExpertVal = IsExpertMode ? 0 : ~0
u;
1893 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt ? 0 : ~0u, 0, 0, 0,
1895 ~0u , ExpertVal, ExpertVal, ExpertVal);
1902bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt(
1903 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1905 assert(!isNormalMode(MaxCounter));
1908 MachineInstr *CombinedLoadDsCntInstr =
nullptr;
1909 MachineInstr *CombinedStoreDsCntInstr =
nullptr;
1910 MachineInstr *WaitcntDepctrInstr =
nullptr;
1914 dbgs() <<
"GFX12Plus::applyPreexistingWaitcnt at: ";
1916 dbgs() <<
"end of block\n";
1922 AMDGPU::Waitcnt RequiredWait;
1927 if (isNonWaitcntMetaInst(
II)) {
1936 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1940 if (Opcode == AMDGPU::S_WAITCNT)
1943 if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) {
1945 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1950 RequiredWait = RequiredWait.combined(OldWait);
1952 if (CombinedLoadDsCntInstr ==
nullptr) {
1953 CombinedLoadDsCntInstr = &
II;
1955 II.eraseFromParent();
1958 }
else if (Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT) {
1960 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1965 RequiredWait = RequiredWait.combined(OldWait);
1967 if (CombinedStoreDsCntInstr ==
nullptr) {
1968 CombinedStoreDsCntInstr = &
II;
1970 II.eraseFromParent();
1973 }
else if (Opcode == AMDGPU::S_WAITCNT_DEPCTR) {
1975 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1976 AMDGPU::Waitcnt OldWait;
1983 ScoreBrackets.simplifyWaitcnt(OldWait);
1985 if (WaitcntDepctrInstr ==
nullptr) {
1986 WaitcntDepctrInstr = &
II;
1995 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
2006 II.eraseFromParent();
2010 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
2013 II.eraseFromParent();
2015 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
2018 unsigned N =
II.getOperand(0).getImm();
2019 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
2022 std::optional<AMDGPU::InstCounterType> CT =
2026 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
2028 Wait.add(CT.value(), OldCnt);
2030 RequiredWait.add(CT.value(), OldCnt);
2032 if (WaitInstrs[CT.value()] ==
nullptr) {
2033 WaitInstrs[CT.value()] = &
II;
2035 II.eraseFromParent();
2041 ScoreBrackets.simplifyWaitcnt(
Wait.combined(RequiredWait),
Wait);
2042 Wait =
Wait.combined(RequiredWait);
2044 if (CombinedLoadDsCntInstr) {
2060 AMDGPU::OpName::simm16, NewEnc);
2061 Modified |= promoteSoftWaitCnt(CombinedLoadDsCntInstr);
2067 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2068 <<
"New Instr at block end: "
2069 << *CombinedLoadDsCntInstr <<
'\n'
2070 :
dbgs() <<
"applied pre-existing waitcnt\n"
2071 <<
"Old Instr: " << *It <<
"New Instr: "
2072 << *CombinedLoadDsCntInstr <<
'\n');
2079 if (CombinedStoreDsCntInstr) {
2084 AMDGPU::OpName::simm16, NewEnc);
2085 Modified |= promoteSoftWaitCnt(CombinedStoreDsCntInstr);
2091 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2092 <<
"New Instr at block end: "
2093 << *CombinedStoreDsCntInstr <<
'\n'
2094 :
dbgs() <<
"applied pre-existing waitcnt\n"
2095 <<
"Old Instr: " << *It <<
"New Instr: "
2096 << *CombinedStoreDsCntInstr <<
'\n');
2126 for (MachineInstr **WI : WaitsToErase) {
2130 DeferredErasures.erase(*WI);
2131 (*WI)->eraseFromParent();
2138 if (!WaitInstrs[CT])
2141 unsigned NewCnt =
Wait.get(CT);
2142 if (NewCnt != ~0u) {
2144 AMDGPU::OpName::simm16, NewCnt);
2145 Modified |= promoteSoftWaitCnt(WaitInstrs[CT]);
2146 DeferredErasures.erase(WaitInstrs[CT]);
2148 ScoreBrackets.applyWaitcnt(CT, NewCnt);
2152 ?
dbgs() <<
"applied pre-existing waitcnt\n"
2153 <<
"New Instr at block end: " << *WaitInstrs[CT]
2155 :
dbgs() <<
"applied pre-existing waitcnt\n"
2156 <<
"Old Instr: " << *It
2157 <<
"New Instr: " << *WaitInstrs[CT] <<
'\n');
2160 DeferredErasures.insert(WaitInstrs[CT]);
2164 if (WaitcntDepctrInstr) {
2168 TII.getNamedOperand(*WaitcntDepctrInstr, AMDGPU::OpName::simm16)
2188 AMDGPU::OpName::simm16, Enc);
2190 <<
"New Instr at block end: "
2191 << *WaitcntDepctrInstr <<
'\n'
2192 :
dbgs() <<
"applyPreexistingWaitcnt\n"
2193 <<
"Old Instr: " << *It <<
"New Instr: "
2194 << *WaitcntDepctrInstr <<
'\n');
2205bool WaitcntGeneratorGFX12Plus::createNewWaitcnt(
2207 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
2208 assert(!isNormalMode(MaxCounter));
2215 if (ExpandWaitcntProfiling) {
2222 if (ScoreBrackets.counterOutOfOrder(CT)) {
2229 unsigned Outstanding =
2230 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
2231 EmitExpandedWaitcnt(Outstanding,
Count, [&](
unsigned Val) {
2243 MachineInstr *SWaitInst =
nullptr;
2267 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2268 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2280 [[maybe_unused]]
auto SWaitInst =
2287 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2288 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2291 if (
Wait.hasWaitDepctr()) {
2300 [[maybe_unused]]
auto SWaitInst =
2306 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2307 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2326bool SIInsertWaitcnts::generateWaitcntInstBefore(
2327 MachineInstr &
MI, WaitcntBrackets &ScoreBrackets,
2328 MachineInstr *OldWaitcntInstr, PreheaderFlushFlags FlushFlags) {
2333 AMDGPU::Waitcnt
Wait;
2334 const unsigned Opc =
MI.getOpcode();
2337 case AMDGPU::BUFFER_WBINVL1:
2338 case AMDGPU::BUFFER_WBINVL1_SC:
2339 case AMDGPU::BUFFER_WBINVL1_VOL:
2340 case AMDGPU::BUFFER_GL0_INV:
2341 case AMDGPU::BUFFER_GL1_INV: {
2349 case AMDGPU::SI_RETURN_TO_EPILOG:
2350 case AMDGPU::SI_RETURN:
2351 case AMDGPU::SI_WHOLE_WAVE_FUNC_RETURN:
2352 case AMDGPU::S_SETPC_B64_return: {
2357 AMDGPU::Waitcnt AllZeroWait =
2358 WCG->getAllZeroWaitcnt(
false);
2359 ScoreBrackets.simplifyVmemInvOnlyWait(AllZeroWait);
2363 case AMDGPU::S_ENDPGM:
2364 case AMDGPU::S_ENDPGM_SAVED: {
2375 !ScoreBrackets.hasPendingEvent(HWEvents::SCRATCH_WRITE_ACCESS);
2378 case AMDGPU::S_SENDMSG:
2379 case AMDGPU::S_SENDMSGHALT: {
2380 if (
ST.hasLegacyGeometry() &&
2395 if (
MI.modifiesRegister(AMDGPU::EXEC, &
TRI)) {
2398 if (ScoreBrackets.hasPendingEvent(HWEvents::EXP_GPR_LOCK) ||
2399 ScoreBrackets.hasPendingEvent(HWEvents::EXP_PARAM_ACCESS) ||
2400 ScoreBrackets.hasPendingEvent(HWEvents::EXP_POS_ACCESS) ||
2401 ScoreBrackets.hasPendingEvent(HWEvents::GDS_GPR_LOCK)) {
2408 if (
TII.isAlwaysGDS(
Opc) && ScoreBrackets.hasPendingGDS())
2416 Wait = AMDGPU::Waitcnt();
2418 const MachineOperand &CallAddrOp =
TII.getCalleeOperand(
MI);
2419 if (CallAddrOp.
isReg()) {
2420 ScoreBrackets.determineWaitForPhysReg(
2423 if (
const auto *RtnAddrOp =
2424 TII.getNamedOperand(
MI, AMDGPU::OpName::dst)) {
2425 ScoreBrackets.determineWaitForPhysReg(
2426 SmemAccessCounter, RtnAddrOp->getReg().asMCReg(),
Wait,
MI);
2429 }
else if (
Opc == AMDGPU::S_BARRIER_WAIT) {
2430 ScoreBrackets.tryClearSCCWriteEvent(&
MI);
2446 for (
const MachineMemOperand *Memop :
MI.memoperands()) {
2447 const Value *Ptr = Memop->getValue();
2448 if (Memop->isStore()) {
2449 if (
auto It = SLoadAddresses.
find(Ptr); It != SLoadAddresses.
end()) {
2450 Wait.add(SmemAccessCounter, 0);
2452 SLoadAddresses.
erase(It);
2455 unsigned AS = Memop->getAddrSpace();
2459 if (
TII.mayWriteLDSThroughDMA(
MI))
2463 unsigned TID = LDSDMA_BEGIN;
2464 if (Ptr && Memop->getAAInfo()) {
2465 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2466 for (
unsigned I = 0,
E = LDSDMAStores.size();
I !=
E; ++
I) {
2467 if (
MI.mayAlias(AA, *LDSDMAStores[
I],
true)) {
2468 if ((
I + 1) >= NUM_LDSDMA) {
2483 if (Memop->isStore()) {
2489 for (
const MachineOperand &
Op :
MI.operands()) {
2494 if (
Op.isTied() &&
Op.isUse() &&
TII.doesNotReadTiedSource(
MI))
2499 const bool IsVGPR =
TRI.isVectorRegister(MRI,
Op.getReg());
2506 if (
Op.isImplicit() &&
MI.mayLoadOrStore())
2524 if (
Op.isUse() || !updateVMCntOnly(
MI) ||
2525 ScoreBrackets.hasDifferentVGPRPendingEvents(
2527 ScoreBrackets.hasPointSamplePendingVmemTypes(
MI,
Reg) ||
2528 !
ST.hasVmemWriteVgprInOrder()) {
2535 ScoreBrackets.clearVGPRPendingEvents(
Reg);
2539 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2544 }
else if (
Op.getReg() == AMDGPU::SCC) {
2547 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter,
Reg,
Wait,
2551 if (
ST.hasWaitXcnt() &&
Op.isDef())
2570 if (
Opc == AMDGPU::S_BARRIER && !
ST.hasAutoWaitcntBeforeBarrier() &&
2571 !
ST.hasBackOffBarrier()) {
2572 Wait =
Wait.combined(WCG->getAllZeroWaitcnt(
true));
2579 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2584 ScoreBrackets.simplifyWaitcnt(
Wait);
2590 if (
TII.isVALU(
MI,
false)) {
2606 Wait = WCG->getAllZeroWaitcnt(
false);
2610 if (!ForceEmitWaitcnt[
T])
2615 if (FlushFlags.FlushVmCnt) {
2621 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
2627 return generateWaitcnt(
Wait,
MI.getIterator(), *
MI.getParent(), ScoreBrackets,
2631bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt
Wait,
2633 MachineBasicBlock &
Block,
2634 WaitcntBrackets &ScoreBrackets,
2635 MachineInstr *OldWaitcntInstr) {
2638 if (OldWaitcntInstr)
2642 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr,
Wait, It);
2647 MachineOperand *WaitExp =
TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2657 <<
"Update Instr: " << *It);
2660 if (WCG->createNewWaitcnt(
Block, It,
Wait, ScoreBrackets))
2665 ScoreBrackets.applyWaitcnt(
Wait);
2670bool SIInsertWaitcnts::isVmemAccess(
const MachineInstr &
MI)
const {
2671 return (
TII.isFLAT(
MI) &&
TII.mayAccessVMEMThroughFlat(
MI)) ||
2678 MachineBasicBlock *
Block)
const {
2679 auto BlockEnd =
Block->getParent()->end();
2680 auto BlockIter =
Block->getIterator();
2684 if (++BlockIter != BlockEnd) {
2685 It = BlockIter->instr_begin();
2692 if (!It->isMetaInstruction())
2700 return It->getOpcode() == AMDGPU::S_ENDPGM;
2704bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2705 MachineBasicBlock &
Block,
2706 WaitcntBrackets &ScoreBrackets) {
2707 AMDGPU::Waitcnt
Wait;
2708 bool NeedsEndPGMCheck =
false;
2716 NeedsEndPGMCheck =
true;
2719 ScoreBrackets.simplifyWaitcnt(
Wait);
2722 bool Result = generateWaitcnt(
Wait, SuccessorIt,
Block, ScoreBrackets,
2725 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &
Block)) {
2733void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2734 WaitcntBrackets *ScoreBrackets) {
2738 ScoreBrackets->updateByEvent(
E, Inst);
2740 if (
TII.isDS(Inst) &&
TII.usesLGKM_CNT(Inst)) {
2742 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2743 ScoreBrackets->setPendingGDS();
2745 }
else if (
TII.isFLAT(Inst)) {
2747 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2754 ScoreBrackets->setPendingFlat();
2756 }
else if (Inst.
isCall()) {
2759 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(
false));
2760 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2761 }
else if (
TII.isVINTERP(Inst)) {
2762 int64_t
Imm =
TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2772bool WaitcntBrackets::mergeScore(
const MergeInfo &M,
unsigned &Score,
2773 unsigned OtherScore) {
2774 unsigned MyShifted = Score <=
M.OldLB ? 0 : Score +
M.MyShift;
2775 unsigned OtherShifted =
2776 OtherScore <=
M.OtherLB ? 0 : OtherScore +
M.OtherShift;
2777 Score = std::max(MyShifted, OtherShifted);
2778 return OtherShifted > MyShifted;
2783 bool StrictDom =
false;
2787 if (AsyncMarks.empty() && OtherMarks.
empty()) {
2794 auto MaxSize = (unsigned)std::max(AsyncMarks.size(), OtherMarks.
size());
2795 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2798 if (AsyncMarks.size() > MaxSize)
2799 AsyncMarks.erase(AsyncMarks.begin(),
2800 AsyncMarks.begin() + (AsyncMarks.size() - MaxSize));
2806 constexpr CounterValueArray ZeroMark{};
2807 AsyncMarks.insert(AsyncMarks.begin(), MaxSize - AsyncMarks.size(), ZeroMark);
2810 dbgs() <<
"Before merge:\n";
2811 for (
const auto &Mark : AsyncMarks) {
2815 dbgs() <<
"Other marks:\n";
2816 for (
const auto &Mark : OtherMarks) {
2829 const unsigned OtherSize = OtherMarks.size();
2830 const unsigned OurSize = AsyncMarks.size();
2837 "AsyncMarks padded to MaxSize >= 1 (needs MaxAsyncMarks != 0)");
2840 const CounterValueArray &OtherMark =
2841 Idx <= OtherSize ? OtherMarks[OtherSize -
Idx] : ZeroMark;
2844 mergeScore(MergeInfos[
T], AsyncMarks[OurSize - Idx][
T], OtherMark[
T]);
2848 dbgs() <<
"After merge:\n";
2849 for (
const auto &Mark : AsyncMarks) {
2863bool WaitcntBrackets::merge(
const WaitcntBrackets &
Other) {
2864 bool StrictDom =
false;
2868 for (
auto K :
Other.VMem.keys())
2869 VMem.try_emplace(K);
2870 for (
auto K :
Other.SGPRs.keys())
2871 SGPRs.try_emplace(K);
2879 const HWEvents OldEvents = PendingEvents & EventsForT;
2880 const HWEvents OtherEvents =
Other.PendingEvents & EventsForT;
2881 if (!OldEvents.
contains(OtherEvents))
2883 PendingEvents |= OtherEvents;
2886 const unsigned MyPending = ScoreUBs[
T] - ScoreLBs[
T];
2887 const unsigned OtherPending =
Other.ScoreUBs[
T] -
Other.ScoreLBs[
T];
2888 const unsigned NewUB = ScoreLBs[
T] + std::max(MyPending, OtherPending);
2889 if (NewUB < ScoreLBs[
T])
2892 MergeInfo &
M = MergeInfos[
T];
2893 M.OldLB = ScoreLBs[
T];
2894 M.OtherLB =
Other.ScoreLBs[
T];
2895 M.MyShift = NewUB - ScoreUBs[
T];
2896 M.OtherShift = NewUB -
Other.ScoreUBs[
T];
2898 ScoreUBs[
T] = NewUB;
2901 StrictDom |= mergeScore(M, LastFlatLoadCnt,
Other.LastFlatLoadCnt);
2904 StrictDom |= mergeScore(M, LastFlatDsCnt,
Other.LastFlatDsCnt);
2905 StrictDom |= mergeScore(M, LastGDS,
Other.LastGDS);
2909 StrictDom |= mergeScore(M, SCCScore,
Other.SCCScore);
2910 if (
Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2911 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2912 PendingSCCWrite =
Other.PendingSCCWrite;
2913 }
else if (PendingSCCWrite !=
Other.PendingSCCWrite) {
2914 PendingSCCWrite =
nullptr;
2919 for (
auto &[RegID, Info] : VMem)
2920 StrictDom |= mergeScore(M,
Info.Scores[
T],
Other.getVMemScore(RegID,
T));
2922 if (isSmemCounter(
T)) {
2923 for (
auto &[RegID, Info] : SGPRs) {
2924 auto It =
Other.SGPRs.find(RegID);
2925 unsigned OtherScore = (It !=
Other.SGPRs.end()) ? It->second.get(
T) : 0;
2926 StrictDom |= mergeScore(M,
Info.get(
T), OtherScore);
2931 for (
auto &[TID, Info] : VMem) {
2932 if (
auto It =
Other.VMem.find(TID); It !=
Other.VMem.end()) {
2934 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
2935 StrictDom |= NewVGPRContext !=
Info.VGPRPendingEvents;
2936 Info.VGPRPendingEvents = NewVGPRContext;
2940 StrictDom |= mergeAsyncMarks(MergeInfos,
Other.AsyncMarks);
2942 StrictDom |= mergeScore(MergeInfos[
T], AsyncScore[
T],
Other.AsyncScore[
T]);
2944 purgeEmptyTrackingData();
2950 return Opcode == AMDGPU::S_WAITCNT ||
2953 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
2954 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
2955 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
2956 Opcode == AMDGPU::WAIT_ASYNCMARK ||
2960void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &
MBB,
2962 bool ExpertMode)
const {
2966 .
addImm(ExpertMode ? 2 : 0)
2984class VCCZWorkaround {
2985 const WaitcntBrackets &ScoreBrackets;
2986 const GCNSubtarget &
ST;
2987 const SIInstrInfo &
TII;
2988 const SIRegisterInfo &
TRI;
2989 bool VCCZCorruptionBug =
false;
2990 bool VCCZNotUpdatedByPartialWrites =
false;
2993 bool MustRecomputeVCCZ =
true;
2996 VCCZWorkaround(
const WaitcntBrackets &ScoreBrackets,
const GCNSubtarget &ST,
2997 const SIInstrInfo &
TII,
const SIRegisterInfo &
TRI)
2999 VCCZCorruptionBug =
ST.hasReadVCCZBug();
3000 VCCZNotUpdatedByPartialWrites = !
ST.partialVCCWritesUpdateVCCZ();
3007 bool tryRecomputeVCCZ(MachineInstr &
MI) {
3009 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
3019 MustRecomputeVCCZ |= VCCZCorruptionBug &&
TII.isSMRD(
MI);
3025 std::optional<bool> PartiallyWritesToVCCOpt;
3026 auto PartiallyWritesToVCC = [](MachineInstr &
MI) {
3027 return MI.definesRegister(AMDGPU::VCC_LO,
nullptr) ||
3028 MI.definesRegister(AMDGPU::VCC_HI,
nullptr);
3030 if (VCCZNotUpdatedByPartialWrites) {
3031 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3034 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
3040 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
3041 !VCCZCorruptionBug) {
3043 if (!PartiallyWritesToVCCOpt)
3044 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3045 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
3046 MI.definesRegister(AMDGPU::VCC,
nullptr);
3049 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
3050 *PartiallyWritesToVCCOpt);
3052 MustRecomputeVCCZ =
false;
3062 TII.get(
ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
3065 MustRecomputeVCCZ =
false;
3076 MachineBasicBlock &
Block,
3077 WaitcntBrackets &ScoreBrackets) {
3081 dbgs() <<
"*** Begin Block: ";
3083 ScoreBrackets.dump();
3085 VCCZWorkaround VCCZW(ScoreBrackets, ST,
TII,
TRI);
3088 MachineInstr *OldWaitcntInstr =
nullptr;
3091 ScoreBrackets.verify();
3094 Iter !=
E; ++Iter) {
3095 MachineInstr &Inst = *Iter;
3096 if (isNonWaitcntMetaInst(Inst))
3101 (IsExpertMode && Inst.
getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3102 if (!OldWaitcntInstr)
3103 OldWaitcntInstr = &Inst;
3107 PreheaderFlushFlags FlushFlags;
3108 if (
Block.getFirstTerminator() == Inst)
3109 FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3112 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3114 OldWaitcntInstr =
nullptr;
3116 if (Inst.
getOpcode() == AMDGPU::ASYNCMARK) {
3120 ScoreBrackets.recordAsyncMark(Inst);
3124 if (
TII.isSMRD(Inst)) {
3125 for (
const MachineMemOperand *Memop : Inst.
memoperands()) {
3128 if (!Memop->isInvariant()) {
3129 const Value *Ptr = Memop->getValue();
3135 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3139 Modified |= insertForcedWaitAfter(Inst,
Block, ScoreBrackets);
3143 ScoreBrackets.dump();
3148 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3150 ScoreBrackets.verify();
3155 AMDGPU::Waitcnt
Wait;
3156 if (
Block.getFirstTerminator() ==
Block.end()) {
3157 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3158 if (FlushFlags.FlushVmCnt) {
3166 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
3175 dbgs() <<
"*** End Block: ";
3177 ScoreBrackets.dump();
3183bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &
Block) {
3184 if (
Block.size() <= 1)
3192 MachineInstr *LastAtomicWithSoftXcnt =
nullptr;
3196 bool IsLDS =
TII.isDS(
MI) ||
3197 (
TII.isFLAT(
MI) &&
TII.mayAccessLDSThroughFlat(
MI, TgSplit));
3198 if (!IsLDS && (
MI.mayLoad() ^
MI.mayStore()))
3199 LastAtomicWithSoftXcnt =
nullptr;
3203 MachineInstr &PrevMI = *
MI.getPrevNode();
3205 if (PrevMI.
getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3208 if (LastAtomicWithSoftXcnt) {
3212 LastAtomicWithSoftXcnt = &
MI;
3220SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &
MBB,
3221 const WaitcntBrackets &ScoreBrackets) {
3229 return PreheaderFlushFlags();
3233 return PreheaderFlushFlags();
3235 if (
Loop->getLoopPreheader() == &
MBB) {
3236 Iterator->second = getPreheaderFlushFlags(
Loop, ScoreBrackets);
3240 return PreheaderFlushFlags();
3243bool SIInsertWaitcnts::isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const {
3245 return TII.mayAccessVMEMThroughFlat(
MI);
3249bool SIInsertWaitcnts::isDSRead(
const MachineInstr &
MI)
const {
3255bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const {
3284SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *
ML,
3285 const WaitcntBrackets &Brackets) {
3286 PreheaderFlushFlags
Flags;
3287 bool HasVMemLoad =
false;
3288 bool HasVMemStore =
false;
3289 bool UsesVgprVMEMLoadedOutside =
false;
3290 bool UsesVgprDSReadOutside =
false;
3291 bool VMemInvalidated =
false;
3295 bool TrackSimpleDSOpt =
ST.hasExtendedWaitCounts();
3296 DenseSet<MCRegUnit> VgprUse;
3297 DenseSet<MCRegUnit> VgprDefVMEM;
3298 DenseSet<MCRegUnit> VgprDefDS;
3304 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3305 unsigned DSReadPosition = 0;
3306 bool IsSingleBlock =
ML->getNumBlocks() == 1;
3307 bool TrackDSFlushPoint =
ST.hasExtendedWaitCounts() && IsSingleBlock;
3308 unsigned LastDSFlushPosition = 0;
3310 for (MachineBasicBlock *
MBB :
ML->blocks()) {
3311 for (MachineInstr &
MI : *
MBB) {
3312 if (isVMEMOrFlatVMEM(
MI)) {
3313 HasVMemLoad |=
MI.mayLoad();
3314 HasVMemStore |=
MI.mayStore();
3318 if (mayStoreIncrementingDSCNT(
MI)) {
3321 if (VMemInvalidated)
3323 TrackSimpleDSOpt =
false;
3324 TrackDSFlushPoint =
false;
3326 bool IsDSRead = isDSRead(
MI);
3331 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3332 if (!TrackDSFlushPoint)
3334 if (
auto It = LastDSReadPositionMap.
find(RU);
3335 It != LastDSReadPositionMap.
end()) {
3339 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3343 for (
const MachineOperand &
Op :
MI.all_uses()) {
3344 if (
Op.isDebug() || !
TRI.isVectorRegister(MRI,
Op.getReg()))
3347 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3351 VMemInvalidated =
true;
3355 TrackSimpleDSOpt =
false;
3358 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3362 updateDSReadFlushTracking(RU);
3367 VMEMID
ID = toVMEMID(RU);
3371 UsesVgprVMEMLoadedOutside =
true;
3376 UsesVgprDSReadOutside =
true;
3381 if (isVMEMOrFlatVMEM(
MI) &&
MI.mayLoad()) {
3382 for (
const MachineOperand &
Op :
MI.all_defs()) {
3383 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3387 VMemInvalidated =
true;
3392 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3403 if (IsDSRead || TrackDSFlushPoint) {
3404 for (
const MachineOperand &
Op :
MI.all_defs()) {
3405 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
3407 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3410 updateDSReadFlushTracking(RU);
3413 if (TrackDSFlushPoint)
3414 LastDSReadPositionMap[RU] = DSReadPosition;
3423 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3424 ((!
ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3425 (HasVMemLoad &&
ST.hasVmemWriteVgprInOrder())))
3426 Flags.FlushVmCnt =
true;
3432 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3435 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3436 bool DSFlushPointPrefetch =
3437 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3439 if (SimpleDSOpt || DSFlushPointPrefetch)
3440 Flags.FlushDsCnt =
true;
3445bool SIInsertWaitcntsLegacy::runOnMachineFunction(
MachineFunction &MF) {
3446 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3448 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3450 if (
auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3451 AA = &AAR->getAAResults();
3453 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3465 if (!SIInsertWaitcnts(MLI, PDT,
AA, MF).
run())
3470 .preserve<AAManager>();
3473bool SIInsertWaitcnts::run() {
3481 if (ST.hasExtendedWaitCounts()) {
3486 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3491 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3495 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3499 MachineBasicBlock &EntryBB = MF.
front();
3510 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3513 if (
ST.hasExtendedWaitCounts()) {
3522 if (!
ST.hasImageInsts() &&
3528 TII.get(instrsForExtendedCounterTypes[CT]))
3541 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(
this);
3542 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3543 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3550 for (
auto *
MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3553 std::unique_ptr<WaitcntBrackets> Brackets;
3558 for (
auto BII = BlockInfos.
begin(), BIE = BlockInfos.
end(); BII != BIE;
3560 MachineBasicBlock *
MBB = BII->first;
3561 BlockInfo &BI = BII->second;
3567 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3569 *Brackets = *BI.Incoming;
3572 Brackets = std::make_unique<WaitcntBrackets>(
this);
3577 Brackets->~WaitcntBrackets();
3578 new (Brackets.get()) WaitcntBrackets(
this);
3582 if (
ST.hasWaitXcnt())
3584 Modified |= insertWaitcntInBlock(MF, *
MBB, *Brackets);
3587 if (Brackets->hasPendingEvent()) {
3588 BlockInfo *MoveBracketsToSucc =
nullptr;
3590 auto *SuccBII = BlockInfos.
find(Succ);
3591 BlockInfo &SuccBI = SuccBII->second;
3592 if (!SuccBI.Incoming) {
3593 SuccBI.Dirty =
true;
3594 if (SuccBII <= BII) {
3598 if (!MoveBracketsToSucc) {
3599 MoveBracketsToSucc = &SuccBI;
3601 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3605 dbgs() <<
"Try to merge ";
3611 if (SuccBI.Incoming->merge(*Brackets)) {
3612 SuccBI.Dirty =
true;
3613 if (SuccBII <= BII) {
3620 if (MoveBracketsToSucc)
3621 MoveBracketsToSucc->Incoming = std::move(Brackets);
3626 Modified |= WCG->eraseDeferredWaitcnts();
3628 if (
ST.hasScalarStores()) {
3629 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3630 bool HaveScalarStores =
false;
3632 for (MachineBasicBlock &
MBB : MF) {
3633 for (MachineInstr &
MI :
MBB) {
3634 if (!HaveScalarStores &&
TII.isScalarStore(
MI))
3635 HaveScalarStores =
true;
3637 if (
MI.getOpcode() == AMDGPU::S_ENDPGM ||
3638 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3643 if (HaveScalarStores) {
3652 for (MachineBasicBlock *
MBB : EndPgmBlocks) {
3653 bool SeenDCacheWB =
false;
3657 if (
I->getOpcode() == AMDGPU::S_DCACHE_WB)
3658 SeenDCacheWB =
true;
3659 else if (
TII.isScalarStore(*
I))
3660 SeenDCacheWB =
false;
3663 if ((
I->getOpcode() == AMDGPU::S_ENDPGM ||
3664 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3680 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3682 setSchedulingMode(EntryBB,
I,
true);
3684 for (MachineInstr *
MI : CallInsts) {
3686 setSchedulingMode(
MBB,
MI,
false);
3687 setSchedulingMode(
MBB, std::next(
MI->getIterator()),
true);
3690 for (MachineInstr *
MI : ReturnInsts)
3691 setSchedulingMode(*
MI->getParent(),
MI,
false);
3702 for (
auto [
MI,
_] : EndPgmInsts) {
3704 TII.get(AMDGPU::S_ALLOC_VGPR))
3708 }
else if (!WCG->isOptNone() &&
3709 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3710 (MF.getFrameInfo().hasCalls() ||
3711 ST.getOccupancyWithNumVGPRs(
3712 TRI.getNumUsedPhysRegs(MRI, AMDGPU::VGPR_32RegClass),
3713 false) <
ST.getMaxWavesPerEU())) {
3714 for (
auto [
MI, Flag] : EndPgmInsts) {
3716 if (
ST.requiresNopBeforeDeallocVGPRs()) {
3718 TII.get(AMDGPU::S_NOP))
3722 TII.get(AMDGPU::S_SENDMSG))
3732 if (
ST.hasWaitXcnt()) {
3736 unsigned RegEncoding =
3739 TII.get(AMDGPU::S_SETREG_IMM32_B32))
3745 if (
ST.hasRequiresInitialUnclausedVmem()) {
3751 AMDGPU::SGPR64_SGPR65)
3755 TII.get(AMDGPU::GLOBAL_PREFETCH_B8_SADDR))
3756 .
addReg(AMDGPU::SGPR64_SGPR65)
3757 .
addReg(AMDGPU::VGPR0, RegState::Undef)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
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)
static cl::opt< bool > ForceEmitZeroLoadFlag("amdgpu-waitcnt-load-forcezero", cl::desc("Force all waitcnt load counters to wait until 0"), cl::init(false), cl::Hidden)
static bool updateOperandIfDifferent(MachineInstr &MI, AMDGPU::OpName OpName, unsigned NewEnc)
static bool isWaitInstr(MachineInstr &Inst)
static cl::opt< bool > ForceEmitZeroFlag("amdgpu-waitcnt-forcezero", cl::desc("Force all waitcnt instrs to be emitted as " "s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)"), cl::init(false), cl::Hidden)
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 mayWriteLDSThroughDMA(const MachineInstr &MI)
static bool usesTENSOR_CNT(const MachineInstr &MI)
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 usesASYNC_CNT(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)
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)
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)
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...
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 isMaybeAtomic(const T &...O)
initializer< Ty > init(const Ty &Val)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
This is an optimization pass for GlobalISel generic memory operations.
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
@ Async
"Asynchronous" unwind tables (instr precise)
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 > >
uint16_t MCPhysReg
An unsigned integer type large enough to represent all physical registers, but not necessarily virtua...
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.