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,
260 using WaitcntGenerator::WaitcntGenerator;
262 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
263 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
266 bool createNewWaitcnt(MachineBasicBlock &
Block,
268 AMDGPU::Waitcnt
Wait,
269 const WaitcntBrackets &ScoreBrackets)
override;
272 HWEvents EVs = WaitEventMaskForInstPreGFX12[
T];
278 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
281class WaitcntGeneratorGFX12Plus final :
public WaitcntGenerator {
286 HWEvents::VMEM_READ_ACCESS | HWEvents::GLOBAL_INV_ACCESS,
287 HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS,
288 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
289 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
290 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
292 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
293 HWEvents::VMEM_SAMPLER_READ_ACCESS,
294 HWEvents::VMEM_BVH_READ_ACCESS,
296 HWEvents::SMEM_ACCESS | HWEvents::SQ_MESSAGE | HWEvents::SCC_WRITE,
297 HWEvents::VMEM_GROUP | HWEvents::SMEM_GROUP,
298 HWEvents::ASYNC_ACCESS,
299 HWEvents::TENSOR_ACCESS,
300 HWEvents::VGPR_CSMACC_WRITE | HWEvents::VGPR_DPMACC_WRITE |
301 HWEvents::VGPR_TRANS_WRITE | HWEvents::VGPR_XDL_WRITE,
302 HWEvents::VGPR_LDS_READ | HWEvents::VGPR_FLAT_READ |
303 HWEvents::VGPR_VMEM_READ};
306 WaitcntGeneratorGFX12Plus() =
delete;
307 WaitcntGeneratorGFX12Plus(
const MachineFunction &MF,
309 const AMDGPU::HardwareLimits &Limits,
311 : WaitcntGenerator(MF, MaxCounter, Limits), IsExpertMode(IsExpertMode) {}
314 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
315 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
318 bool createNewWaitcnt(MachineBasicBlock &
Block,
320 AMDGPU::Waitcnt
Wait,
321 const WaitcntBrackets &ScoreBrackets)
override;
324 return WaitEventMaskForInstGFX12Plus[
T];
327 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
331struct PreheaderFlushFlags {
332 bool FlushVmCnt =
false;
333 bool FlushDsCnt =
false;
336class SIInsertWaitcnts {
337 DenseMap<const Value *, MachineBasicBlock *> SLoadAddresses;
338 DenseMap<MachineBasicBlock *, PreheaderFlushFlags> PreheadersToFlush;
339 MachineLoopInfo &MLI;
340 MachinePostDominatorTree &PDT;
345 std::unique_ptr<WaitcntBrackets> Incoming;
347 BlockInfo() =
default;
348 BlockInfo(BlockInfo &&) =
default;
349 BlockInfo &operator=(BlockInfo &&) =
default;
353 MapVector<MachineBasicBlock *, BlockInfo> BlockInfos;
357 std::unique_ptr<WaitcntGenerator> WCG;
360 DenseSet<MachineInstr *> CallInsts;
361 DenseSet<MachineInstr *> ReturnInsts;
366 DenseMap<MachineInstr *, bool> EndPgmInsts;
368 AMDGPU::HardwareLimits Limits;
371 const GCNSubtarget &ST;
372 const SIInstrInfo &TII;
373 const SIRegisterInfo &TRI;
374 const MachineRegisterInfo &MRI;
377 bool IsExpertMode =
false;
380 SIInsertWaitcnts(MachineLoopInfo &MLI, MachinePostDominatorTree &PDT,
382 : MLI(MLI), PDT(PDT), AA(AA), MF(MF), ST(MF.getSubtarget<GCNSubtarget>()),
383 TII(*ST.getInstrInfo()), TRI(TII.getRegisterInfo()),
384 MRI(MF.getRegInfo()),
385 TgSplit(ST.hasTgSplitSupport() &&
388 const AMDGPU::HardwareLimits &getLimits()
const {
return Limits; }
390 PreheaderFlushFlags getPreheaderFlushFlags(MachineLoop *
ML,
391 const WaitcntBrackets &Brackets);
392 PreheaderFlushFlags isPreheaderToFlush(MachineBasicBlock &
MBB,
393 const WaitcntBrackets &ScoreBrackets);
394 bool isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const;
395 bool isDSRead(
const MachineInstr &
MI)
const;
396 bool mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const;
399 bool isAsync(
const MachineInstr &
MI)
const {
404 const MachineOperand *
Async =
405 TII.getNamedOperand(
MI, AMDGPU::OpName::IsAsync);
409 bool isNonAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
413 bool isAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
417 bool shouldUpdateAsyncMark(
const MachineInstr &
MI,
421 if (!isAsyncLdsDmaWrite(
MI))
428 bool isVmemAccess(
const MachineInstr &
MI)
const;
429 bool generateWaitcntInstBefore(MachineInstr &
MI,
430 WaitcntBrackets &ScoreBrackets,
431 MachineInstr *OldWaitcntInstr,
432 PreheaderFlushFlags FlushFlags);
433 bool generateWaitcnt(AMDGPU::Waitcnt
Wait,
435 MachineBasicBlock &
Block, WaitcntBrackets &ScoreBrackets,
436 MachineInstr *OldWaitcntInstr);
437 void updateEventWaitcntAfter(MachineInstr &Inst,
438 WaitcntBrackets *ScoreBrackets);
440 MachineBasicBlock *
Block)
const;
441 bool insertForcedWaitAfter(MachineInstr &Inst, MachineBasicBlock &
Block,
442 WaitcntBrackets &ScoreBrackets);
443 bool insertWaitcntInBlock(MachineFunction &MF, MachineBasicBlock &
Block,
444 WaitcntBrackets &ScoreBrackets);
447 bool removeRedundantSoftXcnts(MachineBasicBlock &
Block);
449 bool ExpertMode)
const;
451 return WCG->getWaitEvents(
T);
454 return WCG->getCounterFromEvent(
E);
466class WaitcntBrackets {
468 WaitcntBrackets(
const SIInsertWaitcnts *Context) : Context(Context) {
469 assert(Context->TRI.getNumRegUnits() < REGUNITS_END);
474 unsigned NumUnusedVmem = 0, NumUnusedSGPRs = 0;
475 for (
auto &[
ID, Val] : VMem) {
479 for (
auto &[
ID, Val] : SGPRs) {
484 if (NumUnusedVmem || NumUnusedSGPRs) {
485 errs() <<
"WaitcntBracket had unused entries at destruction time: "
486 << NumUnusedVmem <<
" VMem and " << NumUnusedSGPRs
487 <<
" SGPR unused entries\n";
498 return ScoreUBs[
T] - ScoreLBs[
T];
502 return getVMemScore(
ID,
T) > getScoreLB(
T);
520 return getScoreUB(
T) - getScoreLB(
T);
524 auto It = SGPRs.find(RU);
525 return It != SGPRs.end() ? It->second.get(
T) : 0;
529 auto It = VMem.find(TID);
530 return It != VMem.end() ? It->second.Scores[
T] : 0;
537 void simplifyWaitcnt(AMDGPU::Waitcnt &
Wait)
const {
540 void simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
541 AMDGPU::Waitcnt &UpdateWait)
const;
544 void simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
545 AMDGPU::Waitcnt &UpdateWait)
const;
546 void simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
547 AMDGPU::Waitcnt &UpdateWait)
const;
550 AMDGPU::Waitcnt &
Wait,
551 const MachineInstr &
MI)
const;
552 MCPhysReg determineVGPR16Dependency(
const MachineInstr &
MI,
556 AMDGPU::Waitcnt &
Wait)
const;
557 AMDGPU::Waitcnt determineAsyncWait(
unsigned N);
558 void tryClearSCCWriteEvent(MachineInstr *Inst);
560 void applyWaitcnt(
const AMDGPU::Waitcnt &
Wait);
564 void recordAsyncMark(MachineInstr &
MI);
566 HWEvents getPendingEvents()
const {
return PendingEvents; }
567 bool hasPendingEvent()
const {
return PendingEvents.
any(); }
568 bool hasPendingEvent(
HWEvents E)
const {
return PendingEvents.contains(
E); }
570 bool HasPending = (PendingEvents & Context->getWaitEvents(
T)).any();
572 "Expected pending events iff scoreboard is not empty");
577 HWEvents Events = PendingEvents & Context->getWaitEvents(
T);
579 return Events.
size() > 1;
582 bool hasPendingFlat()
const {
589 void setPendingFlat() {
594 bool hasPendingGDS()
const {
599 unsigned getPendingGDSWait()
const {
609 for (MCRegUnit RU : regunits(
Reg)) {
610 auto It = VMem.find(toVMEMID(RU));
611 if (It != VMem.end() && (It->second.VGPRPendingEvents & ~
E).any())
618 for (MCRegUnit RU : regunits(
Reg)) {
619 if (
auto It = VMem.find(toVMEMID(RU)); It != VMem.end()) {
621 if (It->second.empty())
627 void setStateOnFunctionEntryOrReturn() {
633 ArrayRef<const MachineInstr *> getLDSDMAStores()
const {
637 bool hasPointSampleAccel(
const MachineInstr &
MI)
const;
638 bool hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
641 void print(raw_ostream &)
const;
646 void purgeEmptyTrackingData();
650 return Context->getLimits().get(
T);
660 using CounterValueArray = std::array<unsigned, AMDGPU::NUM_INST_CNTS>;
663 AMDGPU::Waitcnt &
Wait)
const;
665 static bool mergeScore(
const MergeInfo &M,
unsigned &Score,
666 unsigned OtherScore);
671 assert(
Reg != AMDGPU::SCC &&
"Shouldn't be used on SCC");
672 if (!Context->TRI.isInAllocatableClass(
Reg))
674 return Context->TRI.regunits(
Reg);
695 const SIRegisterInfo &
TRI = Context->TRI;
696 if (
Reg == AMDGPU::SCC) {
698 }
else if (
TRI.isVectorRegister(Context->MRI,
Reg)) {
699 for (MCRegUnit RU : regunits(
Reg))
700 VMem[toVMEMID(RU)].Scores[
T] = Val;
701 }
else if (
TRI.isSGPRReg(Context->MRI,
Reg)) {
702 for (MCRegUnit RU : regunits(
Reg))
703 SGPRs[RU].get(
T) = Val;
710 VMem[TID].Scores[
T] = Val;
713 void setScoreByOperand(
const MachineOperand &
Op,
716 const SIInsertWaitcnts *Context;
722 unsigned LastFlatDsCnt = 0;
723 unsigned LastFlatLoadCnt = 0;
725 unsigned LastGDS = 0;
742 CounterValueArray Scores{};
756 unsigned ScoreDsKmCnt = 0;
757 unsigned ScoreXCnt = 0;
773 bool empty()
const {
return !ScoreDsKmCnt && !ScoreXCnt; }
776 DenseMap<VMEMID, VMEMInfo> VMem;
777 DenseMap<MCRegUnit, SGPRInfo> SGPRs;
780 unsigned SCCScore = 0;
782 const MachineInstr *PendingSCCWrite =
nullptr;
786 SmallVector<const MachineInstr *> LDSDMAStores;
795 static constexpr unsigned MaxAsyncMarks = 16;
799 CounterValueArray AsyncScore{};
802SIInsertWaitcnts::BlockInfo::~BlockInfo() =
default;
807 SIInsertWaitcntsLegacy() : MachineFunctionPass(ID) {}
809 bool runOnMachineFunction(MachineFunction &MF)
override;
811 StringRef getPassName()
const override {
812 return "SI insert wait instructions";
815 void getAnalysisUsage(AnalysisUsage &AU)
const override {
818 AU.
addRequired<MachinePostDominatorTreeWrapperPass>();
830 setRegScore(
Op.getReg().asMCReg(), CntTy, Score);
838bool WaitcntBrackets::hasPointSampleAccel(
const MachineInstr &
MI)
const {
843 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
853bool WaitcntBrackets::hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
855 if (!hasPointSampleAccel(
MI))
858 return hasDifferentVGPRPendingEvents(
Reg, HWEvents::VMEM_READ_ACCESS);
861void WaitcntBrackets::updateByEvent(
HWEvents E, MachineInstr &Inst) {
862 assert(
E.size() == 1 &&
"Expected singular event!");
866 unsigned UB = getScoreUB(
T);
869 Context->ST.hasVOP3PX2IncrementsVaVdstTwice()) {
881 setScoreUB(
T, CurrScore);
884 const MachineRegisterInfo &MRI =
Context->MRI;
893 if (
const auto *AddrOp =
TII.getNamedOperand(Inst, AMDGPU::OpName::addr))
897 if (
const auto *Data0 =
898 TII.getNamedOperand(Inst, AMDGPU::OpName::data0))
900 if (
const auto *Data1 =
901 TII.getNamedOperand(Inst, AMDGPU::OpName::data1))
905 Inst.
getOpcode() != AMDGPU::DS_CONSUME &&
906 Inst.
getOpcode() != AMDGPU::DS_ORDERED_COUNT) {
907 for (
const MachineOperand &
Op : Inst.
all_uses()) {
908 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
912 }
else if (
TII.isFLAT(Inst)) {
914 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
917 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
920 }
else if (
TII.isMIMG(Inst)) {
924 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
927 }
else if (
TII.isMTBUF(Inst)) {
930 }
else if (
TII.isMUBUF(Inst)) {
934 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
937 }
else if (
TII.isLDSDIR(Inst)) {
939 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::vdst),
942 if (
TII.isEXP(Inst)) {
947 for (MachineOperand &DefMO : Inst.
all_defs()) {
948 if (
TRI.isVGPR(MRI, DefMO.getReg())) {
953 for (
const MachineOperand &
Op : Inst.
all_uses()) {
954 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
960 E == HWEvents::SMEM_GROUP ? HWEvents::VMEM_GROUP : HWEvents::SMEM_GROUP;
961 if (PendingEvents.
contains(OtherEvent)) {
966 setScoreLB(
T, getScoreUB(
T) - 1);
967 PendingEvents -= OtherEvent;
969 for (
const MachineOperand &
Op : Inst.
all_uses())
970 setScoreByOperand(
Op,
T, CurrScore);
974 for (
const MachineOperand &
Op : Inst.
operands()) {
979 setScoreByOperand(
Op,
T, CurrScore);
991 for (
const MachineOperand &
Op : Inst.
defs()) {
994 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
996 if (updateVMCntOnly(Inst)) {
1005 if (hasPointSampleAccel(Inst))
1006 VGPRContext |= HWEvents::VMEM_READ_ACCESS;
1007 for (MCRegUnit RU : regunits(
Op.getReg().asMCReg()))
1008 VMem[toVMEMID(RU)].VGPRPendingEvents |= VGPRContext;
1011 setScoreByOperand(
Op,
T, CurrScore);
1014 (
TII.isDS(Inst) ||
Context->isNonAsyncLdsDmaWrite(Inst))) {
1023 if (!MemOp->isStore() ||
1028 auto AAI = MemOp->getAAInfo();
1034 if (!AAI || !AAI.Scope)
1036 for (
unsigned I = 0,
E = LDSDMAStores.
size();
I !=
E && !Slot; ++
I) {
1037 for (
const auto *MemOp : LDSDMAStores[
I]->memoperands()) {
1038 if (MemOp->isStore() && AAI == MemOp->getAAInfo()) {
1053 setVMemScore(LDSDMA_BEGIN,
T, CurrScore);
1054 if (Slot && Slot < NUM_LDSDMA)
1055 setVMemScore(LDSDMA_BEGIN + Slot,
T, CurrScore);
1058 if (
Context->shouldUpdateAsyncMark(Inst,
T)) {
1059 AsyncScore[
T] = CurrScore;
1063 setRegScore(AMDGPU::SCC,
T, CurrScore);
1064 PendingSCCWrite = &Inst;
1069void WaitcntBrackets::recordAsyncMark(MachineInstr &Inst) {
1075 AsyncMarks.push_back(AsyncScore);
1078 dbgs() <<
"recordAsyncMark:\n" << Inst;
1079 for (
const auto &Mark : AsyncMarks) {
1086void WaitcntBrackets::print(raw_ostream &OS)
const {
1090 unsigned SR = getScoreRange(
T);
1093 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM") <<
"_CNT("
1097 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM") <<
"_CNT("
1101 OS <<
" EXP_CNT(" << SR <<
"):";
1104 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS") <<
"_CNT("
1108 OS <<
" SAMPLE_CNT(" << SR <<
"):";
1111 OS <<
" BVH_CNT(" << SR <<
"):";
1114 OS <<
" KM_CNT(" << SR <<
"):";
1117 OS <<
" X_CNT(" << SR <<
"):";
1120 OS <<
" ASYNC_CNT(" << SR <<
"):";
1123 OS <<
" VA_VDST(" << SR <<
"): ";
1126 OS <<
" VM_VSRC(" << SR <<
"): ";
1129 OS <<
" UNKNOWN(" << SR <<
"):";
1135 unsigned LB = getScoreLB(
T);
1138 sort(SortedVMEMIDs);
1140 for (
auto ID : SortedVMEMIDs) {
1141 unsigned RegScore = VMem.at(
ID).Scores[
T];
1144 unsigned RelScore = RegScore - LB - 1;
1145 if (
ID < REGUNITS_END) {
1146 OS <<
' ' << RelScore <<
":vRU" <<
ID;
1148 assert(
ID >= LDSDMA_BEGIN &&
ID < LDSDMA_END &&
1149 "Unhandled/unexpected ID value!");
1150 OS <<
' ' << RelScore <<
":LDSDMA" <<
ID;
1155 if (isSmemCounter(
T)) {
1157 sort(SortedSMEMIDs);
1158 for (
auto ID : SortedSMEMIDs) {
1159 unsigned RegScore = SGPRs.at(
ID).get(
T);
1162 unsigned RelScore = RegScore - LB - 1;
1163 OS <<
' ' << RelScore <<
":sRU" <<
static_cast<unsigned>(
ID);
1168 OS <<
' ' << SCCScore <<
":scc";
1173 OS <<
"Pending Events: ";
1174 if (hasPendingEvent()) {
1175 OS << getPendingEvents();
1181 OS <<
"Async score: ";
1182 if (AsyncScore.empty())
1188 OS <<
"Async marks: " << AsyncMarks.size() <<
'\n';
1190 for (
const auto &Mark : AsyncMarks) {
1192 unsigned MarkedScore = Mark[
T];
1195 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM")
1196 <<
"_CNT: " << MarkedScore;
1199 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM")
1200 <<
"_CNT: " << MarkedScore;
1203 OS <<
" EXP_CNT: " << MarkedScore;
1206 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS")
1207 <<
"_CNT: " << MarkedScore;
1210 OS <<
" SAMPLE_CNT: " << MarkedScore;
1213 OS <<
" BVH_CNT: " << MarkedScore;
1216 OS <<
" KM_CNT: " << MarkedScore;
1219 OS <<
" X_CNT: " << MarkedScore;
1222 OS <<
" ASYNC_CNT: " << MarkedScore;
1225 OS <<
" UNKNOWN: " << MarkedScore;
1236void WaitcntBrackets::simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
1237 AMDGPU::Waitcnt &UpdateWait)
const {
1245 simplifyXcnt(CheckWait, UpdateWait);
1247 simplifyVmVsrc(CheckWait, UpdateWait);
1252 unsigned &
Count)
const {
1256 if (
Count >= getScoreRange(
T))
1260void WaitcntBrackets::simplifyWaitcnt(AMDGPU::Waitcnt &
Wait,
1262 unsigned Cnt =
Wait.get(
T);
1263 simplifyWaitcnt(
T, Cnt);
1267void WaitcntBrackets::simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
1268 AMDGPU::Waitcnt &UpdateWait)
const {
1278 hasPendingEvent(HWEvents::SMEM_GROUP))
1284 hasPendingEvent(HWEvents::VMEM_GROUP) &&
1291void WaitcntBrackets::simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
1292 AMDGPU::Waitcnt &UpdateWait)
const {
1297 std::min({CheckWait.get(AMDGPU::LOAD_CNT),
1298 CheckWait.get(AMDGPU::STORE_CNT),
1299 CheckWait.get(AMDGPU::SAMPLE_CNT),
1300 CheckWait.get(AMDGPU::BVH_CNT), CheckWait.get(AMDGPU::DS_CNT)}))
1305void WaitcntBrackets::purgeEmptyTrackingData() {
1306 VMem.remove_if([](
const auto &
P) {
return P.second.empty(); });
1307 SGPRs.remove_if([](
const auto &
P) {
return P.second.empty(); });
1311 unsigned ScoreToWait,
1312 AMDGPU::Waitcnt &
Wait)
const {
1313 const unsigned LB = getScoreLB(
T);
1314 const unsigned UB = getScoreUB(
T);
1317 if ((UB >= ScoreToWait) && (ScoreToWait > LB)) {
1319 !
Context->ST.hasFlatLgkmVMemCountInOrder()) {
1324 }
else if (counterOutOfOrder(
T)) {
1332 unsigned NeededWait = std::min(UB - ScoreToWait, getLimit(
T) - 1);
1333 Wait.add(
T, NeededWait);
1338AMDGPU::Waitcnt WaitcntBrackets::determineAsyncWait(
unsigned N) {
1340 dbgs() <<
"Need " <<
N <<
" async marks. Found " << AsyncMarks.size()
1342 for (
const auto &Mark : AsyncMarks) {
1348 if (AsyncMarks.size() == MaxAsyncMarks) {
1353 LLVM_DEBUG(
dbgs() <<
"Possible truncation. Ensuring a non-trivial wait.\n");
1354 N = std::min(
N, (
unsigned)MaxAsyncMarks - 1);
1357 AMDGPU::Waitcnt
Wait;
1358 if (AsyncMarks.size() <=
N) {
1363 size_t MarkIndex = AsyncMarks.size() -
N - 1;
1364 const auto &RequiredMark = AsyncMarks[MarkIndex];
1366 determineWaitForScore(
T, RequiredMark[
T],
Wait);
1372 dbgs() <<
"Removing " << (MarkIndex + 1)
1373 <<
" async marks after determining wait\n";
1375 AsyncMarks.erase(AsyncMarks.begin(), AsyncMarks.begin() + MarkIndex + 1);
1388MCPhysReg WaitcntBrackets::determineVGPR16Dependency(
const MachineInstr &
MI,
1392 unsigned Size =
Context->TRI.getRegSizeInBits(*RC);
1394 if (
Size != 16 || !
Context->ST.hasD16Writes32BitVgpr())
1404 AMDGPU::Waitcnt
Wait;
1405 for (MCRegUnit RU : regunits(OtherHalf))
1406 determineWaitForScore(
T, getVMemScore(toVMEMID(RU),
T),
Wait);
1409 if (!
Wait.hasWait())
1419 HWEvents Events = MIEvents & OtherHalfEvents;
1420 if (Events.
size() > 1)
1427 AMDGPU::Waitcnt &
Wait,
1428 const MachineInstr &
MI)
const {
1429 if (
Reg == AMDGPU::SCC) {
1430 determineWaitForScore(
T, SCCScore,
Wait);
1434 Reg = determineVGPR16Dependency(
MI,
T,
Reg);
1435 for (MCRegUnit RU : regunits(
Reg))
1436 determineWaitForScore(
1437 T, IsVGPR ? getVMemScore(toVMEMID(RU),
T) : getSGPRScore(RU,
T),
1444 AMDGPU::Waitcnt &
Wait)
const {
1445 assert(TID >= LDSDMA_BEGIN && TID < LDSDMA_END);
1446 determineWaitForScore(
T, getVMemScore(TID,
T),
Wait);
1449void WaitcntBrackets::tryClearSCCWriteEvent(MachineInstr *Inst) {
1452 if (PendingSCCWrite &&
1453 PendingSCCWrite->
getOpcode() == AMDGPU::S_BARRIER_SIGNAL_ISFIRST_IMM &&
1455 HWEvents SCC_WRITE_PendingEvent = HWEvents::SCC_WRITE;
1458 SCC_WRITE_PendingEvent) {
1462 PendingEvents -= SCC_WRITE_PendingEvent;
1463 PendingSCCWrite =
nullptr;
1467void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait) {
1469 applyWaitcnt(
Wait,
T);
1473 const unsigned UB = getScoreUB(
T);
1477 if (counterOutOfOrder(
T))
1479 setScoreLB(
T, std::max(getScoreLB(
T), UB -
Count));
1482 PendingEvents -=
Context->getWaitEvents(
T);
1486 hasPendingEvent(HWEvents::SMEM_GROUP)) {
1490 PendingEvents -= HWEvents::SMEM_GROUP;
1496 else if (
Count == 0)
1497 PendingEvents -= HWEvents::VMEM_GROUP;
1501void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait,
1503 unsigned Cnt =
Wait.get(
T);
1504 applyWaitcnt(
T, Cnt);
1511 if ((
T ==
Context->SmemAccessCounter &&
1512 hasPendingEvent(HWEvents::SMEM_ACCESS)) ||
1529 static constexpr HWEvents ExtendedImageEvents =
1530 HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
1531 if (!
Context->ST.hasExtendedWaitCounts() &&
1532 (Events & ExtendedImageEvents).any()) {
1533 Events -= ExtendedImageEvents;
1534 Events |= HWEvents::VMEM_READ_ACCESS;
1540 Events -= HWEvents::GLOBAL_INV_ACCESS;
1544 return Events.
size() > 1;
1547 return hasMixedPendingEvents(
T);
1557char SIInsertWaitcntsLegacy::
ID = 0;
1562 return new SIInsertWaitcntsLegacy();
1567 int OpIdx = AMDGPU::getNamedOperandIdx(
MI.getOpcode(),
OpName);
1572 if (NewEnc == MO.
getImm())
1579bool WaitcntGenerator::promoteSoftWaitCnt(MachineInstr *Waitcnt)
const {
1593bool WaitcntGeneratorPreGFX12::applyPreexistingWaitcnt(
1594 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1596 assert(isNormalMode(MaxCounter));
1599 MachineInstr *WaitcntInstr =
nullptr;
1600 MachineInstr *WaitcntVsCntInstr =
nullptr;
1603 dbgs() <<
"PreGFX12::applyPreexistingWaitcnt at: ";
1605 dbgs() <<
"end of block\n";
1613 if (isNonWaitcntMetaInst(
II)) {
1619 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1623 if (Opcode == AMDGPU::S_WAITCNT) {
1624 unsigned IEnc =
II.getOperand(0).getImm();
1627 ScoreBrackets.simplifyWaitcnt(OldWait);
1631 if (WaitcntInstr || (!
Wait.hasWaitExceptStoreCnt() && TrySimplify)) {
1632 II.eraseFromParent();
1636 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1639 <<
"Before: " <<
Wait <<
'\n';);
1650 II.eraseFromParent();
1651 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1652 unsigned N =
II.getOperand(0).getImm();
1654 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1657 assert(Opcode == AMDGPU::S_WAITCNT_VSCNT);
1658 assert(
II.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
1661 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1667 if (WaitcntVsCntInstr || (!
Wait.hasWaitStoreCnt() && TrySimplify)) {
1668 II.eraseFromParent();
1671 WaitcntVsCntInstr = &
II;
1678 Modified |= promoteSoftWaitCnt(WaitcntInstr);
1687 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
1688 <<
"New Instr at block end: "
1689 << *WaitcntInstr <<
'\n'
1690 :
dbgs() <<
"applied pre-existing waitcnt\n"
1691 <<
"Old Instr: " << *It
1692 <<
"New Instr: " << *WaitcntInstr <<
'\n');
1695 if (WaitcntVsCntInstr) {
1699 Modified |= promoteSoftWaitCnt(WaitcntVsCntInstr);
1705 ?
dbgs() <<
"applied pre-existing waitcnt\n"
1706 <<
"New Instr at block end: " << *WaitcntVsCntInstr
1708 :
dbgs() <<
"applied pre-existing waitcnt\n"
1709 <<
"Old Instr: " << *It
1710 <<
"New Instr: " << *WaitcntVsCntInstr <<
'\n');
1718bool WaitcntGeneratorPreGFX12::createNewWaitcnt(
1720 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
1721 assert(isNormalMode(MaxCounter));
1728 if (
Wait.hasWaitExceptStoreCnt()) {
1730 if (ExpandWaitcntProfiling) {
1734 bool AnyOutOfOrder =
false;
1736 unsigned WaitCnt =
Wait.get(CT);
1737 if (WaitCnt != ~0u && ScoreBrackets.counterOutOfOrder(CT)) {
1738 AnyOutOfOrder =
true;
1743 if (AnyOutOfOrder) {
1751 unsigned WaitCnt =
Wait.get(CT);
1755 unsigned Outstanding =
1756 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
1757 EmitExpandedWaitcnt(Outstanding, WaitCnt, [&](
unsigned Count) {
1769 [[maybe_unused]]
auto SWaitInst =
1774 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1775 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1779 if (
Wait.hasWaitStoreCnt()) {
1785 unsigned Outstanding =
1788 EmitExpandedWaitcnt(
1790 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1791 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1796 [[maybe_unused]]
auto SWaitInst =
1798 .
addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1803 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1804 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1812WaitcntGeneratorPreGFX12::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1813 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt &&
ST.hasVscnt() ? 0 : ~0u);
1817WaitcntGeneratorGFX12Plus::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1818 unsigned ExpertVal = IsExpertMode ? 0 : ~0
u;
1819 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt ? 0 : ~0u, 0, 0, 0,
1821 ~0u , ExpertVal, ExpertVal);
1828bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt(
1829 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1831 assert(!isNormalMode(MaxCounter));
1834 MachineInstr *CombinedLoadDsCntInstr =
nullptr;
1835 MachineInstr *CombinedStoreDsCntInstr =
nullptr;
1836 MachineInstr *WaitcntDepctrInstr =
nullptr;
1840 dbgs() <<
"GFX12Plus::applyPreexistingWaitcnt at: ";
1842 dbgs() <<
"end of block\n";
1848 AMDGPU::Waitcnt RequiredWait;
1853 if (isNonWaitcntMetaInst(
II)) {
1862 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1866 if (Opcode == AMDGPU::S_WAITCNT)
1869 if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) {
1871 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1876 RequiredWait = RequiredWait.combined(OldWait);
1878 if (CombinedLoadDsCntInstr ==
nullptr) {
1879 CombinedLoadDsCntInstr = &
II;
1881 II.eraseFromParent();
1884 }
else if (Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT) {
1886 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1891 RequiredWait = RequiredWait.combined(OldWait);
1893 if (CombinedStoreDsCntInstr ==
nullptr) {
1894 CombinedStoreDsCntInstr = &
II;
1896 II.eraseFromParent();
1899 }
else if (Opcode == AMDGPU::S_WAITCNT_DEPCTR) {
1901 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1902 AMDGPU::Waitcnt OldWait;
1906 ScoreBrackets.simplifyWaitcnt(OldWait);
1908 if (WaitcntDepctrInstr ==
nullptr) {
1909 WaitcntDepctrInstr = &
II;
1918 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1926 II.eraseFromParent();
1930 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1933 II.eraseFromParent();
1935 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1938 unsigned N =
II.getOperand(0).getImm();
1939 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1942 std::optional<AMDGPU::InstCounterType> CT =
1946 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1948 Wait.add(CT.value(), OldCnt);
1950 RequiredWait.add(CT.value(), OldCnt);
1952 if (WaitInstrs[CT.value()] ==
nullptr) {
1953 WaitInstrs[CT.value()] = &
II;
1955 II.eraseFromParent();
1961 ScoreBrackets.simplifyWaitcnt(
Wait.combined(RequiredWait),
Wait);
1962 Wait =
Wait.combined(RequiredWait);
1964 if (CombinedLoadDsCntInstr) {
1980 AMDGPU::OpName::simm16, NewEnc);
1981 Modified |= promoteSoftWaitCnt(CombinedLoadDsCntInstr);
1987 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
1988 <<
"New Instr at block end: "
1989 << *CombinedLoadDsCntInstr <<
'\n'
1990 :
dbgs() <<
"applied pre-existing waitcnt\n"
1991 <<
"Old Instr: " << *It <<
"New Instr: "
1992 << *CombinedLoadDsCntInstr <<
'\n');
1999 if (CombinedStoreDsCntInstr) {
2004 AMDGPU::OpName::simm16, NewEnc);
2005 Modified |= promoteSoftWaitCnt(CombinedStoreDsCntInstr);
2011 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2012 <<
"New Instr at block end: "
2013 << *CombinedStoreDsCntInstr <<
'\n'
2014 :
dbgs() <<
"applied pre-existing waitcnt\n"
2015 <<
"Old Instr: " << *It <<
"New Instr: "
2016 << *CombinedStoreDsCntInstr <<
'\n');
2046 for (MachineInstr **WI : WaitsToErase) {
2050 (*WI)->eraseFromParent();
2057 if (!WaitInstrs[CT])
2060 unsigned NewCnt =
Wait.get(CT);
2061 if (NewCnt != ~0u) {
2063 AMDGPU::OpName::simm16, NewCnt);
2064 Modified |= promoteSoftWaitCnt(WaitInstrs[CT]);
2066 ScoreBrackets.applyWaitcnt(CT, NewCnt);
2070 ?
dbgs() <<
"applied pre-existing waitcnt\n"
2071 <<
"New Instr at block end: " << *WaitInstrs[CT]
2073 :
dbgs() <<
"applied pre-existing waitcnt\n"
2074 <<
"Old Instr: " << *It
2075 <<
"New Instr: " << *WaitInstrs[CT] <<
'\n');
2082 if (WaitcntDepctrInstr) {
2086 TII.getNamedOperand(*WaitcntDepctrInstr, AMDGPU::OpName::simm16)
2101 AMDGPU::OpName::simm16, Enc);
2103 <<
"New Instr at block end: "
2104 << *WaitcntDepctrInstr <<
'\n'
2105 :
dbgs() <<
"applyPreexistingWaitcnt\n"
2106 <<
"Old Instr: " << *It <<
"New Instr: "
2107 << *WaitcntDepctrInstr <<
'\n');
2118bool WaitcntGeneratorGFX12Plus::createNewWaitcnt(
2120 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
2121 assert(!isNormalMode(MaxCounter));
2128 if (ExpandWaitcntProfiling) {
2135 if (ScoreBrackets.counterOutOfOrder(CT)) {
2142 unsigned Outstanding =
2143 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
2144 EmitExpandedWaitcnt(Outstanding,
Count, [&](
unsigned Val) {
2156 MachineInstr *SWaitInst =
nullptr;
2180 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2181 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2193 [[maybe_unused]]
auto SWaitInst =
2200 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2201 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2204 if (
Wait.hasWaitDepctr()) {
2210 [[maybe_unused]]
auto SWaitInst =
2216 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2217 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2236bool SIInsertWaitcnts::generateWaitcntInstBefore(
2237 MachineInstr &
MI, WaitcntBrackets &ScoreBrackets,
2238 MachineInstr *OldWaitcntInstr, PreheaderFlushFlags FlushFlags) {
2243 AMDGPU::Waitcnt
Wait;
2244 const unsigned Opc =
MI.getOpcode();
2247 case AMDGPU::BUFFER_WBINVL1:
2248 case AMDGPU::BUFFER_WBINVL1_SC:
2249 case AMDGPU::BUFFER_WBINVL1_VOL:
2250 case AMDGPU::BUFFER_GL0_INV:
2251 case AMDGPU::BUFFER_GL1_INV: {
2259 case AMDGPU::SI_RETURN_TO_EPILOG:
2260 case AMDGPU::SI_RETURN:
2261 case AMDGPU::SI_WHOLE_WAVE_FUNC_RETURN:
2262 case AMDGPU::S_SETPC_B64_return: {
2267 AMDGPU::Waitcnt AllZeroWait =
2268 WCG->getAllZeroWaitcnt(
false);
2273 if (
ST.hasExtendedWaitCounts() &&
2274 !ScoreBrackets.hasPendingEvent(HWEvents::VMEM_READ_ACCESS))
2279 case AMDGPU::S_ENDPGM:
2280 case AMDGPU::S_ENDPGM_SAVED: {
2291 !ScoreBrackets.hasPendingEvent(HWEvents::SCRATCH_WRITE_ACCESS);
2294 case AMDGPU::S_SENDMSG:
2295 case AMDGPU::S_SENDMSGHALT: {
2296 if (
ST.hasLegacyGeometry() &&
2311 if (
MI.modifiesRegister(AMDGPU::EXEC, &
TRI)) {
2314 if (ScoreBrackets.hasPendingEvent(HWEvents::EXP_GPR_LOCK) ||
2315 ScoreBrackets.hasPendingEvent(HWEvents::EXP_PARAM_ACCESS) ||
2316 ScoreBrackets.hasPendingEvent(HWEvents::EXP_POS_ACCESS) ||
2317 ScoreBrackets.hasPendingEvent(HWEvents::GDS_GPR_LOCK)) {
2324 if (
TII.isAlwaysGDS(
Opc) && ScoreBrackets.hasPendingGDS())
2332 Wait = AMDGPU::Waitcnt();
2334 const MachineOperand &CallAddrOp =
TII.getCalleeOperand(
MI);
2335 if (CallAddrOp.
isReg()) {
2336 ScoreBrackets.determineWaitForPhysReg(
2339 if (
const auto *RtnAddrOp =
2340 TII.getNamedOperand(
MI, AMDGPU::OpName::dst)) {
2341 ScoreBrackets.determineWaitForPhysReg(
2342 SmemAccessCounter, RtnAddrOp->getReg().asMCReg(),
Wait,
MI);
2345 }
else if (
Opc == AMDGPU::S_BARRIER_WAIT) {
2346 ScoreBrackets.tryClearSCCWriteEvent(&
MI);
2362 for (
const MachineMemOperand *Memop :
MI.memoperands()) {
2363 const Value *Ptr = Memop->getValue();
2364 if (Memop->isStore()) {
2365 if (
auto It = SLoadAddresses.
find(Ptr); It != SLoadAddresses.
end()) {
2366 Wait.add(SmemAccessCounter, 0);
2368 SLoadAddresses.
erase(It);
2371 unsigned AS = Memop->getAddrSpace();
2375 if (
TII.mayWriteLDSThroughDMA(
MI))
2379 unsigned TID = LDSDMA_BEGIN;
2380 if (Ptr && Memop->getAAInfo()) {
2381 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2382 for (
unsigned I = 0,
E = LDSDMAStores.size();
I !=
E; ++
I) {
2383 if (
MI.mayAlias(AA, *LDSDMAStores[
I],
true)) {
2384 if ((
I + 1) >= NUM_LDSDMA) {
2399 if (Memop->isStore()) {
2405 for (
const MachineOperand &
Op :
MI.operands()) {
2410 if (
Op.isTied() &&
Op.isUse() &&
TII.doesNotReadTiedSource(
MI))
2415 const bool IsVGPR =
TRI.isVectorRegister(MRI,
Op.getReg());
2422 if (
Op.isImplicit() &&
MI.mayLoadOrStore())
2435 if (
Op.isUse() || !updateVMCntOnly(
MI) ||
2436 ScoreBrackets.hasDifferentVGPRPendingEvents(
2438 ScoreBrackets.hasPointSamplePendingVmemTypes(
MI,
Reg) ||
2439 !
ST.hasVmemWriteVgprInOrder()) {
2446 ScoreBrackets.clearVGPRPendingEvents(
Reg);
2450 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2455 }
else if (
Op.getReg() == AMDGPU::SCC) {
2458 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter,
Reg,
Wait,
2462 if (
ST.hasWaitXcnt() &&
Op.isDef())
2481 if (
Opc == AMDGPU::S_BARRIER && !
ST.hasAutoWaitcntBeforeBarrier() &&
2482 !
ST.hasBackOffBarrier()) {
2483 Wait =
Wait.combined(WCG->getAllZeroWaitcnt(
true));
2490 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2495 ScoreBrackets.simplifyWaitcnt(
Wait);
2515 Wait = WCG->getAllZeroWaitcnt(
false);
2519 if (!ForceEmitWaitcnt[
T])
2524 if (FlushFlags.FlushVmCnt) {
2530 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
2536 return generateWaitcnt(
Wait,
MI.getIterator(), *
MI.getParent(), ScoreBrackets,
2540bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt
Wait,
2542 MachineBasicBlock &
Block,
2543 WaitcntBrackets &ScoreBrackets,
2544 MachineInstr *OldWaitcntInstr) {
2547 if (OldWaitcntInstr)
2551 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr,
Wait, It);
2556 MachineOperand *WaitExp =
TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2566 <<
"Update Instr: " << *It);
2569 if (WCG->createNewWaitcnt(
Block, It,
Wait, ScoreBrackets))
2574 ScoreBrackets.applyWaitcnt(
Wait);
2579bool SIInsertWaitcnts::isVmemAccess(
const MachineInstr &
MI)
const {
2580 return (
TII.isFLAT(
MI) &&
TII.mayAccessVMEMThroughFlat(
MI)) ||
2587 MachineBasicBlock *
Block)
const {
2588 auto BlockEnd =
Block->getParent()->end();
2589 auto BlockIter =
Block->getIterator();
2593 if (++BlockIter != BlockEnd) {
2594 It = BlockIter->instr_begin();
2601 if (!It->isMetaInstruction())
2609 return It->getOpcode() == AMDGPU::S_ENDPGM;
2613bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2614 MachineBasicBlock &
Block,
2615 WaitcntBrackets &ScoreBrackets) {
2616 AMDGPU::Waitcnt
Wait;
2617 bool NeedsEndPGMCheck =
false;
2625 NeedsEndPGMCheck =
true;
2628 ScoreBrackets.simplifyWaitcnt(
Wait);
2631 bool Result = generateWaitcnt(
Wait, SuccessorIt,
Block, ScoreBrackets,
2634 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &
Block)) {
2642void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2643 WaitcntBrackets *ScoreBrackets) {
2647 ScoreBrackets->updateByEvent(
E, Inst);
2649 if (
TII.isDS(Inst) &&
TII.usesLGKM_CNT(Inst)) {
2651 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2652 ScoreBrackets->setPendingGDS();
2654 }
else if (
TII.isFLAT(Inst)) {
2656 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2663 ScoreBrackets->setPendingFlat();
2665 }
else if (Inst.
isCall()) {
2668 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(
false));
2669 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2670 }
else if (
TII.isVINTERP(Inst)) {
2671 int64_t
Imm =
TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2681bool WaitcntBrackets::mergeScore(
const MergeInfo &M,
unsigned &Score,
2682 unsigned OtherScore) {
2683 unsigned MyShifted = Score <=
M.OldLB ? 0 : Score +
M.MyShift;
2684 unsigned OtherShifted =
2685 OtherScore <=
M.OtherLB ? 0 : OtherScore +
M.OtherShift;
2686 Score = std::max(MyShifted, OtherShifted);
2687 return OtherShifted > MyShifted;
2692 bool StrictDom =
false;
2696 if (AsyncMarks.empty() && OtherMarks.
empty()) {
2703 auto MaxSize = (unsigned)std::max(AsyncMarks.size(), OtherMarks.
size());
2704 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2707 if (AsyncMarks.size() > MaxSize)
2708 AsyncMarks.erase(AsyncMarks.begin(),
2709 AsyncMarks.begin() + (AsyncMarks.size() - MaxSize));
2715 constexpr CounterValueArray ZeroMark{};
2716 AsyncMarks.insert(AsyncMarks.begin(), MaxSize - AsyncMarks.size(), ZeroMark);
2719 dbgs() <<
"Before merge:\n";
2720 for (
const auto &Mark : AsyncMarks) {
2724 dbgs() <<
"Other marks:\n";
2725 for (
const auto &Mark : OtherMarks) {
2734 unsigned OtherSize = OtherMarks.size();
2735 unsigned OurSize = AsyncMarks.size();
2736 unsigned MergeCount = std::min(OtherSize, OurSize);
2740 if (MergeCount == 0)
2744 StrictDom |= mergeScore(MergeInfos[
T], AsyncMarks[OurSize - Idx][
T],
2745 OtherMarks[OtherSize - Idx][
T]);
2750 dbgs() <<
"After merge:\n";
2751 for (
const auto &Mark : AsyncMarks) {
2765bool WaitcntBrackets::merge(
const WaitcntBrackets &
Other) {
2766 bool StrictDom =
false;
2770 for (
auto K :
Other.VMem.keys())
2771 VMem.try_emplace(K);
2772 for (
auto K :
Other.SGPRs.keys())
2773 SGPRs.try_emplace(K);
2781 const HWEvents OldEvents = PendingEvents & EventsForT;
2782 const HWEvents OtherEvents =
Other.PendingEvents & EventsForT;
2783 if (!OldEvents.
contains(OtherEvents))
2785 PendingEvents |= OtherEvents;
2788 const unsigned MyPending = ScoreUBs[
T] - ScoreLBs[
T];
2789 const unsigned OtherPending =
Other.ScoreUBs[
T] -
Other.ScoreLBs[
T];
2790 const unsigned NewUB = ScoreLBs[
T] + std::max(MyPending, OtherPending);
2791 if (NewUB < ScoreLBs[
T])
2794 MergeInfo &
M = MergeInfos[
T];
2795 M.OldLB = ScoreLBs[
T];
2796 M.OtherLB =
Other.ScoreLBs[
T];
2797 M.MyShift = NewUB - ScoreUBs[
T];
2798 M.OtherShift = NewUB -
Other.ScoreUBs[
T];
2800 ScoreUBs[
T] = NewUB;
2803 StrictDom |= mergeScore(M, LastFlatLoadCnt,
Other.LastFlatLoadCnt);
2806 StrictDom |= mergeScore(M, LastFlatDsCnt,
Other.LastFlatDsCnt);
2807 StrictDom |= mergeScore(M, LastGDS,
Other.LastGDS);
2811 StrictDom |= mergeScore(M, SCCScore,
Other.SCCScore);
2812 if (
Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2813 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2814 PendingSCCWrite =
Other.PendingSCCWrite;
2815 }
else if (PendingSCCWrite !=
Other.PendingSCCWrite) {
2816 PendingSCCWrite =
nullptr;
2821 for (
auto &[RegID, Info] : VMem)
2822 StrictDom |= mergeScore(M,
Info.Scores[
T],
Other.getVMemScore(RegID,
T));
2824 if (isSmemCounter(
T)) {
2825 for (
auto &[RegID, Info] : SGPRs) {
2826 auto It =
Other.SGPRs.find(RegID);
2827 unsigned OtherScore = (It !=
Other.SGPRs.end()) ? It->second.get(
T) : 0;
2828 StrictDom |= mergeScore(M,
Info.get(
T), OtherScore);
2833 for (
auto &[TID, Info] : VMem) {
2834 if (
auto It =
Other.VMem.find(TID); It !=
Other.VMem.end()) {
2836 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
2837 StrictDom |= NewVGPRContext !=
Info.VGPRPendingEvents;
2838 Info.VGPRPendingEvents = NewVGPRContext;
2842 StrictDom |= mergeAsyncMarks(MergeInfos,
Other.AsyncMarks);
2844 StrictDom |= mergeScore(MergeInfos[
T], AsyncScore[
T],
Other.AsyncScore[
T]);
2846 purgeEmptyTrackingData();
2852 return Opcode == AMDGPU::S_WAITCNT ||
2855 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
2856 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
2857 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
2858 Opcode == AMDGPU::WAIT_ASYNCMARK ||
2862void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &
MBB,
2864 bool ExpertMode)
const {
2868 .
addImm(ExpertMode ? 2 : 0)
2886class VCCZWorkaround {
2887 const WaitcntBrackets &ScoreBrackets;
2888 const GCNSubtarget &
ST;
2889 const SIInstrInfo &
TII;
2890 const SIRegisterInfo &
TRI;
2891 bool VCCZCorruptionBug =
false;
2892 bool VCCZNotUpdatedByPartialWrites =
false;
2895 bool MustRecomputeVCCZ =
true;
2898 VCCZWorkaround(
const WaitcntBrackets &ScoreBrackets,
const GCNSubtarget &ST,
2899 const SIInstrInfo &
TII,
const SIRegisterInfo &
TRI)
2901 VCCZCorruptionBug =
ST.hasReadVCCZBug();
2902 VCCZNotUpdatedByPartialWrites = !
ST.partialVCCWritesUpdateVCCZ();
2909 bool tryRecomputeVCCZ(MachineInstr &
MI) {
2911 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
2921 MustRecomputeVCCZ |= VCCZCorruptionBug &&
TII.isSMRD(
MI);
2927 std::optional<bool> PartiallyWritesToVCCOpt;
2928 auto PartiallyWritesToVCC = [](MachineInstr &
MI) {
2929 return MI.definesRegister(AMDGPU::VCC_LO,
nullptr) ||
2930 MI.definesRegister(AMDGPU::VCC_HI,
nullptr);
2932 if (VCCZNotUpdatedByPartialWrites) {
2933 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
2936 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
2942 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
2943 !VCCZCorruptionBug) {
2945 if (!PartiallyWritesToVCCOpt)
2946 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
2947 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
2948 MI.definesRegister(AMDGPU::VCC,
nullptr);
2951 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
2952 *PartiallyWritesToVCCOpt);
2954 MustRecomputeVCCZ =
false;
2964 TII.get(
ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
2967 MustRecomputeVCCZ =
false;
2977bool SIInsertWaitcnts::insertWaitcntInBlock(MachineFunction &MF,
2978 MachineBasicBlock &
Block,
2979 WaitcntBrackets &ScoreBrackets) {
2983 dbgs() <<
"*** Begin Block: ";
2985 ScoreBrackets.dump();
2987 VCCZWorkaround VCCZW(ScoreBrackets, ST,
TII,
TRI);
2990 MachineInstr *OldWaitcntInstr =
nullptr;
2995 Iter !=
E; ++Iter) {
2996 MachineInstr &Inst = *Iter;
2997 if (isNonWaitcntMetaInst(Inst))
3002 (IsExpertMode && Inst.
getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3003 if (!OldWaitcntInstr)
3004 OldWaitcntInstr = &Inst;
3008 PreheaderFlushFlags FlushFlags;
3009 if (
Block.getFirstTerminator() == Inst)
3010 FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3013 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3015 OldWaitcntInstr =
nullptr;
3017 if (Inst.
getOpcode() == AMDGPU::ASYNCMARK) {
3021 ScoreBrackets.recordAsyncMark(Inst);
3025 if (
TII.isSMRD(Inst)) {
3026 for (
const MachineMemOperand *Memop : Inst.
memoperands()) {
3029 if (!Memop->isInvariant()) {
3030 const Value *Ptr = Memop->getValue();
3036 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3040 Modified |= insertForcedWaitAfter(Inst,
Block, ScoreBrackets);
3044 ScoreBrackets.dump();
3049 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3054 AMDGPU::Waitcnt
Wait;
3055 if (
Block.getFirstTerminator() ==
Block.end()) {
3056 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3057 if (FlushFlags.FlushVmCnt) {
3065 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
3074 dbgs() <<
"*** End Block: ";
3076 ScoreBrackets.dump();
3082bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &
Block) {
3083 if (
Block.size() <= 1)
3091 MachineInstr *LastAtomicWithSoftXcnt =
nullptr;
3095 bool IsLDS =
TII.isDS(
MI) ||
3096 (
TII.isFLAT(
MI) &&
TII.mayAccessLDSThroughFlat(
MI, TgSplit));
3097 if (!IsLDS && (
MI.mayLoad() ^
MI.mayStore()))
3098 LastAtomicWithSoftXcnt =
nullptr;
3102 MachineInstr &PrevMI = *
MI.getPrevNode();
3104 if (PrevMI.
getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3107 if (LastAtomicWithSoftXcnt) {
3111 LastAtomicWithSoftXcnt = &
MI;
3119SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &
MBB,
3120 const WaitcntBrackets &ScoreBrackets) {
3121 auto [Iterator, IsInserted] =
3124 return Iterator->second;
3128 return PreheaderFlushFlags();
3132 return PreheaderFlushFlags();
3135 Iterator->second = getPreheaderFlushFlags(Loop, ScoreBrackets);
3136 return Iterator->second;
3139 return PreheaderFlushFlags();
3142bool SIInsertWaitcnts::isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const {
3144 return TII.mayAccessVMEMThroughFlat(
MI);
3148bool SIInsertWaitcnts::isDSRead(
const MachineInstr &
MI)
const {
3154bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const {
3183SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *
ML,
3184 const WaitcntBrackets &Brackets) {
3185 PreheaderFlushFlags
Flags;
3186 bool HasVMemLoad =
false;
3187 bool HasVMemStore =
false;
3188 bool UsesVgprVMEMLoadedOutside =
false;
3189 bool UsesVgprDSReadOutside =
false;
3190 bool VMemInvalidated =
false;
3194 bool TrackSimpleDSOpt =
ST.hasExtendedWaitCounts();
3195 DenseSet<MCRegUnit> VgprUse;
3196 DenseSet<MCRegUnit> VgprDefVMEM;
3197 DenseSet<MCRegUnit> VgprDefDS;
3203 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3204 unsigned DSReadPosition = 0;
3205 bool IsSingleBlock =
ML->getNumBlocks() == 1;
3206 bool TrackDSFlushPoint =
ST.hasExtendedWaitCounts() && IsSingleBlock;
3207 unsigned LastDSFlushPosition = 0;
3209 for (MachineBasicBlock *
MBB :
ML->blocks()) {
3210 for (MachineInstr &
MI : *
MBB) {
3211 if (isVMEMOrFlatVMEM(
MI)) {
3212 HasVMemLoad |=
MI.mayLoad();
3213 HasVMemStore |=
MI.mayStore();
3217 if (mayStoreIncrementingDSCNT(
MI)) {
3220 if (VMemInvalidated)
3222 TrackSimpleDSOpt =
false;
3223 TrackDSFlushPoint =
false;
3225 bool IsDSRead = isDSRead(
MI);
3230 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3231 if (!TrackDSFlushPoint)
3233 if (
auto It = LastDSReadPositionMap.
find(RU);
3234 It != LastDSReadPositionMap.
end()) {
3238 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3242 for (
const MachineOperand &
Op :
MI.all_uses()) {
3243 if (
Op.isDebug() || !
TRI.isVectorRegister(MRI,
Op.getReg()))
3246 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3250 VMemInvalidated =
true;
3254 TrackSimpleDSOpt =
false;
3257 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3261 updateDSReadFlushTracking(RU);
3266 VMEMID
ID = toVMEMID(RU);
3270 UsesVgprVMEMLoadedOutside =
true;
3275 UsesVgprDSReadOutside =
true;
3280 if (isVMEMOrFlatVMEM(
MI) &&
MI.mayLoad()) {
3281 for (
const MachineOperand &
Op :
MI.all_defs()) {
3282 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3286 VMemInvalidated =
true;
3291 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3302 if (IsDSRead || TrackDSFlushPoint) {
3303 for (
const MachineOperand &
Op :
MI.all_defs()) {
3304 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
3306 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3309 updateDSReadFlushTracking(RU);
3312 if (TrackDSFlushPoint)
3313 LastDSReadPositionMap[RU] = DSReadPosition;
3322 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3323 ((!
ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3324 (HasVMemLoad &&
ST.hasVmemWriteVgprInOrder())))
3325 Flags.FlushVmCnt =
true;
3331 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3334 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3335 bool DSFlushPointPrefetch =
3336 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3338 if (SimpleDSOpt || DSFlushPointPrefetch)
3339 Flags.FlushDsCnt =
true;
3344bool SIInsertWaitcntsLegacy::runOnMachineFunction(MachineFunction &MF) {
3345 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3347 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3349 if (
auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3350 AA = &AAR->getAAResults();
3352 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3364 if (!SIInsertWaitcnts(MLI, PDT,
AA, MF).
run())
3369 .preserve<AAManager>();
3372bool SIInsertWaitcnts::run() {
3380 if (ST.hasExtendedWaitCounts()) {
3381 IsExpertMode = ST.hasExpertSchedulingMode() &&
3390 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3395 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3399 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3403 MachineBasicBlock &EntryBB = MF.
front();
3414 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3417 if (
ST.hasExtendedWaitCounts()) {
3426 if (!
ST.hasImageInsts() &&
3432 TII.get(instrsForExtendedCounterTypes[CT]))
3445 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(
this);
3446 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3447 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3454 for (
auto *
MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3457 std::unique_ptr<WaitcntBrackets> Brackets;
3462 for (
auto BII = BlockInfos.
begin(), BIE = BlockInfos.
end(); BII != BIE;
3464 MachineBasicBlock *
MBB = BII->first;
3465 BlockInfo &BI = BII->second;
3471 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3473 *Brackets = *BI.Incoming;
3476 Brackets = std::make_unique<WaitcntBrackets>(
this);
3481 Brackets->~WaitcntBrackets();
3482 new (Brackets.get()) WaitcntBrackets(
this);
3486 if (
ST.hasWaitXcnt())
3488 Modified |= insertWaitcntInBlock(MF, *
MBB, *Brackets);
3491 if (Brackets->hasPendingEvent()) {
3492 BlockInfo *MoveBracketsToSucc =
nullptr;
3494 auto *SuccBII = BlockInfos.
find(Succ);
3495 BlockInfo &SuccBI = SuccBII->second;
3496 if (!SuccBI.Incoming) {
3497 SuccBI.Dirty =
true;
3498 if (SuccBII <= BII) {
3502 if (!MoveBracketsToSucc) {
3503 MoveBracketsToSucc = &SuccBI;
3505 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3509 dbgs() <<
"Try to merge ";
3515 if (SuccBI.Incoming->merge(*Brackets)) {
3516 SuccBI.Dirty =
true;
3517 if (SuccBII <= BII) {
3524 if (MoveBracketsToSucc)
3525 MoveBracketsToSucc->Incoming = std::move(Brackets);
3530 if (
ST.hasScalarStores()) {
3531 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3532 bool HaveScalarStores =
false;
3534 for (MachineBasicBlock &
MBB : MF) {
3535 for (MachineInstr &
MI :
MBB) {
3536 if (!HaveScalarStores &&
TII.isScalarStore(
MI))
3537 HaveScalarStores =
true;
3539 if (
MI.getOpcode() == AMDGPU::S_ENDPGM ||
3540 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3545 if (HaveScalarStores) {
3554 for (MachineBasicBlock *
MBB : EndPgmBlocks) {
3555 bool SeenDCacheWB =
false;
3559 if (
I->getOpcode() == AMDGPU::S_DCACHE_WB)
3560 SeenDCacheWB =
true;
3561 else if (
TII.isScalarStore(*
I))
3562 SeenDCacheWB =
false;
3565 if ((
I->getOpcode() == AMDGPU::S_ENDPGM ||
3566 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3582 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3584 setSchedulingMode(EntryBB,
I,
true);
3586 for (MachineInstr *
MI : CallInsts) {
3587 MachineBasicBlock &
MBB = *
MI->getParent();
3588 setSchedulingMode(
MBB,
MI,
false);
3589 setSchedulingMode(
MBB, std::next(
MI->getIterator()),
true);
3592 for (MachineInstr *
MI : ReturnInsts)
3593 setSchedulingMode(*
MI->getParent(),
MI,
false);
3604 for (
auto [
MI,
_] : EndPgmInsts) {
3606 TII.get(AMDGPU::S_ALLOC_VGPR))
3610 }
else if (!WCG->isOptNone() &&
3611 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3612 (MF.getFrameInfo().hasCalls() ||
3613 ST.getOccupancyWithNumVGPRs(
3614 TRI.getNumUsedPhysRegs(MRI, AMDGPU::VGPR_32RegClass),
3617 for (
auto [
MI, Flag] : EndPgmInsts) {
3619 if (
ST.requiresNopBeforeDeallocVGPRs()) {
3621 TII.get(AMDGPU::S_NOP))
3625 TII.get(AMDGPU::S_SENDMSG))
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)
auto seq_inclusive(T Begin, T End)
Iterate over an integral type from Begin to End inclusive.
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.
static StringRef getCPU(StringRef CPU)
Processes a CPU name.
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...
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.