45#define DEBUG_TYPE "si-insert-waitcnts"
49 cl::desc(
"Force all waitcnt instrs to be emitted as "
50 "s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)"),
54 "amdgpu-waitcnt-load-forcezero",
55 cl::desc(
"Force all waitcnt load counters to wait until 0"),
59 "amdgpu-expert-scheduling-mode",
60 cl::desc(
"Enable expert scheduling mode 2 for all functions (GFX12+ only)"),
65template <
typename EmitWaitcntFn>
66static void EmitExpandedWaitcnt(
unsigned Outstanding,
unsigned Target,
67 EmitWaitcntFn &&EmitWaitcnt) {
69 for (
unsigned I = Outstanding - 1;
I >
Target &&
I != ~0u; --
I)
89 TRACKINGID_RANGE_LEN = (1 << 16),
94 REGUNITS_END = REGUNITS_BEGIN + TRACKINGID_RANGE_LEN,
99 NUM_LDSDMA = TRACKINGID_RANGE_LEN,
100 LDSDMA_BEGIN = REGUNITS_END,
101 LDSDMA_END = LDSDMA_BEGIN + NUM_LDSDMA,
105static constexpr VMEMID toVMEMID(MCRegUnit RU) {
106 return static_cast<unsigned>(RU);
118 AMDGPU::S_WAIT_LOADCNT, AMDGPU::S_WAIT_DSCNT,
119 AMDGPU::S_WAIT_EXPCNT, AMDGPU::S_WAIT_STORECNT,
120 AMDGPU::S_WAIT_SAMPLECNT, AMDGPU::S_WAIT_BVHCNT,
121 AMDGPU::S_WAIT_KMCNT, AMDGPU::S_WAIT_XCNT,
122 AMDGPU::S_WAIT_ASYNCCNT, AMDGPU::S_WAIT_TENSORCNT};
127 switch (
MI.getOpcode()) {
128 case AMDGPU::ASYNCMARK:
129 case AMDGPU::WAIT_ASYNCMARK:
132 return MI.isMetaInstruction();
147class WaitcntBrackets;
155class WaitcntGenerator {
157 const GCNSubtarget &ST;
158 const SIInstrInfo &TII;
159 AMDGPU::IsaVersion IV;
162 bool ExpandWaitcntProfiling =
false;
163 const AMDGPU::HardwareLimits &Limits;
166 WaitcntGenerator() =
delete;
167 WaitcntGenerator(
const WaitcntGenerator &) =
delete;
170 const AMDGPU::HardwareLimits &Limits)
171 : ST(MF.getSubtarget<GCNSubtarget>()), TII(*ST.getInstrInfo()),
175 ExpandWaitcntProfiling(
176 MF.
getFunction().hasFnAttribute(
"amdgpu-expand-waitcnt-profiling")),
181 bool isOptNone()
const {
return OptNone; }
197 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
198 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
202 bool promoteSoftWaitCnt(MachineInstr *Waitcnt)
const;
207 virtual bool createNewWaitcnt(MachineBasicBlock &
Block,
209 AMDGPU::Waitcnt
Wait,
210 const WaitcntBrackets &ScoreBrackets) = 0;
217 assert(
E.size() == 1 &&
"Cannot handle a mask of events!");
219 if (getWaitEvents(
T) &
E)
230 virtual AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const = 0;
232 virtual ~WaitcntGenerator() =
default;
235class WaitcntGeneratorPreGFX12 final :
public WaitcntGenerator {
238 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_SAMPLER_READ_ACCESS |
239 HWEvents::VMEM_BVH_READ_ACCESS,
240 HWEvents::SMEM_ACCESS | HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS |
241 HWEvents::SQ_MESSAGE,
242 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
243 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
244 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
245 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
257 using WaitcntGenerator::WaitcntGenerator;
259 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
260 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
263 bool createNewWaitcnt(MachineBasicBlock &
Block,
265 AMDGPU::Waitcnt
Wait,
266 const WaitcntBrackets &ScoreBrackets)
override;
269 HWEvents EVs = WaitEventMaskForInstPreGFX12[
T];
275 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
278class WaitcntGeneratorGFX12Plus final :
public WaitcntGenerator {
283 HWEvents::VMEM_READ_ACCESS | HWEvents::GLOBAL_INV_ACCESS,
284 HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS,
285 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
286 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
287 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
289 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
290 HWEvents::VMEM_SAMPLER_READ_ACCESS,
291 HWEvents::VMEM_BVH_READ_ACCESS,
293 HWEvents::SMEM_ACCESS | HWEvents::SQ_MESSAGE | HWEvents::SCC_WRITE,
294 HWEvents::VMEM_GROUP | HWEvents::SMEM_GROUP,
295 HWEvents::ASYNC_ACCESS,
296 HWEvents::TENSOR_ACCESS,
297 HWEvents::VGPR_CSMACC_READ | HWEvents::VGPR_DPMACC_READ |
298 HWEvents::VGPR_TRANS_READ | HWEvents::VGPR_XDL_READ,
299 HWEvents::VGPR_CSMACC_WRITE | HWEvents::VGPR_DPMACC_WRITE |
300 HWEvents::VGPR_TRANS_WRITE | HWEvents::VGPR_XDL_WRITE,
301 HWEvents::VGPR_LDS_READ | HWEvents::VGPR_FLAT_READ |
302 HWEvents::VGPR_VMEM_READ};
305 WaitcntGeneratorGFX12Plus() =
delete;
308 const AMDGPU::HardwareLimits &Limits,
310 : WaitcntGenerator(MF, MaxCounter, Limits), IsExpertMode(IsExpertMode) {}
313 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
314 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
317 bool createNewWaitcnt(MachineBasicBlock &
Block,
319 AMDGPU::Waitcnt
Wait,
320 const WaitcntBrackets &ScoreBrackets)
override;
323 return WaitEventMaskForInstGFX12Plus[
T];
326 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
330struct PreheaderFlushFlags {
331 bool FlushVmCnt =
false;
332 bool FlushDsCnt =
false;
335class SIInsertWaitcnts {
336 DenseMap<const Value *, MachineBasicBlock *> SLoadAddresses;
337 DenseMap<MachineBasicBlock *, PreheaderFlushFlags> PreheadersToFlush;
338 MachineLoopInfo &MLI;
339 MachinePostDominatorTree &PDT;
344 std::unique_ptr<WaitcntBrackets> Incoming;
346 BlockInfo() =
default;
347 BlockInfo(BlockInfo &&) =
default;
348 BlockInfo &operator=(BlockInfo &&) =
default;
352 MapVector<MachineBasicBlock *, BlockInfo> BlockInfos;
356 std::unique_ptr<WaitcntGenerator> WCG;
359 DenseSet<MachineInstr *> CallInsts;
360 DenseSet<MachineInstr *> ReturnInsts;
365 DenseMap<MachineInstr *, bool> EndPgmInsts;
367 AMDGPU::HardwareLimits Limits;
370 const GCNSubtarget &ST;
371 const SIInstrInfo &TII;
372 const SIRegisterInfo &TRI;
373 const MachineRegisterInfo &MRI;
376 bool IsExpertMode =
false;
379 SIInsertWaitcnts(MachineLoopInfo &MLI, MachinePostDominatorTree &PDT,
381 : MLI(MLI), PDT(PDT), AA(AA), MF(MF), ST(MF.getSubtarget<GCNSubtarget>()),
382 TII(*ST.getInstrInfo()), TRI(TII.getRegisterInfo()),
383 MRI(MF.getRegInfo()),
384 TgSplit(ST.hasTgSplitSupport() &&
387 const AMDGPU::HardwareLimits &getLimits()
const {
return Limits; }
389 PreheaderFlushFlags getPreheaderFlushFlags(MachineLoop *
ML,
390 const WaitcntBrackets &Brackets);
391 PreheaderFlushFlags isPreheaderToFlush(MachineBasicBlock &
MBB,
392 const WaitcntBrackets &ScoreBrackets);
393 bool isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const;
394 bool isDSRead(
const MachineInstr &
MI)
const;
395 bool mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const;
398 bool isAsync(
const MachineInstr &
MI)
const {
403 const MachineOperand *
Async =
404 TII.getNamedOperand(
MI, AMDGPU::OpName::IsAsync);
408 bool isNonAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
412 bool isAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
416 bool shouldUpdateAsyncMark(
const MachineInstr &
MI,
420 if (!isAsyncLdsDmaWrite(
MI))
427 bool isVmemAccess(
const MachineInstr &
MI)
const;
428 bool generateWaitcntInstBefore(MachineInstr &
MI,
429 WaitcntBrackets &ScoreBrackets,
430 MachineInstr *OldWaitcntInstr,
431 PreheaderFlushFlags FlushFlags);
432 bool generateWaitcnt(AMDGPU::Waitcnt
Wait,
434 MachineBasicBlock &
Block, WaitcntBrackets &ScoreBrackets,
435 MachineInstr *OldWaitcntInstr);
436 void updateEventWaitcntAfter(MachineInstr &Inst,
437 WaitcntBrackets *ScoreBrackets);
439 MachineBasicBlock *
Block)
const;
440 bool insertForcedWaitAfter(MachineInstr &Inst, MachineBasicBlock &
Block,
441 WaitcntBrackets &ScoreBrackets);
443 WaitcntBrackets &ScoreBrackets);
446 bool removeRedundantSoftXcnts(MachineBasicBlock &
Block);
448 bool ExpertMode)
const;
450 return WCG->getWaitEvents(
T);
453 return WCG->getCounterFromEvent(
E);
465class WaitcntBrackets {
467 WaitcntBrackets(
const SIInsertWaitcnts *Context) : Context(Context) {
468 assert(Context->TRI.getNumRegUnits() < REGUNITS_END);
473 unsigned NumUnusedVmem = 0, NumUnusedSGPRs = 0;
474 for (
auto &[ID, Val] : VMem) {
478 for (
auto &[ID, Val] : SGPRs) {
483 if (NumUnusedVmem || NumUnusedSGPRs) {
484 errs() <<
"WaitcntBracket had unused entries at destruction time: "
485 << NumUnusedVmem <<
" VMem and " << NumUnusedSGPRs
486 <<
" SGPR unused entries\n";
497 return ScoreUBs[
T] - ScoreLBs[
T];
501 return getVMemScore(ID,
T) > getScoreLB(
T);
519 return getScoreUB(
T) - getScoreLB(
T);
523 auto It = SGPRs.find(RU);
524 return It != SGPRs.end() ? It->second.get(
T) : 0;
528 auto It = VMem.find(TID);
529 return It != VMem.end() ? It->second.Scores[
T] : 0;
544 void simplifyWaitcnt(AMDGPU::Waitcnt &
Wait)
const {
547 void simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
548 AMDGPU::Waitcnt &UpdateWait)
const;
551 void simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
552 AMDGPU::Waitcnt &UpdateWait)
const;
553 void simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
554 AMDGPU::Waitcnt &UpdateWait)
const;
557 AMDGPU::Waitcnt &
Wait,
558 const MachineInstr &
MI)
const;
559 MCPhysReg determineVGPR16Dependency(
const MachineInstr &
MI,
563 AMDGPU::Waitcnt &
Wait)
const;
564 AMDGPU::Waitcnt determineAsyncWait(
unsigned N);
565 void tryClearSCCWriteEvent(MachineInstr *Inst);
567 void applyWaitcnt(
const AMDGPU::Waitcnt &
Wait);
571 void recordAsyncMark(MachineInstr &
MI);
573 HWEvents getPendingEvents()
const {
return PendingEvents; }
574 bool hasPendingEvent()
const {
return PendingEvents.
any(); }
575 bool hasPendingEvent(
HWEvents E)
const {
return PendingEvents.contains(
E); }
577 bool HasPending = (PendingEvents & Context->getWaitEvents(
T)).any();
579 "Expected pending events iff scoreboard is not empty");
584 HWEvents Events = PendingEvents & Context->getWaitEvents(
T);
586 return Events.
size() > 1;
589 bool hasPendingFlat()
const {
596 void setPendingFlat() {
601 bool hasPendingGDS()
const {
606 unsigned getPendingGDSWait()
const {
616 for (MCRegUnit RU : regunits(
Reg)) {
617 auto It = VMem.find(toVMEMID(RU));
618 if (It != VMem.end() && (It->second.VGPRPendingEvents & ~
E).any())
625 for (MCRegUnit RU : regunits(
Reg)) {
626 if (
auto It = VMem.find(toVMEMID(RU)); It != VMem.end()) {
628 if (It->second.empty())
634 void setStateOnFunctionEntryOrReturn() {
640 ArrayRef<const MachineInstr *> getLDSDMAStores()
const {
644 bool hasPointSampleAccel(
const MachineInstr &
MI)
const;
645 bool hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
648 void print(raw_ostream &)
const;
653 void purgeEmptyTrackingData();
657 return Context->getLimits().get(
T);
667 using CounterValueArray = std::array<unsigned, AMDGPU::NUM_INST_CNTS>;
670 AMDGPU::Waitcnt &
Wait)
const;
672 static bool mergeScore(
const MergeInfo &M,
unsigned &Score,
673 unsigned OtherScore);
678 assert(
Reg != AMDGPU::SCC &&
"Shouldn't be used on SCC");
679 if (!Context->TRI.isInAllocatableClass(
Reg))
681 return Context->TRI.regunits(
Reg);
702 const SIRegisterInfo &
TRI = Context->TRI;
703 if (
Reg == AMDGPU::SCC) {
705 }
else if (
TRI.isVectorRegister(Context->MRI,
Reg)) {
706 for (MCRegUnit RU : regunits(
Reg))
707 VMem[toVMEMID(RU)].Scores[
T] = Val;
708 }
else if (
TRI.isSGPRReg(Context->MRI,
Reg)) {
709 for (MCRegUnit RU : regunits(
Reg))
710 SGPRs[RU].get(
T) = Val;
717 VMem[TID].Scores[
T] = Val;
720 void setScoreByOperand(
const MachineOperand &
Op,
723 const SIInsertWaitcnts *Context;
729 unsigned LastFlatDsCnt = 0;
730 unsigned LastFlatLoadCnt = 0;
732 unsigned LastGDS = 0;
749 CounterValueArray Scores{};
763 unsigned ScoreDsKmCnt = 0;
764 unsigned ScoreXCnt = 0;
780 bool empty()
const {
return !ScoreDsKmCnt && !ScoreXCnt; }
783 DenseMap<VMEMID, VMEMInfo> VMem;
784 DenseMap<MCRegUnit, SGPRInfo> SGPRs;
787 unsigned SCCScore = 0;
789 const MachineInstr *PendingSCCWrite =
nullptr;
793 SmallVector<const MachineInstr *> LDSDMAStores;
802 static constexpr unsigned MaxAsyncMarks = 16;
806 CounterValueArray AsyncScore{};
809SIInsertWaitcnts::BlockInfo::~BlockInfo() =
default;
814 SIInsertWaitcntsLegacy() : MachineFunctionPass(ID) {}
818 StringRef getPassName()
const override {
819 return "SI insert wait instructions";
822 void getAnalysisUsage(AnalysisUsage &AU)
const override {
825 AU.
addRequired<MachinePostDominatorTreeWrapperPass>();
837 setRegScore(
Op.getReg().asMCReg(), CntTy, Score);
845bool WaitcntBrackets::hasPointSampleAccel(
const MachineInstr &
MI)
const {
850 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
860bool WaitcntBrackets::hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
862 if (!hasPointSampleAccel(
MI))
865 return hasDifferentVGPRPendingEvents(
Reg, HWEvents::VMEM_READ_ACCESS);
868void WaitcntBrackets::updateByEvent(
HWEvents E, MachineInstr &Inst) {
869 assert(
E.size() == 1 &&
"Expected singular event!");
873 unsigned UB = getScoreUB(
T);
877 Context->ST.hasVOP3PX2IncrementsVaVdstTwice()) {
889 setScoreUB(
T, CurrScore);
892 const MachineRegisterInfo &MRI =
Context->MRI;
901 if (
const auto *AddrOp =
TII.getNamedOperand(Inst, AMDGPU::OpName::addr))
905 if (
const auto *Data0 =
906 TII.getNamedOperand(Inst, AMDGPU::OpName::data0))
908 if (
const auto *Data1 =
909 TII.getNamedOperand(Inst, AMDGPU::OpName::data1))
913 Inst.
getOpcode() != AMDGPU::DS_CONSUME &&
914 Inst.
getOpcode() != AMDGPU::DS_ORDERED_COUNT) {
915 for (
const MachineOperand &
Op : Inst.
all_uses()) {
916 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
920 }
else if (
TII.isFLAT(Inst)) {
922 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
925 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
928 }
else if (
TII.isMIMG(Inst)) {
932 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
935 }
else if (
TII.isMTBUF(Inst)) {
938 }
else if (
TII.isMUBUF(Inst)) {
942 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
945 }
else if (
TII.isLDSDIR(Inst)) {
947 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::vdst),
950 if (
TII.isEXP(Inst)) {
955 for (MachineOperand &DefMO : Inst.
all_defs()) {
956 if (
TRI.isVGPR(MRI, DefMO.getReg())) {
961 for (
const MachineOperand &
Op : Inst.
all_uses()) {
962 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
968 E == HWEvents::SMEM_GROUP ? HWEvents::VMEM_GROUP : HWEvents::SMEM_GROUP;
969 if (PendingEvents.
contains(OtherEvent)) {
974 setScoreLB(
T, getScoreUB(
T) - 1);
975 PendingEvents -= OtherEvent;
977 for (
const MachineOperand &
Op : Inst.
all_uses())
978 setScoreByOperand(
Op,
T, CurrScore);
983 for (
const MachineOperand &
Op : Inst.
operands()) {
996 setScoreByOperand(
Op,
T, CurrScore);
1008 for (
const MachineOperand &
Op : Inst.
defs()) {
1011 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
1013 if (updateVMCntOnly(Inst)) {
1022 if (hasPointSampleAccel(Inst))
1023 VGPRContext |= HWEvents::VMEM_READ_ACCESS;
1024 for (MCRegUnit RU : regunits(
Op.getReg().asMCReg()))
1025 VMem[toVMEMID(RU)].VGPRPendingEvents |= VGPRContext;
1028 setScoreByOperand(
Op,
T, CurrScore);
1031 (
TII.isDS(Inst) ||
Context->isNonAsyncLdsDmaWrite(Inst))) {
1040 if (!MemOp->isStore() ||
1045 auto AAI = MemOp->getAAInfo();
1051 if (!AAI || !AAI.Scope)
1053 for (
unsigned I = 0,
E = LDSDMAStores.
size();
I !=
E && !Slot; ++
I) {
1054 for (
const auto *MemOp : LDSDMAStores[
I]->memoperands()) {
1055 if (MemOp->isStore() && AAI == MemOp->getAAInfo()) {
1070 setVMemScore(LDSDMA_BEGIN,
T, CurrScore);
1071 if (Slot && Slot < NUM_LDSDMA)
1072 setVMemScore(LDSDMA_BEGIN + Slot,
T, CurrScore);
1075 if (
Context->shouldUpdateAsyncMark(Inst,
T)) {
1076 AsyncScore[
T] = CurrScore;
1080 setRegScore(AMDGPU::SCC,
T, CurrScore);
1081 PendingSCCWrite = &Inst;
1086void WaitcntBrackets::recordAsyncMark(MachineInstr &Inst) {
1092 AsyncMarks.push_back(AsyncScore);
1094 dbgs() <<
"recordAsyncMark:\n" << Inst;
1095 for (
const auto &Mark : AsyncMarks) {
1102void WaitcntBrackets::print(raw_ostream &OS)
const {
1106 unsigned SR = getScoreRange(
T);
1109 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM") <<
"_CNT("
1113 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM") <<
"_CNT("
1117 OS <<
" EXP_CNT(" << SR <<
"):";
1120 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS") <<
"_CNT("
1124 OS <<
" SAMPLE_CNT(" << SR <<
"):";
1127 OS <<
" BVH_CNT(" << SR <<
"):";
1130 OS <<
" KM_CNT(" << SR <<
"):";
1133 OS <<
" X_CNT(" << SR <<
"):";
1136 OS <<
" ASYNC_CNT(" << SR <<
"):";
1139 OS <<
" VA_VDST_RD(" << SR <<
"): ";
1142 OS <<
" VA_VDST_WR(" << SR <<
"): ";
1145 OS <<
" VM_VSRC(" << SR <<
"): ";
1148 OS <<
" UNKNOWN(" << SR <<
"):";
1154 unsigned LB = getScoreLB(
T);
1157 sort(SortedVMEMIDs);
1159 for (
auto ID : SortedVMEMIDs) {
1160 unsigned RegScore = VMem.at(ID).Scores[
T];
1163 unsigned RelScore = RegScore - LB - 1;
1164 if (ID < REGUNITS_END) {
1165 OS <<
' ' << RelScore <<
':'
1168 assert(ID >= LDSDMA_BEGIN && ID < LDSDMA_END &&
1169 "Unhandled/unexpected ID value!");
1170 OS <<
' ' << RelScore <<
":LDSDMA" <<
ID;
1175 if (isSmemCounter(
T)) {
1177 sort(SortedSMEMIDs);
1178 for (
auto ID : SortedSMEMIDs) {
1179 unsigned RegScore = SGPRs.at(ID).get(
T);
1182 unsigned RelScore = RegScore - LB - 1;
1183 OS <<
' ' << RelScore <<
':'
1189 OS <<
' ' << SCCScore <<
":scc";
1194 OS <<
"Pending Events: ";
1195 if (hasPendingEvent()) {
1196 OS << getPendingEvents();
1202 OS <<
"Async score: ";
1203 if (AsyncScore.empty())
1209 OS <<
"Async marks: " << AsyncMarks.size() <<
'\n';
1211 for (
const auto &Mark : AsyncMarks) {
1213 unsigned MarkedScore = Mark[
T];
1216 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM")
1217 <<
"_CNT: " << MarkedScore;
1220 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM")
1221 <<
"_CNT: " << MarkedScore;
1224 OS <<
" EXP_CNT: " << MarkedScore;
1227 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS")
1228 <<
"_CNT: " << MarkedScore;
1231 OS <<
" SAMPLE_CNT: " << MarkedScore;
1234 OS <<
" BVH_CNT: " << MarkedScore;
1237 OS <<
" KM_CNT: " << MarkedScore;
1240 OS <<
" X_CNT: " << MarkedScore;
1243 OS <<
" ASYNC_CNT: " << MarkedScore;
1246 OS <<
" UNKNOWN: " << MarkedScore;
1257void WaitcntBrackets::simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
1258 AMDGPU::Waitcnt &UpdateWait)
const {
1266 simplifyXcnt(CheckWait, UpdateWait);
1269 simplifyVmVsrc(CheckWait, UpdateWait);
1274 unsigned &
Count)
const {
1278 if (
Count >= getScoreRange(
T))
1282void WaitcntBrackets::simplifyWaitcnt(AMDGPU::Waitcnt &
Wait,
1284 unsigned Cnt =
Wait.get(
T);
1285 simplifyWaitcnt(
T, Cnt);
1289void WaitcntBrackets::simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
1290 AMDGPU::Waitcnt &UpdateWait)
const {
1300 hasPendingEvent(HWEvents::SMEM_GROUP))
1306 hasPendingEvent(HWEvents::VMEM_GROUP) &&
1313void WaitcntBrackets::simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
1314 AMDGPU::Waitcnt &UpdateWait)
const {
1324 return Acc |
Context->getWaitEvents(
T);
1326 HWEvents PendingVmemEvents = PendingEvents & VmemEvents;
1328 unsigned CheckCount = CheckWait.
get(
T);
1330 (CheckCount == 0 || !counterOutOfOrder(
T)) &&
1331 (PendingVmemEvents & ~
Context->getWaitEvents(
T)) == 0)
1338void WaitcntBrackets::purgeEmptyTrackingData() {
1339 VMem.remove_if([](
const auto &
P) {
return P.second.empty(); });
1340 SGPRs.remove_if([](
const auto &
P) {
return P.second.empty(); });
1344 unsigned ScoreToWait,
1345 AMDGPU::Waitcnt &
Wait)
const {
1346 const unsigned LB = getScoreLB(
T);
1347 const unsigned UB = getScoreUB(
T);
1350 if ((UB >= ScoreToWait) && (ScoreToWait > LB)) {
1352 !
Context->ST.hasFlatLgkmVMemCountInOrder()) {
1357 }
else if (counterOutOfOrder(
T)) {
1365 unsigned NeededWait = std::min(UB - ScoreToWait, getLimit(
T) - 1);
1366 Wait.add(
T, NeededWait);
1371AMDGPU::Waitcnt WaitcntBrackets::determineAsyncWait(
unsigned N) {
1373 dbgs() <<
"Need " <<
N <<
" async marks. Found " << AsyncMarks.size()
1375 for (
const auto &Mark : AsyncMarks) {
1381 if (AsyncMarks.size() == MaxAsyncMarks) {
1386 LLVM_DEBUG(
dbgs() <<
"Possible truncation. Ensuring a non-trivial wait.\n");
1387 N = std::min(
N, (
unsigned)MaxAsyncMarks - 1);
1390 AMDGPU::Waitcnt
Wait;
1391 if (AsyncMarks.size() <=
N) {
1396 size_t MarkIndex = AsyncMarks.size() -
N - 1;
1397 const auto &RequiredMark = AsyncMarks[MarkIndex];
1399 determineWaitForScore(
T, RequiredMark[
T],
Wait);
1405 dbgs() <<
"Removing " << (MarkIndex + 1)
1406 <<
" async marks after determining wait\n";
1408 AsyncMarks.erase(AsyncMarks.begin(), AsyncMarks.begin() + MarkIndex + 1);
1421MCPhysReg WaitcntBrackets::determineVGPR16Dependency(
const MachineInstr &
MI,
1425 unsigned Size =
Context->TRI.getRegSizeInBits(*RC);
1427 if (
Size != 16 || !
Context->ST.hasD16Writes32BitVgpr())
1437 AMDGPU::Waitcnt
Wait;
1438 for (MCRegUnit RU : regunits(OtherHalf))
1439 determineWaitForScore(
T, getVMemScore(toVMEMID(RU),
T),
Wait);
1442 if (!
Wait.hasWait())
1452 HWEvents Events = MIEvents & OtherHalfEvents;
1453 if (Events.
size() > 1)
1460 AMDGPU::Waitcnt &
Wait,
1461 const MachineInstr &
MI)
const {
1462 if (
Reg == AMDGPU::SCC) {
1463 determineWaitForScore(
T, SCCScore,
Wait);
1467 Reg = determineVGPR16Dependency(
MI,
T,
Reg);
1468 for (MCRegUnit RU : regunits(
Reg))
1469 determineWaitForScore(
1470 T, IsVGPR ? getVMemScore(toVMEMID(RU),
T) : getSGPRScore(RU,
T),
1477 AMDGPU::Waitcnt &
Wait)
const {
1478 assert(TID >= LDSDMA_BEGIN && TID < LDSDMA_END);
1479 determineWaitForScore(
T, getVMemScore(TID,
T),
Wait);
1482void WaitcntBrackets::tryClearSCCWriteEvent(MachineInstr *Inst) {
1485 if (PendingSCCWrite &&
1486 PendingSCCWrite->
getOpcode() == AMDGPU::S_BARRIER_SIGNAL_ISFIRST_IMM &&
1488 HWEvents SCC_WRITE_PendingEvent = HWEvents::SCC_WRITE;
1491 SCC_WRITE_PendingEvent) {
1495 PendingEvents -= SCC_WRITE_PendingEvent;
1496 PendingSCCWrite =
nullptr;
1500void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait) {
1509 applyWaitcnt(
T, Cnt);
1514 const unsigned UB = getScoreUB(
T);
1518 if (counterOutOfOrder(
T))
1520 setScoreLB(
T, std::max(getScoreLB(
T), UB -
Count));
1523 PendingEvents -=
Context->getWaitEvents(
T);
1527 hasPendingEvent(HWEvents::SMEM_GROUP)) {
1531 PendingEvents -= HWEvents::SMEM_GROUP;
1537 else if (
Count == 0)
1538 PendingEvents -= HWEvents::VMEM_GROUP;
1542void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait,
1544 unsigned Cnt =
Wait.get(
T);
1545 applyWaitcnt(
T, Cnt);
1552 if ((
T ==
Context->SmemAccessCounter &&
1553 hasPendingEvent(HWEvents::SMEM_ACCESS)) ||
1570 static constexpr HWEvents ExtendedImageEvents =
1571 HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
1572 if (!
Context->ST.hasExtendedWaitCounts() &&
1573 (Events & ExtendedImageEvents).any()) {
1574 Events -= ExtendedImageEvents;
1575 Events |= HWEvents::VMEM_READ_ACCESS;
1581 Events -= HWEvents::GLOBAL_INV_ACCESS;
1585 return Events.
size() > 1;
1588 return hasMixedPendingEvents(
T);
1598char SIInsertWaitcntsLegacy::ID = 0;
1603 return new SIInsertWaitcntsLegacy();
1608 int OpIdx = AMDGPU::getNamedOperandIdx(
MI.getOpcode(),
OpName);
1613 if (NewEnc == MO.
getImm())
1620bool WaitcntGenerator::promoteSoftWaitCnt(MachineInstr *Waitcnt)
const {
1634bool WaitcntGeneratorPreGFX12::applyPreexistingWaitcnt(
1635 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1637 assert(isNormalMode(MaxCounter));
1640 MachineInstr *WaitcntInstr =
nullptr;
1641 MachineInstr *WaitcntVsCntInstr =
nullptr;
1644 dbgs() <<
"PreGFX12::applyPreexistingWaitcnt at: ";
1646 dbgs() <<
"end of block\n";
1654 if (isNonWaitcntMetaInst(
II)) {
1660 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1664 if (Opcode == AMDGPU::S_WAITCNT) {
1665 unsigned IEnc =
II.getOperand(0).getImm();
1668 ScoreBrackets.simplifyWaitcnt(OldWait);
1672 if (WaitcntInstr || (!
Wait.hasWaitExceptStoreCnt() && TrySimplify)) {
1673 II.eraseFromParent();
1677 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1680 <<
"Before: " <<
Wait <<
'\n';);
1691 II.eraseFromParent();
1692 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1693 unsigned N =
II.getOperand(0).getImm();
1695 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1698 assert(Opcode == AMDGPU::S_WAITCNT_VSCNT);
1699 assert(
II.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
1702 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1708 if (WaitcntVsCntInstr || (!
Wait.hasWaitStoreCnt() && TrySimplify)) {
1709 II.eraseFromParent();
1712 WaitcntVsCntInstr = &
II;
1719 Modified |= promoteSoftWaitCnt(WaitcntInstr);
1728 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
1729 <<
"New Instr at block end: "
1730 << *WaitcntInstr <<
'\n'
1731 :
dbgs() <<
"applied pre-existing waitcnt\n"
1732 <<
"Old Instr: " << *It
1733 <<
"New Instr: " << *WaitcntInstr <<
'\n');
1736 if (WaitcntVsCntInstr) {
1740 Modified |= promoteSoftWaitCnt(WaitcntVsCntInstr);
1746 ?
dbgs() <<
"applied pre-existing waitcnt\n"
1747 <<
"New Instr at block end: " << *WaitcntVsCntInstr
1749 :
dbgs() <<
"applied pre-existing waitcnt\n"
1750 <<
"Old Instr: " << *It
1751 <<
"New Instr: " << *WaitcntVsCntInstr <<
'\n');
1759bool WaitcntGeneratorPreGFX12::createNewWaitcnt(
1761 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
1762 assert(isNormalMode(MaxCounter));
1769 if (
Wait.hasWaitExceptStoreCnt()) {
1771 if (ExpandWaitcntProfiling) {
1775 bool AnyOutOfOrder =
false;
1777 unsigned WaitCnt =
Wait.get(CT);
1778 if (WaitCnt != ~0u && ScoreBrackets.counterOutOfOrder(CT)) {
1779 AnyOutOfOrder =
true;
1784 if (AnyOutOfOrder) {
1792 unsigned WaitCnt =
Wait.get(CT);
1796 unsigned Outstanding =
1797 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
1798 EmitExpandedWaitcnt(Outstanding, WaitCnt, [&](
unsigned Count) {
1810 [[maybe_unused]]
auto SWaitInst =
1815 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1816 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1820 if (
Wait.hasWaitStoreCnt()) {
1826 unsigned Outstanding =
1829 EmitExpandedWaitcnt(
1831 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1832 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1837 [[maybe_unused]]
auto SWaitInst =
1839 .
addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1844 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1845 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1853WaitcntGeneratorPreGFX12::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1854 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt &&
ST.hasVscnt() ? 0 : ~0u);
1858WaitcntGeneratorGFX12Plus::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1859 unsigned ExpertVal = IsExpertMode ? 0 : ~0
u;
1860 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt ? 0 : ~0u, 0, 0, 0,
1862 ~0u , ExpertVal, ExpertVal, ExpertVal);
1869bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt(
1870 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1872 assert(!isNormalMode(MaxCounter));
1875 MachineInstr *CombinedLoadDsCntInstr =
nullptr;
1876 MachineInstr *CombinedStoreDsCntInstr =
nullptr;
1877 MachineInstr *WaitcntDepctrInstr =
nullptr;
1881 dbgs() <<
"GFX12Plus::applyPreexistingWaitcnt at: ";
1883 dbgs() <<
"end of block\n";
1889 AMDGPU::Waitcnt RequiredWait;
1894 if (isNonWaitcntMetaInst(
II)) {
1903 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1907 if (Opcode == AMDGPU::S_WAITCNT)
1910 if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) {
1912 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1917 RequiredWait = RequiredWait.combined(OldWait);
1919 if (CombinedLoadDsCntInstr ==
nullptr) {
1920 CombinedLoadDsCntInstr = &
II;
1922 II.eraseFromParent();
1925 }
else if (Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT) {
1927 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1932 RequiredWait = RequiredWait.combined(OldWait);
1934 if (CombinedStoreDsCntInstr ==
nullptr) {
1935 CombinedStoreDsCntInstr = &
II;
1937 II.eraseFromParent();
1940 }
else if (Opcode == AMDGPU::S_WAITCNT_DEPCTR) {
1942 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1943 AMDGPU::Waitcnt OldWait;
1950 ScoreBrackets.simplifyWaitcnt(OldWait);
1952 if (WaitcntDepctrInstr ==
nullptr) {
1953 WaitcntDepctrInstr = &
II;
1962 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1973 II.eraseFromParent();
1977 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1980 II.eraseFromParent();
1982 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1985 unsigned N =
II.getOperand(0).getImm();
1986 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1989 std::optional<AMDGPU::InstCounterType> CT =
1993 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1995 Wait.add(CT.value(), OldCnt);
1997 RequiredWait.add(CT.value(), OldCnt);
1999 if (WaitInstrs[CT.value()] ==
nullptr) {
2000 WaitInstrs[CT.value()] = &
II;
2002 II.eraseFromParent();
2008 ScoreBrackets.simplifyWaitcnt(
Wait.combined(RequiredWait),
Wait);
2009 Wait =
Wait.combined(RequiredWait);
2011 if (CombinedLoadDsCntInstr) {
2027 AMDGPU::OpName::simm16, NewEnc);
2028 Modified |= promoteSoftWaitCnt(CombinedLoadDsCntInstr);
2034 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2035 <<
"New Instr at block end: "
2036 << *CombinedLoadDsCntInstr <<
'\n'
2037 :
dbgs() <<
"applied pre-existing waitcnt\n"
2038 <<
"Old Instr: " << *It <<
"New Instr: "
2039 << *CombinedLoadDsCntInstr <<
'\n');
2046 if (CombinedStoreDsCntInstr) {
2051 AMDGPU::OpName::simm16, NewEnc);
2052 Modified |= promoteSoftWaitCnt(CombinedStoreDsCntInstr);
2058 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2059 <<
"New Instr at block end: "
2060 << *CombinedStoreDsCntInstr <<
'\n'
2061 :
dbgs() <<
"applied pre-existing waitcnt\n"
2062 <<
"Old Instr: " << *It <<
"New Instr: "
2063 << *CombinedStoreDsCntInstr <<
'\n');
2093 for (MachineInstr **WI : WaitsToErase) {
2097 (*WI)->eraseFromParent();
2104 if (!WaitInstrs[CT])
2107 unsigned NewCnt =
Wait.get(CT);
2108 if (NewCnt != ~0u) {
2110 AMDGPU::OpName::simm16, NewCnt);
2111 Modified |= promoteSoftWaitCnt(WaitInstrs[CT]);
2113 ScoreBrackets.applyWaitcnt(CT, NewCnt);
2117 ?
dbgs() <<
"applied pre-existing waitcnt\n"
2118 <<
"New Instr at block end: " << *WaitInstrs[CT]
2120 :
dbgs() <<
"applied pre-existing waitcnt\n"
2121 <<
"Old Instr: " << *It
2122 <<
"New Instr: " << *WaitInstrs[CT] <<
'\n');
2129 if (WaitcntDepctrInstr) {
2133 TII.getNamedOperand(*WaitcntDepctrInstr, AMDGPU::OpName::simm16)
2153 AMDGPU::OpName::simm16, Enc);
2155 <<
"New Instr at block end: "
2156 << *WaitcntDepctrInstr <<
'\n'
2157 :
dbgs() <<
"applyPreexistingWaitcnt\n"
2158 <<
"Old Instr: " << *It <<
"New Instr: "
2159 << *WaitcntDepctrInstr <<
'\n');
2170bool WaitcntGeneratorGFX12Plus::createNewWaitcnt(
2172 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
2173 assert(!isNormalMode(MaxCounter));
2180 if (ExpandWaitcntProfiling) {
2187 if (ScoreBrackets.counterOutOfOrder(CT)) {
2194 unsigned Outstanding =
2195 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
2196 EmitExpandedWaitcnt(Outstanding,
Count, [&](
unsigned Val) {
2208 MachineInstr *SWaitInst =
nullptr;
2232 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2233 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2245 [[maybe_unused]]
auto SWaitInst =
2252 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2253 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2256 if (
Wait.hasWaitDepctr()) {
2265 [[maybe_unused]]
auto SWaitInst =
2271 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2272 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2291bool SIInsertWaitcnts::generateWaitcntInstBefore(
2292 MachineInstr &
MI, WaitcntBrackets &ScoreBrackets,
2293 MachineInstr *OldWaitcntInstr, PreheaderFlushFlags FlushFlags) {
2298 AMDGPU::Waitcnt
Wait;
2299 const unsigned Opc =
MI.getOpcode();
2302 case AMDGPU::BUFFER_WBINVL1:
2303 case AMDGPU::BUFFER_WBINVL1_SC:
2304 case AMDGPU::BUFFER_WBINVL1_VOL:
2305 case AMDGPU::BUFFER_GL0_INV:
2306 case AMDGPU::BUFFER_GL1_INV: {
2314 case AMDGPU::SI_RETURN_TO_EPILOG:
2315 case AMDGPU::SI_RETURN:
2316 case AMDGPU::SI_WHOLE_WAVE_FUNC_RETURN:
2317 case AMDGPU::S_SETPC_B64_return: {
2322 AMDGPU::Waitcnt AllZeroWait =
2323 WCG->getAllZeroWaitcnt(
false);
2328 if (
ST.hasExtendedWaitCounts() &&
2329 !ScoreBrackets.hasPendingEvent(HWEvents::VMEM_READ_ACCESS))
2334 case AMDGPU::S_ENDPGM:
2335 case AMDGPU::S_ENDPGM_SAVED: {
2346 !ScoreBrackets.hasPendingEvent(HWEvents::SCRATCH_WRITE_ACCESS);
2349 case AMDGPU::S_SENDMSG:
2350 case AMDGPU::S_SENDMSGHALT: {
2351 if (
ST.hasLegacyGeometry() &&
2366 if (
MI.modifiesRegister(AMDGPU::EXEC, &
TRI)) {
2369 if (ScoreBrackets.hasPendingEvent(HWEvents::EXP_GPR_LOCK) ||
2370 ScoreBrackets.hasPendingEvent(HWEvents::EXP_PARAM_ACCESS) ||
2371 ScoreBrackets.hasPendingEvent(HWEvents::EXP_POS_ACCESS) ||
2372 ScoreBrackets.hasPendingEvent(HWEvents::GDS_GPR_LOCK)) {
2379 if (
TII.isAlwaysGDS(
Opc) && ScoreBrackets.hasPendingGDS())
2387 Wait = AMDGPU::Waitcnt();
2389 const MachineOperand &CallAddrOp =
TII.getCalleeOperand(
MI);
2390 if (CallAddrOp.
isReg()) {
2391 ScoreBrackets.determineWaitForPhysReg(
2394 if (
const auto *RtnAddrOp =
2395 TII.getNamedOperand(
MI, AMDGPU::OpName::dst)) {
2396 ScoreBrackets.determineWaitForPhysReg(
2397 SmemAccessCounter, RtnAddrOp->getReg().asMCReg(),
Wait,
MI);
2400 }
else if (
Opc == AMDGPU::S_BARRIER_WAIT) {
2401 ScoreBrackets.tryClearSCCWriteEvent(&
MI);
2417 for (
const MachineMemOperand *Memop :
MI.memoperands()) {
2418 const Value *Ptr = Memop->getValue();
2419 if (Memop->isStore()) {
2420 if (
auto It = SLoadAddresses.
find(Ptr); It != SLoadAddresses.
end()) {
2421 Wait.add(SmemAccessCounter, 0);
2423 SLoadAddresses.
erase(It);
2426 unsigned AS = Memop->getAddrSpace();
2430 if (
TII.mayWriteLDSThroughDMA(
MI))
2434 unsigned TID = LDSDMA_BEGIN;
2435 if (Ptr && Memop->getAAInfo()) {
2436 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2437 for (
unsigned I = 0,
E = LDSDMAStores.size();
I !=
E; ++
I) {
2438 if (
MI.mayAlias(AA, *LDSDMAStores[
I],
true)) {
2439 if ((
I + 1) >= NUM_LDSDMA) {
2454 if (Memop->isStore()) {
2460 for (
const MachineOperand &
Op :
MI.operands()) {
2465 if (
Op.isTied() &&
Op.isUse() &&
TII.doesNotReadTiedSource(
MI))
2470 const bool IsVGPR =
TRI.isVectorRegister(MRI,
Op.getReg());
2477 if (
Op.isImplicit() &&
MI.mayLoadOrStore())
2495 if (
Op.isUse() || !updateVMCntOnly(
MI) ||
2496 ScoreBrackets.hasDifferentVGPRPendingEvents(
2498 ScoreBrackets.hasPointSamplePendingVmemTypes(
MI,
Reg) ||
2499 !
ST.hasVmemWriteVgprInOrder()) {
2506 ScoreBrackets.clearVGPRPendingEvents(
Reg);
2510 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2515 }
else if (
Op.getReg() == AMDGPU::SCC) {
2518 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter,
Reg,
Wait,
2522 if (
ST.hasWaitXcnt() &&
Op.isDef())
2541 if (
Opc == AMDGPU::S_BARRIER && !
ST.hasAutoWaitcntBeforeBarrier() &&
2542 !
ST.hasBackOffBarrier()) {
2543 Wait =
Wait.combined(WCG->getAllZeroWaitcnt(
true));
2550 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2555 ScoreBrackets.simplifyWaitcnt(
Wait);
2561 if (
TII.isVALU(
MI,
false)) {
2577 Wait = WCG->getAllZeroWaitcnt(
false);
2581 if (!ForceEmitWaitcnt[
T])
2586 if (FlushFlags.FlushVmCnt) {
2592 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
2598 return generateWaitcnt(
Wait,
MI.getIterator(), *
MI.getParent(), ScoreBrackets,
2602bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt
Wait,
2604 MachineBasicBlock &
Block,
2605 WaitcntBrackets &ScoreBrackets,
2606 MachineInstr *OldWaitcntInstr) {
2609 if (OldWaitcntInstr)
2613 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr,
Wait, It);
2618 MachineOperand *WaitExp =
TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2628 <<
"Update Instr: " << *It);
2631 if (WCG->createNewWaitcnt(
Block, It,
Wait, ScoreBrackets))
2636 ScoreBrackets.applyWaitcnt(
Wait);
2641bool SIInsertWaitcnts::isVmemAccess(
const MachineInstr &
MI)
const {
2642 return (
TII.isFLAT(
MI) &&
TII.mayAccessVMEMThroughFlat(
MI)) ||
2649 MachineBasicBlock *
Block)
const {
2650 auto BlockEnd =
Block->getParent()->end();
2651 auto BlockIter =
Block->getIterator();
2655 if (++BlockIter != BlockEnd) {
2656 It = BlockIter->instr_begin();
2663 if (!It->isMetaInstruction())
2671 return It->getOpcode() == AMDGPU::S_ENDPGM;
2675bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2676 MachineBasicBlock &
Block,
2677 WaitcntBrackets &ScoreBrackets) {
2678 AMDGPU::Waitcnt
Wait;
2679 bool NeedsEndPGMCheck =
false;
2687 NeedsEndPGMCheck =
true;
2690 ScoreBrackets.simplifyWaitcnt(
Wait);
2693 bool Result = generateWaitcnt(
Wait, SuccessorIt,
Block, ScoreBrackets,
2696 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &
Block)) {
2704void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2705 WaitcntBrackets *ScoreBrackets) {
2709 ScoreBrackets->updateByEvent(
E, Inst);
2711 if (
TII.isDS(Inst) &&
TII.usesLGKM_CNT(Inst)) {
2713 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2714 ScoreBrackets->setPendingGDS();
2716 }
else if (
TII.isFLAT(Inst)) {
2718 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2725 ScoreBrackets->setPendingFlat();
2727 }
else if (Inst.
isCall()) {
2730 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(
false));
2731 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2732 }
else if (
TII.isVINTERP(Inst)) {
2733 int64_t
Imm =
TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2743bool WaitcntBrackets::mergeScore(
const MergeInfo &M,
unsigned &Score,
2744 unsigned OtherScore) {
2745 unsigned MyShifted = Score <=
M.OldLB ? 0 : Score +
M.MyShift;
2746 unsigned OtherShifted =
2747 OtherScore <=
M.OtherLB ? 0 : OtherScore +
M.OtherShift;
2748 Score = std::max(MyShifted, OtherShifted);
2749 return OtherShifted > MyShifted;
2754 bool StrictDom =
false;
2758 if (AsyncMarks.empty() && OtherMarks.
empty()) {
2765 auto MaxSize = (unsigned)std::max(AsyncMarks.size(), OtherMarks.
size());
2766 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2769 if (AsyncMarks.size() > MaxSize)
2770 AsyncMarks.erase(AsyncMarks.begin(),
2771 AsyncMarks.begin() + (AsyncMarks.size() - MaxSize));
2777 constexpr CounterValueArray ZeroMark{};
2778 AsyncMarks.insert(AsyncMarks.begin(), MaxSize - AsyncMarks.size(), ZeroMark);
2781 dbgs() <<
"Before merge:\n";
2782 for (
const auto &Mark : AsyncMarks) {
2786 dbgs() <<
"Other marks:\n";
2787 for (
const auto &Mark : OtherMarks) {
2800 const unsigned OtherSize = OtherMarks.size();
2801 const unsigned OurSize = AsyncMarks.size();
2808 "AsyncMarks padded to MaxSize >= 1 (needs MaxAsyncMarks != 0)");
2811 const CounterValueArray &OtherMark =
2812 Idx <= OtherSize ? OtherMarks[OtherSize - Idx] : ZeroMark;
2815 mergeScore(MergeInfos[
T], AsyncMarks[OurSize - Idx][
T], OtherMark[
T]);
2819 dbgs() <<
"After merge:\n";
2820 for (
const auto &Mark : AsyncMarks) {
2834bool WaitcntBrackets::merge(
const WaitcntBrackets &
Other) {
2835 bool StrictDom =
false;
2839 for (
auto K :
Other.VMem.keys())
2840 VMem.try_emplace(K);
2841 for (
auto K :
Other.SGPRs.keys())
2842 SGPRs.try_emplace(K);
2850 const HWEvents OldEvents = PendingEvents & EventsForT;
2851 const HWEvents OtherEvents =
Other.PendingEvents & EventsForT;
2852 if (!OldEvents.
contains(OtherEvents))
2854 PendingEvents |= OtherEvents;
2857 const unsigned MyPending = ScoreUBs[
T] - ScoreLBs[
T];
2858 const unsigned OtherPending =
Other.ScoreUBs[
T] -
Other.ScoreLBs[
T];
2859 const unsigned NewUB = ScoreLBs[
T] + std::max(MyPending, OtherPending);
2860 if (NewUB < ScoreLBs[
T])
2863 MergeInfo &
M = MergeInfos[
T];
2864 M.OldLB = ScoreLBs[
T];
2865 M.OtherLB =
Other.ScoreLBs[
T];
2866 M.MyShift = NewUB - ScoreUBs[
T];
2867 M.OtherShift = NewUB -
Other.ScoreUBs[
T];
2869 ScoreUBs[
T] = NewUB;
2872 StrictDom |= mergeScore(M, LastFlatLoadCnt,
Other.LastFlatLoadCnt);
2875 StrictDom |= mergeScore(M, LastFlatDsCnt,
Other.LastFlatDsCnt);
2876 StrictDom |= mergeScore(M, LastGDS,
Other.LastGDS);
2880 StrictDom |= mergeScore(M, SCCScore,
Other.SCCScore);
2881 if (
Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2882 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2883 PendingSCCWrite =
Other.PendingSCCWrite;
2884 }
else if (PendingSCCWrite !=
Other.PendingSCCWrite) {
2885 PendingSCCWrite =
nullptr;
2890 for (
auto &[RegID, Info] : VMem)
2891 StrictDom |= mergeScore(M,
Info.Scores[
T],
Other.getVMemScore(RegID,
T));
2893 if (isSmemCounter(
T)) {
2894 for (
auto &[RegID, Info] : SGPRs) {
2895 auto It =
Other.SGPRs.find(RegID);
2896 unsigned OtherScore = (It !=
Other.SGPRs.end()) ? It->second.get(
T) : 0;
2897 StrictDom |= mergeScore(M,
Info.get(
T), OtherScore);
2902 for (
auto &[TID, Info] : VMem) {
2903 if (
auto It =
Other.VMem.find(TID); It !=
Other.VMem.end()) {
2905 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
2906 StrictDom |= NewVGPRContext !=
Info.VGPRPendingEvents;
2907 Info.VGPRPendingEvents = NewVGPRContext;
2911 StrictDom |= mergeAsyncMarks(MergeInfos,
Other.AsyncMarks);
2913 StrictDom |= mergeScore(MergeInfos[
T], AsyncScore[
T],
Other.AsyncScore[
T]);
2915 purgeEmptyTrackingData();
2921 return Opcode == AMDGPU::S_WAITCNT ||
2924 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
2925 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
2926 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
2927 Opcode == AMDGPU::WAIT_ASYNCMARK ||
2931void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &
MBB,
2933 bool ExpertMode)
const {
2937 .
addImm(ExpertMode ? 2 : 0)
2955class VCCZWorkaround {
2956 const WaitcntBrackets &ScoreBrackets;
2957 const GCNSubtarget &
ST;
2958 const SIInstrInfo &
TII;
2959 const SIRegisterInfo &
TRI;
2960 bool VCCZCorruptionBug =
false;
2961 bool VCCZNotUpdatedByPartialWrites =
false;
2964 bool MustRecomputeVCCZ =
true;
2967 VCCZWorkaround(
const WaitcntBrackets &ScoreBrackets,
const GCNSubtarget &ST,
2968 const SIInstrInfo &
TII,
const SIRegisterInfo &
TRI)
2970 VCCZCorruptionBug =
ST.hasReadVCCZBug();
2971 VCCZNotUpdatedByPartialWrites = !
ST.partialVCCWritesUpdateVCCZ();
2978 bool tryRecomputeVCCZ(MachineInstr &
MI) {
2980 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
2990 MustRecomputeVCCZ |= VCCZCorruptionBug &&
TII.isSMRD(
MI);
2996 std::optional<bool> PartiallyWritesToVCCOpt;
2997 auto PartiallyWritesToVCC = [](MachineInstr &
MI) {
2998 return MI.definesRegister(AMDGPU::VCC_LO,
nullptr) ||
2999 MI.definesRegister(AMDGPU::VCC_HI,
nullptr);
3001 if (VCCZNotUpdatedByPartialWrites) {
3002 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3005 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
3011 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
3012 !VCCZCorruptionBug) {
3014 if (!PartiallyWritesToVCCOpt)
3015 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3016 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
3017 MI.definesRegister(AMDGPU::VCC,
nullptr);
3020 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
3021 *PartiallyWritesToVCCOpt);
3023 MustRecomputeVCCZ =
false;
3033 TII.get(
ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
3036 MustRecomputeVCCZ =
false;
3047 MachineBasicBlock &
Block,
3048 WaitcntBrackets &ScoreBrackets) {
3052 dbgs() <<
"*** Begin Block: ";
3054 ScoreBrackets.dump();
3056 VCCZWorkaround VCCZW(ScoreBrackets, ST,
TII,
TRI);
3059 MachineInstr *OldWaitcntInstr =
nullptr;
3062 ScoreBrackets.verify();
3065 Iter !=
E; ++Iter) {
3066 MachineInstr &Inst = *Iter;
3067 if (isNonWaitcntMetaInst(Inst))
3072 (IsExpertMode && Inst.
getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3073 if (!OldWaitcntInstr)
3074 OldWaitcntInstr = &Inst;
3078 PreheaderFlushFlags FlushFlags;
3079 if (
Block.getFirstTerminator() == Inst)
3080 FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3083 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3085 OldWaitcntInstr =
nullptr;
3087 if (Inst.
getOpcode() == AMDGPU::ASYNCMARK) {
3091 ScoreBrackets.recordAsyncMark(Inst);
3095 if (
TII.isSMRD(Inst)) {
3096 for (
const MachineMemOperand *Memop : Inst.
memoperands()) {
3099 if (!Memop->isInvariant()) {
3100 const Value *Ptr = Memop->getValue();
3106 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3110 Modified |= insertForcedWaitAfter(Inst,
Block, ScoreBrackets);
3114 ScoreBrackets.dump();
3119 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3121 ScoreBrackets.verify();
3126 AMDGPU::Waitcnt
Wait;
3127 if (
Block.getFirstTerminator() ==
Block.end()) {
3128 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3129 if (FlushFlags.FlushVmCnt) {
3137 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
3146 dbgs() <<
"*** End Block: ";
3148 ScoreBrackets.dump();
3154bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &
Block) {
3155 if (
Block.size() <= 1)
3163 MachineInstr *LastAtomicWithSoftXcnt =
nullptr;
3167 bool IsLDS =
TII.isDS(
MI) ||
3168 (
TII.isFLAT(
MI) &&
TII.mayAccessLDSThroughFlat(
MI, TgSplit));
3169 if (!IsLDS && (
MI.mayLoad() ^
MI.mayStore()))
3170 LastAtomicWithSoftXcnt =
nullptr;
3174 MachineInstr &PrevMI = *
MI.getPrevNode();
3176 if (PrevMI.
getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3179 if (LastAtomicWithSoftXcnt) {
3183 LastAtomicWithSoftXcnt = &
MI;
3191SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &
MBB,
3192 const WaitcntBrackets &ScoreBrackets) {
3200 return PreheaderFlushFlags();
3204 return PreheaderFlushFlags();
3206 if (
Loop->getLoopPreheader() == &
MBB) {
3207 Iterator->second = getPreheaderFlushFlags(
Loop, ScoreBrackets);
3211 return PreheaderFlushFlags();
3214bool SIInsertWaitcnts::isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const {
3216 return TII.mayAccessVMEMThroughFlat(
MI);
3220bool SIInsertWaitcnts::isDSRead(
const MachineInstr &
MI)
const {
3226bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const {
3255SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *
ML,
3256 const WaitcntBrackets &Brackets) {
3257 PreheaderFlushFlags
Flags;
3258 bool HasVMemLoad =
false;
3259 bool HasVMemStore =
false;
3260 bool UsesVgprVMEMLoadedOutside =
false;
3261 bool UsesVgprDSReadOutside =
false;
3262 bool VMemInvalidated =
false;
3266 bool TrackSimpleDSOpt =
ST.hasExtendedWaitCounts();
3267 DenseSet<MCRegUnit> VgprUse;
3268 DenseSet<MCRegUnit> VgprDefVMEM;
3269 DenseSet<MCRegUnit> VgprDefDS;
3275 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3276 unsigned DSReadPosition = 0;
3277 bool IsSingleBlock =
ML->getNumBlocks() == 1;
3278 bool TrackDSFlushPoint =
ST.hasExtendedWaitCounts() && IsSingleBlock;
3279 unsigned LastDSFlushPosition = 0;
3281 for (MachineBasicBlock *
MBB :
ML->blocks()) {
3282 for (MachineInstr &
MI : *
MBB) {
3283 if (isVMEMOrFlatVMEM(
MI)) {
3284 HasVMemLoad |=
MI.mayLoad();
3285 HasVMemStore |=
MI.mayStore();
3289 if (mayStoreIncrementingDSCNT(
MI)) {
3292 if (VMemInvalidated)
3294 TrackSimpleDSOpt =
false;
3295 TrackDSFlushPoint =
false;
3297 bool IsDSRead = isDSRead(
MI);
3302 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3303 if (!TrackDSFlushPoint)
3305 if (
auto It = LastDSReadPositionMap.
find(RU);
3306 It != LastDSReadPositionMap.
end()) {
3310 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3314 for (
const MachineOperand &
Op :
MI.all_uses()) {
3315 if (
Op.isDebug() || !
TRI.isVectorRegister(MRI,
Op.getReg()))
3318 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3322 VMemInvalidated =
true;
3326 TrackSimpleDSOpt =
false;
3329 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3333 updateDSReadFlushTracking(RU);
3338 VMEMID
ID = toVMEMID(RU);
3342 UsesVgprVMEMLoadedOutside =
true;
3347 UsesVgprDSReadOutside =
true;
3352 if (isVMEMOrFlatVMEM(
MI) &&
MI.mayLoad()) {
3353 for (
const MachineOperand &
Op :
MI.all_defs()) {
3354 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3358 VMemInvalidated =
true;
3363 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3374 if (IsDSRead || TrackDSFlushPoint) {
3375 for (
const MachineOperand &
Op :
MI.all_defs()) {
3376 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
3378 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3381 updateDSReadFlushTracking(RU);
3384 if (TrackDSFlushPoint)
3385 LastDSReadPositionMap[RU] = DSReadPosition;
3394 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3395 ((!
ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3396 (HasVMemLoad &&
ST.hasVmemWriteVgprInOrder())))
3397 Flags.FlushVmCnt =
true;
3403 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3406 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3407 bool DSFlushPointPrefetch =
3408 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3410 if (SimpleDSOpt || DSFlushPointPrefetch)
3411 Flags.FlushDsCnt =
true;
3416bool SIInsertWaitcntsLegacy::runOnMachineFunction(
MachineFunction &MF) {
3417 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3419 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3421 if (
auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3422 AA = &AAR->getAAResults();
3424 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3436 if (!SIInsertWaitcnts(MLI, PDT,
AA, MF).
run())
3441 .preserve<AAManager>();
3444bool SIInsertWaitcnts::run() {
3452 if (ST.hasExtendedWaitCounts()) {
3453 IsExpertMode = ST.hasExpertSchedulingMode() &&
3462 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3467 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3471 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3475 MachineBasicBlock &EntryBB = MF.
front();
3486 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3489 if (
ST.hasExtendedWaitCounts()) {
3498 if (!
ST.hasImageInsts() &&
3504 TII.get(instrsForExtendedCounterTypes[CT]))
3517 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(
this);
3518 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3519 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3526 for (
auto *
MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3529 std::unique_ptr<WaitcntBrackets> Brackets;
3534 for (
auto BII = BlockInfos.
begin(), BIE = BlockInfos.
end(); BII != BIE;
3536 MachineBasicBlock *
MBB = BII->first;
3537 BlockInfo &BI = BII->second;
3543 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3545 *Brackets = *BI.Incoming;
3548 Brackets = std::make_unique<WaitcntBrackets>(
this);
3553 Brackets->~WaitcntBrackets();
3554 new (Brackets.get()) WaitcntBrackets(
this);
3558 if (
ST.hasWaitXcnt())
3560 Modified |= insertWaitcntInBlock(MF, *
MBB, *Brackets);
3563 if (Brackets->hasPendingEvent()) {
3564 BlockInfo *MoveBracketsToSucc =
nullptr;
3566 auto *SuccBII = BlockInfos.
find(Succ);
3567 BlockInfo &SuccBI = SuccBII->second;
3568 if (!SuccBI.Incoming) {
3569 SuccBI.Dirty =
true;
3570 if (SuccBII <= BII) {
3574 if (!MoveBracketsToSucc) {
3575 MoveBracketsToSucc = &SuccBI;
3577 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3581 dbgs() <<
"Try to merge ";
3587 if (SuccBI.Incoming->merge(*Brackets)) {
3588 SuccBI.Dirty =
true;
3589 if (SuccBII <= BII) {
3596 if (MoveBracketsToSucc)
3597 MoveBracketsToSucc->Incoming = std::move(Brackets);
3602 if (
ST.hasScalarStores()) {
3603 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3604 bool HaveScalarStores =
false;
3606 for (MachineBasicBlock &
MBB : MF) {
3607 for (MachineInstr &
MI :
MBB) {
3608 if (!HaveScalarStores &&
TII.isScalarStore(
MI))
3609 HaveScalarStores =
true;
3611 if (
MI.getOpcode() == AMDGPU::S_ENDPGM ||
3612 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3617 if (HaveScalarStores) {
3626 for (MachineBasicBlock *
MBB : EndPgmBlocks) {
3627 bool SeenDCacheWB =
false;
3631 if (
I->getOpcode() == AMDGPU::S_DCACHE_WB)
3632 SeenDCacheWB =
true;
3633 else if (
TII.isScalarStore(*
I))
3634 SeenDCacheWB =
false;
3637 if ((
I->getOpcode() == AMDGPU::S_ENDPGM ||
3638 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3654 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3656 setSchedulingMode(EntryBB,
I,
true);
3658 for (MachineInstr *
MI : CallInsts) {
3660 setSchedulingMode(
MBB,
MI,
false);
3661 setSchedulingMode(
MBB, std::next(
MI->getIterator()),
true);
3664 for (MachineInstr *
MI : ReturnInsts)
3665 setSchedulingMode(*
MI->getParent(),
MI,
false);
3676 for (
auto [
MI,
_] : EndPgmInsts) {
3678 TII.get(AMDGPU::S_ALLOC_VGPR))
3682 }
else if (!WCG->isOptNone() &&
3683 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3684 (MF.getFrameInfo().hasCalls() ||
3685 ST.getOccupancyWithNumVGPRs(
3686 TRI.getNumUsedPhysRegs(MRI, AMDGPU::VGPR_32RegClass),
3687 false) <
ST.getMaxWavesPerEU())) {
3688 for (
auto [
MI, Flag] : EndPgmInsts) {
3690 if (
ST.requiresNopBeforeDeallocVGPRs()) {
3692 TII.get(AMDGPU::S_NOP))
3696 TII.get(AMDGPU::S_SENDMSG))
3706 if (
ST.hasWaitXcnt()) {
3710 unsigned RegEncoding =
3713 TII.get(AMDGPU::S_SETREG_IMM32_B32))
3719 if (
ST.hasRequiresInitialUnclausedVmem()) {
3725 AMDGPU::SGPR64_SGPR65)
3729 TII.get(AMDGPU::GLOBAL_PREFETCH_B8_SADDR))
3730 .
addReg(AMDGPU::SGPR64_SGPR65)
3731 .
addReg(AMDGPU::VGPR0, RegState::Undef)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
AMD GCN specific subclass of TargetSubtarget.
const HexagonInstrInfo * TII
static bool isOptNone(const MachineFunction &MF)
static LoopDeletionResult merge(LoopDeletionResult A, LoopDeletionResult B)
Register const TargetRegisterInfo * TRI
Promote Memory to Register
static bool isReg(const MCInst &MI, unsigned OpNo)
uint64_t IntrinsicInst * II
#define INITIALIZE_PASS_DEPENDENCY(depName)
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
static cl::opt< bool > ForceEmitZeroLoadFlag("amdgpu-waitcnt-load-forcezero", cl::desc("Force all waitcnt load counters to wait until 0"), cl::init(false), cl::Hidden)
static bool updateOperandIfDifferent(MachineInstr &MI, AMDGPU::OpName OpName, unsigned NewEnc)
static bool isWaitInstr(MachineInstr &Inst)
static cl::opt< bool > ExpertSchedulingModeFlag("amdgpu-expert-scheduling-mode", cl::desc("Enable expert scheduling mode 2 for all functions (GFX12+ only)"), cl::init(false), cl::Hidden)
static cl::opt< bool > ForceEmitZeroFlag("amdgpu-waitcnt-forcezero", cl::desc("Force all waitcnt instrs to be emitted as " "s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)"), cl::init(false), cl::Hidden)
AMDGPU::HWEvents HWEvents
static Function * getFunction(FunctionType *Ty, const Twine &Name, Module *M)
static const uint32_t IV[8]
A manager for alias analyses.
bool isEntryFunction() const
Bit mask of hardware events.
constexpr unsigned size() const
constexpr bool contains(HWEvents Other) const
constexpr bool any() const
unsigned get(InstCounterType T) const
void set(InstCounterType T, unsigned Val)
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
AnalysisUsage & addUsedIfAvailable()
Add the specified Pass class to the set of analyses used by this pass.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
size_t size() const
Get the array size.
bool empty() const
Check if the array is empty.
LLVM_ABI bool getValueAsBool() const
Return the attribute's value as a boolean.
Represents analyses that only rely on functions' control flow.
iterator find(const_arg_type_t< KeyT > Val)
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
bool erase(const KeyT &Val)
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
bool dominates(const DomTreeNodeBase< NodeT > *A, const DomTreeNodeBase< NodeT > *B) const
dominates - Returns true iff A dominates B.
FunctionPass class - This class is used to implement most global optimizations.
Attribute getFnAttribute(Attribute::AttrKind Kind) const
Return the attribute for the given attribute kind.
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
LoopT * getLoopFor(const BlockT *BB) const
Return the inner most loop that BB lives in.
LLVM_ABI const MachineBasicBlock * getSingleSuccessor() const
Return the successor of this block if it has a single successor.
LLVM_ABI DebugLoc findDebugLoc(instr_iterator MBBI)
Find the next valid DebugLoc starting at MBBI, skipping any debug instructions.
Instructions::iterator instr_iterator
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
iterator_range< succ_iterator > successors()
LLVM_ABI void printName(raw_ostream &os, unsigned printNameFlags=PrintNameIr, ModuleSlotTracker *moduleSlotTracker=nullptr) const
Print the basic block's name as:
MachineInstrBundleIterator< MachineInstr > iterator
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
Function & getFunction()
Return the LLVM function that this machine code represents.
Ty * getInfo()
getInfo - Keep track of various per-function pieces of information for backends that would like to do...
const MachineBasicBlock & front() const
const MachineInstrBuilder & addReg(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
Representation of each machine instruction.
mop_range defs()
Returns all explicit operands that are register definitions.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
bool mayLoadOrStore(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly read or modify memory.
const MachineBasicBlock * getParent() const
filtered_mop_range all_defs()
Returns an iterator range over all operands that are (explicit or implicit) register defs.
bool isCall(QueryType Type=AnyInBundle) const
LLVM_ABI void setDesc(const MCInstrDesc &TID)
Replace the instruction descriptor (thus opcode) of the current instruction with a new one.
ArrayRef< MachineMemOperand * > memoperands() const
Access to memory operands of the instruction.
LLVM_ABI void print(raw_ostream &OS, bool IsStandalone=true, bool SkipOpers=false, bool SkipDebugLoc=false, bool AddNewLine=true, const TargetInstrInfo *TII=nullptr) const
Print this MI to OS.
bool mayStore(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly modify memory.
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
filtered_mop_range all_uses()
Returns an iterator range over all operands that are (explicit or implicit) register uses.
const MachineOperand & getOperand(unsigned i) const
LLVM_ABI MachineInstrBundleIterator< MachineInstr > eraseFromParent()
Unlink 'this' from the containing basic block and delete it.
Analysis pass that exposes the MachineLoopInfo for a machine function.
MachineOperand class - Representation of each machine instruction operand.
void setImm(int64_t immVal)
bool isReg() const
isReg - Tests if this is a MO_Register operand.
Register getReg() const
getReg - Returns the register number.
iterator find(const KeyT &Key)
std::pair< iterator, bool > try_emplace(const KeyT &Key, Ts &&...Args)
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
MCRegister asMCReg() const
Utility to check-convert this value to a MCRegister.
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
static bool isCBranchVCCZRead(const MachineInstr &MI)
static bool isDS(const MachineInstr &MI)
static bool isVMEM(const MachineInstr &MI)
static bool isFLATScratch(const MachineInstr &MI)
static bool isXcntDrain(const MachineInstr &MI)
True if MI implicitly drains XCNT.
static bool mayWriteLDSThroughDMA(const MachineInstr &MI)
static bool usesTENSOR_CNT(const MachineInstr &MI)
static bool isGWS(const MachineInstr &MI)
static bool isFLATGlobal(const MachineInstr &MI)
static bool isAtomicRet(const MachineInstr &MI)
static unsigned getNonSoftWaitcntOpcode(unsigned Opcode)
static bool isVINTERP(const MachineInstr &MI)
static bool isSBarrierSCCWrite(unsigned Opcode)
static bool isMIMG(const MachineInstr &MI)
static bool usesASYNC_CNT(const MachineInstr &MI)
static bool isFLAT(const MachineInstr &MI)
static bool isLDSDMA(const MachineInstr &MI)
This class keeps track of the SPI_SP_INPUT_ADDR config register, which tells the hardware which inter...
bool isDynamicVGPREnabled() const
void push_back(const T &Elt)
Target - Wrapper for Target specific information.
std::pair< iterator, bool > insert(const ValueT &V)
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
self_iterator getIterator()
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Abstract Attribute helper functions.
@ LOCAL_ADDRESS
Address space for local memory.
@ FLAT_ADDRESS
Address space for flat memory.
unsigned encodeFieldVaVdst(unsigned Encoded, unsigned VaVdst)
unsigned encodeFieldVmVsrc(unsigned Encoded, unsigned VmVsrc)
unsigned decodeFieldVaVdst(unsigned Encoded)
int getDefaultDepCtrEncoding(const MCSubtargetInfo &STI)
unsigned decodeFieldVmVsrc(unsigned Encoded)
@ ID_DEALLOC_VGPRS_GFX11Plus
LLVM_READONLY const MIMGInfo * getMIMGInfo(unsigned Opc)
bool isHi16Reg(MCRegister Reg, const MCRegisterInfo &MRI)
iota_range< InstCounterType > inst_counter_types(InstCounterType MaxCounter)
unsigned encodeLoadcntDscnt(const IsaVersion &Version, const Waitcnt &Decoded)
bool getHasMatrixScale(unsigned Opc)
LLVM_ABI IsaVersion getIsaVersion(StringRef GPU)
Waitcnt decodeWaitcnt(const IsaVersion &Version, unsigned Encoded)
unsigned encodeWaitcnt(const IsaVersion &Version, const Waitcnt &Decoded)
bool isTgSplitEnabled(const Function &F)
HWEvents getSimplifiedVMEMEventsFor(const MachineInstr &Inst, const SIInstrInfo &TII)
Waitcnt decodeStorecntDscnt(const IsaVersion &Version, unsigned StorecntDscnt)
std::optional< AMDGPU::InstCounterType > counterTypeForInstr(unsigned Opcode)
Determine if MI is a gfx12+ single-counter S_WAIT_*CNT instruction, and if so, which counter it is wa...
HWEvents getEventsFor(const MachineInstr &Inst, const GCNSubtarget &ST, bool IsExpertMode, bool TgSplit)
Waitcnt decodeLoadcntDscnt(const IsaVersion &Version, unsigned LoadcntDscnt)
unsigned encodeStorecntDscnt(const IsaVersion &Version, const Waitcnt &Decoded)
bool getMUBUFIsBufferInv(unsigned Opc)
LLVM_READONLY const MIMGBaseOpcodeInfo * getMIMGBaseOpcodeInfo(unsigned BaseOpcode)
constexpr bool isMaybeAtomic(const T &...O)
initializer< Ty > init(const Ty &Val)
DXILDebugInfoMap run(Module &M)
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
void dump(const SparseBitVector< ElementSize > &LHS, raw_ostream &out)
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Printable print(const GCNRegPressure &RP, const GCNSubtarget *ST=nullptr, unsigned DynamicVGPRBlockSize=0)
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
constexpr auto seq_inclusive(T Begin, T End)
Iterate over an integral type from Begin to End inclusive.
static StringRef getCPU(StringRef CPU)
Processes a CPU name.
auto accumulate(R &&Range, E &&Init)
Wrapper for std::accumulate.
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
void interleaveComma(const Container &c, StreamT &os, UnaryFunctor each_fn)
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
LLVM_ABI Printable printRegUnit(MCRegUnit Unit, const TargetRegisterInfo *TRI)
Create Printable object to print register units on a raw_ostream.
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
constexpr auto equal_to(T &&Arg)
Functor variant of std::equal_to that can be used as a UnaryPredicate in functional algorithms like a...
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
char & SIInsertWaitcntsID
@ Async
"Asynchronous" unwind tables (instr precise)
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
void sort(IteratorTy Start, IteratorTy End)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
CodeGenOptLevel
Code generation optimization level.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
iterator_range(Container &&) -> iterator_range< llvm::detail::IterOfRange< Container > >
uint16_t MCPhysReg
An unsigned integer type large enough to represent all physical registers, but not necessarily virtua...
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
@ Increment
Incrementally increasing token ID.
FunctionPass * createSIInsertWaitcntsPass()
AAResults AliasAnalysis
Temporary typedef for legacy code that uses a generic AliasAnalysis pointer or reference.
MCRegisterClass TargetRegisterClass
static constexpr ValueType Default
static constexpr uint64_t encode(Fields... Values)
Represents the hardware counter limits for different wait count types.
Instruction set architecture version.