49#define DEBUG_TYPE "si-insert-waitcnts"
53 cl::desc(
"Force all waitcnt instrs to be emitted as "
54 "s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)"),
58 "amdgpu-waitcnt-load-forcezero",
59 cl::desc(
"Force all waitcnt load counters to wait until 0"),
63 "amdgpu-expert-scheduling-mode",
64 cl::desc(
"Enable expert scheduling mode 2 for all functions (GFX12+ only)"),
69template <
typename EmitWaitcntFn>
70static void EmitExpandedWaitcnt(
unsigned Outstanding,
unsigned Target,
71 EmitWaitcntFn &&EmitWaitcnt) {
73 for (
unsigned I = Outstanding - 1;
I >
Target &&
I != ~0u; --
I)
93 TRACKINGID_RANGE_LEN = (1 << 16),
98 REGUNITS_END = REGUNITS_BEGIN + TRACKINGID_RANGE_LEN,
103 NUM_LDSDMA = TRACKINGID_RANGE_LEN,
104 LDSDMA_BEGIN = REGUNITS_END,
105 LDSDMA_END = LDSDMA_BEGIN + NUM_LDSDMA,
109static constexpr VMEMID toVMEMID(MCRegUnit RU) {
110 return static_cast<unsigned>(RU);
122 AMDGPU::S_WAIT_LOADCNT, AMDGPU::S_WAIT_DSCNT,
123 AMDGPU::S_WAIT_EXPCNT, AMDGPU::S_WAIT_STORECNT,
124 AMDGPU::S_WAIT_SAMPLECNT, AMDGPU::S_WAIT_BVHCNT,
125 AMDGPU::S_WAIT_KMCNT, AMDGPU::S_WAIT_XCNT,
126 AMDGPU::S_WAIT_ASYNCCNT, AMDGPU::S_WAIT_TENSORCNT};
131 switch (
MI.getOpcode()) {
132 case AMDGPU::ASYNCMARK:
133 case AMDGPU::WAIT_ASYNCMARK:
136 return MI.isMetaInstruction();
151class WaitcntBrackets;
159class WaitcntGenerator {
161 const GCNSubtarget &ST;
162 const SIInstrInfo &TII;
163 AMDGPU::IsaVersion IV;
166 bool ExpandWaitcntProfiling =
false;
167 const AMDGPU::HardwareLimits &Limits;
170 WaitcntGenerator() =
delete;
171 WaitcntGenerator(
const WaitcntGenerator &) =
delete;
172 WaitcntGenerator(
const MachineFunction &MF,
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,
206 bool promoteSoftWaitCnt(MachineInstr *Waitcnt)
const;
211 virtual bool createNewWaitcnt(MachineBasicBlock &
Block,
213 AMDGPU::Waitcnt
Wait,
214 const WaitcntBrackets &ScoreBrackets) = 0;
221 assert(
E.size() == 1 &&
"Cannot handle a mask of events!");
223 if (getWaitEvents(
T) &
E)
234 virtual AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const = 0;
236 virtual ~WaitcntGenerator() =
default;
239class WaitcntGeneratorPreGFX12 final :
public WaitcntGenerator {
242 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_SAMPLER_READ_ACCESS |
243 HWEvents::VMEM_BVH_READ_ACCESS,
244 HWEvents::SMEM_ACCESS | HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS |
245 HWEvents::SQ_MESSAGE,
246 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
247 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
248 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
249 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
261 using WaitcntGenerator::WaitcntGenerator;
263 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
264 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
267 bool createNewWaitcnt(MachineBasicBlock &
Block,
269 AMDGPU::Waitcnt
Wait,
270 const WaitcntBrackets &ScoreBrackets)
override;
273 HWEvents EVs = WaitEventMaskForInstPreGFX12[
T];
279 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
282class WaitcntGeneratorGFX12Plus final :
public WaitcntGenerator {
287 HWEvents::VMEM_READ_ACCESS | HWEvents::GLOBAL_INV_ACCESS,
288 HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS,
289 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
290 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
291 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
293 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
294 HWEvents::VMEM_SAMPLER_READ_ACCESS,
295 HWEvents::VMEM_BVH_READ_ACCESS,
297 HWEvents::SMEM_ACCESS | HWEvents::SQ_MESSAGE | HWEvents::SCC_WRITE,
298 HWEvents::VMEM_GROUP | HWEvents::SMEM_GROUP,
299 HWEvents::ASYNC_ACCESS,
300 HWEvents::TENSOR_ACCESS,
301 HWEvents::VGPR_CSMACC_READ | HWEvents::VGPR_DPMACC_READ |
302 HWEvents::VGPR_TRANS_READ | HWEvents::VGPR_XDL_READ,
303 HWEvents::VGPR_CSMACC_WRITE | HWEvents::VGPR_DPMACC_WRITE |
304 HWEvents::VGPR_TRANS_WRITE | HWEvents::VGPR_XDL_WRITE,
305 HWEvents::VGPR_LDS_READ | HWEvents::VGPR_FLAT_READ |
306 HWEvents::VGPR_VMEM_READ};
309 WaitcntGeneratorGFX12Plus() =
delete;
310 WaitcntGeneratorGFX12Plus(
const MachineFunction &MF,
312 const AMDGPU::HardwareLimits &Limits,
314 : WaitcntGenerator(MF, MaxCounter, Limits), IsExpertMode(IsExpertMode) {}
317 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
318 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
321 bool createNewWaitcnt(MachineBasicBlock &
Block,
323 AMDGPU::Waitcnt
Wait,
324 const WaitcntBrackets &ScoreBrackets)
override;
327 return WaitEventMaskForInstGFX12Plus[
T];
330 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
334struct PreheaderFlushFlags {
335 bool FlushVmCnt =
false;
336 bool FlushDsCnt =
false;
339class SIInsertWaitcnts {
340 DenseMap<const Value *, MachineBasicBlock *> SLoadAddresses;
341 DenseMap<MachineBasicBlock *, PreheaderFlushFlags> PreheadersToFlush;
342 MachineLoopInfo &MLI;
343 MachinePostDominatorTree &PDT;
348 std::unique_ptr<WaitcntBrackets> Incoming;
350 BlockInfo() =
default;
351 BlockInfo(BlockInfo &&) =
default;
352 BlockInfo &operator=(BlockInfo &&) =
default;
356 MapVector<MachineBasicBlock *, BlockInfo> BlockInfos;
360 std::unique_ptr<WaitcntGenerator> WCG;
363 DenseSet<MachineInstr *> CallInsts;
364 DenseSet<MachineInstr *> ReturnInsts;
369 DenseMap<MachineInstr *, bool> EndPgmInsts;
371 AMDGPU::HardwareLimits Limits;
374 const GCNSubtarget &ST;
375 const SIInstrInfo &TII;
376 const SIRegisterInfo &TRI;
377 const MachineRegisterInfo &MRI;
380 bool IsExpertMode =
false;
383 SIInsertWaitcnts(MachineLoopInfo &MLI, MachinePostDominatorTree &PDT,
385 : MLI(MLI), PDT(PDT), AA(AA), MF(MF), ST(MF.getSubtarget<GCNSubtarget>()),
386 TII(*ST.getInstrInfo()), TRI(TII.getRegisterInfo()),
387 MRI(MF.getRegInfo()),
388 TgSplit(ST.hasTgSplitSupport() &&
391 const AMDGPU::HardwareLimits &getLimits()
const {
return Limits; }
393 PreheaderFlushFlags getPreheaderFlushFlags(MachineLoop *
ML,
394 const WaitcntBrackets &Brackets);
395 PreheaderFlushFlags isPreheaderToFlush(MachineBasicBlock &
MBB,
396 const WaitcntBrackets &ScoreBrackets);
397 bool isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const;
398 bool isDSRead(
const MachineInstr &
MI)
const;
399 bool mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const;
402 bool isAsync(
const MachineInstr &
MI)
const {
407 const MachineOperand *
Async =
408 TII.getNamedOperand(
MI, AMDGPU::OpName::IsAsync);
412 bool isNonAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
416 bool isAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
420 bool shouldUpdateAsyncMark(
const MachineInstr &
MI,
424 if (!isAsyncLdsDmaWrite(
MI))
431 bool isVmemAccess(
const MachineInstr &
MI)
const;
432 bool generateWaitcntInstBefore(MachineInstr &
MI,
433 WaitcntBrackets &ScoreBrackets,
434 MachineInstr *OldWaitcntInstr,
435 PreheaderFlushFlags FlushFlags);
436 bool generateWaitcnt(AMDGPU::Waitcnt
Wait,
438 MachineBasicBlock &
Block, WaitcntBrackets &ScoreBrackets,
439 MachineInstr *OldWaitcntInstr);
440 void updateEventWaitcntAfter(MachineInstr &Inst,
441 WaitcntBrackets *ScoreBrackets);
443 MachineBasicBlock *
Block)
const;
444 bool insertForcedWaitAfter(MachineInstr &Inst, MachineBasicBlock &
Block,
445 WaitcntBrackets &ScoreBrackets);
446 bool insertWaitcntInBlock(MachineFunction &MF, MachineBasicBlock &
Block,
447 WaitcntBrackets &ScoreBrackets);
450 bool removeRedundantSoftXcnts(MachineBasicBlock &
Block);
452 bool ExpertMode)
const;
454 return WCG->getWaitEvents(
T);
457 return WCG->getCounterFromEvent(
E);
469class WaitcntBrackets {
471 WaitcntBrackets(
const SIInsertWaitcnts *Context) : Context(Context) {
472 assert(Context->TRI.getNumRegUnits() < REGUNITS_END);
477 unsigned NumUnusedVmem = 0, NumUnusedSGPRs = 0;
478 for (
auto &[ID, Val] : VMem) {
482 for (
auto &[ID, Val] : SGPRs) {
487 if (NumUnusedVmem || NumUnusedSGPRs) {
488 errs() <<
"WaitcntBracket had unused entries at destruction time: "
489 << NumUnusedVmem <<
" VMem and " << NumUnusedSGPRs
490 <<
" SGPR unused entries\n";
501 return ScoreUBs[
T] - ScoreLBs[
T];
505 return getVMemScore(ID,
T) > getScoreLB(
T);
523 return getScoreUB(
T) - getScoreLB(
T);
527 auto It = SGPRs.find(RU);
528 return It != SGPRs.end() ? It->second.get(
T) : 0;
532 auto It = VMem.find(TID);
533 return It != VMem.end() ? It->second.Scores[
T] : 0;
548 void simplifyWaitcnt(AMDGPU::Waitcnt &
Wait)
const {
551 void simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
552 AMDGPU::Waitcnt &UpdateWait)
const;
555 void simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
556 AMDGPU::Waitcnt &UpdateWait)
const;
557 void simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
558 AMDGPU::Waitcnt &UpdateWait)
const;
561 AMDGPU::Waitcnt &
Wait,
562 const MachineInstr &
MI)
const;
563 MCPhysReg determineVGPR16Dependency(
const MachineInstr &
MI,
567 AMDGPU::Waitcnt &
Wait)
const;
568 AMDGPU::Waitcnt determineAsyncWait(
unsigned N);
569 void tryClearSCCWriteEvent(MachineInstr *Inst);
571 void applyWaitcnt(
const AMDGPU::Waitcnt &
Wait);
575 void recordAsyncMark(MachineInstr &
MI);
577 HWEvents getPendingEvents()
const {
return PendingEvents; }
578 bool hasPendingEvent()
const {
return PendingEvents.
any(); }
579 bool hasPendingEvent(
HWEvents E)
const {
return PendingEvents.contains(
E); }
581 bool HasPending = (PendingEvents & Context->getWaitEvents(
T)).any();
583 "Expected pending events iff scoreboard is not empty");
588 HWEvents Events = PendingEvents & Context->getWaitEvents(
T);
590 return Events.
size() > 1;
593 bool hasPendingFlat()
const {
600 void setPendingFlat() {
605 bool hasPendingGDS()
const {
610 unsigned getPendingGDSWait()
const {
620 for (MCRegUnit RU : regunits(
Reg)) {
621 auto It = VMem.find(toVMEMID(RU));
622 if (It != VMem.end() && (It->second.VGPRPendingEvents & ~
E).any())
629 for (MCRegUnit RU : regunits(
Reg)) {
630 if (
auto It = VMem.find(toVMEMID(RU)); It != VMem.end()) {
632 if (It->second.empty())
638 void setStateOnFunctionEntryOrReturn() {
644 ArrayRef<const MachineInstr *> getLDSDMAStores()
const {
648 bool hasPointSampleAccel(
const MachineInstr &
MI)
const;
649 bool hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
652 void print(raw_ostream &)
const;
657 void purgeEmptyTrackingData();
661 return Context->getLimits().get(
T);
671 using CounterValueArray = std::array<unsigned, AMDGPU::NUM_INST_CNTS>;
674 AMDGPU::Waitcnt &
Wait)
const;
676 static bool mergeScore(
const MergeInfo &M,
unsigned &Score,
677 unsigned OtherScore);
682 assert(
Reg != AMDGPU::SCC &&
"Shouldn't be used on SCC");
683 if (!Context->TRI.isInAllocatableClass(
Reg))
685 return Context->TRI.regunits(
Reg);
706 const SIRegisterInfo &
TRI = Context->TRI;
707 if (
Reg == AMDGPU::SCC) {
709 }
else if (
TRI.isVectorRegister(Context->MRI,
Reg)) {
710 for (MCRegUnit RU : regunits(
Reg))
711 VMem[toVMEMID(RU)].Scores[
T] = Val;
712 }
else if (
TRI.isSGPRReg(Context->MRI,
Reg)) {
713 for (MCRegUnit RU : regunits(
Reg))
714 SGPRs[RU].get(
T) = Val;
721 VMem[TID].Scores[
T] = Val;
724 void setScoreByOperand(
const MachineOperand &
Op,
727 const SIInsertWaitcnts *Context;
733 unsigned LastFlatDsCnt = 0;
734 unsigned LastFlatLoadCnt = 0;
736 unsigned LastGDS = 0;
753 CounterValueArray Scores{};
767 unsigned ScoreDsKmCnt = 0;
768 unsigned ScoreXCnt = 0;
784 bool empty()
const {
return !ScoreDsKmCnt && !ScoreXCnt; }
787 DenseMap<VMEMID, VMEMInfo> VMem;
788 DenseMap<MCRegUnit, SGPRInfo> SGPRs;
791 unsigned SCCScore = 0;
793 const MachineInstr *PendingSCCWrite =
nullptr;
797 SmallVector<const MachineInstr *> LDSDMAStores;
806 static constexpr unsigned MaxAsyncMarks = 16;
810 CounterValueArray AsyncScore{};
813SIInsertWaitcnts::BlockInfo::~BlockInfo() =
default;
818 SIInsertWaitcntsLegacy() : MachineFunctionPass(ID) {}
820 bool runOnMachineFunction(MachineFunction &MF)
override;
822 StringRef getPassName()
const override {
823 return "SI insert wait instructions";
826 void getAnalysisUsage(AnalysisUsage &AU)
const override {
829 AU.
addRequired<MachinePostDominatorTreeWrapperPass>();
841 setRegScore(
Op.getReg().asMCReg(), CntTy, Score);
849bool WaitcntBrackets::hasPointSampleAccel(
const MachineInstr &
MI)
const {
854 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
864bool WaitcntBrackets::hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
866 if (!hasPointSampleAccel(
MI))
869 return hasDifferentVGPRPendingEvents(
Reg, HWEvents::VMEM_READ_ACCESS);
872void WaitcntBrackets::updateByEvent(
HWEvents E, MachineInstr &Inst) {
873 assert(
E.size() == 1 &&
"Expected singular event!");
877 unsigned UB = getScoreUB(
T);
881 Context->ST.hasVOP3PX2IncrementsVaVdstTwice()) {
893 setScoreUB(
T, CurrScore);
896 const MachineRegisterInfo &MRI =
Context->MRI;
905 if (
const auto *AddrOp =
TII.getNamedOperand(Inst, AMDGPU::OpName::addr))
909 if (
const auto *Data0 =
910 TII.getNamedOperand(Inst, AMDGPU::OpName::data0))
912 if (
const auto *Data1 =
913 TII.getNamedOperand(Inst, AMDGPU::OpName::data1))
917 Inst.
getOpcode() != AMDGPU::DS_CONSUME &&
918 Inst.
getOpcode() != AMDGPU::DS_ORDERED_COUNT) {
919 for (
const MachineOperand &
Op : Inst.
all_uses()) {
920 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
924 }
else if (
TII.isFLAT(Inst)) {
926 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
929 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
932 }
else if (
TII.isMIMG(Inst)) {
936 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
939 }
else if (
TII.isMTBUF(Inst)) {
942 }
else if (
TII.isMUBUF(Inst)) {
946 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
949 }
else if (
TII.isLDSDIR(Inst)) {
951 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::vdst),
954 if (
TII.isEXP(Inst)) {
959 for (MachineOperand &DefMO : Inst.
all_defs()) {
960 if (
TRI.isVGPR(MRI, DefMO.getReg())) {
965 for (
const MachineOperand &
Op : Inst.
all_uses()) {
966 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
972 E == HWEvents::SMEM_GROUP ? HWEvents::VMEM_GROUP : HWEvents::SMEM_GROUP;
973 if (PendingEvents.
contains(OtherEvent)) {
978 setScoreLB(
T, getScoreUB(
T) - 1);
979 PendingEvents -= OtherEvent;
981 for (
const MachineOperand &
Op : Inst.
all_uses())
982 setScoreByOperand(
Op,
T, CurrScore);
987 for (
const MachineOperand &
Op : Inst.
operands()) {
1000 setScoreByOperand(
Op,
T, CurrScore);
1012 for (
const MachineOperand &
Op : Inst.
defs()) {
1015 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
1017 if (updateVMCntOnly(Inst)) {
1026 if (hasPointSampleAccel(Inst))
1027 VGPRContext |= HWEvents::VMEM_READ_ACCESS;
1028 for (MCRegUnit RU : regunits(
Op.getReg().asMCReg()))
1029 VMem[toVMEMID(RU)].VGPRPendingEvents |= VGPRContext;
1032 setScoreByOperand(
Op,
T, CurrScore);
1035 (
TII.isDS(Inst) ||
Context->isNonAsyncLdsDmaWrite(Inst))) {
1044 if (!MemOp->isStore() ||
1049 auto AAI = MemOp->getAAInfo();
1055 if (!AAI || !AAI.Scope)
1057 for (
unsigned I = 0,
E = LDSDMAStores.
size();
I !=
E && !Slot; ++
I) {
1058 for (
const auto *MemOp : LDSDMAStores[
I]->memoperands()) {
1059 if (MemOp->isStore() && AAI == MemOp->getAAInfo()) {
1074 setVMemScore(LDSDMA_BEGIN,
T, CurrScore);
1075 if (Slot && Slot < NUM_LDSDMA)
1076 setVMemScore(LDSDMA_BEGIN + Slot,
T, CurrScore);
1079 if (
Context->shouldUpdateAsyncMark(Inst,
T)) {
1080 AsyncScore[
T] = CurrScore;
1084 setRegScore(AMDGPU::SCC,
T, CurrScore);
1085 PendingSCCWrite = &Inst;
1090void WaitcntBrackets::recordAsyncMark(MachineInstr &Inst) {
1096 AsyncMarks.push_back(AsyncScore);
1098 dbgs() <<
"recordAsyncMark:\n" << Inst;
1099 for (
const auto &Mark : AsyncMarks) {
1106void WaitcntBrackets::print(raw_ostream &OS)
const {
1110 unsigned SR = getScoreRange(
T);
1113 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM") <<
"_CNT("
1117 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM") <<
"_CNT("
1121 OS <<
" EXP_CNT(" << SR <<
"):";
1124 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS") <<
"_CNT("
1128 OS <<
" SAMPLE_CNT(" << SR <<
"):";
1131 OS <<
" BVH_CNT(" << SR <<
"):";
1134 OS <<
" KM_CNT(" << SR <<
"):";
1137 OS <<
" X_CNT(" << SR <<
"):";
1140 OS <<
" ASYNC_CNT(" << SR <<
"):";
1143 OS <<
" VA_VDST_RD(" << SR <<
"): ";
1146 OS <<
" VA_VDST_WR(" << SR <<
"): ";
1149 OS <<
" VM_VSRC(" << SR <<
"): ";
1152 OS <<
" UNKNOWN(" << SR <<
"):";
1158 unsigned LB = getScoreLB(
T);
1161 sort(SortedVMEMIDs);
1163 for (
auto ID : SortedVMEMIDs) {
1164 unsigned RegScore = VMem.at(ID).Scores[
T];
1167 unsigned RelScore = RegScore - LB - 1;
1168 if (ID < REGUNITS_END) {
1169 OS <<
' ' << RelScore <<
':'
1172 assert(ID >= LDSDMA_BEGIN && ID < LDSDMA_END &&
1173 "Unhandled/unexpected ID value!");
1174 OS <<
' ' << RelScore <<
":LDSDMA" <<
ID;
1179 if (isSmemCounter(
T)) {
1181 sort(SortedSMEMIDs);
1182 for (
auto ID : SortedSMEMIDs) {
1183 unsigned RegScore = SGPRs.at(ID).get(
T);
1186 unsigned RelScore = RegScore - LB - 1;
1187 OS <<
' ' << RelScore <<
':'
1193 OS <<
' ' << SCCScore <<
":scc";
1198 OS <<
"Pending Events: ";
1199 if (hasPendingEvent()) {
1200 OS << getPendingEvents();
1206 OS <<
"Async score: ";
1207 if (AsyncScore.empty())
1213 OS <<
"Async marks: " << AsyncMarks.size() <<
'\n';
1215 for (
const auto &Mark : AsyncMarks) {
1217 unsigned MarkedScore = Mark[
T];
1220 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM")
1221 <<
"_CNT: " << MarkedScore;
1224 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM")
1225 <<
"_CNT: " << MarkedScore;
1228 OS <<
" EXP_CNT: " << MarkedScore;
1231 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS")
1232 <<
"_CNT: " << MarkedScore;
1235 OS <<
" SAMPLE_CNT: " << MarkedScore;
1238 OS <<
" BVH_CNT: " << MarkedScore;
1241 OS <<
" KM_CNT: " << MarkedScore;
1244 OS <<
" X_CNT: " << MarkedScore;
1247 OS <<
" ASYNC_CNT: " << MarkedScore;
1250 OS <<
" UNKNOWN: " << MarkedScore;
1261void WaitcntBrackets::simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
1262 AMDGPU::Waitcnt &UpdateWait)
const {
1270 simplifyXcnt(CheckWait, UpdateWait);
1273 simplifyVmVsrc(CheckWait, UpdateWait);
1278 unsigned &
Count)
const {
1282 if (
Count >= getScoreRange(
T))
1286void WaitcntBrackets::simplifyWaitcnt(AMDGPU::Waitcnt &
Wait,
1288 unsigned Cnt =
Wait.get(
T);
1289 simplifyWaitcnt(
T, Cnt);
1293void WaitcntBrackets::simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
1294 AMDGPU::Waitcnt &UpdateWait)
const {
1304 hasPendingEvent(HWEvents::SMEM_GROUP))
1310 hasPendingEvent(HWEvents::VMEM_GROUP) &&
1317void WaitcntBrackets::simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
1318 AMDGPU::Waitcnt &UpdateWait)
const {
1328 return Acc |
Context->getWaitEvents(
T);
1330 HWEvents PendingVmemEvents = PendingEvents & VmemEvents;
1332 unsigned CheckCount = CheckWait.
get(
T);
1334 (CheckCount == 0 || !counterOutOfOrder(
T)) &&
1335 (PendingVmemEvents & ~
Context->getWaitEvents(
T)) == 0)
1342void WaitcntBrackets::purgeEmptyTrackingData() {
1343 VMem.remove_if([](
const auto &
P) {
return P.second.empty(); });
1344 SGPRs.remove_if([](
const auto &
P) {
return P.second.empty(); });
1348 unsigned ScoreToWait,
1349 AMDGPU::Waitcnt &
Wait)
const {
1350 const unsigned LB = getScoreLB(
T);
1351 const unsigned UB = getScoreUB(
T);
1354 if ((UB >= ScoreToWait) && (ScoreToWait > LB)) {
1356 !
Context->ST.hasFlatLgkmVMemCountInOrder()) {
1361 }
else if (counterOutOfOrder(
T)) {
1369 unsigned NeededWait = std::min(UB - ScoreToWait, getLimit(
T) - 1);
1370 Wait.add(
T, NeededWait);
1375AMDGPU::Waitcnt WaitcntBrackets::determineAsyncWait(
unsigned N) {
1377 dbgs() <<
"Need " <<
N <<
" async marks. Found " << AsyncMarks.size()
1379 for (
const auto &Mark : AsyncMarks) {
1385 if (AsyncMarks.size() == MaxAsyncMarks) {
1390 LLVM_DEBUG(
dbgs() <<
"Possible truncation. Ensuring a non-trivial wait.\n");
1391 N = std::min(
N, (
unsigned)MaxAsyncMarks - 1);
1394 AMDGPU::Waitcnt
Wait;
1395 if (AsyncMarks.size() <=
N) {
1400 size_t MarkIndex = AsyncMarks.size() -
N - 1;
1401 const auto &RequiredMark = AsyncMarks[MarkIndex];
1403 determineWaitForScore(
T, RequiredMark[
T],
Wait);
1409 dbgs() <<
"Removing " << (MarkIndex + 1)
1410 <<
" async marks after determining wait\n";
1412 AsyncMarks.erase(AsyncMarks.begin(), AsyncMarks.begin() + MarkIndex + 1);
1425MCPhysReg WaitcntBrackets::determineVGPR16Dependency(
const MachineInstr &
MI,
1429 unsigned Size =
Context->TRI.getRegSizeInBits(*RC);
1431 if (
Size != 16 || !
Context->ST.hasD16Writes32BitVgpr())
1441 AMDGPU::Waitcnt
Wait;
1442 for (MCRegUnit RU : regunits(OtherHalf))
1443 determineWaitForScore(
T, getVMemScore(toVMEMID(RU),
T),
Wait);
1446 if (!
Wait.hasWait())
1456 HWEvents Events = MIEvents & OtherHalfEvents;
1457 if (Events.
size() > 1)
1464 AMDGPU::Waitcnt &
Wait,
1465 const MachineInstr &
MI)
const {
1466 if (
Reg == AMDGPU::SCC) {
1467 determineWaitForScore(
T, SCCScore,
Wait);
1471 Reg = determineVGPR16Dependency(
MI,
T,
Reg);
1472 for (MCRegUnit RU : regunits(
Reg))
1473 determineWaitForScore(
1474 T, IsVGPR ? getVMemScore(toVMEMID(RU),
T) : getSGPRScore(RU,
T),
1481 AMDGPU::Waitcnt &
Wait)
const {
1482 assert(TID >= LDSDMA_BEGIN && TID < LDSDMA_END);
1483 determineWaitForScore(
T, getVMemScore(TID,
T),
Wait);
1486void WaitcntBrackets::tryClearSCCWriteEvent(MachineInstr *Inst) {
1489 if (PendingSCCWrite &&
1490 PendingSCCWrite->
getOpcode() == AMDGPU::S_BARRIER_SIGNAL_ISFIRST_IMM &&
1492 HWEvents SCC_WRITE_PendingEvent = HWEvents::SCC_WRITE;
1495 SCC_WRITE_PendingEvent) {
1499 PendingEvents -= SCC_WRITE_PendingEvent;
1500 PendingSCCWrite =
nullptr;
1504void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait) {
1513 applyWaitcnt(
T, Cnt);
1518 const unsigned UB = getScoreUB(
T);
1522 if (counterOutOfOrder(
T))
1524 setScoreLB(
T, std::max(getScoreLB(
T), UB -
Count));
1527 PendingEvents -=
Context->getWaitEvents(
T);
1531 hasPendingEvent(HWEvents::SMEM_GROUP)) {
1535 PendingEvents -= HWEvents::SMEM_GROUP;
1541 else if (
Count == 0)
1542 PendingEvents -= HWEvents::VMEM_GROUP;
1546void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait,
1548 unsigned Cnt =
Wait.get(
T);
1549 applyWaitcnt(
T, Cnt);
1556 if ((
T ==
Context->SmemAccessCounter &&
1557 hasPendingEvent(HWEvents::SMEM_ACCESS)) ||
1574 static constexpr HWEvents ExtendedImageEvents =
1575 HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
1576 if (!
Context->ST.hasExtendedWaitCounts() &&
1577 (Events & ExtendedImageEvents).any()) {
1578 Events -= ExtendedImageEvents;
1579 Events |= HWEvents::VMEM_READ_ACCESS;
1585 Events -= HWEvents::GLOBAL_INV_ACCESS;
1589 return Events.
size() > 1;
1592 return hasMixedPendingEvents(
T);
1602char SIInsertWaitcntsLegacy::ID = 0;
1607 return new SIInsertWaitcntsLegacy();
1612 int OpIdx = AMDGPU::getNamedOperandIdx(
MI.getOpcode(),
OpName);
1617 if (NewEnc == MO.
getImm())
1624bool WaitcntGenerator::promoteSoftWaitCnt(MachineInstr *Waitcnt)
const {
1638bool WaitcntGeneratorPreGFX12::applyPreexistingWaitcnt(
1639 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1641 assert(isNormalMode(MaxCounter));
1644 MachineInstr *WaitcntInstr =
nullptr;
1645 MachineInstr *WaitcntVsCntInstr =
nullptr;
1648 dbgs() <<
"PreGFX12::applyPreexistingWaitcnt at: ";
1650 dbgs() <<
"end of block\n";
1658 if (isNonWaitcntMetaInst(
II)) {
1664 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1668 if (Opcode == AMDGPU::S_WAITCNT) {
1669 unsigned IEnc =
II.getOperand(0).getImm();
1672 ScoreBrackets.simplifyWaitcnt(OldWait);
1676 if (WaitcntInstr || (!
Wait.hasWaitExceptStoreCnt() && TrySimplify)) {
1677 II.eraseFromParent();
1681 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1684 <<
"Before: " <<
Wait <<
'\n';);
1695 II.eraseFromParent();
1696 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1697 unsigned N =
II.getOperand(0).getImm();
1699 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1702 assert(Opcode == AMDGPU::S_WAITCNT_VSCNT);
1703 assert(
II.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
1706 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1712 if (WaitcntVsCntInstr || (!
Wait.hasWaitStoreCnt() && TrySimplify)) {
1713 II.eraseFromParent();
1716 WaitcntVsCntInstr = &
II;
1723 Modified |= promoteSoftWaitCnt(WaitcntInstr);
1732 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
1733 <<
"New Instr at block end: "
1734 << *WaitcntInstr <<
'\n'
1735 :
dbgs() <<
"applied pre-existing waitcnt\n"
1736 <<
"Old Instr: " << *It
1737 <<
"New Instr: " << *WaitcntInstr <<
'\n');
1740 if (WaitcntVsCntInstr) {
1744 Modified |= promoteSoftWaitCnt(WaitcntVsCntInstr);
1750 ?
dbgs() <<
"applied pre-existing waitcnt\n"
1751 <<
"New Instr at block end: " << *WaitcntVsCntInstr
1753 :
dbgs() <<
"applied pre-existing waitcnt\n"
1754 <<
"Old Instr: " << *It
1755 <<
"New Instr: " << *WaitcntVsCntInstr <<
'\n');
1763bool WaitcntGeneratorPreGFX12::createNewWaitcnt(
1765 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
1766 assert(isNormalMode(MaxCounter));
1773 if (
Wait.hasWaitExceptStoreCnt()) {
1775 if (ExpandWaitcntProfiling) {
1779 bool AnyOutOfOrder =
false;
1781 unsigned WaitCnt =
Wait.get(CT);
1782 if (WaitCnt != ~0u && ScoreBrackets.counterOutOfOrder(CT)) {
1783 AnyOutOfOrder =
true;
1788 if (AnyOutOfOrder) {
1796 unsigned WaitCnt =
Wait.get(CT);
1800 unsigned Outstanding =
1801 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
1802 EmitExpandedWaitcnt(Outstanding, WaitCnt, [&](
unsigned Count) {
1814 [[maybe_unused]]
auto SWaitInst =
1819 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1820 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1824 if (
Wait.hasWaitStoreCnt()) {
1830 unsigned Outstanding =
1833 EmitExpandedWaitcnt(
1835 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1836 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1841 [[maybe_unused]]
auto SWaitInst =
1843 .
addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1848 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1849 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1857WaitcntGeneratorPreGFX12::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1858 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt &&
ST.hasVscnt() ? 0 : ~0u);
1862WaitcntGeneratorGFX12Plus::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1863 unsigned ExpertVal = IsExpertMode ? 0 : ~0
u;
1864 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt ? 0 : ~0u, 0, 0, 0,
1866 ~0u , ExpertVal, ExpertVal, ExpertVal);
1873bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt(
1874 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1876 assert(!isNormalMode(MaxCounter));
1879 MachineInstr *CombinedLoadDsCntInstr =
nullptr;
1880 MachineInstr *CombinedStoreDsCntInstr =
nullptr;
1881 MachineInstr *WaitcntDepctrInstr =
nullptr;
1885 dbgs() <<
"GFX12Plus::applyPreexistingWaitcnt at: ";
1887 dbgs() <<
"end of block\n";
1893 AMDGPU::Waitcnt RequiredWait;
1898 if (isNonWaitcntMetaInst(
II)) {
1907 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1911 if (Opcode == AMDGPU::S_WAITCNT)
1914 if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) {
1916 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1921 RequiredWait = RequiredWait.combined(OldWait);
1923 if (CombinedLoadDsCntInstr ==
nullptr) {
1924 CombinedLoadDsCntInstr = &
II;
1926 II.eraseFromParent();
1929 }
else if (Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT) {
1931 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1936 RequiredWait = RequiredWait.combined(OldWait);
1938 if (CombinedStoreDsCntInstr ==
nullptr) {
1939 CombinedStoreDsCntInstr = &
II;
1941 II.eraseFromParent();
1944 }
else if (Opcode == AMDGPU::S_WAITCNT_DEPCTR) {
1946 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1947 AMDGPU::Waitcnt OldWait;
1954 ScoreBrackets.simplifyWaitcnt(OldWait);
1956 if (WaitcntDepctrInstr ==
nullptr) {
1957 WaitcntDepctrInstr = &
II;
1966 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1977 II.eraseFromParent();
1981 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1984 II.eraseFromParent();
1986 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1989 unsigned N =
II.getOperand(0).getImm();
1990 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1993 std::optional<AMDGPU::InstCounterType> CT =
1997 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1999 Wait.add(CT.value(), OldCnt);
2001 RequiredWait.add(CT.value(), OldCnt);
2003 if (WaitInstrs[CT.value()] ==
nullptr) {
2004 WaitInstrs[CT.value()] = &
II;
2006 II.eraseFromParent();
2012 ScoreBrackets.simplifyWaitcnt(
Wait.combined(RequiredWait),
Wait);
2013 Wait =
Wait.combined(RequiredWait);
2015 if (CombinedLoadDsCntInstr) {
2031 AMDGPU::OpName::simm16, NewEnc);
2032 Modified |= promoteSoftWaitCnt(CombinedLoadDsCntInstr);
2038 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2039 <<
"New Instr at block end: "
2040 << *CombinedLoadDsCntInstr <<
'\n'
2041 :
dbgs() <<
"applied pre-existing waitcnt\n"
2042 <<
"Old Instr: " << *It <<
"New Instr: "
2043 << *CombinedLoadDsCntInstr <<
'\n');
2050 if (CombinedStoreDsCntInstr) {
2055 AMDGPU::OpName::simm16, NewEnc);
2056 Modified |= promoteSoftWaitCnt(CombinedStoreDsCntInstr);
2062 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2063 <<
"New Instr at block end: "
2064 << *CombinedStoreDsCntInstr <<
'\n'
2065 :
dbgs() <<
"applied pre-existing waitcnt\n"
2066 <<
"Old Instr: " << *It <<
"New Instr: "
2067 << *CombinedStoreDsCntInstr <<
'\n');
2097 for (MachineInstr **WI : WaitsToErase) {
2101 (*WI)->eraseFromParent();
2108 if (!WaitInstrs[CT])
2111 unsigned NewCnt =
Wait.get(CT);
2112 if (NewCnt != ~0u) {
2114 AMDGPU::OpName::simm16, NewCnt);
2115 Modified |= promoteSoftWaitCnt(WaitInstrs[CT]);
2117 ScoreBrackets.applyWaitcnt(CT, NewCnt);
2121 ?
dbgs() <<
"applied pre-existing waitcnt\n"
2122 <<
"New Instr at block end: " << *WaitInstrs[CT]
2124 :
dbgs() <<
"applied pre-existing waitcnt\n"
2125 <<
"Old Instr: " << *It
2126 <<
"New Instr: " << *WaitInstrs[CT] <<
'\n');
2133 if (WaitcntDepctrInstr) {
2137 TII.getNamedOperand(*WaitcntDepctrInstr, AMDGPU::OpName::simm16)
2157 AMDGPU::OpName::simm16, Enc);
2159 <<
"New Instr at block end: "
2160 << *WaitcntDepctrInstr <<
'\n'
2161 :
dbgs() <<
"applyPreexistingWaitcnt\n"
2162 <<
"Old Instr: " << *It <<
"New Instr: "
2163 << *WaitcntDepctrInstr <<
'\n');
2174bool WaitcntGeneratorGFX12Plus::createNewWaitcnt(
2176 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
2177 assert(!isNormalMode(MaxCounter));
2184 if (ExpandWaitcntProfiling) {
2191 if (ScoreBrackets.counterOutOfOrder(CT)) {
2198 unsigned Outstanding =
2199 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
2200 EmitExpandedWaitcnt(Outstanding,
Count, [&](
unsigned Val) {
2212 MachineInstr *SWaitInst =
nullptr;
2236 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2237 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2249 [[maybe_unused]]
auto SWaitInst =
2256 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2257 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2260 if (
Wait.hasWaitDepctr()) {
2269 [[maybe_unused]]
auto SWaitInst =
2275 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2276 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2295bool SIInsertWaitcnts::generateWaitcntInstBefore(
2296 MachineInstr &
MI, WaitcntBrackets &ScoreBrackets,
2297 MachineInstr *OldWaitcntInstr, PreheaderFlushFlags FlushFlags) {
2302 AMDGPU::Waitcnt
Wait;
2303 const unsigned Opc =
MI.getOpcode();
2306 case AMDGPU::BUFFER_WBINVL1:
2307 case AMDGPU::BUFFER_WBINVL1_SC:
2308 case AMDGPU::BUFFER_WBINVL1_VOL:
2309 case AMDGPU::BUFFER_GL0_INV:
2310 case AMDGPU::BUFFER_GL1_INV: {
2318 case AMDGPU::SI_RETURN_TO_EPILOG:
2319 case AMDGPU::SI_RETURN:
2320 case AMDGPU::SI_WHOLE_WAVE_FUNC_RETURN:
2321 case AMDGPU::S_SETPC_B64_return: {
2326 AMDGPU::Waitcnt AllZeroWait =
2327 WCG->getAllZeroWaitcnt(
false);
2332 if (
ST.hasExtendedWaitCounts() &&
2333 !ScoreBrackets.hasPendingEvent(HWEvents::VMEM_READ_ACCESS))
2338 case AMDGPU::S_ENDPGM:
2339 case AMDGPU::S_ENDPGM_SAVED: {
2350 !ScoreBrackets.hasPendingEvent(HWEvents::SCRATCH_WRITE_ACCESS);
2353 case AMDGPU::S_SENDMSG:
2354 case AMDGPU::S_SENDMSGHALT: {
2355 if (
ST.hasLegacyGeometry() &&
2370 if (
MI.modifiesRegister(AMDGPU::EXEC, &
TRI)) {
2373 if (ScoreBrackets.hasPendingEvent(HWEvents::EXP_GPR_LOCK) ||
2374 ScoreBrackets.hasPendingEvent(HWEvents::EXP_PARAM_ACCESS) ||
2375 ScoreBrackets.hasPendingEvent(HWEvents::EXP_POS_ACCESS) ||
2376 ScoreBrackets.hasPendingEvent(HWEvents::GDS_GPR_LOCK)) {
2383 if (
TII.isAlwaysGDS(
Opc) && ScoreBrackets.hasPendingGDS())
2391 Wait = AMDGPU::Waitcnt();
2393 const MachineOperand &CallAddrOp =
TII.getCalleeOperand(
MI);
2394 if (CallAddrOp.
isReg()) {
2395 ScoreBrackets.determineWaitForPhysReg(
2398 if (
const auto *RtnAddrOp =
2399 TII.getNamedOperand(
MI, AMDGPU::OpName::dst)) {
2400 ScoreBrackets.determineWaitForPhysReg(
2401 SmemAccessCounter, RtnAddrOp->getReg().asMCReg(),
Wait,
MI);
2404 }
else if (
Opc == AMDGPU::S_BARRIER_WAIT) {
2405 ScoreBrackets.tryClearSCCWriteEvent(&
MI);
2421 for (
const MachineMemOperand *Memop :
MI.memoperands()) {
2422 const Value *Ptr = Memop->getValue();
2423 if (Memop->isStore()) {
2424 if (
auto It = SLoadAddresses.
find(Ptr); It != SLoadAddresses.
end()) {
2425 Wait.add(SmemAccessCounter, 0);
2427 SLoadAddresses.
erase(It);
2430 unsigned AS = Memop->getAddrSpace();
2434 if (
TII.mayWriteLDSThroughDMA(
MI))
2438 unsigned TID = LDSDMA_BEGIN;
2439 if (Ptr && Memop->getAAInfo()) {
2440 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2441 for (
unsigned I = 0,
E = LDSDMAStores.size();
I !=
E; ++
I) {
2442 if (
MI.mayAlias(AA, *LDSDMAStores[
I],
true)) {
2443 if ((
I + 1) >= NUM_LDSDMA) {
2458 if (Memop->isStore()) {
2464 for (
const MachineOperand &
Op :
MI.operands()) {
2469 if (
Op.isTied() &&
Op.isUse() &&
TII.doesNotReadTiedSource(
MI))
2474 const bool IsVGPR =
TRI.isVectorRegister(MRI,
Op.getReg());
2481 if (
Op.isImplicit() &&
MI.mayLoadOrStore())
2499 if (
Op.isUse() || !updateVMCntOnly(
MI) ||
2500 ScoreBrackets.hasDifferentVGPRPendingEvents(
2502 ScoreBrackets.hasPointSamplePendingVmemTypes(
MI,
Reg) ||
2503 !
ST.hasVmemWriteVgprInOrder()) {
2510 ScoreBrackets.clearVGPRPendingEvents(
Reg);
2514 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2519 }
else if (
Op.getReg() == AMDGPU::SCC) {
2522 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter,
Reg,
Wait,
2526 if (
ST.hasWaitXcnt() &&
Op.isDef())
2545 if (
Opc == AMDGPU::S_BARRIER && !
ST.hasAutoWaitcntBeforeBarrier() &&
2546 !
ST.hasBackOffBarrier()) {
2547 Wait =
Wait.combined(WCG->getAllZeroWaitcnt(
true));
2554 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2559 ScoreBrackets.simplifyWaitcnt(
Wait);
2565 if (
TII.isVALU(
MI,
false)) {
2581 Wait = WCG->getAllZeroWaitcnt(
false);
2585 if (!ForceEmitWaitcnt[
T])
2590 if (FlushFlags.FlushVmCnt) {
2596 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
2602 return generateWaitcnt(
Wait,
MI.getIterator(), *
MI.getParent(), ScoreBrackets,
2606bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt
Wait,
2608 MachineBasicBlock &
Block,
2609 WaitcntBrackets &ScoreBrackets,
2610 MachineInstr *OldWaitcntInstr) {
2613 if (OldWaitcntInstr)
2617 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr,
Wait, It);
2622 MachineOperand *WaitExp =
TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2632 <<
"Update Instr: " << *It);
2635 if (WCG->createNewWaitcnt(
Block, It,
Wait, ScoreBrackets))
2640 ScoreBrackets.applyWaitcnt(
Wait);
2645bool SIInsertWaitcnts::isVmemAccess(
const MachineInstr &
MI)
const {
2646 return (
TII.isFLAT(
MI) &&
TII.mayAccessVMEMThroughFlat(
MI)) ||
2653 MachineBasicBlock *
Block)
const {
2654 auto BlockEnd =
Block->getParent()->end();
2655 auto BlockIter =
Block->getIterator();
2659 if (++BlockIter != BlockEnd) {
2660 It = BlockIter->instr_begin();
2667 if (!It->isMetaInstruction())
2675 return It->getOpcode() == AMDGPU::S_ENDPGM;
2679bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2680 MachineBasicBlock &
Block,
2681 WaitcntBrackets &ScoreBrackets) {
2682 AMDGPU::Waitcnt
Wait;
2683 bool NeedsEndPGMCheck =
false;
2691 NeedsEndPGMCheck =
true;
2694 ScoreBrackets.simplifyWaitcnt(
Wait);
2697 bool Result = generateWaitcnt(
Wait, SuccessorIt,
Block, ScoreBrackets,
2700 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &
Block)) {
2708void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2709 WaitcntBrackets *ScoreBrackets) {
2713 ScoreBrackets->updateByEvent(
E, Inst);
2715 if (
TII.isDS(Inst) &&
TII.usesLGKM_CNT(Inst)) {
2717 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2718 ScoreBrackets->setPendingGDS();
2720 }
else if (
TII.isFLAT(Inst)) {
2722 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2729 ScoreBrackets->setPendingFlat();
2731 }
else if (Inst.
isCall()) {
2734 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(
false));
2735 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2736 }
else if (
TII.isVINTERP(Inst)) {
2737 int64_t
Imm =
TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2747bool WaitcntBrackets::mergeScore(
const MergeInfo &M,
unsigned &Score,
2748 unsigned OtherScore) {
2749 unsigned MyShifted = Score <=
M.OldLB ? 0 : Score +
M.MyShift;
2750 unsigned OtherShifted =
2751 OtherScore <=
M.OtherLB ? 0 : OtherScore +
M.OtherShift;
2752 Score = std::max(MyShifted, OtherShifted);
2753 return OtherShifted > MyShifted;
2758 bool StrictDom =
false;
2762 if (AsyncMarks.empty() && OtherMarks.
empty()) {
2769 auto MaxSize = (unsigned)std::max(AsyncMarks.size(), OtherMarks.
size());
2770 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2773 if (AsyncMarks.size() > MaxSize)
2774 AsyncMarks.erase(AsyncMarks.begin(),
2775 AsyncMarks.begin() + (AsyncMarks.size() - MaxSize));
2781 constexpr CounterValueArray ZeroMark{};
2782 AsyncMarks.insert(AsyncMarks.begin(), MaxSize - AsyncMarks.size(), ZeroMark);
2785 dbgs() <<
"Before merge:\n";
2786 for (
const auto &Mark : AsyncMarks) {
2790 dbgs() <<
"Other marks:\n";
2791 for (
const auto &Mark : OtherMarks) {
2804 const unsigned OtherSize = OtherMarks.size();
2805 const unsigned OurSize = AsyncMarks.size();
2812 "AsyncMarks padded to MaxSize >= 1 (needs MaxAsyncMarks != 0)");
2815 const CounterValueArray &OtherMark =
2816 Idx <= OtherSize ? OtherMarks[OtherSize - Idx] : ZeroMark;
2819 mergeScore(MergeInfos[
T], AsyncMarks[OurSize - Idx][
T], OtherMark[
T]);
2823 dbgs() <<
"After merge:\n";
2824 for (
const auto &Mark : AsyncMarks) {
2838bool WaitcntBrackets::merge(
const WaitcntBrackets &
Other) {
2839 bool StrictDom =
false;
2843 for (
auto K :
Other.VMem.keys())
2844 VMem.try_emplace(K);
2845 for (
auto K :
Other.SGPRs.keys())
2846 SGPRs.try_emplace(K);
2854 const HWEvents OldEvents = PendingEvents & EventsForT;
2855 const HWEvents OtherEvents =
Other.PendingEvents & EventsForT;
2856 if (!OldEvents.
contains(OtherEvents))
2858 PendingEvents |= OtherEvents;
2861 const unsigned MyPending = ScoreUBs[
T] - ScoreLBs[
T];
2862 const unsigned OtherPending =
Other.ScoreUBs[
T] -
Other.ScoreLBs[
T];
2863 const unsigned NewUB = ScoreLBs[
T] + std::max(MyPending, OtherPending);
2864 if (NewUB < ScoreLBs[
T])
2867 MergeInfo &
M = MergeInfos[
T];
2868 M.OldLB = ScoreLBs[
T];
2869 M.OtherLB =
Other.ScoreLBs[
T];
2870 M.MyShift = NewUB - ScoreUBs[
T];
2871 M.OtherShift = NewUB -
Other.ScoreUBs[
T];
2873 ScoreUBs[
T] = NewUB;
2876 StrictDom |= mergeScore(M, LastFlatLoadCnt,
Other.LastFlatLoadCnt);
2879 StrictDom |= mergeScore(M, LastFlatDsCnt,
Other.LastFlatDsCnt);
2880 StrictDom |= mergeScore(M, LastGDS,
Other.LastGDS);
2884 StrictDom |= mergeScore(M, SCCScore,
Other.SCCScore);
2885 if (
Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2886 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2887 PendingSCCWrite =
Other.PendingSCCWrite;
2888 }
else if (PendingSCCWrite !=
Other.PendingSCCWrite) {
2889 PendingSCCWrite =
nullptr;
2894 for (
auto &[RegID, Info] : VMem)
2895 StrictDom |= mergeScore(M,
Info.Scores[
T],
Other.getVMemScore(RegID,
T));
2897 if (isSmemCounter(
T)) {
2898 for (
auto &[RegID, Info] : SGPRs) {
2899 auto It =
Other.SGPRs.find(RegID);
2900 unsigned OtherScore = (It !=
Other.SGPRs.end()) ? It->second.get(
T) : 0;
2901 StrictDom |= mergeScore(M,
Info.get(
T), OtherScore);
2906 for (
auto &[TID, Info] : VMem) {
2907 if (
auto It =
Other.VMem.find(TID); It !=
Other.VMem.end()) {
2909 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
2910 StrictDom |= NewVGPRContext !=
Info.VGPRPendingEvents;
2911 Info.VGPRPendingEvents = NewVGPRContext;
2915 StrictDom |= mergeAsyncMarks(MergeInfos,
Other.AsyncMarks);
2917 StrictDom |= mergeScore(MergeInfos[
T], AsyncScore[
T],
Other.AsyncScore[
T]);
2919 purgeEmptyTrackingData();
2925 return Opcode == AMDGPU::S_WAITCNT ||
2928 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
2929 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
2930 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
2931 Opcode == AMDGPU::WAIT_ASYNCMARK ||
2935void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &
MBB,
2937 bool ExpertMode)
const {
2941 .
addImm(ExpertMode ? 2 : 0)
2959class VCCZWorkaround {
2960 const WaitcntBrackets &ScoreBrackets;
2961 const GCNSubtarget &
ST;
2962 const SIInstrInfo &
TII;
2963 const SIRegisterInfo &
TRI;
2964 bool VCCZCorruptionBug =
false;
2965 bool VCCZNotUpdatedByPartialWrites =
false;
2968 bool MustRecomputeVCCZ =
true;
2971 VCCZWorkaround(
const WaitcntBrackets &ScoreBrackets,
const GCNSubtarget &ST,
2972 const SIInstrInfo &
TII,
const SIRegisterInfo &
TRI)
2974 VCCZCorruptionBug =
ST.hasReadVCCZBug();
2975 VCCZNotUpdatedByPartialWrites = !
ST.partialVCCWritesUpdateVCCZ();
2982 bool tryRecomputeVCCZ(MachineInstr &
MI) {
2984 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
2994 MustRecomputeVCCZ |= VCCZCorruptionBug &&
TII.isSMRD(
MI);
3000 std::optional<bool> PartiallyWritesToVCCOpt;
3001 auto PartiallyWritesToVCC = [](MachineInstr &
MI) {
3002 return MI.definesRegister(AMDGPU::VCC_LO,
nullptr) ||
3003 MI.definesRegister(AMDGPU::VCC_HI,
nullptr);
3005 if (VCCZNotUpdatedByPartialWrites) {
3006 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3009 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
3015 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
3016 !VCCZCorruptionBug) {
3018 if (!PartiallyWritesToVCCOpt)
3019 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3020 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
3021 MI.definesRegister(AMDGPU::VCC,
nullptr);
3024 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
3025 *PartiallyWritesToVCCOpt);
3027 MustRecomputeVCCZ =
false;
3037 TII.get(
ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
3040 MustRecomputeVCCZ =
false;
3050bool SIInsertWaitcnts::insertWaitcntInBlock(MachineFunction &MF,
3051 MachineBasicBlock &
Block,
3052 WaitcntBrackets &ScoreBrackets) {
3056 dbgs() <<
"*** Begin Block: ";
3058 ScoreBrackets.dump();
3060 VCCZWorkaround VCCZW(ScoreBrackets, ST,
TII,
TRI);
3063 MachineInstr *OldWaitcntInstr =
nullptr;
3066 ScoreBrackets.verify();
3069 Iter !=
E; ++Iter) {
3070 MachineInstr &Inst = *Iter;
3071 if (isNonWaitcntMetaInst(Inst))
3076 (IsExpertMode && Inst.
getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3077 if (!OldWaitcntInstr)
3078 OldWaitcntInstr = &Inst;
3082 PreheaderFlushFlags FlushFlags;
3083 if (
Block.getFirstTerminator() == Inst)
3084 FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3087 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3089 OldWaitcntInstr =
nullptr;
3091 if (Inst.
getOpcode() == AMDGPU::ASYNCMARK) {
3095 ScoreBrackets.recordAsyncMark(Inst);
3099 if (
TII.isSMRD(Inst)) {
3100 for (
const MachineMemOperand *Memop : Inst.
memoperands()) {
3103 if (!Memop->isInvariant()) {
3104 const Value *Ptr = Memop->getValue();
3110 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3114 Modified |= insertForcedWaitAfter(Inst,
Block, ScoreBrackets);
3118 ScoreBrackets.dump();
3123 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3125 ScoreBrackets.verify();
3130 AMDGPU::Waitcnt
Wait;
3131 if (
Block.getFirstTerminator() ==
Block.end()) {
3132 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3133 if (FlushFlags.FlushVmCnt) {
3141 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
3150 dbgs() <<
"*** End Block: ";
3152 ScoreBrackets.dump();
3158bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &
Block) {
3159 if (
Block.size() <= 1)
3167 MachineInstr *LastAtomicWithSoftXcnt =
nullptr;
3171 bool IsLDS =
TII.isDS(
MI) ||
3172 (
TII.isFLAT(
MI) &&
TII.mayAccessLDSThroughFlat(
MI, TgSplit));
3173 if (!IsLDS && (
MI.mayLoad() ^
MI.mayStore()))
3174 LastAtomicWithSoftXcnt =
nullptr;
3178 MachineInstr &PrevMI = *
MI.getPrevNode();
3180 if (PrevMI.
getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3183 if (LastAtomicWithSoftXcnt) {
3187 LastAtomicWithSoftXcnt = &
MI;
3195SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &
MBB,
3196 const WaitcntBrackets &ScoreBrackets) {
3197 auto [Iterator, IsInserted] =
3200 return Iterator->second;
3204 return PreheaderFlushFlags();
3208 return PreheaderFlushFlags();
3211 Iterator->second = getPreheaderFlushFlags(Loop, ScoreBrackets);
3212 return Iterator->second;
3215 return PreheaderFlushFlags();
3218bool SIInsertWaitcnts::isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const {
3220 return TII.mayAccessVMEMThroughFlat(
MI);
3224bool SIInsertWaitcnts::isDSRead(
const MachineInstr &
MI)
const {
3230bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const {
3259SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *
ML,
3260 const WaitcntBrackets &Brackets) {
3261 PreheaderFlushFlags
Flags;
3262 bool HasVMemLoad =
false;
3263 bool HasVMemStore =
false;
3264 bool UsesVgprVMEMLoadedOutside =
false;
3265 bool UsesVgprDSReadOutside =
false;
3266 bool VMemInvalidated =
false;
3270 bool TrackSimpleDSOpt =
ST.hasExtendedWaitCounts();
3271 DenseSet<MCRegUnit> VgprUse;
3272 DenseSet<MCRegUnit> VgprDefVMEM;
3273 DenseSet<MCRegUnit> VgprDefDS;
3279 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3280 unsigned DSReadPosition = 0;
3281 bool IsSingleBlock =
ML->getNumBlocks() == 1;
3282 bool TrackDSFlushPoint =
ST.hasExtendedWaitCounts() && IsSingleBlock;
3283 unsigned LastDSFlushPosition = 0;
3285 for (MachineBasicBlock *
MBB :
ML->blocks()) {
3286 for (MachineInstr &
MI : *
MBB) {
3287 if (isVMEMOrFlatVMEM(
MI)) {
3288 HasVMemLoad |=
MI.mayLoad();
3289 HasVMemStore |=
MI.mayStore();
3293 if (mayStoreIncrementingDSCNT(
MI)) {
3296 if (VMemInvalidated)
3298 TrackSimpleDSOpt =
false;
3299 TrackDSFlushPoint =
false;
3301 bool IsDSRead = isDSRead(
MI);
3306 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3307 if (!TrackDSFlushPoint)
3309 if (
auto It = LastDSReadPositionMap.
find(RU);
3310 It != LastDSReadPositionMap.
end()) {
3314 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3318 for (
const MachineOperand &
Op :
MI.all_uses()) {
3319 if (
Op.isDebug() || !
TRI.isVectorRegister(MRI,
Op.getReg()))
3322 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3326 VMemInvalidated =
true;
3330 TrackSimpleDSOpt =
false;
3333 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3337 updateDSReadFlushTracking(RU);
3342 VMEMID
ID = toVMEMID(RU);
3346 UsesVgprVMEMLoadedOutside =
true;
3351 UsesVgprDSReadOutside =
true;
3356 if (isVMEMOrFlatVMEM(
MI) &&
MI.mayLoad()) {
3357 for (
const MachineOperand &
Op :
MI.all_defs()) {
3358 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3362 VMemInvalidated =
true;
3367 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3378 if (IsDSRead || TrackDSFlushPoint) {
3379 for (
const MachineOperand &
Op :
MI.all_defs()) {
3380 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
3382 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3385 updateDSReadFlushTracking(RU);
3388 if (TrackDSFlushPoint)
3389 LastDSReadPositionMap[RU] = DSReadPosition;
3398 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3399 ((!
ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3400 (HasVMemLoad &&
ST.hasVmemWriteVgprInOrder())))
3401 Flags.FlushVmCnt =
true;
3407 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3410 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3411 bool DSFlushPointPrefetch =
3412 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3414 if (SimpleDSOpt || DSFlushPointPrefetch)
3415 Flags.FlushDsCnt =
true;
3420bool SIInsertWaitcntsLegacy::runOnMachineFunction(MachineFunction &MF) {
3421 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3423 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3425 if (
auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3426 AA = &AAR->getAAResults();
3428 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3440 if (!SIInsertWaitcnts(MLI, PDT,
AA, MF).
run())
3445 .preserve<AAManager>();
3448bool SIInsertWaitcnts::run() {
3456 if (ST.hasExtendedWaitCounts()) {
3457 IsExpertMode = ST.hasExpertSchedulingMode() &&
3466 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3471 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3475 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3479 MachineBasicBlock &EntryBB = MF.
front();
3490 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3493 if (
ST.hasExtendedWaitCounts()) {
3502 if (!
ST.hasImageInsts() &&
3508 TII.get(instrsForExtendedCounterTypes[CT]))
3521 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(
this);
3522 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3523 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3530 for (
auto *
MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3533 std::unique_ptr<WaitcntBrackets> Brackets;
3538 for (
auto BII = BlockInfos.
begin(), BIE = BlockInfos.
end(); BII != BIE;
3540 MachineBasicBlock *
MBB = BII->first;
3541 BlockInfo &BI = BII->second;
3547 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3549 *Brackets = *BI.Incoming;
3552 Brackets = std::make_unique<WaitcntBrackets>(
this);
3557 Brackets->~WaitcntBrackets();
3558 new (Brackets.get()) WaitcntBrackets(
this);
3562 if (
ST.hasWaitXcnt())
3564 Modified |= insertWaitcntInBlock(MF, *
MBB, *Brackets);
3567 if (Brackets->hasPendingEvent()) {
3568 BlockInfo *MoveBracketsToSucc =
nullptr;
3570 auto *SuccBII = BlockInfos.
find(Succ);
3571 BlockInfo &SuccBI = SuccBII->second;
3572 if (!SuccBI.Incoming) {
3573 SuccBI.Dirty =
true;
3574 if (SuccBII <= BII) {
3578 if (!MoveBracketsToSucc) {
3579 MoveBracketsToSucc = &SuccBI;
3581 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3585 dbgs() <<
"Try to merge ";
3591 if (SuccBI.Incoming->merge(*Brackets)) {
3592 SuccBI.Dirty =
true;
3593 if (SuccBII <= BII) {
3600 if (MoveBracketsToSucc)
3601 MoveBracketsToSucc->Incoming = std::move(Brackets);
3606 if (
ST.hasScalarStores()) {
3607 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3608 bool HaveScalarStores =
false;
3610 for (MachineBasicBlock &
MBB : MF) {
3611 for (MachineInstr &
MI :
MBB) {
3612 if (!HaveScalarStores &&
TII.isScalarStore(
MI))
3613 HaveScalarStores =
true;
3615 if (
MI.getOpcode() == AMDGPU::S_ENDPGM ||
3616 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3621 if (HaveScalarStores) {
3630 for (MachineBasicBlock *
MBB : EndPgmBlocks) {
3631 bool SeenDCacheWB =
false;
3635 if (
I->getOpcode() == AMDGPU::S_DCACHE_WB)
3636 SeenDCacheWB =
true;
3637 else if (
TII.isScalarStore(*
I))
3638 SeenDCacheWB =
false;
3641 if ((
I->getOpcode() == AMDGPU::S_ENDPGM ||
3642 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3658 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3660 setSchedulingMode(EntryBB,
I,
true);
3662 for (MachineInstr *
MI : CallInsts) {
3663 MachineBasicBlock &
MBB = *
MI->getParent();
3664 setSchedulingMode(
MBB,
MI,
false);
3665 setSchedulingMode(
MBB, std::next(
MI->getIterator()),
true);
3668 for (MachineInstr *
MI : ReturnInsts)
3669 setSchedulingMode(*
MI->getParent(),
MI,
false);
3680 for (
auto [
MI,
_] : EndPgmInsts) {
3682 TII.get(AMDGPU::S_ALLOC_VGPR))
3686 }
else if (!WCG->isOptNone() &&
3687 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3688 (MF.getFrameInfo().hasCalls() ||
3689 ST.getOccupancyWithNumVGPRs(
3690 TRI.getNumUsedPhysRegs(MRI, AMDGPU::VGPR_32RegClass),
3693 for (
auto [
MI, Flag] : EndPgmInsts) {
3695 if (
ST.requiresNopBeforeDeallocVGPRs()) {
3697 TII.get(AMDGPU::S_NOP))
3701 TII.get(AMDGPU::S_SENDMSG))
3714 .
addReg(AMDGPU::SGPR0_SGPR1, RegState::Undef)
3715 .
addReg(AMDGPU::VGPR0, RegState::Undef)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Provides AMDGPU specific target descriptions.
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
This file implements a map that provides insertion order iteration.
Promote Memory to Register
static bool isReg(const MCInst &MI, unsigned OpNo)
MachineInstr unsigned OpIdx
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)
This file builds on the ADT/GraphTraits.h file to build a generic graph post order iterator.
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
Provides some synthesis utilities to produce sequences of values.
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.
BlockT * getLoopPreheader() const
If there is a preheader for this loop, return it.
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
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)
virtual void print(raw_ostream &OS, const Module *M) const
print - Print out the internal state of the pass.
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)
unsigned getMaxWavesPerEU(const MCSubtargetInfo &STI)
@ 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.