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"),
59 "amdgpu-expert-scheduling-mode",
60 cl::desc(
"Enable expert scheduling mode 2 for all functions (GFX12+ only)"),
65template <
typename EmitWaitcntFn>
66static void EmitExpandedWaitcnt(
unsigned Outstanding,
unsigned Target,
67 EmitWaitcntFn &&EmitWaitcnt) {
69 for (
unsigned I = Outstanding - 1;
I >
Target &&
I != ~0u; --
I)
89 TRACKINGID_RANGE_LEN = (1 << 16),
94 REGUNITS_END = REGUNITS_BEGIN + TRACKINGID_RANGE_LEN,
99 NUM_LDSDMA = TRACKINGID_RANGE_LEN,
100 LDSDMA_BEGIN = REGUNITS_END,
101 LDSDMA_END = LDSDMA_BEGIN + NUM_LDSDMA,
105static constexpr VMEMID toVMEMID(MCRegUnit RU) {
106 return static_cast<unsigned>(RU);
118 AMDGPU::S_WAIT_LOADCNT, AMDGPU::S_WAIT_DSCNT,
119 AMDGPU::S_WAIT_EXPCNT, AMDGPU::S_WAIT_STORECNT,
120 AMDGPU::S_WAIT_SAMPLECNT, AMDGPU::S_WAIT_BVHCNT,
121 AMDGPU::S_WAIT_KMCNT, AMDGPU::S_WAIT_XCNT,
122 AMDGPU::S_WAIT_ASYNCCNT, AMDGPU::S_WAIT_TENSORCNT};
127 switch (
MI.getOpcode()) {
128 case AMDGPU::ASYNCMARK:
129 case AMDGPU::WAIT_ASYNCMARK:
132 return MI.isMetaInstruction();
147class WaitcntBrackets;
155class WaitcntGenerator {
157 const GCNSubtarget &ST;
158 const SIInstrInfo &TII;
159 AMDGPU::IsaVersion IV;
162 bool ExpandWaitcntProfiling =
false;
163 const AMDGPU::HardwareLimits &Limits;
167 DenseSet<MachineInstr *> DeferredErasures;
170 WaitcntGenerator() =
delete;
171 WaitcntGenerator(
const WaitcntGenerator &) =
delete;
174 const AMDGPU::HardwareLimits &Limits)
175 : ST(MF.getSubtarget<GCNSubtarget>()), TII(*ST.getInstrInfo()),
179 ExpandWaitcntProfiling(
180 MF.
getFunction().hasFnAttribute(
"amdgpu-expand-waitcnt-profiling")),
185 bool isOptNone()
const {
return OptNone; }
201 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
202 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
205 bool eraseDeferredWaitcnts() {
207 for (MachineInstr *
MI : DeferredErasures) {
208 MI->eraseFromParent();
211 DeferredErasures.clear();
216 bool promoteSoftWaitCnt(MachineInstr *Waitcnt)
const;
221 virtual bool createNewWaitcnt(MachineBasicBlock &
Block,
223 AMDGPU::Waitcnt
Wait,
224 const WaitcntBrackets &ScoreBrackets) = 0;
231 assert(
E.size() == 1 &&
"Cannot handle a mask of events!");
233 if (getWaitEvents(
T) &
E)
244 virtual AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const = 0;
246 virtual ~WaitcntGenerator() =
default;
249class WaitcntGeneratorPreGFX12 final :
public WaitcntGenerator {
252 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_SAMPLER_READ_ACCESS |
253 HWEvents::VMEM_BVH_READ_ACCESS | HWEvents::VMEM_INV_ACCESS,
254 HWEvents::SMEM_ACCESS | HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS |
255 HWEvents::SQ_MESSAGE,
256 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
257 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
258 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
259 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
271 using WaitcntGenerator::WaitcntGenerator;
272 bool applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
273 MachineInstr &OldWaitcntInstr,
274 AMDGPU::Waitcnt &
Wait,
277 bool createNewWaitcnt(MachineBasicBlock &
Block,
279 AMDGPU::Waitcnt
Wait,
280 const WaitcntBrackets &ScoreBrackets)
override;
283 HWEvents EVs = WaitEventMaskForInstPreGFX12[
T];
289 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
292class WaitcntGeneratorGFX12Plus final :
public WaitcntGenerator {
297 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_INV_ACCESS,
298 HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS,
299 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
300 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
301 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
303 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
304 HWEvents::VMEM_SAMPLER_READ_ACCESS,
305 HWEvents::VMEM_BVH_READ_ACCESS,
307 HWEvents::SMEM_ACCESS | HWEvents::SQ_MESSAGE | HWEvents::SCC_WRITE,
308 HWEvents::VMEM_GROUP | HWEvents::SMEM_GROUP,
309 HWEvents::ASYNC_ACCESS,
310 HWEvents::TENSOR_ACCESS,
311 HWEvents::VGPR_CSMACC_READ | HWEvents::VGPR_DPMACC_READ |
312 HWEvents::VGPR_TRANS_READ | HWEvents::VGPR_XDL_READ,
313 HWEvents::VGPR_CSMACC_WRITE | HWEvents::VGPR_DPMACC_WRITE |
314 HWEvents::VGPR_TRANS_WRITE | HWEvents::VGPR_XDL_WRITE,
315 HWEvents::VGPR_LDS_READ | HWEvents::VGPR_FLAT_READ |
316 HWEvents::VGPR_VMEM_READ};
319 WaitcntGeneratorGFX12Plus() =
delete;
322 const AMDGPU::HardwareLimits &Limits,
324 : WaitcntGenerator(MF, MaxCounter, Limits), IsExpertMode(IsExpertMode) {}
326 bool applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
327 MachineInstr &OldWaitcntInstr,
328 AMDGPU::Waitcnt &
Wait,
331 bool createNewWaitcnt(MachineBasicBlock &
Block,
333 AMDGPU::Waitcnt
Wait,
334 const WaitcntBrackets &ScoreBrackets)
override;
337 return WaitEventMaskForInstGFX12Plus[
T];
340 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
344struct PreheaderFlushFlags {
345 bool FlushVmCnt =
false;
346 bool FlushDsCnt =
false;
349class SIInsertWaitcnts {
350 DenseMap<const Value *, MachineBasicBlock *> SLoadAddresses;
351 DenseMap<MachineBasicBlock *, PreheaderFlushFlags> PreheadersToFlush;
352 MachineLoopInfo &MLI;
353 MachinePostDominatorTree &PDT;
358 std::unique_ptr<WaitcntBrackets> Incoming;
360 BlockInfo() =
default;
361 BlockInfo(BlockInfo &&) =
default;
362 BlockInfo &operator=(BlockInfo &&) =
default;
366 MapVector<MachineBasicBlock *, BlockInfo> BlockInfos;
370 std::unique_ptr<WaitcntGenerator> WCG;
373 DenseSet<MachineInstr *> CallInsts;
374 DenseSet<MachineInstr *> ReturnInsts;
379 DenseMap<MachineInstr *, bool> EndPgmInsts;
381 AMDGPU::HardwareLimits Limits;
384 const GCNSubtarget &ST;
385 const SIInstrInfo &TII;
386 const SIRegisterInfo &TRI;
387 const MachineRegisterInfo &MRI;
390 bool IsExpertMode =
false;
393 SIInsertWaitcnts(MachineLoopInfo &MLI, MachinePostDominatorTree &PDT,
395 : MLI(MLI), PDT(PDT), AA(AA), MF(MF), ST(MF.getSubtarget<GCNSubtarget>()),
396 TII(*ST.getInstrInfo()), TRI(TII.getRegisterInfo()),
397 MRI(MF.getRegInfo()),
398 TgSplit(ST.hasTgSplitSupport() &&
401 const AMDGPU::HardwareLimits &getLimits()
const {
return Limits; }
403 PreheaderFlushFlags getPreheaderFlushFlags(MachineLoop *
ML,
404 const WaitcntBrackets &Brackets);
405 PreheaderFlushFlags isPreheaderToFlush(MachineBasicBlock &
MBB,
406 const WaitcntBrackets &ScoreBrackets);
407 bool isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const;
408 bool isDSRead(
const MachineInstr &
MI)
const;
409 bool mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const;
412 bool isAsync(
const MachineInstr &
MI)
const {
417 const MachineOperand *
Async =
418 TII.getNamedOperand(
MI, AMDGPU::OpName::IsAsync);
422 bool isNonAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
426 bool isAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
430 bool shouldUpdateAsyncMark(
const MachineInstr &
MI,
434 if (!isAsyncLdsDmaWrite(
MI))
441 bool isVmemAccess(
const MachineInstr &
MI)
const;
442 bool generateWaitcntInstBefore(MachineInstr &
MI,
443 WaitcntBrackets &ScoreBrackets,
444 MachineInstr *OldWaitcntInstr,
445 PreheaderFlushFlags FlushFlags);
446 bool generateWaitcnt(AMDGPU::Waitcnt
Wait,
448 MachineBasicBlock &
Block, WaitcntBrackets &ScoreBrackets,
449 MachineInstr *OldWaitcntInstr);
450 void updateEventWaitcntAfter(MachineInstr &Inst,
451 WaitcntBrackets *ScoreBrackets);
453 MachineBasicBlock *
Block)
const;
454 bool insertForcedWaitAfter(MachineInstr &Inst, MachineBasicBlock &
Block,
455 WaitcntBrackets &ScoreBrackets);
457 WaitcntBrackets &ScoreBrackets);
460 bool removeRedundantSoftXcnts(MachineBasicBlock &
Block);
462 bool ExpertMode)
const;
464 return WCG->getWaitEvents(
T);
467 return WCG->getCounterFromEvent(
E);
479class WaitcntBrackets {
481 WaitcntBrackets(
const SIInsertWaitcnts *Context) : Context(Context) {
482 assert(Context->TRI.getNumRegUnits() < REGUNITS_END);
487 unsigned NumUnusedVmem = 0, NumUnusedSGPRs = 0;
488 for (
auto &[ID, Val] : VMem) {
492 for (
auto &[ID, Val] : SGPRs) {
497 if (NumUnusedVmem || NumUnusedSGPRs) {
498 errs() <<
"WaitcntBracket had unused entries at destruction time: "
499 << NumUnusedVmem <<
" VMem and " << NumUnusedSGPRs
500 <<
" SGPR unused entries\n";
511 return ScoreUBs[
T] - ScoreLBs[
T];
515 return getVMemScore(ID,
T) > getScoreLB(
T);
533 return getScoreUB(
T) - getScoreLB(
T);
537 auto It = SGPRs.find(RU);
538 return It != SGPRs.end() ? It->second.get(
T) : 0;
542 auto It = VMem.find(TID);
543 return It != VMem.end() ? It->second.Scores[
T] : 0;
558 void simplifyWaitcnt(AMDGPU::Waitcnt &
Wait)
const {
561 void simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
562 AMDGPU::Waitcnt &UpdateWait)
const;
565 void simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
566 AMDGPU::Waitcnt &UpdateWait)
const;
567 void simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
568 AMDGPU::Waitcnt &UpdateWait)
const;
571 void simplifyVmemInvOnlyWait(AMDGPU::Waitcnt &
Wait)
const {
576 if (LoadEvents == HWEvents::VMEM_INV_ACCESS)
581 AMDGPU::Waitcnt &
Wait,
582 const MachineInstr &
MI)
const;
583 MCPhysReg determineVGPR16Dependency(
const MachineInstr &
MI,
587 AMDGPU::Waitcnt &
Wait)
const;
588 AMDGPU::Waitcnt determineAsyncWait(
unsigned N);
589 void tryClearSCCWriteEvent(MachineInstr *Inst);
591 void applyWaitcnt(
const AMDGPU::Waitcnt &
Wait);
595 void recordAsyncMark(MachineInstr &
MI);
597 HWEvents getPendingEvents()
const {
return PendingEvents; }
598 bool hasPendingEvent()
const {
return PendingEvents.
any(); }
599 bool hasPendingEvent(
HWEvents E)
const {
return PendingEvents.contains(
E); }
601 bool HasPending = (PendingEvents & Context->getWaitEvents(
T)).any();
603 "Expected pending events iff scoreboard is not empty");
608 HWEvents Events = PendingEvents & Context->getWaitEvents(
T);
610 return Events.
size() > 1;
613 bool hasPendingFlat()
const {
620 void setPendingFlat() {
625 bool hasPendingGDS()
const {
630 unsigned getPendingGDSWait()
const {
640 for (MCRegUnit RU : regunits(
Reg)) {
641 auto It = VMem.find(toVMEMID(RU));
642 if (It != VMem.end() && (It->second.VGPRPendingEvents & ~
E).any())
649 for (MCRegUnit RU : regunits(
Reg)) {
650 if (
auto It = VMem.find(toVMEMID(RU)); It != VMem.end()) {
652 if (It->second.empty())
658 void setStateOnFunctionEntryOrReturn() {
664 ArrayRef<const MachineInstr *> getLDSDMAStores()
const {
668 bool hasPointSampleAccel(
const MachineInstr &
MI)
const;
669 bool hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
672 void print(raw_ostream &)
const;
677 void purgeEmptyTrackingData();
681 return Context->getLimits().get(
T);
691 using CounterValueArray = std::array<unsigned, AMDGPU::NUM_INST_CNTS>;
694 AMDGPU::Waitcnt &
Wait)
const;
696 static bool mergeScore(
const MergeInfo &M,
unsigned &Score,
697 unsigned OtherScore);
702 assert(
Reg != AMDGPU::SCC &&
"Shouldn't be used on SCC");
703 if (!Context->TRI.isInAllocatableClass(
Reg))
705 return Context->TRI.regunits(
Reg);
726 const SIRegisterInfo &
TRI = Context->TRI;
727 if (
Reg == AMDGPU::SCC) {
729 }
else if (
TRI.isVectorRegister(Context->MRI,
Reg)) {
730 for (MCRegUnit RU : regunits(
Reg))
731 VMem[toVMEMID(RU)].Scores[
T] = Val;
732 }
else if (
TRI.isSGPRReg(Context->MRI,
Reg)) {
733 for (MCRegUnit RU : regunits(
Reg))
734 SGPRs[RU].get(
T) = Val;
741 VMem[TID].Scores[
T] = Val;
744 void setScoreByOperand(
const MachineOperand &
Op,
747 const SIInsertWaitcnts *Context;
753 unsigned LastFlatDsCnt = 0;
754 unsigned LastFlatLoadCnt = 0;
756 unsigned LastGDS = 0;
773 CounterValueArray Scores{};
787 unsigned ScoreDsKmCnt = 0;
788 unsigned ScoreXCnt = 0;
804 bool empty()
const {
return !ScoreDsKmCnt && !ScoreXCnt; }
807 DenseMap<VMEMID, VMEMInfo> VMem;
808 DenseMap<MCRegUnit, SGPRInfo> SGPRs;
811 unsigned SCCScore = 0;
813 const MachineInstr *PendingSCCWrite =
nullptr;
817 SmallVector<const MachineInstr *> LDSDMAStores;
826 static constexpr unsigned MaxAsyncMarks = 16;
830 CounterValueArray AsyncScore{};
833SIInsertWaitcnts::BlockInfo::~BlockInfo() =
default;
838 SIInsertWaitcntsLegacy() : MachineFunctionPass(ID) {}
842 StringRef getPassName()
const override {
843 return "SI insert wait instructions";
846 void getAnalysisUsage(AnalysisUsage &AU)
const override {
849 AU.
addRequired<MachinePostDominatorTreeWrapperPass>();
861 setRegScore(
Op.getReg().asMCReg(), CntTy, Score);
869bool WaitcntBrackets::hasPointSampleAccel(
const MachineInstr &
MI)
const {
874 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
884bool WaitcntBrackets::hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
886 if (!hasPointSampleAccel(
MI))
889 return hasDifferentVGPRPendingEvents(
Reg, HWEvents::VMEM_READ_ACCESS);
892void WaitcntBrackets::updateByEvent(
HWEvents E, MachineInstr &Inst) {
893 assert(
E.size() == 1 &&
"Expected singular event!");
897 unsigned UB = getScoreUB(
T);
901 Context->ST.hasVOP3PX2IncrementsVaVdstTwice()) {
913 setScoreUB(
T, CurrScore);
916 const MachineRegisterInfo &MRI =
Context->MRI;
925 if (
const auto *AddrOp =
TII.getNamedOperand(Inst, AMDGPU::OpName::addr))
929 if (
const auto *Data0 =
930 TII.getNamedOperand(Inst, AMDGPU::OpName::data0))
932 if (
const auto *Data1 =
933 TII.getNamedOperand(Inst, AMDGPU::OpName::data1))
937 Inst.
getOpcode() != AMDGPU::DS_CONSUME &&
938 Inst.
getOpcode() != AMDGPU::DS_ORDERED_COUNT) {
939 for (
const MachineOperand &
Op : Inst.
all_uses()) {
940 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
944 }
else if (
TII.isFLAT(Inst)) {
946 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
949 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
952 }
else if (
TII.isMIMG(Inst)) {
956 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
959 }
else if (
TII.isMTBUF(Inst)) {
962 }
else if (
TII.isMUBUF(Inst)) {
966 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
969 }
else if (
TII.isLDSDIR(Inst)) {
971 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::vdst),
974 if (
TII.isEXP(Inst)) {
979 for (MachineOperand &DefMO : Inst.
all_defs()) {
980 if (
TRI.isVGPR(MRI, DefMO.getReg())) {
985 for (
const MachineOperand &
Op : Inst.
all_uses()) {
986 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
992 E == HWEvents::SMEM_GROUP ? HWEvents::VMEM_GROUP : HWEvents::SMEM_GROUP;
993 if (PendingEvents.
contains(OtherEvent)) {
998 setScoreLB(
T, getScoreUB(
T) - 1);
999 PendingEvents -= OtherEvent;
1001 for (
const MachineOperand &
Op : Inst.
all_uses())
1002 setScoreByOperand(
Op,
T, CurrScore);
1007 for (
const MachineOperand &
Op : Inst.
operands()) {
1020 setScoreByOperand(
Op,
T, CurrScore);
1032 for (
const MachineOperand &
Op : Inst.
defs()) {
1035 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
1037 if (updateVMCntOnly(Inst)) {
1046 if (hasPointSampleAccel(Inst))
1047 VGPRContext |= HWEvents::VMEM_READ_ACCESS;
1048 for (MCRegUnit RU : regunits(
Op.getReg().asMCReg()))
1049 VMem[toVMEMID(RU)].VGPRPendingEvents |= VGPRContext;
1052 setScoreByOperand(
Op,
T, CurrScore);
1055 (
TII.isDS(Inst) ||
Context->isNonAsyncLdsDmaWrite(Inst))) {
1064 if (!MemOp->isStore() ||
1069 auto AAI = MemOp->getAAInfo();
1075 if (!AAI || !AAI.Scope)
1077 for (
unsigned I = 0,
E = LDSDMAStores.
size();
I !=
E && !Slot; ++
I) {
1078 for (
const auto *MemOp : LDSDMAStores[
I]->memoperands()) {
1079 if (MemOp->isStore() && AAI == MemOp->getAAInfo()) {
1094 setVMemScore(LDSDMA_BEGIN,
T, CurrScore);
1095 if (Slot && Slot < NUM_LDSDMA)
1096 setVMemScore(LDSDMA_BEGIN + Slot,
T, CurrScore);
1099 if (
Context->shouldUpdateAsyncMark(Inst,
T)) {
1100 AsyncScore[
T] = CurrScore;
1104 setRegScore(AMDGPU::SCC,
T, CurrScore);
1105 PendingSCCWrite = &Inst;
1110void WaitcntBrackets::recordAsyncMark(MachineInstr &Inst) {
1116 AsyncMarks.push_back(AsyncScore);
1118 dbgs() <<
"recordAsyncMark:\n" << Inst;
1119 for (
const auto &Mark : AsyncMarks) {
1126void WaitcntBrackets::print(raw_ostream &OS)
const {
1130 unsigned SR = getScoreRange(
T);
1133 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM") <<
"_CNT("
1137 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM") <<
"_CNT("
1141 OS <<
" EXP_CNT(" << SR <<
"):";
1144 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS") <<
"_CNT("
1148 OS <<
" SAMPLE_CNT(" << SR <<
"):";
1151 OS <<
" BVH_CNT(" << SR <<
"):";
1154 OS <<
" KM_CNT(" << SR <<
"):";
1157 OS <<
" X_CNT(" << SR <<
"):";
1160 OS <<
" ASYNC_CNT(" << SR <<
"):";
1163 OS <<
" VA_VDST_RD(" << SR <<
"): ";
1166 OS <<
" VA_VDST_WR(" << SR <<
"): ";
1169 OS <<
" VM_VSRC(" << SR <<
"): ";
1172 OS <<
" UNKNOWN(" << SR <<
"):";
1178 unsigned LB = getScoreLB(
T);
1181 sort(SortedVMEMIDs);
1183 for (
auto ID : SortedVMEMIDs) {
1184 unsigned RegScore = VMem.at(ID).Scores[
T];
1187 unsigned RelScore = RegScore - LB - 1;
1188 if (ID < REGUNITS_END) {
1189 OS <<
' ' << RelScore <<
':'
1192 assert(ID >= LDSDMA_BEGIN && ID < LDSDMA_END &&
1193 "Unhandled/unexpected ID value!");
1194 OS <<
' ' << RelScore <<
":LDSDMA" <<
ID;
1199 if (isSmemCounter(
T)) {
1201 sort(SortedSMEMIDs);
1202 for (
auto ID : SortedSMEMIDs) {
1203 unsigned RegScore = SGPRs.at(ID).get(
T);
1206 unsigned RelScore = RegScore - LB - 1;
1207 OS <<
' ' << RelScore <<
':'
1213 OS <<
' ' << SCCScore <<
":scc";
1218 OS <<
"Pending Events: ";
1219 if (hasPendingEvent()) {
1220 OS << getPendingEvents();
1226 OS <<
"Async score: ";
1227 if (AsyncScore.empty())
1233 OS <<
"Async marks: " << AsyncMarks.size() <<
'\n';
1235 for (
const auto &Mark : AsyncMarks) {
1237 unsigned MarkedScore = Mark[
T];
1240 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM")
1241 <<
"_CNT: " << MarkedScore;
1244 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM")
1245 <<
"_CNT: " << MarkedScore;
1248 OS <<
" EXP_CNT: " << MarkedScore;
1251 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS")
1252 <<
"_CNT: " << MarkedScore;
1255 OS <<
" SAMPLE_CNT: " << MarkedScore;
1258 OS <<
" BVH_CNT: " << MarkedScore;
1261 OS <<
" KM_CNT: " << MarkedScore;
1264 OS <<
" X_CNT: " << MarkedScore;
1267 OS <<
" ASYNC_CNT: " << MarkedScore;
1270 OS <<
" UNKNOWN: " << MarkedScore;
1281void WaitcntBrackets::simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
1282 AMDGPU::Waitcnt &UpdateWait)
const {
1290 simplifyXcnt(CheckWait, UpdateWait);
1293 simplifyVmVsrc(CheckWait, UpdateWait);
1298 unsigned &
Count)
const {
1302 if (
Count >= getScoreRange(
T))
1306void WaitcntBrackets::simplifyWaitcnt(AMDGPU::Waitcnt &
Wait,
1308 unsigned Cnt =
Wait.get(
T);
1309 simplifyWaitcnt(
T, Cnt);
1313void WaitcntBrackets::simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
1314 AMDGPU::Waitcnt &UpdateWait)
const {
1324 hasPendingEvent(HWEvents::SMEM_GROUP))
1330 hasPendingEvent(HWEvents::VMEM_GROUP) &&
1337void WaitcntBrackets::simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
1338 AMDGPU::Waitcnt &UpdateWait)
const {
1348 return Acc |
Context->getWaitEvents(
T);
1350 HWEvents PendingVmemEvents = PendingEvents & VmemEvents;
1352 unsigned CheckCount = CheckWait.
get(
T);
1354 (CheckCount == 0 || !counterOutOfOrder(
T)) &&
1355 (PendingVmemEvents & ~
Context->getWaitEvents(
T)) == 0)
1362void WaitcntBrackets::purgeEmptyTrackingData() {
1363 VMem.remove_if([](
const auto &
P) {
return P.second.empty(); });
1364 SGPRs.remove_if([](
const auto &
P) {
return P.second.empty(); });
1368 unsigned ScoreToWait,
1369 AMDGPU::Waitcnt &
Wait)
const {
1370 const unsigned LB = getScoreLB(
T);
1371 const unsigned UB = getScoreUB(
T);
1374 if ((UB >= ScoreToWait) && (ScoreToWait > LB)) {
1376 !
Context->ST.hasFlatLgkmVMemCountInOrder()) {
1381 }
else if (counterOutOfOrder(
T)) {
1389 unsigned NeededWait = std::min(UB - ScoreToWait, getLimit(
T) - 1);
1390 Wait.add(
T, NeededWait);
1395AMDGPU::Waitcnt WaitcntBrackets::determineAsyncWait(
unsigned N) {
1397 dbgs() <<
"Need " <<
N <<
" async marks. Found " << AsyncMarks.size()
1399 for (
const auto &Mark : AsyncMarks) {
1405 if (AsyncMarks.size() == MaxAsyncMarks) {
1410 LLVM_DEBUG(
dbgs() <<
"Possible truncation. Ensuring a non-trivial wait.\n");
1411 N = std::min(
N, (
unsigned)MaxAsyncMarks - 1);
1414 AMDGPU::Waitcnt
Wait;
1415 if (AsyncMarks.size() <=
N) {
1420 size_t MarkIndex = AsyncMarks.size() -
N - 1;
1421 const auto &RequiredMark = AsyncMarks[MarkIndex];
1423 determineWaitForScore(
T, RequiredMark[
T],
Wait);
1429 dbgs() <<
"Removing " << (MarkIndex + 1)
1430 <<
" async marks after determining wait\n";
1432 AsyncMarks.erase(AsyncMarks.begin(), AsyncMarks.begin() + MarkIndex + 1);
1445MCPhysReg WaitcntBrackets::determineVGPR16Dependency(
const MachineInstr &
MI,
1449 unsigned Size =
Context->TRI.getRegSizeInBits(*RC);
1451 if (
Size != 16 || !
Context->ST.hasD16Writes32BitVgpr())
1461 AMDGPU::Waitcnt
Wait;
1462 for (MCRegUnit RU : regunits(OtherHalf))
1463 determineWaitForScore(
T, getVMemScore(toVMEMID(RU),
T),
Wait);
1466 if (!
Wait.hasWait())
1476 HWEvents Events = MIEvents & OtherHalfEvents;
1477 if (Events.
size() > 1)
1484 AMDGPU::Waitcnt &
Wait,
1485 const MachineInstr &
MI)
const {
1486 if (
Reg == AMDGPU::SCC) {
1487 determineWaitForScore(
T, SCCScore,
Wait);
1491 Reg = determineVGPR16Dependency(
MI,
T,
Reg);
1492 for (MCRegUnit RU : regunits(
Reg))
1493 determineWaitForScore(
1494 T, IsVGPR ? getVMemScore(toVMEMID(RU),
T) : getSGPRScore(RU,
T),
1501 AMDGPU::Waitcnt &
Wait)
const {
1502 assert(TID >= LDSDMA_BEGIN && TID < LDSDMA_END);
1503 determineWaitForScore(
T, getVMemScore(TID,
T),
Wait);
1506void WaitcntBrackets::tryClearSCCWriteEvent(MachineInstr *Inst) {
1509 if (PendingSCCWrite &&
1510 PendingSCCWrite->
getOpcode() == AMDGPU::S_BARRIER_SIGNAL_ISFIRST_IMM &&
1512 HWEvents SCC_WRITE_PendingEvent = HWEvents::SCC_WRITE;
1515 SCC_WRITE_PendingEvent) {
1519 PendingEvents -= SCC_WRITE_PendingEvent;
1520 PendingSCCWrite =
nullptr;
1524void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait) {
1533 applyWaitcnt(
T, Cnt);
1538 const unsigned UB = getScoreUB(
T);
1542 if (counterOutOfOrder(
T))
1544 setScoreLB(
T, std::max(getScoreLB(
T), UB -
Count));
1547 PendingEvents -=
Context->getWaitEvents(
T);
1551 hasPendingEvent(HWEvents::SMEM_GROUP)) {
1555 PendingEvents -= HWEvents::SMEM_GROUP;
1561 else if (
Count == 0)
1562 PendingEvents -= HWEvents::VMEM_GROUP;
1566void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait,
1568 unsigned Cnt =
Wait.get(
T);
1569 applyWaitcnt(
T, Cnt);
1576 if ((
T ==
Context->SmemAccessCounter &&
1577 hasPendingEvent(HWEvents::SMEM_ACCESS)) ||
1594 static constexpr HWEvents ExtendedImageEvents =
1595 HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
1596 if (!
Context->ST.hasExtendedWaitCounts() &&
1597 (Events & ExtendedImageEvents).any()) {
1598 Events -= ExtendedImageEvents;
1599 Events |= HWEvents::VMEM_READ_ACCESS;
1605 Events -= HWEvents::VMEM_INV_ACCESS;
1607 return Events.
size() > 1;
1610 return hasMixedPendingEvents(
T);
1620char SIInsertWaitcntsLegacy::ID = 0;
1625 return new SIInsertWaitcntsLegacy();
1630 int OpIdx = AMDGPU::getNamedOperandIdx(
MI.getOpcode(),
OpName);
1635 if (NewEnc == MO.
getImm())
1642bool WaitcntGenerator::promoteSoftWaitCnt(MachineInstr *Waitcnt)
const {
1656bool WaitcntGeneratorPreGFX12::applyPreexistingWaitcnt(
1657 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1659 assert(isNormalMode(MaxCounter));
1662 MachineInstr *WaitcntInstr =
nullptr;
1663 MachineInstr *WaitcntVsCntInstr =
nullptr;
1666 dbgs() <<
"PreGFX12::applyPreexistingWaitcnt at: ";
1668 dbgs() <<
"end of block\n";
1676 if (isNonWaitcntMetaInst(
II)) {
1682 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1686 if (Opcode == AMDGPU::S_WAITCNT) {
1687 unsigned IEnc =
II.getOperand(0).getImm();
1690 ScoreBrackets.simplifyWaitcnt(OldWait);
1691 ScoreBrackets.simplifyVmemInvOnlyWait(OldWait);
1697 DeferredErasures.erase(&
II);
1698 II.eraseFromParent();
1700 }
else if (!
Wait.hasWaitExceptStoreCnt() && TrySimplify) {
1704 DeferredErasures.insert(&
II);
1707 DeferredErasures.erase(&
II);
1709 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1712 <<
"Before: " <<
Wait <<
'\n';);
1723 II.eraseFromParent();
1724 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1725 unsigned N =
II.getOperand(0).getImm();
1727 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1730 assert(Opcode == AMDGPU::S_WAITCNT_VSCNT);
1731 assert(
II.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
1734 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1740 if (WaitcntVsCntInstr) {
1741 DeferredErasures.erase(&
II);
1742 II.eraseFromParent();
1744 }
else if (!
Wait.hasWaitStoreCnt() && TrySimplify) {
1746 DeferredErasures.insert(&
II);
1748 WaitcntVsCntInstr = &
II;
1749 DeferredErasures.erase(&
II);
1757 Modified |= promoteSoftWaitCnt(WaitcntInstr);
1766 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
1767 <<
"New Instr at block end: "
1768 << *WaitcntInstr <<
'\n'
1769 :
dbgs() <<
"applied pre-existing waitcnt\n"
1770 <<
"Old Instr: " << *It
1771 <<
"New Instr: " << *WaitcntInstr <<
'\n');
1774 if (WaitcntVsCntInstr) {
1778 Modified |= promoteSoftWaitCnt(WaitcntVsCntInstr);
1784 ?
dbgs() <<
"applied pre-existing waitcnt\n"
1785 <<
"New Instr at block end: " << *WaitcntVsCntInstr
1787 :
dbgs() <<
"applied pre-existing waitcnt\n"
1788 <<
"Old Instr: " << *It
1789 <<
"New Instr: " << *WaitcntVsCntInstr <<
'\n');
1797bool WaitcntGeneratorPreGFX12::createNewWaitcnt(
1799 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
1800 assert(isNormalMode(MaxCounter));
1807 if (
Wait.hasWaitExceptStoreCnt()) {
1809 if (ExpandWaitcntProfiling) {
1813 bool AnyOutOfOrder =
false;
1815 unsigned WaitCnt =
Wait.get(CT);
1816 if (WaitCnt != ~0u && ScoreBrackets.counterOutOfOrder(CT)) {
1817 AnyOutOfOrder =
true;
1822 if (AnyOutOfOrder) {
1830 unsigned WaitCnt =
Wait.get(CT);
1834 unsigned Outstanding =
1835 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
1836 EmitExpandedWaitcnt(Outstanding, WaitCnt, [&](
unsigned Count) {
1848 [[maybe_unused]]
auto SWaitInst =
1853 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1854 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1858 if (
Wait.hasWaitStoreCnt()) {
1864 unsigned Outstanding =
1867 EmitExpandedWaitcnt(
1869 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1870 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1875 [[maybe_unused]]
auto SWaitInst =
1877 .
addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1882 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1883 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1891WaitcntGeneratorPreGFX12::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1892 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt &&
ST.hasVscnt() ? 0 : ~0u);
1896WaitcntGeneratorGFX12Plus::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1897 unsigned ExpertVal = IsExpertMode ? 0 : ~0
u;
1898 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt ? 0 : ~0u, 0, 0, 0,
1900 ~0u , ExpertVal, ExpertVal, ExpertVal);
1907bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt(
1908 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1910 assert(!isNormalMode(MaxCounter));
1913 MachineInstr *CombinedLoadDsCntInstr =
nullptr;
1914 MachineInstr *CombinedStoreDsCntInstr =
nullptr;
1915 MachineInstr *WaitcntDepctrInstr =
nullptr;
1919 dbgs() <<
"GFX12Plus::applyPreexistingWaitcnt at: ";
1921 dbgs() <<
"end of block\n";
1927 AMDGPU::Waitcnt RequiredWait;
1932 if (isNonWaitcntMetaInst(
II)) {
1941 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1945 if (Opcode == AMDGPU::S_WAITCNT)
1948 if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) {
1950 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1955 RequiredWait = RequiredWait.combined(OldWait);
1957 if (CombinedLoadDsCntInstr ==
nullptr) {
1958 CombinedLoadDsCntInstr = &
II;
1960 II.eraseFromParent();
1963 }
else if (Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT) {
1965 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1970 RequiredWait = RequiredWait.combined(OldWait);
1972 if (CombinedStoreDsCntInstr ==
nullptr) {
1973 CombinedStoreDsCntInstr = &
II;
1975 II.eraseFromParent();
1978 }
else if (Opcode == AMDGPU::S_WAITCNT_DEPCTR) {
1980 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1981 AMDGPU::Waitcnt OldWait;
1988 ScoreBrackets.simplifyWaitcnt(OldWait);
1990 if (WaitcntDepctrInstr ==
nullptr) {
1991 WaitcntDepctrInstr = &
II;
2000 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
2011 II.eraseFromParent();
2015 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
2018 II.eraseFromParent();
2020 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
2023 unsigned N =
II.getOperand(0).getImm();
2024 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
2027 std::optional<AMDGPU::InstCounterType> CT =
2031 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
2033 Wait.add(CT.value(), OldCnt);
2035 RequiredWait.add(CT.value(), OldCnt);
2037 if (WaitInstrs[CT.value()] ==
nullptr) {
2038 WaitInstrs[CT.value()] = &
II;
2040 II.eraseFromParent();
2046 ScoreBrackets.simplifyWaitcnt(
Wait.combined(RequiredWait),
Wait);
2047 Wait =
Wait.combined(RequiredWait);
2049 if (CombinedLoadDsCntInstr) {
2065 AMDGPU::OpName::simm16, NewEnc);
2066 Modified |= promoteSoftWaitCnt(CombinedLoadDsCntInstr);
2072 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2073 <<
"New Instr at block end: "
2074 << *CombinedLoadDsCntInstr <<
'\n'
2075 :
dbgs() <<
"applied pre-existing waitcnt\n"
2076 <<
"Old Instr: " << *It <<
"New Instr: "
2077 << *CombinedLoadDsCntInstr <<
'\n');
2084 if (CombinedStoreDsCntInstr) {
2089 AMDGPU::OpName::simm16, NewEnc);
2090 Modified |= promoteSoftWaitCnt(CombinedStoreDsCntInstr);
2096 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2097 <<
"New Instr at block end: "
2098 << *CombinedStoreDsCntInstr <<
'\n'
2099 :
dbgs() <<
"applied pre-existing waitcnt\n"
2100 <<
"Old Instr: " << *It <<
"New Instr: "
2101 << *CombinedStoreDsCntInstr <<
'\n');
2131 for (MachineInstr **WI : WaitsToErase) {
2135 DeferredErasures.erase(*WI);
2136 (*WI)->eraseFromParent();
2143 if (!WaitInstrs[CT])
2146 unsigned NewCnt =
Wait.get(CT);
2147 if (NewCnt != ~0u) {
2149 AMDGPU::OpName::simm16, NewCnt);
2150 Modified |= promoteSoftWaitCnt(WaitInstrs[CT]);
2151 DeferredErasures.erase(WaitInstrs[CT]);
2153 ScoreBrackets.applyWaitcnt(CT, NewCnt);
2157 ?
dbgs() <<
"applied pre-existing waitcnt\n"
2158 <<
"New Instr at block end: " << *WaitInstrs[CT]
2160 :
dbgs() <<
"applied pre-existing waitcnt\n"
2161 <<
"Old Instr: " << *It
2162 <<
"New Instr: " << *WaitInstrs[CT] <<
'\n');
2165 DeferredErasures.insert(WaitInstrs[CT]);
2169 if (WaitcntDepctrInstr) {
2173 TII.getNamedOperand(*WaitcntDepctrInstr, AMDGPU::OpName::simm16)
2193 AMDGPU::OpName::simm16, Enc);
2195 <<
"New Instr at block end: "
2196 << *WaitcntDepctrInstr <<
'\n'
2197 :
dbgs() <<
"applyPreexistingWaitcnt\n"
2198 <<
"Old Instr: " << *It <<
"New Instr: "
2199 << *WaitcntDepctrInstr <<
'\n');
2210bool WaitcntGeneratorGFX12Plus::createNewWaitcnt(
2212 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
2213 assert(!isNormalMode(MaxCounter));
2220 if (ExpandWaitcntProfiling) {
2227 if (ScoreBrackets.counterOutOfOrder(CT)) {
2234 unsigned Outstanding =
2235 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
2236 EmitExpandedWaitcnt(Outstanding,
Count, [&](
unsigned Val) {
2248 MachineInstr *SWaitInst =
nullptr;
2272 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2273 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2285 [[maybe_unused]]
auto SWaitInst =
2292 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2293 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2296 if (
Wait.hasWaitDepctr()) {
2305 [[maybe_unused]]
auto SWaitInst =
2311 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2312 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2331bool SIInsertWaitcnts::generateWaitcntInstBefore(
2332 MachineInstr &
MI, WaitcntBrackets &ScoreBrackets,
2333 MachineInstr *OldWaitcntInstr, PreheaderFlushFlags FlushFlags) {
2338 AMDGPU::Waitcnt
Wait;
2339 const unsigned Opc =
MI.getOpcode();
2342 case AMDGPU::BUFFER_WBINVL1:
2343 case AMDGPU::BUFFER_WBINVL1_SC:
2344 case AMDGPU::BUFFER_WBINVL1_VOL:
2345 case AMDGPU::BUFFER_GL0_INV:
2346 case AMDGPU::BUFFER_GL1_INV: {
2354 case AMDGPU::SI_RETURN_TO_EPILOG:
2355 case AMDGPU::SI_RETURN:
2356 case AMDGPU::SI_WHOLE_WAVE_FUNC_RETURN:
2357 case AMDGPU::S_SETPC_B64_return: {
2362 AMDGPU::Waitcnt AllZeroWait =
2363 WCG->getAllZeroWaitcnt(
false);
2364 ScoreBrackets.simplifyVmemInvOnlyWait(AllZeroWait);
2368 case AMDGPU::S_ENDPGM:
2369 case AMDGPU::S_ENDPGM_SAVED: {
2380 !ScoreBrackets.hasPendingEvent(HWEvents::SCRATCH_WRITE_ACCESS);
2383 case AMDGPU::S_SENDMSG:
2384 case AMDGPU::S_SENDMSGHALT: {
2385 if (
ST.hasLegacyGeometry() &&
2400 if (
MI.modifiesRegister(AMDGPU::EXEC, &
TRI)) {
2403 if (ScoreBrackets.hasPendingEvent(HWEvents::EXP_GPR_LOCK) ||
2404 ScoreBrackets.hasPendingEvent(HWEvents::EXP_PARAM_ACCESS) ||
2405 ScoreBrackets.hasPendingEvent(HWEvents::EXP_POS_ACCESS) ||
2406 ScoreBrackets.hasPendingEvent(HWEvents::GDS_GPR_LOCK)) {
2413 if (
TII.isAlwaysGDS(
Opc) && ScoreBrackets.hasPendingGDS())
2421 Wait = AMDGPU::Waitcnt();
2423 const MachineOperand &CallAddrOp =
TII.getCalleeOperand(
MI);
2424 if (CallAddrOp.
isReg()) {
2425 ScoreBrackets.determineWaitForPhysReg(
2428 if (
const auto *RtnAddrOp =
2429 TII.getNamedOperand(
MI, AMDGPU::OpName::dst)) {
2430 ScoreBrackets.determineWaitForPhysReg(
2431 SmemAccessCounter, RtnAddrOp->getReg().asMCReg(),
Wait,
MI);
2434 }
else if (
Opc == AMDGPU::S_BARRIER_WAIT) {
2435 ScoreBrackets.tryClearSCCWriteEvent(&
MI);
2451 for (
const MachineMemOperand *Memop :
MI.memoperands()) {
2452 const Value *Ptr = Memop->getValue();
2453 if (Memop->isStore()) {
2454 if (
auto It = SLoadAddresses.
find(Ptr); It != SLoadAddresses.
end()) {
2455 Wait.add(SmemAccessCounter, 0);
2457 SLoadAddresses.
erase(It);
2460 unsigned AS = Memop->getAddrSpace();
2464 if (
TII.mayWriteLDSThroughDMA(
MI))
2468 unsigned TID = LDSDMA_BEGIN;
2469 if (Ptr && Memop->getAAInfo()) {
2470 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2471 for (
unsigned I = 0,
E = LDSDMAStores.size();
I !=
E; ++
I) {
2472 if (
MI.mayAlias(AA, *LDSDMAStores[
I],
true)) {
2473 if ((
I + 1) >= NUM_LDSDMA) {
2488 if (Memop->isStore()) {
2494 for (
const MachineOperand &
Op :
MI.operands()) {
2499 if (
Op.isTied() &&
Op.isUse() &&
TII.doesNotReadTiedSource(
MI))
2504 const bool IsVGPR =
TRI.isVectorRegister(MRI,
Op.getReg());
2511 if (
Op.isImplicit() &&
MI.mayLoadOrStore())
2529 if (
Op.isUse() || !updateVMCntOnly(
MI) ||
2530 ScoreBrackets.hasDifferentVGPRPendingEvents(
2532 ScoreBrackets.hasPointSamplePendingVmemTypes(
MI,
Reg) ||
2533 !
ST.hasVmemWriteVgprInOrder()) {
2540 ScoreBrackets.clearVGPRPendingEvents(
Reg);
2544 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2549 }
else if (
Op.getReg() == AMDGPU::SCC) {
2552 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter,
Reg,
Wait,
2556 if (
ST.hasWaitXcnt() &&
Op.isDef())
2575 if (
Opc == AMDGPU::S_BARRIER && !
ST.hasAutoWaitcntBeforeBarrier() &&
2576 !
ST.hasBackOffBarrier()) {
2577 Wait =
Wait.combined(WCG->getAllZeroWaitcnt(
true));
2584 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2589 ScoreBrackets.simplifyWaitcnt(
Wait);
2595 if (
TII.isVALU(
MI,
false)) {
2611 Wait = WCG->getAllZeroWaitcnt(
false);
2615 if (!ForceEmitWaitcnt[
T])
2620 if (FlushFlags.FlushVmCnt) {
2626 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
2632 return generateWaitcnt(
Wait,
MI.getIterator(), *
MI.getParent(), ScoreBrackets,
2636bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt
Wait,
2638 MachineBasicBlock &
Block,
2639 WaitcntBrackets &ScoreBrackets,
2640 MachineInstr *OldWaitcntInstr) {
2643 if (OldWaitcntInstr)
2647 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr,
Wait, It);
2652 MachineOperand *WaitExp =
TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2662 <<
"Update Instr: " << *It);
2665 if (WCG->createNewWaitcnt(
Block, It,
Wait, ScoreBrackets))
2670 ScoreBrackets.applyWaitcnt(
Wait);
2675bool SIInsertWaitcnts::isVmemAccess(
const MachineInstr &
MI)
const {
2676 return (
TII.isFLAT(
MI) &&
TII.mayAccessVMEMThroughFlat(
MI)) ||
2683 MachineBasicBlock *
Block)
const {
2684 auto BlockEnd =
Block->getParent()->end();
2685 auto BlockIter =
Block->getIterator();
2689 if (++BlockIter != BlockEnd) {
2690 It = BlockIter->instr_begin();
2697 if (!It->isMetaInstruction())
2705 return It->getOpcode() == AMDGPU::S_ENDPGM;
2709bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2710 MachineBasicBlock &
Block,
2711 WaitcntBrackets &ScoreBrackets) {
2712 AMDGPU::Waitcnt
Wait;
2713 bool NeedsEndPGMCheck =
false;
2721 NeedsEndPGMCheck =
true;
2724 ScoreBrackets.simplifyWaitcnt(
Wait);
2727 bool Result = generateWaitcnt(
Wait, SuccessorIt,
Block, ScoreBrackets,
2730 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &
Block)) {
2738void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2739 WaitcntBrackets *ScoreBrackets) {
2743 ScoreBrackets->updateByEvent(
E, Inst);
2745 if (
TII.isDS(Inst) &&
TII.usesLGKM_CNT(Inst)) {
2747 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2748 ScoreBrackets->setPendingGDS();
2750 }
else if (
TII.isFLAT(Inst)) {
2752 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2759 ScoreBrackets->setPendingFlat();
2761 }
else if (Inst.
isCall()) {
2764 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(
false));
2765 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2766 }
else if (
TII.isVINTERP(Inst)) {
2767 int64_t
Imm =
TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2777bool WaitcntBrackets::mergeScore(
const MergeInfo &M,
unsigned &Score,
2778 unsigned OtherScore) {
2779 unsigned MyShifted = Score <=
M.OldLB ? 0 : Score +
M.MyShift;
2780 unsigned OtherShifted =
2781 OtherScore <=
M.OtherLB ? 0 : OtherScore +
M.OtherShift;
2782 Score = std::max(MyShifted, OtherShifted);
2783 return OtherShifted > MyShifted;
2788 bool StrictDom =
false;
2792 if (AsyncMarks.empty() && OtherMarks.
empty()) {
2799 auto MaxSize = (unsigned)std::max(AsyncMarks.size(), OtherMarks.
size());
2800 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2803 if (AsyncMarks.size() > MaxSize)
2804 AsyncMarks.erase(AsyncMarks.begin(),
2805 AsyncMarks.begin() + (AsyncMarks.size() - MaxSize));
2811 constexpr CounterValueArray ZeroMark{};
2812 AsyncMarks.insert(AsyncMarks.begin(), MaxSize - AsyncMarks.size(), ZeroMark);
2815 dbgs() <<
"Before merge:\n";
2816 for (
const auto &Mark : AsyncMarks) {
2820 dbgs() <<
"Other marks:\n";
2821 for (
const auto &Mark : OtherMarks) {
2834 const unsigned OtherSize = OtherMarks.size();
2835 const unsigned OurSize = AsyncMarks.size();
2842 "AsyncMarks padded to MaxSize >= 1 (needs MaxAsyncMarks != 0)");
2845 const CounterValueArray &OtherMark =
2846 Idx <= OtherSize ? OtherMarks[OtherSize - Idx] : ZeroMark;
2849 mergeScore(MergeInfos[
T], AsyncMarks[OurSize - Idx][
T], OtherMark[
T]);
2853 dbgs() <<
"After merge:\n";
2854 for (
const auto &Mark : AsyncMarks) {
2868bool WaitcntBrackets::merge(
const WaitcntBrackets &
Other) {
2869 bool StrictDom =
false;
2873 for (
auto K :
Other.VMem.keys())
2874 VMem.try_emplace(K);
2875 for (
auto K :
Other.SGPRs.keys())
2876 SGPRs.try_emplace(K);
2884 const HWEvents OldEvents = PendingEvents & EventsForT;
2885 const HWEvents OtherEvents =
Other.PendingEvents & EventsForT;
2886 if (!OldEvents.
contains(OtherEvents))
2888 PendingEvents |= OtherEvents;
2891 const unsigned MyPending = ScoreUBs[
T] - ScoreLBs[
T];
2892 const unsigned OtherPending =
Other.ScoreUBs[
T] -
Other.ScoreLBs[
T];
2893 const unsigned NewUB = ScoreLBs[
T] + std::max(MyPending, OtherPending);
2894 if (NewUB < ScoreLBs[
T])
2897 MergeInfo &
M = MergeInfos[
T];
2898 M.OldLB = ScoreLBs[
T];
2899 M.OtherLB =
Other.ScoreLBs[
T];
2900 M.MyShift = NewUB - ScoreUBs[
T];
2901 M.OtherShift = NewUB -
Other.ScoreUBs[
T];
2903 ScoreUBs[
T] = NewUB;
2906 StrictDom |= mergeScore(M, LastFlatLoadCnt,
Other.LastFlatLoadCnt);
2909 StrictDom |= mergeScore(M, LastFlatDsCnt,
Other.LastFlatDsCnt);
2910 StrictDom |= mergeScore(M, LastGDS,
Other.LastGDS);
2914 StrictDom |= mergeScore(M, SCCScore,
Other.SCCScore);
2915 if (
Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2916 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2917 PendingSCCWrite =
Other.PendingSCCWrite;
2918 }
else if (PendingSCCWrite !=
Other.PendingSCCWrite) {
2919 PendingSCCWrite =
nullptr;
2924 for (
auto &[RegID, Info] : VMem)
2925 StrictDom |= mergeScore(M,
Info.Scores[
T],
Other.getVMemScore(RegID,
T));
2927 if (isSmemCounter(
T)) {
2928 for (
auto &[RegID, Info] : SGPRs) {
2929 auto It =
Other.SGPRs.find(RegID);
2930 unsigned OtherScore = (It !=
Other.SGPRs.end()) ? It->second.get(
T) : 0;
2931 StrictDom |= mergeScore(M,
Info.get(
T), OtherScore);
2936 for (
auto &[TID, Info] : VMem) {
2937 if (
auto It =
Other.VMem.find(TID); It !=
Other.VMem.end()) {
2939 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
2940 StrictDom |= NewVGPRContext !=
Info.VGPRPendingEvents;
2941 Info.VGPRPendingEvents = NewVGPRContext;
2945 StrictDom |= mergeAsyncMarks(MergeInfos,
Other.AsyncMarks);
2947 StrictDom |= mergeScore(MergeInfos[
T], AsyncScore[
T],
Other.AsyncScore[
T]);
2949 purgeEmptyTrackingData();
2955 return Opcode == AMDGPU::S_WAITCNT ||
2958 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
2959 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
2960 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
2961 Opcode == AMDGPU::WAIT_ASYNCMARK ||
2965void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &
MBB,
2967 bool ExpertMode)
const {
2971 .
addImm(ExpertMode ? 2 : 0)
2989class VCCZWorkaround {
2990 const WaitcntBrackets &ScoreBrackets;
2991 const GCNSubtarget &
ST;
2992 const SIInstrInfo &
TII;
2993 const SIRegisterInfo &
TRI;
2994 bool VCCZCorruptionBug =
false;
2995 bool VCCZNotUpdatedByPartialWrites =
false;
2998 bool MustRecomputeVCCZ =
true;
3001 VCCZWorkaround(
const WaitcntBrackets &ScoreBrackets,
const GCNSubtarget &ST,
3002 const SIInstrInfo &
TII,
const SIRegisterInfo &
TRI)
3004 VCCZCorruptionBug =
ST.hasReadVCCZBug();
3005 VCCZNotUpdatedByPartialWrites = !
ST.partialVCCWritesUpdateVCCZ();
3012 bool tryRecomputeVCCZ(MachineInstr &
MI) {
3014 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
3024 MustRecomputeVCCZ |= VCCZCorruptionBug &&
TII.isSMRD(
MI);
3030 std::optional<bool> PartiallyWritesToVCCOpt;
3031 auto PartiallyWritesToVCC = [](MachineInstr &
MI) {
3032 return MI.definesRegister(AMDGPU::VCC_LO,
nullptr) ||
3033 MI.definesRegister(AMDGPU::VCC_HI,
nullptr);
3035 if (VCCZNotUpdatedByPartialWrites) {
3036 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3039 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
3045 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
3046 !VCCZCorruptionBug) {
3048 if (!PartiallyWritesToVCCOpt)
3049 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3050 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
3051 MI.definesRegister(AMDGPU::VCC,
nullptr);
3054 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
3055 *PartiallyWritesToVCCOpt);
3057 MustRecomputeVCCZ =
false;
3067 TII.get(
ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
3070 MustRecomputeVCCZ =
false;
3081 MachineBasicBlock &
Block,
3082 WaitcntBrackets &ScoreBrackets) {
3086 dbgs() <<
"*** Begin Block: ";
3088 ScoreBrackets.dump();
3090 VCCZWorkaround VCCZW(ScoreBrackets, ST,
TII,
TRI);
3093 MachineInstr *OldWaitcntInstr =
nullptr;
3096 ScoreBrackets.verify();
3099 Iter !=
E; ++Iter) {
3100 MachineInstr &Inst = *Iter;
3101 if (isNonWaitcntMetaInst(Inst))
3106 (IsExpertMode && Inst.
getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3107 if (!OldWaitcntInstr)
3108 OldWaitcntInstr = &Inst;
3112 PreheaderFlushFlags FlushFlags;
3113 if (
Block.getFirstTerminator() == Inst)
3114 FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3117 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3119 OldWaitcntInstr =
nullptr;
3121 if (Inst.
getOpcode() == AMDGPU::ASYNCMARK) {
3125 ScoreBrackets.recordAsyncMark(Inst);
3129 if (
TII.isSMRD(Inst)) {
3130 for (
const MachineMemOperand *Memop : Inst.
memoperands()) {
3133 if (!Memop->isInvariant()) {
3134 const Value *Ptr = Memop->getValue();
3140 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3144 Modified |= insertForcedWaitAfter(Inst,
Block, ScoreBrackets);
3148 ScoreBrackets.dump();
3153 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3155 ScoreBrackets.verify();
3160 AMDGPU::Waitcnt
Wait;
3161 if (
Block.getFirstTerminator() ==
Block.end()) {
3162 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3163 if (FlushFlags.FlushVmCnt) {
3171 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
3180 dbgs() <<
"*** End Block: ";
3182 ScoreBrackets.dump();
3188bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &
Block) {
3189 if (
Block.size() <= 1)
3197 MachineInstr *LastAtomicWithSoftXcnt =
nullptr;
3201 bool IsLDS =
TII.isDS(
MI) ||
3202 (
TII.isFLAT(
MI) &&
TII.mayAccessLDSThroughFlat(
MI, TgSplit));
3203 if (!IsLDS && (
MI.mayLoad() ^
MI.mayStore()))
3204 LastAtomicWithSoftXcnt =
nullptr;
3208 MachineInstr &PrevMI = *
MI.getPrevNode();
3210 if (PrevMI.
getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3213 if (LastAtomicWithSoftXcnt) {
3217 LastAtomicWithSoftXcnt = &
MI;
3225SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &
MBB,
3226 const WaitcntBrackets &ScoreBrackets) {
3234 return PreheaderFlushFlags();
3238 return PreheaderFlushFlags();
3240 if (
Loop->getLoopPreheader() == &
MBB) {
3241 Iterator->second = getPreheaderFlushFlags(
Loop, ScoreBrackets);
3245 return PreheaderFlushFlags();
3248bool SIInsertWaitcnts::isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const {
3250 return TII.mayAccessVMEMThroughFlat(
MI);
3254bool SIInsertWaitcnts::isDSRead(
const MachineInstr &
MI)
const {
3260bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const {
3289SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *
ML,
3290 const WaitcntBrackets &Brackets) {
3291 PreheaderFlushFlags
Flags;
3292 bool HasVMemLoad =
false;
3293 bool HasVMemStore =
false;
3294 bool UsesVgprVMEMLoadedOutside =
false;
3295 bool UsesVgprDSReadOutside =
false;
3296 bool VMemInvalidated =
false;
3300 bool TrackSimpleDSOpt =
ST.hasExtendedWaitCounts();
3301 DenseSet<MCRegUnit> VgprUse;
3302 DenseSet<MCRegUnit> VgprDefVMEM;
3303 DenseSet<MCRegUnit> VgprDefDS;
3309 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3310 unsigned DSReadPosition = 0;
3311 bool IsSingleBlock =
ML->getNumBlocks() == 1;
3312 bool TrackDSFlushPoint =
ST.hasExtendedWaitCounts() && IsSingleBlock;
3313 unsigned LastDSFlushPosition = 0;
3315 for (MachineBasicBlock *
MBB :
ML->blocks()) {
3316 for (MachineInstr &
MI : *
MBB) {
3317 if (isVMEMOrFlatVMEM(
MI)) {
3318 HasVMemLoad |=
MI.mayLoad();
3319 HasVMemStore |=
MI.mayStore();
3323 if (mayStoreIncrementingDSCNT(
MI)) {
3326 if (VMemInvalidated)
3328 TrackSimpleDSOpt =
false;
3329 TrackDSFlushPoint =
false;
3331 bool IsDSRead = isDSRead(
MI);
3336 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3337 if (!TrackDSFlushPoint)
3339 if (
auto It = LastDSReadPositionMap.
find(RU);
3340 It != LastDSReadPositionMap.
end()) {
3344 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3348 for (
const MachineOperand &
Op :
MI.all_uses()) {
3349 if (
Op.isDebug() || !
TRI.isVectorRegister(MRI,
Op.getReg()))
3352 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3356 VMemInvalidated =
true;
3360 TrackSimpleDSOpt =
false;
3363 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3367 updateDSReadFlushTracking(RU);
3372 VMEMID
ID = toVMEMID(RU);
3376 UsesVgprVMEMLoadedOutside =
true;
3381 UsesVgprDSReadOutside =
true;
3386 if (isVMEMOrFlatVMEM(
MI) &&
MI.mayLoad()) {
3387 for (
const MachineOperand &
Op :
MI.all_defs()) {
3388 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3392 VMemInvalidated =
true;
3397 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3408 if (IsDSRead || TrackDSFlushPoint) {
3409 for (
const MachineOperand &
Op :
MI.all_defs()) {
3410 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
3412 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3415 updateDSReadFlushTracking(RU);
3418 if (TrackDSFlushPoint)
3419 LastDSReadPositionMap[RU] = DSReadPosition;
3428 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3429 ((!
ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3430 (HasVMemLoad &&
ST.hasVmemWriteVgprInOrder())))
3431 Flags.FlushVmCnt =
true;
3437 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3440 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3441 bool DSFlushPointPrefetch =
3442 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3444 if (SimpleDSOpt || DSFlushPointPrefetch)
3445 Flags.FlushDsCnt =
true;
3450bool SIInsertWaitcntsLegacy::runOnMachineFunction(
MachineFunction &MF) {
3451 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3453 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3455 if (
auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3456 AA = &AAR->getAAResults();
3458 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3470 if (!SIInsertWaitcnts(MLI, PDT,
AA, MF).
run())
3475 .preserve<AAManager>();
3478bool SIInsertWaitcnts::run() {
3486 if (ST.hasExtendedWaitCounts()) {
3487 IsExpertMode = ST.hasExpertSchedulingMode() &&
3496 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3501 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3505 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3509 MachineBasicBlock &EntryBB = MF.
front();
3520 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3523 if (
ST.hasExtendedWaitCounts()) {
3532 if (!
ST.hasImageInsts() &&
3538 TII.get(instrsForExtendedCounterTypes[CT]))
3551 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(
this);
3552 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3553 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3560 for (
auto *
MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3563 std::unique_ptr<WaitcntBrackets> Brackets;
3568 for (
auto BII = BlockInfos.
begin(), BIE = BlockInfos.
end(); BII != BIE;
3570 MachineBasicBlock *
MBB = BII->first;
3571 BlockInfo &BI = BII->second;
3577 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3579 *Brackets = *BI.Incoming;
3582 Brackets = std::make_unique<WaitcntBrackets>(
this);
3587 Brackets->~WaitcntBrackets();
3588 new (Brackets.get()) WaitcntBrackets(
this);
3592 if (
ST.hasWaitXcnt())
3594 Modified |= insertWaitcntInBlock(MF, *
MBB, *Brackets);
3597 if (Brackets->hasPendingEvent()) {
3598 BlockInfo *MoveBracketsToSucc =
nullptr;
3600 auto *SuccBII = BlockInfos.
find(Succ);
3601 BlockInfo &SuccBI = SuccBII->second;
3602 if (!SuccBI.Incoming) {
3603 SuccBI.Dirty =
true;
3604 if (SuccBII <= BII) {
3608 if (!MoveBracketsToSucc) {
3609 MoveBracketsToSucc = &SuccBI;
3611 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3615 dbgs() <<
"Try to merge ";
3621 if (SuccBI.Incoming->merge(*Brackets)) {
3622 SuccBI.Dirty =
true;
3623 if (SuccBII <= BII) {
3630 if (MoveBracketsToSucc)
3631 MoveBracketsToSucc->Incoming = std::move(Brackets);
3636 Modified |= WCG->eraseDeferredWaitcnts();
3638 if (
ST.hasScalarStores()) {
3639 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3640 bool HaveScalarStores =
false;
3642 for (MachineBasicBlock &
MBB : MF) {
3643 for (MachineInstr &
MI :
MBB) {
3644 if (!HaveScalarStores &&
TII.isScalarStore(
MI))
3645 HaveScalarStores =
true;
3647 if (
MI.getOpcode() == AMDGPU::S_ENDPGM ||
3648 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3653 if (HaveScalarStores) {
3662 for (MachineBasicBlock *
MBB : EndPgmBlocks) {
3663 bool SeenDCacheWB =
false;
3667 if (
I->getOpcode() == AMDGPU::S_DCACHE_WB)
3668 SeenDCacheWB =
true;
3669 else if (
TII.isScalarStore(*
I))
3670 SeenDCacheWB =
false;
3673 if ((
I->getOpcode() == AMDGPU::S_ENDPGM ||
3674 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3690 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3692 setSchedulingMode(EntryBB,
I,
true);
3694 for (MachineInstr *
MI : CallInsts) {
3696 setSchedulingMode(
MBB,
MI,
false);
3697 setSchedulingMode(
MBB, std::next(
MI->getIterator()),
true);
3700 for (MachineInstr *
MI : ReturnInsts)
3701 setSchedulingMode(*
MI->getParent(),
MI,
false);
3712 for (
auto [
MI,
_] : EndPgmInsts) {
3714 TII.get(AMDGPU::S_ALLOC_VGPR))
3718 }
else if (!WCG->isOptNone() &&
3719 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3720 (MF.getFrameInfo().hasCalls() ||
3721 ST.getOccupancyWithNumVGPRs(
3722 TRI.getNumUsedPhysRegs(MRI, AMDGPU::VGPR_32RegClass),
3723 false) <
ST.getMaxWavesPerEU())) {
3724 for (
auto [
MI, Flag] : EndPgmInsts) {
3726 if (
ST.requiresNopBeforeDeallocVGPRs()) {
3728 TII.get(AMDGPU::S_NOP))
3732 TII.get(AMDGPU::S_SENDMSG))
3742 if (
ST.hasWaitXcnt()) {
3746 unsigned RegEncoding =
3749 TII.get(AMDGPU::S_SETREG_IMM32_B32))
3755 if (
ST.hasRequiresInitialUnclausedVmem()) {
3761 AMDGPU::SGPR64_SGPR65)
3765 TII.get(AMDGPU::GLOBAL_PREFETCH_B8_SADDR))
3766 .
addReg(AMDGPU::SGPR64_SGPR65)
3767 .
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 > ExpertSchedulingModeFlag("amdgpu-expert-scheduling-mode", cl::desc("Enable expert scheduling mode 2 for all functions (GFX12+ only)"), cl::init(false), cl::Hidden)
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.
LLVM_ABI bool getValueAsBool() const
Return the attribute's value as a boolean.
Represents analyses that only rely on functions' control flow.
iterator find(const_arg_type_t< KeyT > Val)
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
bool erase(const KeyT &Val)
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
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.
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
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)
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)
DXILDebugInfoMap run(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.