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);
1099 dbgs() <<
"recordAsyncMark:\n" << Inst;
1100 for (
const auto &Mark : AsyncMarks) {
1107void WaitcntBrackets::print(raw_ostream &OS)
const {
1111 unsigned SR = getScoreRange(
T);
1114 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM") <<
"_CNT("
1118 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM") <<
"_CNT("
1122 OS <<
" EXP_CNT(" << SR <<
"):";
1125 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS") <<
"_CNT("
1129 OS <<
" SAMPLE_CNT(" << SR <<
"):";
1132 OS <<
" BVH_CNT(" << SR <<
"):";
1135 OS <<
" KM_CNT(" << SR <<
"):";
1138 OS <<
" X_CNT(" << SR <<
"):";
1141 OS <<
" ASYNC_CNT(" << SR <<
"):";
1144 OS <<
" VA_VDST_RD(" << SR <<
"): ";
1147 OS <<
" VA_VDST_WR(" << SR <<
"): ";
1150 OS <<
" VM_VSRC(" << SR <<
"): ";
1153 OS <<
" UNKNOWN(" << SR <<
"):";
1159 unsigned LB = getScoreLB(
T);
1162 sort(SortedVMEMIDs);
1164 for (
auto ID : SortedVMEMIDs) {
1165 unsigned RegScore = VMem.at(
ID).Scores[
T];
1168 unsigned RelScore = RegScore - LB - 1;
1169 if (
ID < REGUNITS_END) {
1170 OS <<
' ' << RelScore <<
':'
1173 assert(
ID >= LDSDMA_BEGIN &&
ID < LDSDMA_END &&
1174 "Unhandled/unexpected ID value!");
1175 OS <<
' ' << RelScore <<
":LDSDMA" <<
ID;
1180 if (isSmemCounter(
T)) {
1182 sort(SortedSMEMIDs);
1183 for (
auto ID : SortedSMEMIDs) {
1184 unsigned RegScore = SGPRs.at(
ID).get(
T);
1187 unsigned RelScore = RegScore - LB - 1;
1188 OS <<
' ' << RelScore <<
':'
1194 OS <<
' ' << SCCScore <<
":scc";
1199 OS <<
"Pending Events: ";
1200 if (hasPendingEvent()) {
1201 OS << getPendingEvents();
1207 OS <<
"Async score: ";
1208 if (AsyncScore.empty())
1214 OS <<
"Async marks: " << AsyncMarks.size() <<
'\n';
1216 for (
const auto &Mark : AsyncMarks) {
1218 unsigned MarkedScore = Mark[
T];
1221 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM")
1222 <<
"_CNT: " << MarkedScore;
1225 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM")
1226 <<
"_CNT: " << MarkedScore;
1229 OS <<
" EXP_CNT: " << MarkedScore;
1232 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS")
1233 <<
"_CNT: " << MarkedScore;
1236 OS <<
" SAMPLE_CNT: " << MarkedScore;
1239 OS <<
" BVH_CNT: " << MarkedScore;
1242 OS <<
" KM_CNT: " << MarkedScore;
1245 OS <<
" X_CNT: " << MarkedScore;
1248 OS <<
" ASYNC_CNT: " << MarkedScore;
1251 OS <<
" UNKNOWN: " << MarkedScore;
1262void WaitcntBrackets::simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
1263 AMDGPU::Waitcnt &UpdateWait)
const {
1271 simplifyXcnt(CheckWait, UpdateWait);
1274 simplifyVmVsrc(CheckWait, UpdateWait);
1279 unsigned &
Count)
const {
1283 if (
Count >= getScoreRange(
T))
1287void WaitcntBrackets::simplifyWaitcnt(AMDGPU::Waitcnt &
Wait,
1289 unsigned Cnt =
Wait.get(
T);
1290 simplifyWaitcnt(
T, Cnt);
1294void WaitcntBrackets::simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
1295 AMDGPU::Waitcnt &UpdateWait)
const {
1305 hasPendingEvent(HWEvents::SMEM_GROUP))
1311 hasPendingEvent(HWEvents::VMEM_GROUP) &&
1318void WaitcntBrackets::simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
1319 AMDGPU::Waitcnt &UpdateWait)
const {
1329 return Acc |
Context->getWaitEvents(
T);
1331 HWEvents PendingVmemEvents = PendingEvents & VmemEvents;
1333 unsigned CheckCount = CheckWait.
get(
T);
1335 (CheckCount == 0 || !counterOutOfOrder(
T)) &&
1336 (PendingVmemEvents & ~
Context->getWaitEvents(
T)) == 0)
1343void WaitcntBrackets::purgeEmptyTrackingData() {
1344 VMem.remove_if([](
const auto &
P) {
return P.second.empty(); });
1345 SGPRs.remove_if([](
const auto &
P) {
return P.second.empty(); });
1349 unsigned ScoreToWait,
1350 AMDGPU::Waitcnt &
Wait)
const {
1351 const unsigned LB = getScoreLB(
T);
1352 const unsigned UB = getScoreUB(
T);
1355 if ((UB >= ScoreToWait) && (ScoreToWait > LB)) {
1357 !
Context->ST.hasFlatLgkmVMemCountInOrder()) {
1362 }
else if (counterOutOfOrder(
T)) {
1370 unsigned NeededWait = std::min(UB - ScoreToWait, getLimit(
T) - 1);
1371 Wait.add(
T, NeededWait);
1376AMDGPU::Waitcnt WaitcntBrackets::determineAsyncWait(
unsigned N) {
1378 dbgs() <<
"Need " <<
N <<
" async marks. Found " << AsyncMarks.size()
1380 for (
const auto &Mark : AsyncMarks) {
1386 if (AsyncMarks.size() == MaxAsyncMarks) {
1391 LLVM_DEBUG(
dbgs() <<
"Possible truncation. Ensuring a non-trivial wait.\n");
1392 N = std::min(
N, (
unsigned)MaxAsyncMarks - 1);
1395 AMDGPU::Waitcnt
Wait;
1396 if (AsyncMarks.size() <=
N) {
1401 size_t MarkIndex = AsyncMarks.size() -
N - 1;
1402 const auto &RequiredMark = AsyncMarks[MarkIndex];
1404 determineWaitForScore(
T, RequiredMark[
T],
Wait);
1410 dbgs() <<
"Removing " << (MarkIndex + 1)
1411 <<
" async marks after determining wait\n";
1413 AsyncMarks.erase(AsyncMarks.begin(), AsyncMarks.begin() + MarkIndex + 1);
1426MCPhysReg WaitcntBrackets::determineVGPR16Dependency(
const MachineInstr &
MI,
1430 unsigned Size =
Context->TRI.getRegSizeInBits(*RC);
1432 if (
Size != 16 || !
Context->ST.hasD16Writes32BitVgpr())
1442 AMDGPU::Waitcnt
Wait;
1443 for (MCRegUnit RU : regunits(OtherHalf))
1444 determineWaitForScore(
T, getVMemScore(toVMEMID(RU),
T),
Wait);
1447 if (!
Wait.hasWait())
1457 HWEvents Events = MIEvents & OtherHalfEvents;
1458 if (Events.
size() > 1)
1465 AMDGPU::Waitcnt &
Wait,
1466 const MachineInstr &
MI)
const {
1467 if (
Reg == AMDGPU::SCC) {
1468 determineWaitForScore(
T, SCCScore,
Wait);
1472 Reg = determineVGPR16Dependency(
MI,
T,
Reg);
1473 for (MCRegUnit RU : regunits(
Reg))
1474 determineWaitForScore(
1475 T, IsVGPR ? getVMemScore(toVMEMID(RU),
T) : getSGPRScore(RU,
T),
1482 AMDGPU::Waitcnt &
Wait)
const {
1483 assert(TID >= LDSDMA_BEGIN && TID < LDSDMA_END);
1484 determineWaitForScore(
T, getVMemScore(TID,
T),
Wait);
1487void WaitcntBrackets::tryClearSCCWriteEvent(MachineInstr *Inst) {
1490 if (PendingSCCWrite &&
1491 PendingSCCWrite->
getOpcode() == AMDGPU::S_BARRIER_SIGNAL_ISFIRST_IMM &&
1493 HWEvents SCC_WRITE_PendingEvent = HWEvents::SCC_WRITE;
1496 SCC_WRITE_PendingEvent) {
1500 PendingEvents -= SCC_WRITE_PendingEvent;
1501 PendingSCCWrite =
nullptr;
1505void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait) {
1514 applyWaitcnt(
T, Cnt);
1519 const unsigned UB = getScoreUB(
T);
1523 if (counterOutOfOrder(
T))
1525 setScoreLB(
T, std::max(getScoreLB(
T), UB -
Count));
1528 PendingEvents -=
Context->getWaitEvents(
T);
1532 hasPendingEvent(HWEvents::SMEM_GROUP)) {
1536 PendingEvents -= HWEvents::SMEM_GROUP;
1542 else if (
Count == 0)
1543 PendingEvents -= HWEvents::VMEM_GROUP;
1547void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait,
1549 unsigned Cnt =
Wait.get(
T);
1550 applyWaitcnt(
T, Cnt);
1557 if ((
T ==
Context->SmemAccessCounter &&
1558 hasPendingEvent(HWEvents::SMEM_ACCESS)) ||
1575 static constexpr HWEvents ExtendedImageEvents =
1576 HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
1577 if (!
Context->ST.hasExtendedWaitCounts() &&
1578 (Events & ExtendedImageEvents).any()) {
1579 Events -= ExtendedImageEvents;
1580 Events |= HWEvents::VMEM_READ_ACCESS;
1586 Events -= HWEvents::GLOBAL_INV_ACCESS;
1590 return Events.
size() > 1;
1593 return hasMixedPendingEvents(
T);
1603char SIInsertWaitcntsLegacy::
ID = 0;
1608 return new SIInsertWaitcntsLegacy();
1613 int OpIdx = AMDGPU::getNamedOperandIdx(
MI.getOpcode(),
OpName);
1618 if (NewEnc == MO.
getImm())
1625bool WaitcntGenerator::promoteSoftWaitCnt(MachineInstr *Waitcnt)
const {
1639bool WaitcntGeneratorPreGFX12::applyPreexistingWaitcnt(
1640 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1642 assert(isNormalMode(MaxCounter));
1645 MachineInstr *WaitcntInstr =
nullptr;
1646 MachineInstr *WaitcntVsCntInstr =
nullptr;
1649 dbgs() <<
"PreGFX12::applyPreexistingWaitcnt at: ";
1651 dbgs() <<
"end of block\n";
1659 if (isNonWaitcntMetaInst(
II)) {
1665 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1669 if (Opcode == AMDGPU::S_WAITCNT) {
1670 unsigned IEnc =
II.getOperand(0).getImm();
1673 ScoreBrackets.simplifyWaitcnt(OldWait);
1677 if (WaitcntInstr || (!
Wait.hasWaitExceptStoreCnt() && TrySimplify)) {
1678 II.eraseFromParent();
1682 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1685 <<
"Before: " <<
Wait <<
'\n';);
1696 II.eraseFromParent();
1697 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1698 unsigned N =
II.getOperand(0).getImm();
1700 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1703 assert(Opcode == AMDGPU::S_WAITCNT_VSCNT);
1704 assert(
II.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
1707 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1713 if (WaitcntVsCntInstr || (!
Wait.hasWaitStoreCnt() && TrySimplify)) {
1714 II.eraseFromParent();
1717 WaitcntVsCntInstr = &
II;
1724 Modified |= promoteSoftWaitCnt(WaitcntInstr);
1733 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
1734 <<
"New Instr at block end: "
1735 << *WaitcntInstr <<
'\n'
1736 :
dbgs() <<
"applied pre-existing waitcnt\n"
1737 <<
"Old Instr: " << *It
1738 <<
"New Instr: " << *WaitcntInstr <<
'\n');
1741 if (WaitcntVsCntInstr) {
1745 Modified |= promoteSoftWaitCnt(WaitcntVsCntInstr);
1751 ?
dbgs() <<
"applied pre-existing waitcnt\n"
1752 <<
"New Instr at block end: " << *WaitcntVsCntInstr
1754 :
dbgs() <<
"applied pre-existing waitcnt\n"
1755 <<
"Old Instr: " << *It
1756 <<
"New Instr: " << *WaitcntVsCntInstr <<
'\n');
1764bool WaitcntGeneratorPreGFX12::createNewWaitcnt(
1766 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
1767 assert(isNormalMode(MaxCounter));
1774 if (
Wait.hasWaitExceptStoreCnt()) {
1776 if (ExpandWaitcntProfiling) {
1780 bool AnyOutOfOrder =
false;
1782 unsigned WaitCnt =
Wait.get(CT);
1783 if (WaitCnt != ~0u && ScoreBrackets.counterOutOfOrder(CT)) {
1784 AnyOutOfOrder =
true;
1789 if (AnyOutOfOrder) {
1797 unsigned WaitCnt =
Wait.get(CT);
1801 unsigned Outstanding =
1802 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
1803 EmitExpandedWaitcnt(Outstanding, WaitCnt, [&](
unsigned Count) {
1815 [[maybe_unused]]
auto SWaitInst =
1820 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1821 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1825 if (
Wait.hasWaitStoreCnt()) {
1831 unsigned Outstanding =
1834 EmitExpandedWaitcnt(
1836 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1837 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1842 [[maybe_unused]]
auto SWaitInst =
1844 .
addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1849 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1850 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1858WaitcntGeneratorPreGFX12::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1859 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt &&
ST.hasVscnt() ? 0 : ~0u);
1863WaitcntGeneratorGFX12Plus::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1864 unsigned ExpertVal = IsExpertMode ? 0 : ~0
u;
1865 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt ? 0 : ~0u, 0, 0, 0,
1867 ~0u , ExpertVal, ExpertVal, ExpertVal);
1874bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt(
1875 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1877 assert(!isNormalMode(MaxCounter));
1880 MachineInstr *CombinedLoadDsCntInstr =
nullptr;
1881 MachineInstr *CombinedStoreDsCntInstr =
nullptr;
1882 MachineInstr *WaitcntDepctrInstr =
nullptr;
1886 dbgs() <<
"GFX12Plus::applyPreexistingWaitcnt at: ";
1888 dbgs() <<
"end of block\n";
1894 AMDGPU::Waitcnt RequiredWait;
1899 if (isNonWaitcntMetaInst(
II)) {
1908 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1912 if (Opcode == AMDGPU::S_WAITCNT)
1915 if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) {
1917 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1922 RequiredWait = RequiredWait.combined(OldWait);
1924 if (CombinedLoadDsCntInstr ==
nullptr) {
1925 CombinedLoadDsCntInstr = &
II;
1927 II.eraseFromParent();
1930 }
else if (Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT) {
1932 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1937 RequiredWait = RequiredWait.combined(OldWait);
1939 if (CombinedStoreDsCntInstr ==
nullptr) {
1940 CombinedStoreDsCntInstr = &
II;
1942 II.eraseFromParent();
1945 }
else if (Opcode == AMDGPU::S_WAITCNT_DEPCTR) {
1947 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1948 AMDGPU::Waitcnt OldWait;
1955 ScoreBrackets.simplifyWaitcnt(OldWait);
1957 if (WaitcntDepctrInstr ==
nullptr) {
1958 WaitcntDepctrInstr = &
II;
1967 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1978 II.eraseFromParent();
1982 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1985 II.eraseFromParent();
1987 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1990 unsigned N =
II.getOperand(0).getImm();
1991 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1994 std::optional<AMDGPU::InstCounterType> CT =
1998 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
2000 Wait.add(CT.value(), OldCnt);
2002 RequiredWait.add(CT.value(), OldCnt);
2004 if (WaitInstrs[CT.value()] ==
nullptr) {
2005 WaitInstrs[CT.value()] = &
II;
2007 II.eraseFromParent();
2013 ScoreBrackets.simplifyWaitcnt(
Wait.combined(RequiredWait),
Wait);
2014 Wait =
Wait.combined(RequiredWait);
2016 if (CombinedLoadDsCntInstr) {
2032 AMDGPU::OpName::simm16, NewEnc);
2033 Modified |= promoteSoftWaitCnt(CombinedLoadDsCntInstr);
2039 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2040 <<
"New Instr at block end: "
2041 << *CombinedLoadDsCntInstr <<
'\n'
2042 :
dbgs() <<
"applied pre-existing waitcnt\n"
2043 <<
"Old Instr: " << *It <<
"New Instr: "
2044 << *CombinedLoadDsCntInstr <<
'\n');
2051 if (CombinedStoreDsCntInstr) {
2056 AMDGPU::OpName::simm16, NewEnc);
2057 Modified |= promoteSoftWaitCnt(CombinedStoreDsCntInstr);
2063 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2064 <<
"New Instr at block end: "
2065 << *CombinedStoreDsCntInstr <<
'\n'
2066 :
dbgs() <<
"applied pre-existing waitcnt\n"
2067 <<
"Old Instr: " << *It <<
"New Instr: "
2068 << *CombinedStoreDsCntInstr <<
'\n');
2098 for (MachineInstr **WI : WaitsToErase) {
2102 (*WI)->eraseFromParent();
2109 if (!WaitInstrs[CT])
2112 unsigned NewCnt =
Wait.get(CT);
2113 if (NewCnt != ~0u) {
2115 AMDGPU::OpName::simm16, NewCnt);
2116 Modified |= promoteSoftWaitCnt(WaitInstrs[CT]);
2118 ScoreBrackets.applyWaitcnt(CT, NewCnt);
2122 ?
dbgs() <<
"applied pre-existing waitcnt\n"
2123 <<
"New Instr at block end: " << *WaitInstrs[CT]
2125 :
dbgs() <<
"applied pre-existing waitcnt\n"
2126 <<
"Old Instr: " << *It
2127 <<
"New Instr: " << *WaitInstrs[CT] <<
'\n');
2134 if (WaitcntDepctrInstr) {
2138 TII.getNamedOperand(*WaitcntDepctrInstr, AMDGPU::OpName::simm16)
2158 AMDGPU::OpName::simm16, Enc);
2160 <<
"New Instr at block end: "
2161 << *WaitcntDepctrInstr <<
'\n'
2162 :
dbgs() <<
"applyPreexistingWaitcnt\n"
2163 <<
"Old Instr: " << *It <<
"New Instr: "
2164 << *WaitcntDepctrInstr <<
'\n');
2175bool WaitcntGeneratorGFX12Plus::createNewWaitcnt(
2177 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
2178 assert(!isNormalMode(MaxCounter));
2185 if (ExpandWaitcntProfiling) {
2192 if (ScoreBrackets.counterOutOfOrder(CT)) {
2199 unsigned Outstanding =
2200 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
2201 EmitExpandedWaitcnt(Outstanding,
Count, [&](
unsigned Val) {
2213 MachineInstr *SWaitInst =
nullptr;
2237 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2238 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2250 [[maybe_unused]]
auto SWaitInst =
2257 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2258 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2261 if (
Wait.hasWaitDepctr()) {
2270 [[maybe_unused]]
auto SWaitInst =
2276 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2277 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2296bool SIInsertWaitcnts::generateWaitcntInstBefore(
2297 MachineInstr &
MI, WaitcntBrackets &ScoreBrackets,
2298 MachineInstr *OldWaitcntInstr, PreheaderFlushFlags FlushFlags) {
2303 AMDGPU::Waitcnt
Wait;
2304 const unsigned Opc =
MI.getOpcode();
2307 case AMDGPU::BUFFER_WBINVL1:
2308 case AMDGPU::BUFFER_WBINVL1_SC:
2309 case AMDGPU::BUFFER_WBINVL1_VOL:
2310 case AMDGPU::BUFFER_GL0_INV:
2311 case AMDGPU::BUFFER_GL1_INV: {
2319 case AMDGPU::SI_RETURN_TO_EPILOG:
2320 case AMDGPU::SI_RETURN:
2321 case AMDGPU::SI_WHOLE_WAVE_FUNC_RETURN:
2322 case AMDGPU::S_SETPC_B64_return: {
2327 AMDGPU::Waitcnt AllZeroWait =
2328 WCG->getAllZeroWaitcnt(
false);
2333 if (
ST.hasExtendedWaitCounts() &&
2334 !ScoreBrackets.hasPendingEvent(HWEvents::VMEM_READ_ACCESS))
2339 case AMDGPU::S_ENDPGM:
2340 case AMDGPU::S_ENDPGM_SAVED: {
2351 !ScoreBrackets.hasPendingEvent(HWEvents::SCRATCH_WRITE_ACCESS);
2354 case AMDGPU::S_SENDMSG:
2355 case AMDGPU::S_SENDMSGHALT: {
2356 if (
ST.hasLegacyGeometry() &&
2371 if (
MI.modifiesRegister(AMDGPU::EXEC, &
TRI)) {
2374 if (ScoreBrackets.hasPendingEvent(HWEvents::EXP_GPR_LOCK) ||
2375 ScoreBrackets.hasPendingEvent(HWEvents::EXP_PARAM_ACCESS) ||
2376 ScoreBrackets.hasPendingEvent(HWEvents::EXP_POS_ACCESS) ||
2377 ScoreBrackets.hasPendingEvent(HWEvents::GDS_GPR_LOCK)) {
2384 if (
TII.isAlwaysGDS(
Opc) && ScoreBrackets.hasPendingGDS())
2392 Wait = AMDGPU::Waitcnt();
2394 const MachineOperand &CallAddrOp =
TII.getCalleeOperand(
MI);
2395 if (CallAddrOp.
isReg()) {
2396 ScoreBrackets.determineWaitForPhysReg(
2399 if (
const auto *RtnAddrOp =
2400 TII.getNamedOperand(
MI, AMDGPU::OpName::dst)) {
2401 ScoreBrackets.determineWaitForPhysReg(
2402 SmemAccessCounter, RtnAddrOp->getReg().asMCReg(),
Wait,
MI);
2405 }
else if (
Opc == AMDGPU::S_BARRIER_WAIT) {
2406 ScoreBrackets.tryClearSCCWriteEvent(&
MI);
2422 for (
const MachineMemOperand *Memop :
MI.memoperands()) {
2423 const Value *Ptr = Memop->getValue();
2424 if (Memop->isStore()) {
2425 if (
auto It = SLoadAddresses.
find(Ptr); It != SLoadAddresses.
end()) {
2426 Wait.add(SmemAccessCounter, 0);
2428 SLoadAddresses.
erase(It);
2431 unsigned AS = Memop->getAddrSpace();
2435 if (
TII.mayWriteLDSThroughDMA(
MI))
2439 unsigned TID = LDSDMA_BEGIN;
2440 if (Ptr && Memop->getAAInfo()) {
2441 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2442 for (
unsigned I = 0,
E = LDSDMAStores.size();
I !=
E; ++
I) {
2443 if (
MI.mayAlias(AA, *LDSDMAStores[
I],
true)) {
2444 if ((
I + 1) >= NUM_LDSDMA) {
2459 if (Memop->isStore()) {
2465 for (
const MachineOperand &
Op :
MI.operands()) {
2470 if (
Op.isTied() &&
Op.isUse() &&
TII.doesNotReadTiedSource(
MI))
2475 const bool IsVGPR =
TRI.isVectorRegister(MRI,
Op.getReg());
2482 if (
Op.isImplicit() &&
MI.mayLoadOrStore())
2500 if (
Op.isUse() || !updateVMCntOnly(
MI) ||
2501 ScoreBrackets.hasDifferentVGPRPendingEvents(
2503 ScoreBrackets.hasPointSamplePendingVmemTypes(
MI,
Reg) ||
2504 !
ST.hasVmemWriteVgprInOrder()) {
2511 ScoreBrackets.clearVGPRPendingEvents(
Reg);
2515 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2520 }
else if (
Op.getReg() == AMDGPU::SCC) {
2523 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter,
Reg,
Wait,
2527 if (
ST.hasWaitXcnt() &&
Op.isDef())
2546 if (
Opc == AMDGPU::S_BARRIER && !
ST.hasAutoWaitcntBeforeBarrier() &&
2547 !
ST.hasBackOffBarrier()) {
2548 Wait =
Wait.combined(WCG->getAllZeroWaitcnt(
true));
2555 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2560 ScoreBrackets.simplifyWaitcnt(
Wait);
2566 if (
TII.isVALU(
MI,
false)) {
2582 Wait = WCG->getAllZeroWaitcnt(
false);
2586 if (!ForceEmitWaitcnt[
T])
2591 if (FlushFlags.FlushVmCnt) {
2597 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
2603 return generateWaitcnt(
Wait,
MI.getIterator(), *
MI.getParent(), ScoreBrackets,
2607bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt
Wait,
2609 MachineBasicBlock &
Block,
2610 WaitcntBrackets &ScoreBrackets,
2611 MachineInstr *OldWaitcntInstr) {
2614 if (OldWaitcntInstr)
2618 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr,
Wait, It);
2623 MachineOperand *WaitExp =
TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2633 <<
"Update Instr: " << *It);
2636 if (WCG->createNewWaitcnt(
Block, It,
Wait, ScoreBrackets))
2641 ScoreBrackets.applyWaitcnt(
Wait);
2646bool SIInsertWaitcnts::isVmemAccess(
const MachineInstr &
MI)
const {
2647 return (
TII.isFLAT(
MI) &&
TII.mayAccessVMEMThroughFlat(
MI)) ||
2654 MachineBasicBlock *
Block)
const {
2655 auto BlockEnd =
Block->getParent()->end();
2656 auto BlockIter =
Block->getIterator();
2660 if (++BlockIter != BlockEnd) {
2661 It = BlockIter->instr_begin();
2668 if (!It->isMetaInstruction())
2676 return It->getOpcode() == AMDGPU::S_ENDPGM;
2680bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2681 MachineBasicBlock &
Block,
2682 WaitcntBrackets &ScoreBrackets) {
2683 AMDGPU::Waitcnt
Wait;
2684 bool NeedsEndPGMCheck =
false;
2692 NeedsEndPGMCheck =
true;
2695 ScoreBrackets.simplifyWaitcnt(
Wait);
2698 bool Result = generateWaitcnt(
Wait, SuccessorIt,
Block, ScoreBrackets,
2701 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &
Block)) {
2709void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2710 WaitcntBrackets *ScoreBrackets) {
2714 ScoreBrackets->updateByEvent(
E, Inst);
2716 if (
TII.isDS(Inst) &&
TII.usesLGKM_CNT(Inst)) {
2718 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2719 ScoreBrackets->setPendingGDS();
2721 }
else if (
TII.isFLAT(Inst)) {
2723 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2730 ScoreBrackets->setPendingFlat();
2732 }
else if (Inst.
isCall()) {
2735 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(
false));
2736 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2737 }
else if (
TII.isVINTERP(Inst)) {
2738 int64_t
Imm =
TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2748bool WaitcntBrackets::mergeScore(
const MergeInfo &M,
unsigned &Score,
2749 unsigned OtherScore) {
2750 unsigned MyShifted = Score <=
M.OldLB ? 0 : Score +
M.MyShift;
2751 unsigned OtherShifted =
2752 OtherScore <=
M.OtherLB ? 0 : OtherScore +
M.OtherShift;
2753 Score = std::max(MyShifted, OtherShifted);
2754 return OtherShifted > MyShifted;
2759 bool StrictDom =
false;
2763 if (AsyncMarks.empty() && OtherMarks.
empty()) {
2770 auto MaxSize = (unsigned)std::max(AsyncMarks.size(), OtherMarks.
size());
2771 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2774 if (AsyncMarks.size() > MaxSize)
2775 AsyncMarks.erase(AsyncMarks.begin(),
2776 AsyncMarks.begin() + (AsyncMarks.size() - MaxSize));
2782 constexpr CounterValueArray ZeroMark{};
2783 AsyncMarks.insert(AsyncMarks.begin(), MaxSize - AsyncMarks.size(), ZeroMark);
2786 dbgs() <<
"Before merge:\n";
2787 for (
const auto &Mark : AsyncMarks) {
2791 dbgs() <<
"Other marks:\n";
2792 for (
const auto &Mark : OtherMarks) {
2801 unsigned OtherSize = OtherMarks.size();
2802 unsigned OurSize = AsyncMarks.size();
2803 unsigned MergeCount = std::min(OtherSize, OurSize);
2807 if (MergeCount == 0)
2811 StrictDom |= mergeScore(MergeInfos[
T], AsyncMarks[OurSize - Idx][
T],
2812 OtherMarks[OtherSize - Idx][
T]);
2817 dbgs() <<
"After merge:\n";
2818 for (
const auto &Mark : AsyncMarks) {
2832bool WaitcntBrackets::merge(
const WaitcntBrackets &
Other) {
2833 bool StrictDom =
false;
2837 for (
auto K :
Other.VMem.keys())
2838 VMem.try_emplace(K);
2839 for (
auto K :
Other.SGPRs.keys())
2840 SGPRs.try_emplace(K);
2848 const HWEvents OldEvents = PendingEvents & EventsForT;
2849 const HWEvents OtherEvents =
Other.PendingEvents & EventsForT;
2850 if (!OldEvents.
contains(OtherEvents))
2852 PendingEvents |= OtherEvents;
2855 const unsigned MyPending = ScoreUBs[
T] - ScoreLBs[
T];
2856 const unsigned OtherPending =
Other.ScoreUBs[
T] -
Other.ScoreLBs[
T];
2857 const unsigned NewUB = ScoreLBs[
T] + std::max(MyPending, OtherPending);
2858 if (NewUB < ScoreLBs[
T])
2861 MergeInfo &
M = MergeInfos[
T];
2862 M.OldLB = ScoreLBs[
T];
2863 M.OtherLB =
Other.ScoreLBs[
T];
2864 M.MyShift = NewUB - ScoreUBs[
T];
2865 M.OtherShift = NewUB -
Other.ScoreUBs[
T];
2867 ScoreUBs[
T] = NewUB;
2870 StrictDom |= mergeScore(M, LastFlatLoadCnt,
Other.LastFlatLoadCnt);
2873 StrictDom |= mergeScore(M, LastFlatDsCnt,
Other.LastFlatDsCnt);
2874 StrictDom |= mergeScore(M, LastGDS,
Other.LastGDS);
2878 StrictDom |= mergeScore(M, SCCScore,
Other.SCCScore);
2879 if (
Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2880 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2881 PendingSCCWrite =
Other.PendingSCCWrite;
2882 }
else if (PendingSCCWrite !=
Other.PendingSCCWrite) {
2883 PendingSCCWrite =
nullptr;
2888 for (
auto &[RegID, Info] : VMem)
2889 StrictDom |= mergeScore(M,
Info.Scores[
T],
Other.getVMemScore(RegID,
T));
2891 if (isSmemCounter(
T)) {
2892 for (
auto &[RegID, Info] : SGPRs) {
2893 auto It =
Other.SGPRs.find(RegID);
2894 unsigned OtherScore = (It !=
Other.SGPRs.end()) ? It->second.get(
T) : 0;
2895 StrictDom |= mergeScore(M,
Info.get(
T), OtherScore);
2900 for (
auto &[TID, Info] : VMem) {
2901 if (
auto It =
Other.VMem.find(TID); It !=
Other.VMem.end()) {
2903 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
2904 StrictDom |= NewVGPRContext !=
Info.VGPRPendingEvents;
2905 Info.VGPRPendingEvents = NewVGPRContext;
2909 StrictDom |= mergeAsyncMarks(MergeInfos,
Other.AsyncMarks);
2911 StrictDom |= mergeScore(MergeInfos[
T], AsyncScore[
T],
Other.AsyncScore[
T]);
2913 purgeEmptyTrackingData();
2919 return Opcode == AMDGPU::S_WAITCNT ||
2922 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
2923 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
2924 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
2925 Opcode == AMDGPU::WAIT_ASYNCMARK ||
2929void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &
MBB,
2931 bool ExpertMode)
const {
2935 .
addImm(ExpertMode ? 2 : 0)
2953class VCCZWorkaround {
2954 const WaitcntBrackets &ScoreBrackets;
2955 const GCNSubtarget &
ST;
2956 const SIInstrInfo &
TII;
2957 const SIRegisterInfo &
TRI;
2958 bool VCCZCorruptionBug =
false;
2959 bool VCCZNotUpdatedByPartialWrites =
false;
2962 bool MustRecomputeVCCZ =
true;
2965 VCCZWorkaround(
const WaitcntBrackets &ScoreBrackets,
const GCNSubtarget &ST,
2966 const SIInstrInfo &
TII,
const SIRegisterInfo &
TRI)
2968 VCCZCorruptionBug =
ST.hasReadVCCZBug();
2969 VCCZNotUpdatedByPartialWrites = !
ST.partialVCCWritesUpdateVCCZ();
2976 bool tryRecomputeVCCZ(MachineInstr &
MI) {
2978 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
2988 MustRecomputeVCCZ |= VCCZCorruptionBug &&
TII.isSMRD(
MI);
2994 std::optional<bool> PartiallyWritesToVCCOpt;
2995 auto PartiallyWritesToVCC = [](MachineInstr &
MI) {
2996 return MI.definesRegister(AMDGPU::VCC_LO,
nullptr) ||
2997 MI.definesRegister(AMDGPU::VCC_HI,
nullptr);
2999 if (VCCZNotUpdatedByPartialWrites) {
3000 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3003 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
3009 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
3010 !VCCZCorruptionBug) {
3012 if (!PartiallyWritesToVCCOpt)
3013 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3014 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
3015 MI.definesRegister(AMDGPU::VCC,
nullptr);
3018 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
3019 *PartiallyWritesToVCCOpt);
3021 MustRecomputeVCCZ =
false;
3031 TII.get(
ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
3034 MustRecomputeVCCZ =
false;
3044bool SIInsertWaitcnts::insertWaitcntInBlock(MachineFunction &MF,
3045 MachineBasicBlock &
Block,
3046 WaitcntBrackets &ScoreBrackets) {
3050 dbgs() <<
"*** Begin Block: ";
3052 ScoreBrackets.dump();
3054 VCCZWorkaround VCCZW(ScoreBrackets, ST,
TII,
TRI);
3057 MachineInstr *OldWaitcntInstr =
nullptr;
3060 ScoreBrackets.verify();
3063 Iter !=
E; ++Iter) {
3064 MachineInstr &Inst = *Iter;
3065 if (isNonWaitcntMetaInst(Inst))
3070 (IsExpertMode && Inst.
getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3071 if (!OldWaitcntInstr)
3072 OldWaitcntInstr = &Inst;
3076 PreheaderFlushFlags FlushFlags;
3077 if (
Block.getFirstTerminator() == Inst)
3078 FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3081 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3083 OldWaitcntInstr =
nullptr;
3085 if (Inst.
getOpcode() == AMDGPU::ASYNCMARK) {
3089 ScoreBrackets.recordAsyncMark(Inst);
3093 if (
TII.isSMRD(Inst)) {
3094 for (
const MachineMemOperand *Memop : Inst.
memoperands()) {
3097 if (!Memop->isInvariant()) {
3098 const Value *Ptr = Memop->getValue();
3104 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3108 Modified |= insertForcedWaitAfter(Inst,
Block, ScoreBrackets);
3112 ScoreBrackets.dump();
3117 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3119 ScoreBrackets.verify();
3124 AMDGPU::Waitcnt
Wait;
3125 if (
Block.getFirstTerminator() ==
Block.end()) {
3126 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3127 if (FlushFlags.FlushVmCnt) {
3135 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
3144 dbgs() <<
"*** End Block: ";
3146 ScoreBrackets.dump();
3152bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &
Block) {
3153 if (
Block.size() <= 1)
3161 MachineInstr *LastAtomicWithSoftXcnt =
nullptr;
3165 bool IsLDS =
TII.isDS(
MI) ||
3166 (
TII.isFLAT(
MI) &&
TII.mayAccessLDSThroughFlat(
MI, TgSplit));
3167 if (!IsLDS && (
MI.mayLoad() ^
MI.mayStore()))
3168 LastAtomicWithSoftXcnt =
nullptr;
3172 MachineInstr &PrevMI = *
MI.getPrevNode();
3174 if (PrevMI.
getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3177 if (LastAtomicWithSoftXcnt) {
3181 LastAtomicWithSoftXcnt = &
MI;
3189SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &
MBB,
3190 const WaitcntBrackets &ScoreBrackets) {
3191 auto [Iterator, IsInserted] =
3194 return Iterator->second;
3198 return PreheaderFlushFlags();
3202 return PreheaderFlushFlags();
3205 Iterator->second = getPreheaderFlushFlags(Loop, ScoreBrackets);
3206 return Iterator->second;
3209 return PreheaderFlushFlags();
3212bool SIInsertWaitcnts::isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const {
3214 return TII.mayAccessVMEMThroughFlat(
MI);
3218bool SIInsertWaitcnts::isDSRead(
const MachineInstr &
MI)
const {
3224bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const {
3253SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *
ML,
3254 const WaitcntBrackets &Brackets) {
3255 PreheaderFlushFlags
Flags;
3256 bool HasVMemLoad =
false;
3257 bool HasVMemStore =
false;
3258 bool UsesVgprVMEMLoadedOutside =
false;
3259 bool UsesVgprDSReadOutside =
false;
3260 bool VMemInvalidated =
false;
3264 bool TrackSimpleDSOpt =
ST.hasExtendedWaitCounts();
3265 DenseSet<MCRegUnit> VgprUse;
3266 DenseSet<MCRegUnit> VgprDefVMEM;
3267 DenseSet<MCRegUnit> VgprDefDS;
3273 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3274 unsigned DSReadPosition = 0;
3275 bool IsSingleBlock =
ML->getNumBlocks() == 1;
3276 bool TrackDSFlushPoint =
ST.hasExtendedWaitCounts() && IsSingleBlock;
3277 unsigned LastDSFlushPosition = 0;
3279 for (MachineBasicBlock *
MBB :
ML->blocks()) {
3280 for (MachineInstr &
MI : *
MBB) {
3281 if (isVMEMOrFlatVMEM(
MI)) {
3282 HasVMemLoad |=
MI.mayLoad();
3283 HasVMemStore |=
MI.mayStore();
3287 if (mayStoreIncrementingDSCNT(
MI)) {
3290 if (VMemInvalidated)
3292 TrackSimpleDSOpt =
false;
3293 TrackDSFlushPoint =
false;
3295 bool IsDSRead = isDSRead(
MI);
3300 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3301 if (!TrackDSFlushPoint)
3303 if (
auto It = LastDSReadPositionMap.
find(RU);
3304 It != LastDSReadPositionMap.
end()) {
3308 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3312 for (
const MachineOperand &
Op :
MI.all_uses()) {
3313 if (
Op.isDebug() || !
TRI.isVectorRegister(MRI,
Op.getReg()))
3316 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3320 VMemInvalidated =
true;
3324 TrackSimpleDSOpt =
false;
3327 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3331 updateDSReadFlushTracking(RU);
3336 VMEMID
ID = toVMEMID(RU);
3340 UsesVgprVMEMLoadedOutside =
true;
3345 UsesVgprDSReadOutside =
true;
3350 if (isVMEMOrFlatVMEM(
MI) &&
MI.mayLoad()) {
3351 for (
const MachineOperand &
Op :
MI.all_defs()) {
3352 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3356 VMemInvalidated =
true;
3361 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3372 if (IsDSRead || TrackDSFlushPoint) {
3373 for (
const MachineOperand &
Op :
MI.all_defs()) {
3374 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
3376 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3379 updateDSReadFlushTracking(RU);
3382 if (TrackDSFlushPoint)
3383 LastDSReadPositionMap[RU] = DSReadPosition;
3392 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3393 ((!
ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3394 (HasVMemLoad &&
ST.hasVmemWriteVgprInOrder())))
3395 Flags.FlushVmCnt =
true;
3401 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3404 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3405 bool DSFlushPointPrefetch =
3406 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3408 if (SimpleDSOpt || DSFlushPointPrefetch)
3409 Flags.FlushDsCnt =
true;
3414bool SIInsertWaitcntsLegacy::runOnMachineFunction(MachineFunction &MF) {
3415 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3417 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3419 if (
auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3420 AA = &AAR->getAAResults();
3422 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3434 if (!SIInsertWaitcnts(MLI, PDT,
AA, MF).
run())
3439 .preserve<AAManager>();
3442bool SIInsertWaitcnts::run() {
3450 if (ST.hasExtendedWaitCounts()) {
3451 IsExpertMode = ST.hasExpertSchedulingMode() &&
3460 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3465 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3469 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3473 MachineBasicBlock &EntryBB = MF.
front();
3484 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3487 if (
ST.hasExtendedWaitCounts()) {
3496 if (!
ST.hasImageInsts() &&
3502 TII.get(instrsForExtendedCounterTypes[CT]))
3515 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(
this);
3516 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3517 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3524 for (
auto *
MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3527 std::unique_ptr<WaitcntBrackets> Brackets;
3532 for (
auto BII = BlockInfos.
begin(), BIE = BlockInfos.
end(); BII != BIE;
3534 MachineBasicBlock *
MBB = BII->first;
3535 BlockInfo &BI = BII->second;
3541 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3543 *Brackets = *BI.Incoming;
3546 Brackets = std::make_unique<WaitcntBrackets>(
this);
3551 Brackets->~WaitcntBrackets();
3552 new (Brackets.get()) WaitcntBrackets(
this);
3556 if (
ST.hasWaitXcnt())
3558 Modified |= insertWaitcntInBlock(MF, *
MBB, *Brackets);
3561 if (Brackets->hasPendingEvent()) {
3562 BlockInfo *MoveBracketsToSucc =
nullptr;
3564 auto *SuccBII = BlockInfos.
find(Succ);
3565 BlockInfo &SuccBI = SuccBII->second;
3566 if (!SuccBI.Incoming) {
3567 SuccBI.Dirty =
true;
3568 if (SuccBII <= BII) {
3572 if (!MoveBracketsToSucc) {
3573 MoveBracketsToSucc = &SuccBI;
3575 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3579 dbgs() <<
"Try to merge ";
3585 if (SuccBI.Incoming->merge(*Brackets)) {
3586 SuccBI.Dirty =
true;
3587 if (SuccBII <= BII) {
3594 if (MoveBracketsToSucc)
3595 MoveBracketsToSucc->Incoming = std::move(Brackets);
3600 if (
ST.hasScalarStores()) {
3601 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3602 bool HaveScalarStores =
false;
3604 for (MachineBasicBlock &
MBB : MF) {
3605 for (MachineInstr &
MI :
MBB) {
3606 if (!HaveScalarStores &&
TII.isScalarStore(
MI))
3607 HaveScalarStores =
true;
3609 if (
MI.getOpcode() == AMDGPU::S_ENDPGM ||
3610 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3615 if (HaveScalarStores) {
3624 for (MachineBasicBlock *
MBB : EndPgmBlocks) {
3625 bool SeenDCacheWB =
false;
3629 if (
I->getOpcode() == AMDGPU::S_DCACHE_WB)
3630 SeenDCacheWB =
true;
3631 else if (
TII.isScalarStore(*
I))
3632 SeenDCacheWB =
false;
3635 if ((
I->getOpcode() == AMDGPU::S_ENDPGM ||
3636 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3652 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3654 setSchedulingMode(EntryBB,
I,
true);
3656 for (MachineInstr *
MI : CallInsts) {
3657 MachineBasicBlock &
MBB = *
MI->getParent();
3658 setSchedulingMode(
MBB,
MI,
false);
3659 setSchedulingMode(
MBB, std::next(
MI->getIterator()),
true);
3662 for (MachineInstr *
MI : ReturnInsts)
3663 setSchedulingMode(*
MI->getParent(),
MI,
false);
3674 for (
auto [
MI,
_] : EndPgmInsts) {
3676 TII.get(AMDGPU::S_ALLOC_VGPR))
3680 }
else if (!WCG->isOptNone() &&
3681 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3682 (MF.getFrameInfo().hasCalls() ||
3683 ST.getOccupancyWithNumVGPRs(
3684 TRI.getNumUsedPhysRegs(MRI, AMDGPU::VGPR_32RegClass),
3687 for (
auto [
MI, Flag] : EndPgmInsts) {
3689 if (
ST.requiresNopBeforeDeallocVGPRs()) {
3691 TII.get(AMDGPU::S_NOP))
3695 TII.get(AMDGPU::S_SENDMSG))
3708 .
addReg(AMDGPU::SGPR0_SGPR1, RegState::Undef)
3709 .
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)
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
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.