46#define DEBUG_TYPE "si-insert-waitcnts"
50 cl::desc(
"Force all waitcnt instrs to be emitted as "
51 "s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)"),
55 "amdgpu-waitcnt-load-forcezero",
56 cl::desc(
"Force all waitcnt load counters to wait until 0"),
61template <
typename EmitWaitcntFn>
62static void EmitExpandedWaitcnt(
unsigned Outstanding,
unsigned Target,
63 EmitWaitcntFn &&EmitWaitcnt) {
65 for (
unsigned I = Outstanding - 1;
I >
Target &&
I != ~0u; --
I)
85 TRACKINGID_RANGE_LEN = (1 << 16),
90 REGUNITS_END = REGUNITS_BEGIN + TRACKINGID_RANGE_LEN,
95 NUM_LDSDMA = TRACKINGID_RANGE_LEN,
96 LDSDMA_BEGIN = REGUNITS_END,
97 LDSDMA_END = LDSDMA_BEGIN + NUM_LDSDMA,
101static constexpr VMEMID toVMEMID(MCRegUnit RU) {
102 return static_cast<unsigned>(RU);
114 AMDGPU::S_WAIT_LOADCNT, AMDGPU::S_WAIT_DSCNT,
115 AMDGPU::S_WAIT_EXPCNT, AMDGPU::S_WAIT_STORECNT,
116 AMDGPU::S_WAIT_SAMPLECNT, AMDGPU::S_WAIT_BVHCNT,
117 AMDGPU::S_WAIT_KMCNT, AMDGPU::S_WAIT_XCNT,
118 AMDGPU::S_WAIT_ASYNCCNT, AMDGPU::S_WAIT_TENSORCNT};
123 switch (
MI.getOpcode()) {
124 case AMDGPU::ASYNCMARK:
125 case AMDGPU::WAIT_ASYNCMARK:
128 return MI.isMetaInstruction();
143class WaitcntBrackets;
151class WaitcntGenerator {
153 const GCNSubtarget &ST;
154 const SIInstrInfo &TII;
155 AMDGPU::IsaVersion IV;
158 bool ExpandWaitcntProfiling =
false;
159 const AMDGPU::HardwareLimits &Limits;
163 DenseSet<MachineInstr *> DeferredErasures;
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,
201 bool eraseDeferredWaitcnts() {
203 for (MachineInstr *
MI : DeferredErasures) {
204 MI->eraseFromParent();
207 DeferredErasures.clear();
212 bool promoteSoftWaitCnt(MachineInstr *Waitcnt)
const;
217 virtual bool createNewWaitcnt(MachineBasicBlock &
Block,
219 AMDGPU::Waitcnt
Wait,
220 const WaitcntBrackets &ScoreBrackets) = 0;
227 assert(
E.size() == 1 &&
"Cannot handle a mask of events!");
229 if (getWaitEvents(
T) &
E)
240 virtual AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const = 0;
242 virtual ~WaitcntGenerator() =
default;
245class WaitcntGeneratorPreGFX12 final :
public WaitcntGenerator {
248 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_SAMPLER_READ_ACCESS |
249 HWEvents::VMEM_BVH_READ_ACCESS | HWEvents::VMEM_INV_ACCESS,
250 HWEvents::SMEM_ACCESS | HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS |
251 HWEvents::SQ_MESSAGE,
252 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
253 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
254 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
255 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
267 using WaitcntGenerator::WaitcntGenerator;
268 bool applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
269 MachineInstr &OldWaitcntInstr,
270 AMDGPU::Waitcnt &
Wait,
273 bool createNewWaitcnt(MachineBasicBlock &
Block,
275 AMDGPU::Waitcnt
Wait,
276 const WaitcntBrackets &ScoreBrackets)
override;
283 HWEvents EVs = WaitEventMaskForInstPreGFX12[
T];
289 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
292class WaitcntGeneratorGFX12Plus final :
public WaitcntGenerator {
297 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_INV_ACCESS,
298 HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS,
299 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
300 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
301 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
303 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
304 HWEvents::VMEM_SAMPLER_READ_ACCESS,
305 HWEvents::VMEM_BVH_READ_ACCESS,
307 HWEvents::SMEM_ACCESS | HWEvents::SQ_MESSAGE | HWEvents::SCC_WRITE,
308 HWEvents::VMEM_GROUP | HWEvents::SMEM_GROUP,
309 HWEvents::ASYNC_ACCESS,
310 HWEvents::TENSOR_ACCESS,
311 HWEvents::VGPR_CSMACC_READ | HWEvents::VGPR_DPMACC_READ |
312 HWEvents::VGPR_TRANS_READ | HWEvents::VGPR_XDL_READ,
313 HWEvents::VGPR_CSMACC_WRITE | HWEvents::VGPR_DPMACC_WRITE |
314 HWEvents::VGPR_TRANS_WRITE | HWEvents::VGPR_XDL_WRITE,
315 HWEvents::VGPR_LDS_READ | HWEvents::VGPR_FLAT_READ |
316 HWEvents::VGPR_VMEM_READ};
319 WaitcntGeneratorGFX12Plus() =
delete;
322 const AMDGPU::HardwareLimits &Limits,
324 : WaitcntGenerator(MF, MaxCounter, Limits), IsExpertMode(IsExpertMode) {}
326 bool applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
327 MachineInstr &OldWaitcntInstr,
328 AMDGPU::Waitcnt &
Wait,
331 bool createNewWaitcnt(MachineBasicBlock &
Block,
333 AMDGPU::Waitcnt
Wait,
334 const WaitcntBrackets &ScoreBrackets)
override;
337 return WaitEventMaskForInstGFX12Plus[
T];
340 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
344struct PreheaderFlushFlags {
345 bool FlushVmCnt =
false;
346 bool FlushDsCnt =
false;
349class SIInsertWaitcnts {
350 DenseMap<const Value *, MachineBasicBlock *> SLoadAddresses;
351 DenseMap<MachineBasicBlock *, PreheaderFlushFlags> PreheadersToFlush;
352 MachineLoopInfo &MLI;
353 MachinePostDominatorTree &PDT;
358 std::unique_ptr<WaitcntBrackets> Incoming;
360 BlockInfo() =
default;
361 BlockInfo(BlockInfo &&) =
default;
362 BlockInfo &operator=(BlockInfo &&) =
default;
366 MapVector<MachineBasicBlock *, BlockInfo> BlockInfos;
370 std::unique_ptr<WaitcntGenerator> WCG;
373 DenseSet<MachineInstr *> CallInsts;
374 DenseSet<MachineInstr *> ReturnInsts;
379 DenseMap<MachineInstr *, bool> EndPgmInsts;
381 AMDGPU::HardwareLimits Limits;
384 const GCNSubtarget &ST;
385 const SIInstrInfo &TII;
386 const SIRegisterInfo &TRI;
387 const MachineRegisterInfo &MRI;
390 bool IsExpertMode =
false;
393 SIInsertWaitcnts(MachineLoopInfo &MLI, MachinePostDominatorTree &PDT,
395 : MLI(MLI), PDT(PDT), AA(AA), MF(MF), ST(MF.getSubtarget<GCNSubtarget>()),
396 TII(*ST.getInstrInfo()), TRI(TII.getRegisterInfo()),
397 MRI(MF.getRegInfo()),
398 TgSplit(ST.hasTgSplitSupport() &&
401 const AMDGPU::HardwareLimits &getLimits()
const {
return Limits; }
403 PreheaderFlushFlags getPreheaderFlushFlags(MachineLoop *
ML,
404 const WaitcntBrackets &Brackets);
405 PreheaderFlushFlags isPreheaderToFlush(MachineBasicBlock &
MBB,
406 const WaitcntBrackets &ScoreBrackets);
407 bool isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const;
408 bool isDSRead(
const MachineInstr &
MI)
const;
409 bool mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const;
412 bool isVmemAccess(
const MachineInstr &
MI)
const;
413 bool generateWaitcntInstBefore(MachineInstr &
MI,
414 WaitcntBrackets &ScoreBrackets,
415 MachineInstr *OldWaitcntInstr,
416 PreheaderFlushFlags FlushFlags);
417 bool generateWaitcnt(AMDGPU::Waitcnt
Wait,
419 MachineBasicBlock &
Block, WaitcntBrackets &ScoreBrackets,
420 MachineInstr *OldWaitcntInstr);
421 void updateEventWaitcntAfter(MachineInstr &Inst,
422 WaitcntBrackets *ScoreBrackets);
424 MachineBasicBlock *
Block)
const;
425 bool insertForcedWaitAfter(MachineInstr &Inst, MachineBasicBlock &
Block,
426 WaitcntBrackets &ScoreBrackets);
428 WaitcntBrackets &ScoreBrackets);
431 bool removeRedundantSoftXcnts(MachineBasicBlock &
Block);
433 bool ExpertMode)
const;
435 return WCG->getWaitEvents(
T);
438 return WCG->getCounterFromEvent(
E);
450class WaitcntBrackets {
452 WaitcntBrackets(
const SIInsertWaitcnts *Context) : Context(Context) {
453 assert(Context->TRI.getNumRegUnits() < REGUNITS_END);
458 unsigned NumUnusedVmem = 0, NumUnusedSGPRs = 0;
459 for (
auto &[ID, Val] : VMem) {
463 for (
auto &[ID, Val] : SGPRs) {
468 if (NumUnusedVmem || NumUnusedSGPRs) {
469 errs() <<
"WaitcntBracket had unused entries at destruction time: "
470 << NumUnusedVmem <<
" VMem and " << NumUnusedSGPRs
471 <<
" SGPR unused entries\n";
482 return ScoreUBs[
T] - ScoreLBs[
T];
486 return getVMemScore(ID,
T) > getScoreLB(
T);
504 return getScoreUB(
T) - getScoreLB(
T);
508 auto It = SGPRs.find(RU);
509 return It != SGPRs.end() ? It->second.get(
T) : 0;
513 auto It = VMem.find(TID);
514 return It != VMem.end() ? It->second.Scores[
T] : 0;
529 void simplifyWaitcnt(AMDGPU::Waitcnt &
Wait)
const {
532 void simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
533 AMDGPU::Waitcnt &UpdateWait)
const;
536 void simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
537 AMDGPU::Waitcnt &UpdateWait)
const;
538 void simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
539 AMDGPU::Waitcnt &UpdateWait)
const;
542 void simplifyVmemInvOnlyWait(AMDGPU::Waitcnt &
Wait)
const {
547 if (LoadEvents == HWEvents::VMEM_INV_ACCESS)
552 AMDGPU::Waitcnt &
Wait,
553 const MachineInstr &
MI)
const;
554 MCRegister determineVGPR16Dependency(
const MachineInstr &
MI,
556 MCRegister
Reg)
const;
558 AMDGPU::Waitcnt &
Wait)
const;
560 void tryClearSCCWriteEvent(MachineInstr *Inst);
562 void applyWaitcnt(
const AMDGPU::Waitcnt &
Wait);
568 HWEvents getPendingEvents()
const {
return PendingEvents; }
569 bool hasPendingEvent()
const {
return PendingEvents.
any(); }
570 bool hasPendingEvent(
HWEvents E)
const {
return PendingEvents.contains(
E); }
572 bool HasPending = (PendingEvents & Context->getWaitEvents(
T)).any();
574 "Expected pending events iff scoreboard is not empty");
579 HWEvents Events = PendingEvents & Context->getWaitEvents(
T);
581 return Events.
size() > 1;
584 bool hasPendingFlat()
const {
591 void setPendingFlat() {
596 bool hasPendingGDS()
const {
601 unsigned getPendingGDSWait()
const {
610 bool hasDifferentVGPRPendingEvents(MCRegister
Reg,
HWEvents E)
const {
611 for (MCRegUnit RU : regunits(
Reg)) {
612 auto It = VMem.find(toVMEMID(RU));
613 if (It != VMem.end() && (It->second.VGPRPendingEvents & ~
E).any())
619 void clearVGPRPendingEvents(MCRegister
Reg) {
620 for (MCRegUnit RU : regunits(
Reg)) {
621 if (
auto It = VMem.find(toVMEMID(RU)); It != VMem.end()) {
623 if (It->second.empty())
629 void setStateOnFunctionEntryOrReturn() {
635 ArrayRef<const MachineInstr *> getLDSDMAStores()
const {
639 bool hasPointSampleAccel(
const MachineInstr &
MI)
const;
640 bool hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
641 MCRegister RU)
const;
643 void print(raw_ostream &)
const;
648 void purgeEmptyTrackingData();
652 return Context->getLimits().get(
T);
662 using CounterValueArray = std::array<unsigned, AMDGPU::NUM_INST_CNTS>;
665 AMDGPU::Waitcnt &
Wait)
const;
667 static bool mergeScore(
const MergeInfo &M,
unsigned &Score,
668 unsigned OtherScore);
674 assert(
Reg != AMDGPU::SCC &&
"Shouldn't be used on SCC");
675 if (!Context->TRI.isInAllocatableClass(
Reg))
677 return Context->TRI.regunits(
Reg);
698 const SIRegisterInfo &
TRI = Context->TRI;
699 if (
Reg == AMDGPU::SCC) {
701 }
else if (
TRI.isVectorRegister(Context->MRI,
Reg)) {
702 for (MCRegUnit RU : regunits(
Reg))
703 VMem[toVMEMID(RU)].Scores[
T] = Val;
704 }
else if (
TRI.isSGPRReg(Context->MRI,
Reg)) {
705 for (MCRegUnit RU : regunits(
Reg))
706 SGPRs[RU].get(
T) = Val;
713 VMem[TID].Scores[
T] = Val;
716 void setScoreByOperand(
const MachineOperand &
Op,
719 const SIInsertWaitcnts *Context;
725 unsigned LastFlatDsCnt = 0;
726 unsigned LastFlatLoadCnt = 0;
728 unsigned LastGDS = 0;
745 CounterValueArray Scores{};
759 unsigned ScoreDsKmCnt = 0;
760 unsigned ScoreXCnt = 0;
776 bool empty()
const {
return !ScoreDsKmCnt && !ScoreXCnt; }
779 DenseMap<VMEMID, VMEMInfo> VMem;
780 DenseMap<MCRegUnit, SGPRInfo> SGPRs;
783 unsigned SCCScore = 0;
785 const MachineInstr *PendingSCCWrite =
nullptr;
789 SmallVector<const MachineInstr *> LDSDMAStores;
799 static constexpr unsigned MaxAsyncMarks = 16;
803 std::array<CounterValueArray, AMDGPU::NumAsyncStages> AsyncScore{};
806SIInsertWaitcnts::BlockInfo::~BlockInfo() =
default;
811 SIInsertWaitcntsLegacy() : MachineFunctionPass(ID) {}
815 StringRef getPassName()
const override {
816 return "SI insert wait instructions";
819 void getAnalysisUsage(AnalysisUsage &AU)
const override {
822 AU.
addRequired<MachinePostDominatorTreeWrapperPass>();
834 setRegScore(
Op.getReg().asMCReg(), CntTy, Score);
842bool WaitcntBrackets::hasPointSampleAccel(
const MachineInstr &
MI)
const {
847 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
857bool WaitcntBrackets::hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
858 MCRegister
Reg)
const {
859 if (!hasPointSampleAccel(
MI))
862 return hasDifferentVGPRPendingEvents(
Reg, HWEvents::VMEM_READ_ACCESS);
865void WaitcntBrackets::updateByEvent(
HWEvents E, MachineInstr &Inst) {
866 assert(
E.size() == 1 &&
"Expected singular event!");
870 unsigned UB = getScoreUB(
T);
874 Context->ST.hasVOP3PX2IncrementsVaVdstTwice()) {
886 setScoreUB(
T, CurrScore);
889 const MachineRegisterInfo &
MRI =
Context->MRI;
898 if (
const auto *AddrOp =
TII.getNamedOperand(Inst, AMDGPU::OpName::addr))
902 if (
const auto *Data0 =
903 TII.getNamedOperand(Inst, AMDGPU::OpName::data0))
905 if (
const auto *Data1 =
906 TII.getNamedOperand(Inst, AMDGPU::OpName::data1))
910 Inst.
getOpcode() != AMDGPU::DS_CONSUME &&
911 Inst.
getOpcode() != AMDGPU::DS_ORDERED_COUNT) {
912 for (
const MachineOperand &
Op : Inst.
all_uses()) {
913 if (
TRI.isVectorRegister(
MRI,
Op.getReg()))
917 }
else if (
TII.isFLAT(Inst)) {
919 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
922 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
925 }
else if (
TII.isMIMG(Inst)) {
929 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
932 }
else if (
TII.isMTBUF(Inst)) {
935 }
else if (
TII.isMUBUF(Inst)) {
939 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
942 }
else if (
TII.isLDSDIR(Inst)) {
944 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::vdst),
947 if (
TII.isEXP(Inst)) {
952 for (MachineOperand &DefMO : Inst.
all_defs()) {
953 if (
TRI.isVGPR(
MRI, DefMO.getReg())) {
958 for (
const MachineOperand &
Op : Inst.
all_uses()) {
959 if (
TRI.isVectorRegister(
MRI,
Op.getReg()))
965 E == HWEvents::SMEM_GROUP ? HWEvents::VMEM_GROUP : HWEvents::SMEM_GROUP;
966 if (PendingEvents.
contains(OtherEvent)) {
971 setScoreLB(
T, getScoreUB(
T) - 1);
972 PendingEvents -= OtherEvent;
974 for (
const MachineOperand &
Op : Inst.
all_uses())
975 setScoreByOperand(
Op,
T, CurrScore);
980 for (
const MachineOperand &
Op : Inst.
operands()) {
993 setScoreByOperand(
Op,
T, CurrScore);
1005 for (
const MachineOperand &
Op : Inst.
defs()) {
1008 if (!
TRI.isVectorRegister(
MRI,
Op.getReg()))
1010 if (updateVMCntOnly(Inst)) {
1019 if (hasPointSampleAccel(Inst))
1020 VGPRContext |= HWEvents::VMEM_READ_ACCESS;
1021 for (MCRegUnit RU : regunits(
Op.getReg().asMCReg()))
1022 VMem[toVMEMID(RU)].VGPRPendingEvents |= VGPRContext;
1025 setScoreByOperand(
Op,
T, CurrScore);
1028 (
TII.isDS(Inst) ||
TII.isNonAsyncLdsDmaWrite(Inst))) {
1037 if (!MemOp->isStore() ||
1042 auto AAI = MemOp->getAAInfo();
1048 if (!AAI || !AAI.Scope)
1050 for (
size_t I = 0,
E = LDSDMAStores.
size();
I !=
E && !Slot; ++
I) {
1051 for (
const auto *MemOp : LDSDMAStores[
I]->memoperands()) {
1052 if (MemOp->isStore() && AAI == MemOp->getAAInfo()) {
1067 setVMemScore(LDSDMA_BEGIN,
T, CurrScore);
1068 if (Slot && Slot < NUM_LDSDMA)
1069 setVMemScore(LDSDMA_BEGIN +
static_cast<VMEMID
>(Slot),
T, CurrScore);
1077 setRegScore(AMDGPU::SCC,
T, CurrScore);
1078 PendingSCCWrite = &Inst;
1083void WaitcntBrackets::recordAsyncMark(MachineInstr &Inst,
1093 LLVM_DEBUG(
dbgs() <<
"recordAsyncMark(stages=" << Stages <<
"):\n" << Inst);
1096 AsyncMarks[
SI].push_back(AsyncScore[SI]);
1098 dbgs() <<
" stage " << S <<
":\n";
1099 for (
const auto &Mark : AsyncMarks[SI]) {
1108void WaitcntBrackets::print(raw_ostream &OS)
const {
1112 unsigned SR = getScoreRange(
T);
1115 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM") <<
"_CNT("
1119 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM") <<
"_CNT("
1123 OS <<
" EXP_CNT(" << SR <<
"):";
1126 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS") <<
"_CNT("
1130 OS <<
" SAMPLE_CNT(" << SR <<
"):";
1133 OS <<
" BVH_CNT(" << SR <<
"):";
1136 OS <<
" KM_CNT(" << SR <<
"):";
1139 OS <<
" X_CNT(" << SR <<
"):";
1142 OS <<
" ASYNC_CNT(" << SR <<
"):";
1145 OS <<
" VA_VDST_RD(" << SR <<
"): ";
1148 OS <<
" VA_VDST_WR(" << SR <<
"): ";
1151 OS <<
" VM_VSRC(" << SR <<
"): ";
1154 OS <<
" UNKNOWN(" << SR <<
"):";
1160 unsigned LB = getScoreLB(
T);
1163 sort(SortedVMEMIDs);
1165 for (
auto ID : SortedVMEMIDs) {
1166 unsigned RegScore = VMem.at(ID).Scores[
T];
1169 unsigned RelScore = RegScore - LB - 1;
1170 if (ID < REGUNITS_END) {
1171 OS <<
' ' << RelScore <<
':'
1174 assert(ID >= LDSDMA_BEGIN && ID < LDSDMA_END &&
1175 "Unhandled/unexpected ID value!");
1176 OS <<
' ' << RelScore <<
":LDSDMA" <<
ID;
1181 if (isSmemCounter(
T)) {
1183 sort(SortedSMEMIDs);
1184 for (
auto ID : SortedSMEMIDs) {
1185 unsigned RegScore = SGPRs.at(ID).get(
T);
1188 unsigned RelScore = RegScore - LB - 1;
1189 OS <<
' ' << RelScore <<
':'
1195 OS <<
' ' << SCCScore <<
":scc";
1200 OS <<
"Pending Events: ";
1201 if (hasPendingEvent()) {
1202 OS << getPendingEvents();
1210 const CounterValueArray &Score = AsyncScore[
SI];
1211 OS <<
"Async score (stage " << S <<
"): ";
1218 OS <<
"Async marks (stage " << S <<
"): " << AsyncMarks[
SI].size() <<
'\n';
1220 for (
const auto &Mark : AsyncMarks[SI]) {
1222 unsigned MarkedScore = Mark[
T];
1225 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM")
1226 <<
"_CNT: " << MarkedScore;
1229 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM")
1230 <<
"_CNT: " << MarkedScore;
1233 OS <<
" EXP_CNT: " << MarkedScore;
1236 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS")
1237 <<
"_CNT: " << MarkedScore;
1240 OS <<
" SAMPLE_CNT: " << MarkedScore;
1243 OS <<
" BVH_CNT: " << MarkedScore;
1246 OS <<
" KM_CNT: " << MarkedScore;
1249 OS <<
" X_CNT: " << MarkedScore;
1252 OS <<
" ASYNC_CNT: " << MarkedScore;
1255 OS <<
" UNKNOWN: " << MarkedScore;
1267void WaitcntBrackets::simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
1268 AMDGPU::Waitcnt &UpdateWait)
const {
1276 simplifyXcnt(CheckWait, UpdateWait);
1279 simplifyVmVsrc(CheckWait, UpdateWait);
1284 unsigned &
Count)
const {
1288 if (
Count >= getScoreRange(
T))
1292void WaitcntBrackets::simplifyWaitcnt(AMDGPU::Waitcnt &
Wait,
1294 unsigned Cnt =
Wait.get(
T);
1295 simplifyWaitcnt(
T, Cnt);
1299void WaitcntBrackets::simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
1300 AMDGPU::Waitcnt &UpdateWait)
const {
1310 hasPendingEvent(HWEvents::SMEM_GROUP))
1316 hasPendingEvent(HWEvents::VMEM_GROUP) &&
1323void WaitcntBrackets::simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
1324 AMDGPU::Waitcnt &UpdateWait)
const {
1334 return Acc |
Context->getWaitEvents(
T);
1336 HWEvents PendingVmemEvents = PendingEvents & VmemEvents;
1338 unsigned CheckCount = CheckWait.
get(
T);
1340 (CheckCount == 0 || !counterOutOfOrder(
T)) &&
1341 (PendingVmemEvents & ~
Context->getWaitEvents(
T)) == 0)
1348void WaitcntBrackets::purgeEmptyTrackingData() {
1349 VMem.remove_if([](
const auto &
P) {
return P.second.empty(); });
1350 SGPRs.remove_if([](
const auto &
P) {
return P.second.empty(); });
1354 unsigned ScoreToWait,
1355 AMDGPU::Waitcnt &
Wait)
const {
1356 const unsigned LB = getScoreLB(
T);
1357 const unsigned UB = getScoreUB(
T);
1360 if ((UB >= ScoreToWait) && (ScoreToWait > LB)) {
1362 !
Context->ST.hasFlatLgkmVMemCountInOrder()) {
1367 }
else if (counterOutOfOrder(
T)) {
1375 unsigned NeededWait = std::min(UB - ScoreToWait, getLimit(
T) - 1);
1376 Wait.add(
T, NeededWait);
1386 AMDGPU::Waitcnt
Wait;
1390 dbgs() <<
"Need " <<
N <<
" async marks in stage " << S <<
". Found "
1391 << StageMarks.size() <<
":\n";
1392 for (
const auto &Mark : StageMarks) {
1398 unsigned StageN =
N;
1399 if (StageMarks.size() == MaxAsyncMarks) {
1405 dbgs() <<
"Possible truncation. Ensuring a non-trivial wait.\n");
1406 StageN = std::min(StageN, (
unsigned)MaxAsyncMarks - 1);
1409 if (StageMarks.size() <= StageN) {
1414 size_t MarkIndex = StageMarks.size() - StageN - 1;
1415 const auto &RequiredMark = StageMarks[MarkIndex];
1417 determineWaitForScore(
T, RequiredMark[
T],
Wait);
1424 dbgs() <<
"Removing " << (MarkIndex + 1)
1425 <<
" async marks after determining wait\n";
1427 StageMarks.erase(StageMarks.begin(), StageMarks.begin() + MarkIndex + 1);
1441MCRegister WaitcntBrackets::determineVGPR16Dependency(
const MachineInstr &
MI,
1443 MCRegister
Reg)
const {
1447 if (
Size != 16 || !
Context->ST.hasD16Writes32BitVgpr())
1457 AMDGPU::Waitcnt
Wait;
1458 for (MCRegUnit RU : regunits(OtherHalf))
1459 determineWaitForScore(
T, getVMemScore(toVMEMID(RU),
T),
Wait);
1462 if (!
Wait.hasWait())
1472 HWEvents Events = MIEvents & OtherHalfEvents;
1473 if (Events.
size() > 1)
1480 AMDGPU::Waitcnt &
Wait,
1481 const MachineInstr &
MI)
const {
1482 if (
Reg == AMDGPU::SCC) {
1483 determineWaitForScore(
T, SCCScore,
Wait);
1487 Reg = determineVGPR16Dependency(
MI,
T,
Reg);
1488 for (MCRegUnit RU : regunits(
Reg))
1489 determineWaitForScore(
1490 T, IsVGPR ? getVMemScore(toVMEMID(RU),
T) : getSGPRScore(RU,
T),
1497 AMDGPU::Waitcnt &
Wait)
const {
1498 assert(TID >= LDSDMA_BEGIN && TID < LDSDMA_END);
1499 determineWaitForScore(
T, getVMemScore(TID,
T),
Wait);
1502void WaitcntBrackets::tryClearSCCWriteEvent(MachineInstr *Inst) {
1505 if (PendingSCCWrite &&
1506 PendingSCCWrite->
getOpcode() == AMDGPU::S_BARRIER_SIGNAL_ISFIRST_IMM &&
1508 HWEvents SCC_WRITE_PendingEvent = HWEvents::SCC_WRITE;
1511 SCC_WRITE_PendingEvent) {
1515 PendingEvents -= SCC_WRITE_PendingEvent;
1516 PendingSCCWrite =
nullptr;
1520void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait) {
1529 applyWaitcnt(
T, Cnt);
1534 const unsigned UB = getScoreUB(
T);
1538 if (counterOutOfOrder(
T))
1540 setScoreLB(
T, std::max(getScoreLB(
T), UB -
Count));
1543 PendingEvents -=
Context->getWaitEvents(
T);
1547 hasPendingEvent(HWEvents::SMEM_GROUP)) {
1551 PendingEvents -= HWEvents::SMEM_GROUP;
1557 else if (
Count == 0)
1558 PendingEvents -= HWEvents::VMEM_GROUP;
1562void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait,
1564 unsigned Cnt =
Wait.get(
T);
1565 applyWaitcnt(
T, Cnt);
1572 if ((
T ==
Context->SmemAccessCounter &&
1573 hasPendingEvent(HWEvents::SMEM_ACCESS)) ||
1590 static constexpr HWEvents ExtendedImageEvents =
1591 HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
1592 if (!
Context->ST.hasExtendedWaitCounts() &&
1593 (Events & ExtendedImageEvents).any()) {
1594 Events -= ExtendedImageEvents;
1595 Events |= HWEvents::VMEM_READ_ACCESS;
1601 Events -= HWEvents::VMEM_INV_ACCESS;
1603 return Events.
size() > 1;
1606 return hasMixedPendingEvents(
T);
1616char SIInsertWaitcntsLegacy::ID = 0;
1621 return new SIInsertWaitcntsLegacy();
1626 int OpIdx = AMDGPU::getNamedOperandIdx(
MI.getOpcode(),
OpName);
1631 if (NewEnc == MO.
getImm())
1638bool WaitcntGenerator::promoteSoftWaitCnt(MachineInstr *Waitcnt)
const {
1652bool WaitcntGeneratorPreGFX12::applyPreexistingWaitcnt(
1653 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1655 assert(isNormalMode(MaxCounter));
1658 MachineInstr *WaitcntInstr =
nullptr;
1659 MachineInstr *WaitcntVsCntInstr =
nullptr;
1662 dbgs() <<
"PreGFX12::applyPreexistingWaitcnt at: ";
1664 dbgs() <<
"end of block\n";
1672 if (isNonWaitcntMetaInst(
II)) {
1678 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1682 if (Opcode == AMDGPU::S_WAITCNT) {
1683 unsigned IEnc =
static_cast<unsigned>(
II.getOperand(0).
getImm());
1686 ScoreBrackets.simplifyWaitcnt(OldWait);
1687 ScoreBrackets.simplifyVmemInvOnlyWait(OldWait);
1693 DeferredErasures.erase(&
II);
1694 II.eraseFromParent();
1696 }
else if (!
Wait.hasWaitExceptStoreCnt() && TrySimplify) {
1700 DeferredErasures.insert(&
II);
1703 DeferredErasures.erase(&
II);
1705 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1708 <<
"Before: " <<
Wait <<
'\n';);
1719 II.eraseFromParent();
1720 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1721 unsigned N =
static_cast<unsigned>(
II.getOperand(0).
getImm());
1723 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
1727 assert(Opcode == AMDGPU::S_WAITCNT_VSCNT);
1728 assert(
II.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
1730 unsigned OldVSCnt =
static_cast<unsigned>(
1731 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
1737 if (WaitcntVsCntInstr) {
1738 DeferredErasures.erase(&
II);
1739 II.eraseFromParent();
1741 }
else if (!
Wait.hasWaitStoreCnt() && TrySimplify) {
1743 DeferredErasures.insert(&
II);
1745 WaitcntVsCntInstr = &
II;
1746 DeferredErasures.erase(&
II);
1754 Modified |= promoteSoftWaitCnt(WaitcntInstr);
1763 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
1764 <<
"New Instr at block end: "
1765 << *WaitcntInstr <<
'\n'
1766 :
dbgs() <<
"applied pre-existing waitcnt\n"
1767 <<
"Old Instr: " << *It
1768 <<
"New Instr: " << *WaitcntInstr <<
'\n');
1771 if (WaitcntVsCntInstr) {
1775 Modified |= promoteSoftWaitCnt(WaitcntVsCntInstr);
1781 ?
dbgs() <<
"applied pre-existing waitcnt\n"
1782 <<
"New Instr at block end: " << *WaitcntVsCntInstr
1784 :
dbgs() <<
"applied pre-existing waitcnt\n"
1785 <<
"Old Instr: " << *It
1786 <<
"New Instr: " << *WaitcntVsCntInstr <<
'\n');
1794bool WaitcntGeneratorPreGFX12::createNewWaitcnt(
1796 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
1797 assert(isNormalMode(MaxCounter));
1804 if (
Wait.hasWaitExceptStoreCnt()) {
1806 if (ExpandWaitcntProfiling) {
1810 bool AnyOutOfOrder =
false;
1812 unsigned WaitCnt =
Wait.get(CT);
1813 if (WaitCnt != ~0u && ScoreBrackets.counterOutOfOrder(CT)) {
1814 AnyOutOfOrder =
true;
1819 if (AnyOutOfOrder) {
1827 unsigned WaitCnt =
Wait.get(CT);
1831 unsigned Outstanding =
1832 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
1833 EmitExpandedWaitcnt(Outstanding, WaitCnt, [&](
unsigned Count) {
1845 [[maybe_unused]]
auto SWaitInst =
1850 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1851 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1855 if (
Wait.hasWaitStoreCnt()) {
1861 unsigned Outstanding =
1864 EmitExpandedWaitcnt(
1866 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1867 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1872 [[maybe_unused]]
auto SWaitInst =
1874 .
addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1879 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1880 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1888WaitcntGeneratorPreGFX12::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1889 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt &&
ST.hasVscnt() ? 0 : ~0u);
1893WaitcntGeneratorGFX12Plus::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1894 unsigned ExpertVal = IsExpertMode ? 0 : ~0
u;
1895 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt ? 0 : ~0u, 0, 0, 0,
1897 ~0u , ExpertVal, ExpertVal, ExpertVal);
1904bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt(
1905 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1907 assert(!isNormalMode(MaxCounter));
1910 MachineInstr *CombinedLoadDsCntInstr =
nullptr;
1911 MachineInstr *CombinedStoreDsCntInstr =
nullptr;
1912 MachineInstr *WaitcntDepctrInstr =
nullptr;
1916 dbgs() <<
"GFX12Plus::applyPreexistingWaitcnt at: ";
1918 dbgs() <<
"end of block\n";
1924 AMDGPU::Waitcnt RequiredWait;
1929 if (isNonWaitcntMetaInst(
II)) {
1938 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1942 if (Opcode == AMDGPU::S_WAITCNT)
1945 if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) {
1946 unsigned OldEnc =
static_cast<unsigned>(
1947 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
1952 RequiredWait = RequiredWait.combined(OldWait);
1954 if (CombinedLoadDsCntInstr ==
nullptr) {
1955 CombinedLoadDsCntInstr = &
II;
1957 II.eraseFromParent();
1960 }
else if (Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT) {
1961 unsigned OldEnc =
static_cast<unsigned>(
1962 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
1967 RequiredWait = RequiredWait.combined(OldWait);
1969 if (CombinedStoreDsCntInstr ==
nullptr) {
1970 CombinedStoreDsCntInstr = &
II;
1972 II.eraseFromParent();
1975 }
else if (Opcode == AMDGPU::S_WAITCNT_DEPCTR) {
1976 unsigned OldEnc =
static_cast<unsigned>(
1977 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
1978 AMDGPU::Waitcnt OldWait;
1985 ScoreBrackets.simplifyWaitcnt(OldWait);
1987 if (WaitcntDepctrInstr ==
nullptr) {
1988 WaitcntDepctrInstr = &
II;
1996 unsigned Enc =
static_cast<unsigned>(
1997 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
2008 II.eraseFromParent();
2012 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
2015 II.eraseFromParent();
2017 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
2020 unsigned N =
static_cast<unsigned>(
II.getOperand(0).
getImm());
2021 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
2025 std::optional<AMDGPU::InstCounterType> CT =
2028 unsigned OldCnt =
static_cast<unsigned>(
2029 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->
getImm());
2031 Wait.add(CT.value(), OldCnt);
2033 RequiredWait.add(CT.value(), OldCnt);
2035 if (WaitInstrs[CT.value()] ==
nullptr) {
2036 WaitInstrs[CT.value()] = &
II;
2038 II.eraseFromParent();
2044 ScoreBrackets.simplifyWaitcnt(
Wait.combined(RequiredWait),
Wait);
2045 Wait =
Wait.combined(RequiredWait);
2047 if (CombinedLoadDsCntInstr) {
2063 AMDGPU::OpName::simm16, NewEnc);
2064 Modified |= promoteSoftWaitCnt(CombinedLoadDsCntInstr);
2070 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2071 <<
"New Instr at block end: "
2072 << *CombinedLoadDsCntInstr <<
'\n'
2073 :
dbgs() <<
"applied pre-existing waitcnt\n"
2074 <<
"Old Instr: " << *It <<
"New Instr: "
2075 << *CombinedLoadDsCntInstr <<
'\n');
2082 if (CombinedStoreDsCntInstr) {
2087 AMDGPU::OpName::simm16, NewEnc);
2088 Modified |= promoteSoftWaitCnt(CombinedStoreDsCntInstr);
2094 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2095 <<
"New Instr at block end: "
2096 << *CombinedStoreDsCntInstr <<
'\n'
2097 :
dbgs() <<
"applied pre-existing waitcnt\n"
2098 <<
"Old Instr: " << *It <<
"New Instr: "
2099 << *CombinedStoreDsCntInstr <<
'\n');
2129 for (MachineInstr **WI : WaitsToErase) {
2133 DeferredErasures.erase(*WI);
2134 (*WI)->eraseFromParent();
2141 if (!WaitInstrs[CT])
2144 unsigned NewCnt =
Wait.get(CT);
2145 if (NewCnt != ~0u) {
2147 AMDGPU::OpName::simm16, NewCnt);
2148 Modified |= promoteSoftWaitCnt(WaitInstrs[CT]);
2149 DeferredErasures.erase(WaitInstrs[CT]);
2151 ScoreBrackets.applyWaitcnt(CT, NewCnt);
2155 ?
dbgs() <<
"applied pre-existing waitcnt\n"
2156 <<
"New Instr at block end: " << *WaitInstrs[CT]
2158 :
dbgs() <<
"applied pre-existing waitcnt\n"
2159 <<
"Old Instr: " << *It
2160 <<
"New Instr: " << *WaitInstrs[CT] <<
'\n');
2163 DeferredErasures.insert(WaitInstrs[CT]);
2167 if (WaitcntDepctrInstr) {
2170 unsigned Enc =
static_cast<unsigned>(
2171 TII.getNamedOperand(*WaitcntDepctrInstr, AMDGPU::OpName::simm16)
2191 AMDGPU::OpName::simm16, Enc);
2193 <<
"New Instr at block end: "
2194 << *WaitcntDepctrInstr <<
'\n'
2195 :
dbgs() <<
"applyPreexistingWaitcnt\n"
2196 <<
"Old Instr: " << *It <<
"New Instr: "
2197 << *WaitcntDepctrInstr <<
'\n');
2208bool WaitcntGeneratorGFX12Plus::createNewWaitcnt(
2210 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
2211 assert(!isNormalMode(MaxCounter));
2218 if (ExpandWaitcntProfiling) {
2225 if (ScoreBrackets.counterOutOfOrder(CT)) {
2232 unsigned Outstanding =
2233 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
2234 EmitExpandedWaitcnt(Outstanding,
Count, [&](
unsigned Val) {
2246 MachineInstr *SWaitInst =
nullptr;
2270 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2271 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2283 [[maybe_unused]]
auto SWaitInst =
2290 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2291 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2294 if (
Wait.hasWaitDepctr()) {
2303 [[maybe_unused]]
auto SWaitInst =
2309 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2310 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2329bool SIInsertWaitcnts::generateWaitcntInstBefore(
2330 MachineInstr &
MI, WaitcntBrackets &ScoreBrackets,
2331 MachineInstr *OldWaitcntInstr, PreheaderFlushFlags FlushFlags) {
2336 AMDGPU::Waitcnt
Wait;
2337 const unsigned Opc =
MI.getOpcode();
2340 case AMDGPU::BUFFER_WBINVL1:
2341 case AMDGPU::BUFFER_WBINVL1_SC:
2342 case AMDGPU::BUFFER_WBINVL1_VOL:
2343 case AMDGPU::BUFFER_GL0_INV:
2344 case AMDGPU::BUFFER_GL1_INV: {
2352 case AMDGPU::SI_RETURN_TO_EPILOG:
2353 case AMDGPU::SI_RETURN:
2354 case AMDGPU::SI_WHOLE_WAVE_FUNC_RETURN:
2355 case AMDGPU::S_SETPC_B64_return: {
2360 AMDGPU::Waitcnt AllZeroWait =
2361 WCG->getAllZeroWaitcnt(
false);
2362 ScoreBrackets.simplifyVmemInvOnlyWait(AllZeroWait);
2366 case AMDGPU::S_ENDPGM:
2367 case AMDGPU::S_ENDPGM_SAVED: {
2378 !ScoreBrackets.hasPendingEvent(HWEvents::SCRATCH_WRITE_ACCESS);
2381 case AMDGPU::S_SENDMSG:
2382 case AMDGPU::S_SENDMSGHALT: {
2383 if (
ST.hasLegacyGeometry() &&
2398 if (
MI.modifiesRegister(AMDGPU::EXEC, &
TRI)) {
2401 if (ScoreBrackets.hasPendingEvent(HWEvents::EXP_GPR_LOCK) ||
2402 ScoreBrackets.hasPendingEvent(HWEvents::EXP_PARAM_ACCESS) ||
2403 ScoreBrackets.hasPendingEvent(HWEvents::EXP_POS_ACCESS) ||
2404 ScoreBrackets.hasPendingEvent(HWEvents::GDS_GPR_LOCK)) {
2411 if (
TII.isAlwaysGDS(
Opc) && ScoreBrackets.hasPendingGDS())
2419 Wait = AMDGPU::Waitcnt();
2421 const MachineOperand &CallAddrOp =
TII.getCalleeOperand(
MI);
2422 if (CallAddrOp.
isReg()) {
2423 ScoreBrackets.determineWaitForPhysReg(
2426 if (
const auto *RtnAddrOp =
2427 TII.getNamedOperand(
MI, AMDGPU::OpName::dst)) {
2428 ScoreBrackets.determineWaitForPhysReg(
2429 SmemAccessCounter, RtnAddrOp->getReg().asMCReg(),
Wait,
MI);
2432 }
else if (
Opc == AMDGPU::S_BARRIER_WAIT) {
2433 ScoreBrackets.tryClearSCCWriteEvent(&
MI);
2449 for (
const MachineMemOperand *Memop :
MI.memoperands()) {
2450 const Value *Ptr = Memop->getValue();
2451 if (Memop->isStore()) {
2452 if (
auto It = SLoadAddresses.
find(Ptr); It != SLoadAddresses.
end()) {
2453 Wait.add(SmemAccessCounter, 0);
2455 SLoadAddresses.
erase(It);
2458 unsigned AS = Memop->getAddrSpace();
2462 if (
TII.mayWriteLDSThroughDMA(
MI))
2466 VMEMID TID = LDSDMA_BEGIN;
2467 if (Ptr && Memop->getAAInfo()) {
2468 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2469 for (
size_t I = 0,
E = LDSDMAStores.size();
I !=
E; ++
I) {
2470 if (
MI.mayAlias(AA, *LDSDMAStores[
I],
true)) {
2471 size_t Slot =
I + 1;
2472 if (Slot >= NUM_LDSDMA) {
2480 ScoreBrackets.determineWaitForLDSDMA(
2487 if (Memop->isStore()) {
2493 for (
const MachineOperand &
Op :
MI.operands()) {
2498 if (
Op.isTied() &&
Op.isUse() &&
TII.doesNotReadTiedSource(
MI))
2501 MCRegister
Reg =
Op.getReg().asMCReg();
2503 const bool IsVGPR =
TRI.isVectorRegister(
MRI,
Op.getReg());
2510 if (
Op.isImplicit() &&
MI.mayLoadOrStore())
2528 if (
Op.isUse() || !updateVMCntOnly(
MI) ||
2529 ScoreBrackets.hasDifferentVGPRPendingEvents(
2531 ScoreBrackets.hasPointSamplePendingVmemTypes(
MI,
Reg) ||
2532 !
ST.hasVmemWriteVgprInOrder()) {
2539 ScoreBrackets.clearVGPRPendingEvents(
Reg);
2543 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2548 }
else if (
Op.getReg() == AMDGPU::SCC) {
2551 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter,
Reg,
Wait,
2555 if (
ST.hasWaitXcnt() &&
ST.isXNACKEnabled() &&
Op.isDef())
2574 if (
Opc == AMDGPU::S_BARRIER && !
ST.hasAutoWaitcntBeforeBarrier() &&
2575 !
ST.hasBackOffBarrier()) {
2576 Wait =
Wait.combined(WCG->getAllZeroWaitcnt(
true));
2583 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2588 ScoreBrackets.simplifyWaitcnt(
Wait);
2594 if (
TII.isVALU(
MI,
false)) {
2610 Wait = WCG->getAllZeroWaitcnt(
false);
2614 if (!ForceEmitWaitcnt[
T])
2619 if (FlushFlags.FlushVmCnt) {
2625 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
2631 return generateWaitcnt(
Wait,
MI.getIterator(), *
MI.getParent(), ScoreBrackets,
2635bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt
Wait,
2637 MachineBasicBlock &
Block,
2638 WaitcntBrackets &ScoreBrackets,
2639 MachineInstr *OldWaitcntInstr) {
2642 if (OldWaitcntInstr)
2646 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr,
Wait, It);
2651 MachineOperand *WaitExp =
TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2661 <<
"Update Instr: " << *It);
2664 if (WCG->createNewWaitcnt(
Block, It,
Wait, ScoreBrackets))
2669 ScoreBrackets.applyWaitcnt(
Wait);
2674bool SIInsertWaitcnts::isVmemAccess(
const MachineInstr &
MI)
const {
2675 return (
TII.isFLAT(
MI) &&
TII.mayAccessVMEMThroughFlat(
MI)) ||
2682 MachineBasicBlock *
Block)
const {
2683 auto BlockEnd =
Block->getParent()->end();
2684 auto BlockIter =
Block->getIterator();
2688 if (++BlockIter != BlockEnd) {
2689 It = BlockIter->instr_begin();
2696 if (!It->isMetaInstruction())
2704 return It->getOpcode() == AMDGPU::S_ENDPGM;
2708bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2709 MachineBasicBlock &
Block,
2710 WaitcntBrackets &ScoreBrackets) {
2711 AMDGPU::Waitcnt
Wait;
2712 bool NeedsEndPGMCheck =
false;
2720 NeedsEndPGMCheck =
true;
2723 ScoreBrackets.simplifyWaitcnt(
Wait);
2726 bool Result = generateWaitcnt(
Wait, SuccessorIt,
Block, ScoreBrackets,
2729 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &
Block)) {
2737void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2738 WaitcntBrackets *ScoreBrackets) {
2742 ScoreBrackets->updateByEvent(
E, Inst);
2744 if (
TII.isDS(Inst) &&
TII.usesLGKM_CNT(Inst)) {
2746 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2747 ScoreBrackets->setPendingGDS();
2749 }
else if (
TII.isFLAT(Inst)) {
2751 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2758 ScoreBrackets->setPendingFlat();
2760 }
else if (Inst.
isCall()) {
2763 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(
false));
2764 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2765 }
else if (
TII.isVINTERP(Inst)) {
2766 int64_t
Imm =
TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2776bool WaitcntBrackets::mergeScore(
const MergeInfo &M,
unsigned &Score,
2777 unsigned OtherScore) {
2778 unsigned MyShifted = Score <=
M.OldLB ? 0 : Score +
M.MyShift;
2779 unsigned OtherShifted =
2780 OtherScore <=
M.OtherLB ? 0 : OtherScore +
M.OtherShift;
2781 Score = std::max(MyShifted, OtherShifted);
2782 return OtherShifted > MyShifted;
2788 bool StrictDom =
false;
2791 LLVM_DEBUG(
dbgs() <<
"Merging async marks of stage " << Stage <<
" ...");
2793 if (StageMarks.empty() && OtherMarks.
empty()) {
2800 auto MaxSize = (unsigned)std::max(StageMarks.size(), OtherMarks.
size());
2801 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2804 if (StageMarks.size() > MaxSize)
2805 StageMarks.erase(StageMarks.begin(),
2806 StageMarks.begin() + (StageMarks.size() - MaxSize));
2812 constexpr CounterValueArray ZeroMark{};
2813 StageMarks.insert(StageMarks.begin(), MaxSize - StageMarks.size(), ZeroMark);
2816 dbgs() <<
"Before merge:\n";
2817 for (
const auto &Mark : StageMarks) {
2821 dbgs() <<
"Other marks:\n";
2822 for (
const auto &Mark : OtherMarks) {
2835 const size_t OtherSize = OtherMarks.size();
2836 const size_t OurSize = StageMarks.size();
2843 "StageMarks padded to MaxSize >= 1 (needs MaxAsyncMarks != 0)");
2846 const CounterValueArray &OtherMark =
2847 Idx <= OtherSize ? OtherMarks[OtherSize -
Idx] : ZeroMark;
2850 mergeScore(MergeInfos[
T], StageMarks[OurSize - Idx][
T], OtherMark[
T]);
2854 dbgs() <<
"After merge:\n";
2855 for (
const auto &Mark : StageMarks) {
2869bool WaitcntBrackets::merge(
const WaitcntBrackets &
Other) {
2870 bool StrictDom =
false;
2874 for (
auto K :
Other.VMem.keys())
2875 VMem.try_emplace(K);
2876 for (
auto K :
Other.SGPRs.keys())
2877 SGPRs.try_emplace(K);
2885 const HWEvents OldEvents = PendingEvents & EventsForT;
2886 const HWEvents OtherEvents =
Other.PendingEvents & EventsForT;
2887 if (!OldEvents.
contains(OtherEvents))
2889 PendingEvents |= OtherEvents;
2892 const unsigned MyPending = ScoreUBs[
T] - ScoreLBs[
T];
2893 const unsigned OtherPending =
Other.ScoreUBs[
T] -
Other.ScoreLBs[
T];
2894 const unsigned NewUB = ScoreLBs[
T] + std::max(MyPending, OtherPending);
2895 if (NewUB < ScoreLBs[
T])
2898 MergeInfo &
M = MergeInfos[
T];
2899 M.OldLB = ScoreLBs[
T];
2900 M.OtherLB =
Other.ScoreLBs[
T];
2901 M.MyShift = NewUB - ScoreUBs[
T];
2902 M.OtherShift = NewUB -
Other.ScoreUBs[
T];
2904 ScoreUBs[
T] = NewUB;
2907 StrictDom |= mergeScore(M, LastFlatLoadCnt,
Other.LastFlatLoadCnt);
2910 StrictDom |= mergeScore(M, LastFlatDsCnt,
Other.LastFlatDsCnt);
2911 StrictDom |= mergeScore(M, LastGDS,
Other.LastGDS);
2915 StrictDom |= mergeScore(M, SCCScore,
Other.SCCScore);
2916 if (
Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2917 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2918 PendingSCCWrite =
Other.PendingSCCWrite;
2919 }
else if (PendingSCCWrite !=
Other.PendingSCCWrite) {
2920 PendingSCCWrite =
nullptr;
2925 for (
auto &[RegID, Info] : VMem)
2926 StrictDom |= mergeScore(M,
Info.Scores[
T],
Other.getVMemScore(RegID,
T));
2928 if (isSmemCounter(
T)) {
2929 for (
auto &[RegID, Info] : SGPRs) {
2930 auto It =
Other.SGPRs.find(RegID);
2931 unsigned OtherScore = (It !=
Other.SGPRs.end()) ? It->second.get(
T) : 0;
2932 StrictDom |= mergeScore(M,
Info.get(
T), OtherScore);
2937 for (
auto &[TID, Info] : VMem) {
2938 if (
auto It =
Other.VMem.find(TID); It !=
Other.VMem.end()) {
2940 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
2941 StrictDom |= NewVGPRContext !=
Info.VGPRPendingEvents;
2942 Info.VGPRPendingEvents = NewVGPRContext;
2948 StrictDom |= mergeAsyncMarks(S, MergeInfos,
Other.AsyncMarks[SI]);
2951 mergeScore(MergeInfos[
T], AsyncScore[SI][
T],
Other.AsyncScore[SI][
T]);
2954 purgeEmptyTrackingData();
2960 return Opcode == AMDGPU::S_WAITCNT ||
2963 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
2964 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
2965 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
2966 Opcode == AMDGPU::WAIT_ASYNCMARK ||
2970void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &
MBB,
2972 bool ExpertMode)
const {
2976 .
addImm(ExpertMode ? 2 : 0)
2994class VCCZWorkaround {
2995 const WaitcntBrackets &ScoreBrackets;
2996 const GCNSubtarget &
ST;
2997 const SIInstrInfo &
TII;
2998 const SIRegisterInfo &
TRI;
2999 bool VCCZCorruptionBug =
false;
3000 bool VCCZNotUpdatedByPartialWrites =
false;
3003 bool MustRecomputeVCCZ =
true;
3006 VCCZWorkaround(
const WaitcntBrackets &ScoreBrackets,
const GCNSubtarget &ST,
3007 const SIInstrInfo &
TII,
const SIRegisterInfo &
TRI)
3009 VCCZCorruptionBug =
ST.hasReadVCCZBug();
3010 VCCZNotUpdatedByPartialWrites = !
ST.partialVCCWritesUpdateVCCZ();
3017 bool tryRecomputeVCCZ(MachineInstr &
MI) {
3019 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
3029 MustRecomputeVCCZ |= VCCZCorruptionBug &&
TII.isSMRD(
MI);
3035 std::optional<bool> PartiallyWritesToVCCOpt;
3036 auto PartiallyWritesToVCC = [](MachineInstr &
MI) {
3037 return MI.definesRegister(AMDGPU::VCC_LO,
nullptr) ||
3038 MI.definesRegister(AMDGPU::VCC_HI,
nullptr);
3040 if (VCCZNotUpdatedByPartialWrites) {
3041 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3044 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
3050 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
3051 !VCCZCorruptionBug) {
3053 if (!PartiallyWritesToVCCOpt)
3054 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
3055 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
3056 MI.definesRegister(AMDGPU::VCC,
nullptr);
3059 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
3060 *PartiallyWritesToVCCOpt);
3062 MustRecomputeVCCZ =
false;
3072 TII.get(
ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
3075 MustRecomputeVCCZ =
false;
3086 MachineBasicBlock &
Block,
3087 WaitcntBrackets &ScoreBrackets) {
3091 dbgs() <<
"*** Begin Block: ";
3093 ScoreBrackets.dump();
3095 VCCZWorkaround VCCZW(ScoreBrackets, ST,
TII,
TRI);
3098 MachineInstr *OldWaitcntInstr =
nullptr;
3101 ScoreBrackets.verify();
3104 Iter !=
E; ++Iter) {
3105 MachineInstr &Inst = *Iter;
3106 if (isNonWaitcntMetaInst(Inst))
3111 (IsExpertMode && Inst.
getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3112 if (!OldWaitcntInstr)
3113 OldWaitcntInstr = &Inst;
3117 PreheaderFlushFlags FlushFlags;
3118 if (
Block.getFirstTerminator() == Inst)
3119 FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3122 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3124 OldWaitcntInstr =
nullptr;
3126 if (Inst.
getOpcode() == AMDGPU::ASYNCMARK) {
3135 if (
TII.isSMRD(Inst)) {
3136 for (
const MachineMemOperand *Memop : Inst.
memoperands()) {
3139 if (!Memop->isInvariant()) {
3140 const Value *Ptr = Memop->getValue();
3146 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3150 Modified |= insertForcedWaitAfter(Inst,
Block, ScoreBrackets);
3154 ScoreBrackets.dump();
3159 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3161 ScoreBrackets.verify();
3166 AMDGPU::Waitcnt
Wait;
3167 if (
Block.getFirstTerminator() ==
Block.end()) {
3168 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3169 if (FlushFlags.FlushVmCnt) {
3177 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
3186 dbgs() <<
"*** End Block: ";
3188 ScoreBrackets.dump();
3194bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &
Block) {
3195 if (
Block.size() <= 1)
3203 MachineInstr *LastAtomicWithSoftXcnt =
nullptr;
3207 bool IsLDS =
TII.isDS(
MI) ||
3208 (
TII.isFLAT(
MI) &&
TII.mayAccessLDSThroughFlat(
MI, TgSplit));
3209 if (!IsLDS && (
MI.mayLoad() ^
MI.mayStore()))
3210 LastAtomicWithSoftXcnt =
nullptr;
3214 MachineInstr &PrevMI = *
MI.getPrevNode();
3216 if (PrevMI.
getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3219 if (LastAtomicWithSoftXcnt) {
3223 LastAtomicWithSoftXcnt = &
MI;
3231SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &
MBB,
3232 const WaitcntBrackets &ScoreBrackets) {
3240 return PreheaderFlushFlags();
3244 return PreheaderFlushFlags();
3246 if (
Loop->getLoopPreheader() == &
MBB) {
3247 Iterator->second = getPreheaderFlushFlags(
Loop, ScoreBrackets);
3251 return PreheaderFlushFlags();
3254bool SIInsertWaitcnts::isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const {
3256 return TII.mayAccessVMEMThroughFlat(
MI);
3260bool SIInsertWaitcnts::isDSRead(
const MachineInstr &
MI)
const {
3266bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const {
3295SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *
ML,
3296 const WaitcntBrackets &Brackets) {
3297 PreheaderFlushFlags
Flags;
3298 bool HasVMemLoad =
false;
3299 bool HasVMemStore =
false;
3300 bool UsesVgprVMEMLoadedOutside =
false;
3301 bool UsesVgprDSReadOutside =
false;
3302 bool VMemInvalidated =
false;
3306 bool TrackSimpleDSOpt =
ST.hasExtendedWaitCounts();
3307 DenseSet<MCRegUnit> VgprUse;
3308 DenseSet<MCRegUnit> VgprDefVMEM;
3309 DenseSet<MCRegUnit> VgprDefDS;
3315 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3316 unsigned DSReadPosition = 0;
3317 bool IsSingleBlock =
ML->getNumBlocks() == 1;
3318 bool TrackDSFlushPoint =
ST.hasExtendedWaitCounts() && IsSingleBlock;
3319 unsigned LastDSFlushPosition = 0;
3321 for (MachineBasicBlock *
MBB :
ML->blocks()) {
3322 for (MachineInstr &
MI : *
MBB) {
3323 if (isVMEMOrFlatVMEM(
MI)) {
3324 HasVMemLoad |=
MI.mayLoad();
3325 HasVMemStore |=
MI.mayStore();
3329 if (mayStoreIncrementingDSCNT(
MI)) {
3332 if (VMemInvalidated)
3334 TrackSimpleDSOpt =
false;
3335 TrackDSFlushPoint =
false;
3337 bool IsDSRead = isDSRead(
MI);
3342 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3343 if (!TrackDSFlushPoint)
3345 if (
auto It = LastDSReadPositionMap.
find(RU);
3346 It != LastDSReadPositionMap.
end()) {
3350 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3354 for (
const MachineOperand &
Op :
MI.all_uses()) {
3355 if (
Op.isDebug() || !
TRI.isVectorRegister(
MRI,
Op.getReg()))
3358 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3362 VMemInvalidated =
true;
3366 TrackSimpleDSOpt =
false;
3369 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3373 updateDSReadFlushTracking(RU);
3378 VMEMID
ID = toVMEMID(RU);
3382 UsesVgprVMEMLoadedOutside =
true;
3387 UsesVgprDSReadOutside =
true;
3392 if (isVMEMOrFlatVMEM(
MI) &&
MI.mayLoad()) {
3393 for (
const MachineOperand &
Op :
MI.all_defs()) {
3394 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3398 VMemInvalidated =
true;
3403 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3414 if (IsDSRead || TrackDSFlushPoint) {
3415 for (
const MachineOperand &
Op :
MI.all_defs()) {
3416 if (!
TRI.isVectorRegister(
MRI,
Op.getReg()))
3418 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3421 updateDSReadFlushTracking(RU);
3424 if (TrackDSFlushPoint)
3425 LastDSReadPositionMap[RU] = DSReadPosition;
3434 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3435 ((!
ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3436 (HasVMemLoad &&
ST.hasVmemWriteVgprInOrder())))
3437 Flags.FlushVmCnt =
true;
3443 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3446 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3447 bool DSFlushPointPrefetch =
3448 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3450 if (SimpleDSOpt || DSFlushPointPrefetch)
3451 Flags.FlushDsCnt =
true;
3456bool SIInsertWaitcntsLegacy::runOnMachineFunction(
MachineFunction &MF) {
3457 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3459 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3461 if (
auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3462 AA = &AAR->getAAResults();
3464 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3476 if (!SIInsertWaitcnts(MLI, PDT,
AA, MF).
run())
3481 .preserve<AAManager>();
3484bool SIInsertWaitcnts::run() {
3492 if (ST.hasExtendedWaitCounts()) {
3497 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3502 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3506 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3510 MachineBasicBlock &EntryBB = MF.
front();
3521 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3524 if (
ST.hasExtendedWaitCounts()) {
3533 if (!
ST.hasImageInsts() &&
3542 TII.get(instrsForExtendedCounterTypes[CT]))
3555 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(
this);
3556 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3557 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3564 for (
auto *
MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3567 std::unique_ptr<WaitcntBrackets> Brackets;
3572 for (
auto BII = BlockInfos.
begin(), BIE = BlockInfos.
end(); BII != BIE;
3574 MachineBasicBlock *
MBB = BII->first;
3575 BlockInfo &BI = BII->second;
3581 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3583 *Brackets = *BI.Incoming;
3586 Brackets = std::make_unique<WaitcntBrackets>(
this);
3591 Brackets->~WaitcntBrackets();
3592 new (Brackets.get()) WaitcntBrackets(
this);
3596 if (
ST.hasWaitXcnt() &&
ST.isXNACKEnabled())
3598 Modified |= insertWaitcntInBlock(MF, *
MBB, *Brackets);
3601 if (Brackets->hasPendingEvent()) {
3602 BlockInfo *MoveBracketsToSucc =
nullptr;
3604 auto *SuccBII = BlockInfos.
find(Succ);
3605 BlockInfo &SuccBI = SuccBII->second;
3606 if (!SuccBI.Incoming) {
3607 SuccBI.Dirty =
true;
3608 if (SuccBII <= BII) {
3612 if (!MoveBracketsToSucc) {
3613 MoveBracketsToSucc = &SuccBI;
3615 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3619 dbgs() <<
"Try to merge ";
3625 if (SuccBI.Incoming->merge(*Brackets)) {
3626 SuccBI.Dirty =
true;
3627 if (SuccBII <= BII) {
3634 if (MoveBracketsToSucc)
3635 MoveBracketsToSucc->Incoming = std::move(Brackets);
3640 Modified |= WCG->eraseDeferredWaitcnts();
3642 if (
ST.hasScalarStores()) {
3643 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3644 bool HaveScalarStores =
false;
3646 for (MachineBasicBlock &
MBB : MF) {
3647 for (MachineInstr &
MI :
MBB) {
3648 if (!HaveScalarStores &&
TII.isScalarStore(
MI))
3649 HaveScalarStores =
true;
3651 if (
MI.getOpcode() == AMDGPU::S_ENDPGM ||
3652 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3657 if (HaveScalarStores) {
3666 for (MachineBasicBlock *
MBB : EndPgmBlocks) {
3667 bool SeenDCacheWB =
false;
3671 if (
I->getOpcode() == AMDGPU::S_DCACHE_WB)
3672 SeenDCacheWB =
true;
3673 else if (
TII.isScalarStore(*
I))
3674 SeenDCacheWB =
false;
3677 if ((
I->getOpcode() == AMDGPU::S_ENDPGM ||
3678 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3694 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3696 setSchedulingMode(EntryBB,
I,
true);
3698 for (MachineInstr *
MI : CallInsts) {
3700 setSchedulingMode(
MBB,
MI,
false);
3701 setSchedulingMode(
MBB, std::next(
MI->getIterator()),
true);
3704 for (MachineInstr *
MI : ReturnInsts)
3705 setSchedulingMode(*
MI->getParent(),
MI,
false);
3716 for (
auto [
MI,
_] : EndPgmInsts) {
3718 TII.get(AMDGPU::S_ALLOC_VGPR))
3722 }
else if (!WCG->isOptNone() &&
3723 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3724 (MF.getFrameInfo().hasCalls() ||
3725 ST.getOccupancyWithNumVGPRs(
3726 TRI.getNumUsedPhysRegs(
MRI, AMDGPU::VGPR_32RegClass),
3727 false) <
ST.getMaxWavesPerEU())) {
3728 for (
auto [
MI, Flag] : EndPgmInsts) {
3730 if (
ST.requiresNopBeforeDeallocVGPRs()) {
3732 TII.get(AMDGPU::S_NOP))
3736 TII.get(AMDGPU::S_SENDMSG))
3746 if (
ST.hasWaitXcnt() &&
ST.isXNACKEnabled()) {
3753 TII.get(AMDGPU::S_SETREG_IMM32_B32))
3759 if (
ST.hasRequiresInitialUnclausedVmem()) {
3765 AMDGPU::SGPR64_SGPR65)
3769 TII.get(AMDGPU::GLOBAL_PREFETCH_B8_SADDR))
3770 .
addReg(AMDGPU::SGPR64_SGPR65)
3771 .
addReg(AMDGPU::VGPR0, RegState::Undef)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Shared AMDGPU asyncmark stage definitions.
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)
MachineRegisterInfo & MRI
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 > 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.
Represents analyses that only rely on functions' control flow.
iterator find(const_arg_type_t< KeyT > Val)
bool erase(const KeyT &Val)
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
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.
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 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 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)
constexpr AsyncStages asyncStagesFromMaskOperand(int64_t Mask)
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)
AsyncStages
Bit mask of the async stages tracked by the asyncmark / wait_asyncmark intrinsics.
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)
bool isExpertSchedulingMode(const GCNSubtarget &ST, const Function &F)
Return whether expert scheduling mode is available and enabled for F.
HWEvents getSimplifiedVMEMEventsFor(const MachineInstr &Inst, const SIInstrInfo &TII)
Waitcnt decodeStorecntDscnt(const IsaVersion &Version, unsigned StorecntDscnt)
auto asyncStages(AsyncStages Mask)
unsigned getAsyncStageIndex(AsyncStages Stage)
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...
constexpr unsigned NumAsyncStages
The number of stages, which is also the number of bits in a stage mask.
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 any(E Val)
constexpr bool isMaybeAtomic(const T &...O)
initializer< Ty > init(const Ty &Val)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
This is an optimization pass for GlobalISel generic memory operations.
IterT next_nodbg(IterT It, IterT End, bool SkipPseudoOp=true)
Increment It, then continue incrementing it while it points to a debug instruction.
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
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
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 > >
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.