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 MCRegister determineVGPR16Dependency(
const MachineInstr &
MI,
580 MCRegister
Reg)
const;
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 {
634 bool hasDifferentVGPRPendingEvents(MCRegister
Reg,
HWEvents E)
const {
635 for (MCRegUnit RU : regunits(
Reg)) {
636 auto It = VMem.find(toVMEMID(RU));
637 if (It != VMem.end() && (It->second.VGPRPendingEvents & ~
E).any())
643 void clearVGPRPendingEvents(MCRegister
Reg) {
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,
665 MCRegister RU)
const;
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,
880 MCRegister
Reg)
const {
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 (
size_t 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 +
static_cast<VMEMID
>(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);
1440MCRegister WaitcntBrackets::determineVGPR16Dependency(
const MachineInstr &
MI,
1442 MCRegister
Reg)
const {
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 =
static_cast<unsigned>(
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 =
static_cast<unsigned>(
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);
1728 unsigned OldVSCnt =
static_cast<unsigned>(
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) {
1944 unsigned OldEnc =
static_cast<unsigned>(
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) {
1959 unsigned OldEnc =
static_cast<unsigned>(
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) {
1974 unsigned OldEnc =
static_cast<unsigned>(
1975 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
1976 AMDGPU::Waitcnt OldWait;
1983 ScoreBrackets.simplifyWaitcnt(OldWait);
1985 if (WaitcntDepctrInstr ==
nullptr) {
1986 WaitcntDepctrInstr = &
II;
1994 unsigned Enc =
static_cast<unsigned>(
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 =
static_cast<unsigned>(
II.getOperand(0).
getImm());
2019 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
2022 std::optional<AMDGPU::InstCounterType> CT =
2025 unsigned OldCnt =
static_cast<unsigned>(
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) {
2167 unsigned Enc =
static_cast<unsigned>(
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 VMEMID TID = LDSDMA_BEGIN;
2464 if (Ptr && Memop->getAAInfo()) {
2465 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2466 for (
size_t I = 0,
E = LDSDMAStores.size();
I !=
E; ++
I) {
2467 if (
MI.mayAlias(AA, *LDSDMAStores[
I],
true)) {
2468 size_t Slot =
I + 1;
2469 if (Slot >= NUM_LDSDMA) {
2477 ScoreBrackets.determineWaitForLDSDMA(
2484 if (Memop->isStore()) {
2490 for (
const MachineOperand &
Op :
MI.operands()) {
2495 if (
Op.isTied() &&
Op.isUse() &&
TII.doesNotReadTiedSource(
MI))
2498 MCRegister
Reg =
Op.getReg().asMCReg();
2500 const bool IsVGPR =
TRI.isVectorRegister(MRI,
Op.getReg());
2507 if (
Op.isImplicit() &&
MI.mayLoadOrStore())
2525 if (
Op.isUse() || !updateVMCntOnly(
MI) ||
2526 ScoreBrackets.hasDifferentVGPRPendingEvents(
2528 ScoreBrackets.hasPointSamplePendingVmemTypes(
MI,
Reg) ||
2529 !
ST.hasVmemWriteVgprInOrder()) {
2536 ScoreBrackets.clearVGPRPendingEvents(
Reg);
2540 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2545 }
else if (
Op.getReg() == AMDGPU::SCC) {
2548 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter,
Reg,
Wait,
2552 if (
ST.hasWaitXcnt() &&
ST.isXNACKEnabled() &&
Op.isDef())
2571 if (
Opc == AMDGPU::S_BARRIER && !
ST.hasAutoWaitcntBeforeBarrier() &&
2572 !
ST.hasBackOffBarrier()) {
2573 Wait =
Wait.combined(WCG->getAllZeroWaitcnt(
true));
2580 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2585 ScoreBrackets.simplifyWaitcnt(
Wait);
2591 if (
TII.isVALU(
MI,
false)) {
2607 Wait = WCG->getAllZeroWaitcnt(
false);
2611 if (!ForceEmitWaitcnt[
T])
2616 if (FlushFlags.FlushVmCnt) {
2622 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
2628 return generateWaitcnt(
Wait,
MI.getIterator(), *
MI.getParent(), ScoreBrackets,
2632bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt
Wait,
2634 MachineBasicBlock &
Block,
2635 WaitcntBrackets &ScoreBrackets,
2636 MachineInstr *OldWaitcntInstr) {
2639 if (OldWaitcntInstr)
2643 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr,
Wait, It);
2648 MachineOperand *WaitExp =
TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2658 <<
"Update Instr: " << *It);
2661 if (WCG->createNewWaitcnt(
Block, It,
Wait, ScoreBrackets))
2666 ScoreBrackets.applyWaitcnt(
Wait);
2671bool SIInsertWaitcnts::isVmemAccess(
const MachineInstr &
MI)
const {
2672 return (
TII.isFLAT(
MI) &&
TII.mayAccessVMEMThroughFlat(
MI)) ||
2679 MachineBasicBlock *
Block)
const {
2680 auto BlockEnd =
Block->getParent()->end();
2681 auto BlockIter =
Block->getIterator();
2685 if (++BlockIter != BlockEnd) {
2686 It = BlockIter->instr_begin();
2693 if (!It->isMetaInstruction())
2701 return It->getOpcode() == AMDGPU::S_ENDPGM;
2705bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2706 MachineBasicBlock &
Block,
2707 WaitcntBrackets &ScoreBrackets) {
2708 AMDGPU::Waitcnt
Wait;
2709 bool NeedsEndPGMCheck =
false;
2717 NeedsEndPGMCheck =
true;
2720 ScoreBrackets.simplifyWaitcnt(
Wait);
2723 bool Result = generateWaitcnt(
Wait, SuccessorIt,
Block, ScoreBrackets,
2726 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &
Block)) {
2734void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2735 WaitcntBrackets *ScoreBrackets) {
2739 ScoreBrackets->updateByEvent(
E, Inst);
2741 if (
TII.isDS(Inst) &&
TII.usesLGKM_CNT(Inst)) {
2743 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2744 ScoreBrackets->setPendingGDS();
2746 }
else if (
TII.isFLAT(Inst)) {
2748 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2755 ScoreBrackets->setPendingFlat();
2757 }
else if (Inst.
isCall()) {
2760 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(
false));
2761 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2762 }
else if (
TII.isVINTERP(Inst)) {
2763 int64_t
Imm =
TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2773bool WaitcntBrackets::mergeScore(
const MergeInfo &M,
unsigned &Score,
2774 unsigned OtherScore) {
2775 unsigned MyShifted = Score <=
M.OldLB ? 0 : Score +
M.MyShift;
2776 unsigned OtherShifted =
2777 OtherScore <=
M.OtherLB ? 0 : OtherScore +
M.OtherShift;
2778 Score = std::max(MyShifted, OtherShifted);
2779 return OtherShifted > MyShifted;
2784 bool StrictDom =
false;
2788 if (AsyncMarks.empty() && OtherMarks.
empty()) {
2795 auto MaxSize = (unsigned)std::max(AsyncMarks.size(), OtherMarks.
size());
2796 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2799 if (AsyncMarks.size() > MaxSize)
2800 AsyncMarks.erase(AsyncMarks.begin(),
2801 AsyncMarks.begin() + (AsyncMarks.size() - MaxSize));
2807 constexpr CounterValueArray ZeroMark{};
2808 AsyncMarks.insert(AsyncMarks.begin(), MaxSize - AsyncMarks.size(), ZeroMark);
2811 dbgs() <<
"Before merge:\n";
2812 for (
const auto &Mark : AsyncMarks) {
2816 dbgs() <<
"Other marks:\n";
2817 for (
const auto &Mark : OtherMarks) {
2830 const size_t OtherSize = OtherMarks.size();
2831 const size_t OurSize = AsyncMarks.size();
2838 "AsyncMarks padded to MaxSize >= 1 (needs MaxAsyncMarks != 0)");
2841 const CounterValueArray &OtherMark =
2842 Idx <= OtherSize ? OtherMarks[OtherSize -
Idx] : ZeroMark;
2845 mergeScore(MergeInfos[
T], AsyncMarks[OurSize - Idx][
T], OtherMark[
T]);
2849 dbgs() <<
"After merge:\n";
2850 for (
const auto &Mark : AsyncMarks) {
2864bool WaitcntBrackets::merge(
const WaitcntBrackets &
Other) {
2865 bool StrictDom =
false;
2869 for (
auto K :
Other.VMem.keys())
2870 VMem.try_emplace(K);
2871 for (
auto K :
Other.SGPRs.keys())
2872 SGPRs.try_emplace(K);
2880 const HWEvents OldEvents = PendingEvents & EventsForT;
2881 const HWEvents OtherEvents =
Other.PendingEvents & EventsForT;
2882 if (!OldEvents.
contains(OtherEvents))
2884 PendingEvents |= OtherEvents;
2887 const unsigned MyPending = ScoreUBs[
T] - ScoreLBs[
T];
2888 const unsigned OtherPending =
Other.ScoreUBs[
T] -
Other.ScoreLBs[
T];
2889 const unsigned NewUB = ScoreLBs[
T] + std::max(MyPending, OtherPending);
2890 if (NewUB < ScoreLBs[
T])
2893 MergeInfo &
M = MergeInfos[
T];
2894 M.OldLB = ScoreLBs[
T];
2895 M.OtherLB =
Other.ScoreLBs[
T];
2896 M.MyShift = NewUB - ScoreUBs[
T];
2897 M.OtherShift = NewUB -
Other.ScoreUBs[
T];
2899 ScoreUBs[
T] = NewUB;
2902 StrictDom |= mergeScore(M, LastFlatLoadCnt,
Other.LastFlatLoadCnt);
2905 StrictDom |= mergeScore(M, LastFlatDsCnt,
Other.LastFlatDsCnt);
2906 StrictDom |= mergeScore(M, LastGDS,
Other.LastGDS);
2910 StrictDom |= mergeScore(M, SCCScore,
Other.SCCScore);
2911 if (
Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2912 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2913 PendingSCCWrite =
Other.PendingSCCWrite;
2914 }
else if (PendingSCCWrite !=
Other.PendingSCCWrite) {
2915 PendingSCCWrite =
nullptr;
2920 for (
auto &[RegID, Info] : VMem)
2921 StrictDom |= mergeScore(M,
Info.Scores[
T],
Other.getVMemScore(RegID,
T));
2923 if (isSmemCounter(
T)) {
2924 for (
auto &[RegID, Info] : SGPRs) {
2925 auto It =
Other.SGPRs.find(RegID);
2926 unsigned OtherScore = (It !=
Other.SGPRs.end()) ? It->second.get(
T) : 0;
2927 StrictDom |= mergeScore(M,
Info.get(
T), OtherScore);
2932 for (
auto &[TID, Info] : VMem) {
2933 if (
auto It =
Other.VMem.find(TID); It !=
Other.VMem.end()) {
2935 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
2936 StrictDom |= NewVGPRContext !=
Info.VGPRPendingEvents;
2937 Info.VGPRPendingEvents = NewVGPRContext;
2941 StrictDom |= mergeAsyncMarks(MergeInfos,
Other.AsyncMarks);
2943 StrictDom |= mergeScore(MergeInfos[
T], AsyncScore[
T],
Other.AsyncScore[
T]);
2945 purgeEmptyTrackingData();
2951 return Opcode == AMDGPU::S_WAITCNT ||
2954 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
2955 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
2956 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
2957 Opcode == AMDGPU::WAIT_ASYNCMARK ||
2961void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &
MBB,
2963 bool ExpertMode)
const {
2967 .
addImm(ExpertMode ? 2 : 0)
2985class VCCZWorkaround {
2986 const WaitcntBrackets &ScoreBrackets;
2987 const GCNSubtarget &
ST;
2988 const SIInstrInfo &
TII;
2989 const SIRegisterInfo &
TRI;
2990 bool VCCZCorruptionBug =
false;
2991 bool VCCZNotUpdatedByPartialWrites =
false;
2994 bool MustRecomputeVCCZ =
true;
2997 VCCZWorkaround(
const WaitcntBrackets &ScoreBrackets,
const GCNSubtarget &ST,
2998 const SIInstrInfo &
TII,
const SIRegisterInfo &
TRI)
3000 VCCZCorruptionBug =
ST.hasReadVCCZBug();
3001 VCCZNotUpdatedByPartialWrites = !
ST.partialVCCWritesUpdateVCCZ();
3008 bool tryRecomputeVCCZ(MachineInstr &
MI) {
3010 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
3020 MustRecomputeVCCZ |= VCCZCorruptionBug &&
TII.isSMRD(
MI);
3026 std::optional<bool> PartiallyWritesToVCCOpt;
3027 auto PartiallyWritesToVCC = [](MachineInstr &
MI) {
3028 return MI.definesRegister(AMDGPU::VCC_LO,
nullptr) ||
3029 MI.definesRegister(AMDGPU::VCC_HI,
nullptr);
3031 if (VCCZNotUpdatedByPartialWrites) {
3032 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3035 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
3041 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
3042 !VCCZCorruptionBug) {
3044 if (!PartiallyWritesToVCCOpt)
3045 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3046 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
3047 MI.definesRegister(AMDGPU::VCC,
nullptr);
3050 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
3051 *PartiallyWritesToVCCOpt);
3053 MustRecomputeVCCZ =
false;
3063 TII.get(
ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
3066 MustRecomputeVCCZ =
false;
3077 MachineBasicBlock &
Block,
3078 WaitcntBrackets &ScoreBrackets) {
3082 dbgs() <<
"*** Begin Block: ";
3084 ScoreBrackets.dump();
3086 VCCZWorkaround VCCZW(ScoreBrackets, ST,
TII,
TRI);
3089 MachineInstr *OldWaitcntInstr =
nullptr;
3092 ScoreBrackets.verify();
3095 Iter !=
E; ++Iter) {
3096 MachineInstr &Inst = *Iter;
3097 if (isNonWaitcntMetaInst(Inst))
3102 (IsExpertMode && Inst.
getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3103 if (!OldWaitcntInstr)
3104 OldWaitcntInstr = &Inst;
3108 PreheaderFlushFlags FlushFlags;
3109 if (
Block.getFirstTerminator() == Inst)
3110 FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3113 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3115 OldWaitcntInstr =
nullptr;
3117 if (Inst.
getOpcode() == AMDGPU::ASYNCMARK) {
3121 ScoreBrackets.recordAsyncMark(Inst);
3125 if (
TII.isSMRD(Inst)) {
3126 for (
const MachineMemOperand *Memop : Inst.
memoperands()) {
3129 if (!Memop->isInvariant()) {
3130 const Value *Ptr = Memop->getValue();
3136 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3140 Modified |= insertForcedWaitAfter(Inst,
Block, ScoreBrackets);
3144 ScoreBrackets.dump();
3149 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3151 ScoreBrackets.verify();
3156 AMDGPU::Waitcnt
Wait;
3157 if (
Block.getFirstTerminator() ==
Block.end()) {
3158 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3159 if (FlushFlags.FlushVmCnt) {
3167 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
3176 dbgs() <<
"*** End Block: ";
3178 ScoreBrackets.dump();
3184bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &
Block) {
3185 if (
Block.size() <= 1)
3193 MachineInstr *LastAtomicWithSoftXcnt =
nullptr;
3197 bool IsLDS =
TII.isDS(
MI) ||
3198 (
TII.isFLAT(
MI) &&
TII.mayAccessLDSThroughFlat(
MI, TgSplit));
3199 if (!IsLDS && (
MI.mayLoad() ^
MI.mayStore()))
3200 LastAtomicWithSoftXcnt =
nullptr;
3204 MachineInstr &PrevMI = *
MI.getPrevNode();
3206 if (PrevMI.
getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3209 if (LastAtomicWithSoftXcnt) {
3213 LastAtomicWithSoftXcnt = &
MI;
3221SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &
MBB,
3222 const WaitcntBrackets &ScoreBrackets) {
3230 return PreheaderFlushFlags();
3234 return PreheaderFlushFlags();
3236 if (
Loop->getLoopPreheader() == &
MBB) {
3237 Iterator->second = getPreheaderFlushFlags(
Loop, ScoreBrackets);
3241 return PreheaderFlushFlags();
3244bool SIInsertWaitcnts::isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const {
3246 return TII.mayAccessVMEMThroughFlat(
MI);
3250bool SIInsertWaitcnts::isDSRead(
const MachineInstr &
MI)
const {
3256bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const {
3285SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *
ML,
3286 const WaitcntBrackets &Brackets) {
3287 PreheaderFlushFlags
Flags;
3288 bool HasVMemLoad =
false;
3289 bool HasVMemStore =
false;
3290 bool UsesVgprVMEMLoadedOutside =
false;
3291 bool UsesVgprDSReadOutside =
false;
3292 bool VMemInvalidated =
false;
3296 bool TrackSimpleDSOpt =
ST.hasExtendedWaitCounts();
3297 DenseSet<MCRegUnit> VgprUse;
3298 DenseSet<MCRegUnit> VgprDefVMEM;
3299 DenseSet<MCRegUnit> VgprDefDS;
3305 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3306 unsigned DSReadPosition = 0;
3307 bool IsSingleBlock =
ML->getNumBlocks() == 1;
3308 bool TrackDSFlushPoint =
ST.hasExtendedWaitCounts() && IsSingleBlock;
3309 unsigned LastDSFlushPosition = 0;
3311 for (MachineBasicBlock *
MBB :
ML->blocks()) {
3312 for (MachineInstr &
MI : *
MBB) {
3313 if (isVMEMOrFlatVMEM(
MI)) {
3314 HasVMemLoad |=
MI.mayLoad();
3315 HasVMemStore |=
MI.mayStore();
3319 if (mayStoreIncrementingDSCNT(
MI)) {
3322 if (VMemInvalidated)
3324 TrackSimpleDSOpt =
false;
3325 TrackDSFlushPoint =
false;
3327 bool IsDSRead = isDSRead(
MI);
3332 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3333 if (!TrackDSFlushPoint)
3335 if (
auto It = LastDSReadPositionMap.
find(RU);
3336 It != LastDSReadPositionMap.
end()) {
3340 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3344 for (
const MachineOperand &
Op :
MI.all_uses()) {
3345 if (
Op.isDebug() || !
TRI.isVectorRegister(MRI,
Op.getReg()))
3348 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3352 VMemInvalidated =
true;
3356 TrackSimpleDSOpt =
false;
3359 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3363 updateDSReadFlushTracking(RU);
3368 VMEMID
ID = toVMEMID(RU);
3372 UsesVgprVMEMLoadedOutside =
true;
3377 UsesVgprDSReadOutside =
true;
3382 if (isVMEMOrFlatVMEM(
MI) &&
MI.mayLoad()) {
3383 for (
const MachineOperand &
Op :
MI.all_defs()) {
3384 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3388 VMemInvalidated =
true;
3393 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3404 if (IsDSRead || TrackDSFlushPoint) {
3405 for (
const MachineOperand &
Op :
MI.all_defs()) {
3406 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
3408 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3411 updateDSReadFlushTracking(RU);
3414 if (TrackDSFlushPoint)
3415 LastDSReadPositionMap[RU] = DSReadPosition;
3424 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3425 ((!
ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3426 (HasVMemLoad &&
ST.hasVmemWriteVgprInOrder())))
3427 Flags.FlushVmCnt =
true;
3433 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3436 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3437 bool DSFlushPointPrefetch =
3438 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3440 if (SimpleDSOpt || DSFlushPointPrefetch)
3441 Flags.FlushDsCnt =
true;
3446bool SIInsertWaitcntsLegacy::runOnMachineFunction(
MachineFunction &MF) {
3447 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3449 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3451 if (
auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3452 AA = &AAR->getAAResults();
3454 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3466 if (!SIInsertWaitcnts(MLI, PDT,
AA, MF).
run())
3471 .preserve<AAManager>();
3474bool SIInsertWaitcnts::run() {
3482 if (ST.hasExtendedWaitCounts()) {
3487 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3492 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3496 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3500 MachineBasicBlock &EntryBB = MF.
front();
3511 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3514 if (
ST.hasExtendedWaitCounts()) {
3523 if (!
ST.hasImageInsts() &&
3529 TII.get(instrsForExtendedCounterTypes[CT]))
3542 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(
this);
3543 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3544 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3551 for (
auto *
MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3554 std::unique_ptr<WaitcntBrackets> Brackets;
3559 for (
auto BII = BlockInfos.
begin(), BIE = BlockInfos.
end(); BII != BIE;
3561 MachineBasicBlock *
MBB = BII->first;
3562 BlockInfo &BI = BII->second;
3568 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3570 *Brackets = *BI.Incoming;
3573 Brackets = std::make_unique<WaitcntBrackets>(
this);
3578 Brackets->~WaitcntBrackets();
3579 new (Brackets.get()) WaitcntBrackets(
this);
3583 if (
ST.hasWaitXcnt() &&
ST.isXNACKEnabled())
3585 Modified |= insertWaitcntInBlock(MF, *
MBB, *Brackets);
3588 if (Brackets->hasPendingEvent()) {
3589 BlockInfo *MoveBracketsToSucc =
nullptr;
3591 auto *SuccBII = BlockInfos.
find(Succ);
3592 BlockInfo &SuccBI = SuccBII->second;
3593 if (!SuccBI.Incoming) {
3594 SuccBI.Dirty =
true;
3595 if (SuccBII <= BII) {
3599 if (!MoveBracketsToSucc) {
3600 MoveBracketsToSucc = &SuccBI;
3602 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3606 dbgs() <<
"Try to merge ";
3612 if (SuccBI.Incoming->merge(*Brackets)) {
3613 SuccBI.Dirty =
true;
3614 if (SuccBII <= BII) {
3621 if (MoveBracketsToSucc)
3622 MoveBracketsToSucc->Incoming = std::move(Brackets);
3627 Modified |= WCG->eraseDeferredWaitcnts();
3629 if (
ST.hasScalarStores()) {
3630 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3631 bool HaveScalarStores =
false;
3633 for (MachineBasicBlock &
MBB : MF) {
3634 for (MachineInstr &
MI :
MBB) {
3635 if (!HaveScalarStores &&
TII.isScalarStore(
MI))
3636 HaveScalarStores =
true;
3638 if (
MI.getOpcode() == AMDGPU::S_ENDPGM ||
3639 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3644 if (HaveScalarStores) {
3653 for (MachineBasicBlock *
MBB : EndPgmBlocks) {
3654 bool SeenDCacheWB =
false;
3658 if (
I->getOpcode() == AMDGPU::S_DCACHE_WB)
3659 SeenDCacheWB =
true;
3660 else if (
TII.isScalarStore(*
I))
3661 SeenDCacheWB =
false;
3664 if ((
I->getOpcode() == AMDGPU::S_ENDPGM ||
3665 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3681 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3683 setSchedulingMode(EntryBB,
I,
true);
3685 for (MachineInstr *
MI : CallInsts) {
3687 setSchedulingMode(
MBB,
MI,
false);
3688 setSchedulingMode(
MBB, std::next(
MI->getIterator()),
true);
3691 for (MachineInstr *
MI : ReturnInsts)
3692 setSchedulingMode(*
MI->getParent(),
MI,
false);
3703 for (
auto [
MI,
_] : EndPgmInsts) {
3705 TII.get(AMDGPU::S_ALLOC_VGPR))
3709 }
else if (!WCG->isOptNone() &&
3710 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3711 (MF.getFrameInfo().hasCalls() ||
3712 ST.getOccupancyWithNumVGPRs(
3713 TRI.getNumUsedPhysRegs(MRI, AMDGPU::VGPR_32RegClass),
3714 false) <
ST.getMaxWavesPerEU())) {
3715 for (
auto [
MI, Flag] : EndPgmInsts) {
3717 if (
ST.requiresNopBeforeDeallocVGPRs()) {
3719 TII.get(AMDGPU::S_NOP))
3723 TII.get(AMDGPU::S_SENDMSG))
3733 if (
ST.hasWaitXcnt() &&
ST.isXNACKEnabled()) {
3740 TII.get(AMDGPU::S_SETREG_IMM32_B32))
3746 if (
ST.hasRequiresInitialUnclausedVmem()) {
3752 AMDGPU::SGPR64_SGPR65)
3756 TII.get(AMDGPU::GLOBAL_PREFETCH_B8_SADDR))
3757 .
addReg(AMDGPU::SGPR64_SGPR65)
3758 .
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.
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)
@ 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 > >
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.