49#define DEBUG_TYPE "si-insert-waitcnts"
53 cl::desc(
"Force all waitcnt instrs to be emitted as "
54 "s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)"),
58 "amdgpu-waitcnt-load-forcezero",
59 cl::desc(
"Force all waitcnt load counters to wait until 0"),
63 "amdgpu-expert-scheduling-mode",
64 cl::desc(
"Enable expert scheduling mode 2 for all functions (GFX12+ only)"),
69template <
typename EmitWaitcntFn>
70static void EmitExpandedWaitcnt(
unsigned Outstanding,
unsigned Target,
71 EmitWaitcntFn &&EmitWaitcnt) {
73 for (
unsigned I = Outstanding - 1;
I >
Target &&
I != ~0u; --
I)
93 TRACKINGID_RANGE_LEN = (1 << 16),
98 REGUNITS_END = REGUNITS_BEGIN + TRACKINGID_RANGE_LEN,
103 NUM_LDSDMA = TRACKINGID_RANGE_LEN,
104 LDSDMA_BEGIN = REGUNITS_END,
105 LDSDMA_END = LDSDMA_BEGIN + NUM_LDSDMA,
109static constexpr VMEMID toVMEMID(MCRegUnit RU) {
110 return static_cast<unsigned>(RU);
122 AMDGPU::S_WAIT_LOADCNT, AMDGPU::S_WAIT_DSCNT,
123 AMDGPU::S_WAIT_EXPCNT, AMDGPU::S_WAIT_STORECNT,
124 AMDGPU::S_WAIT_SAMPLECNT, AMDGPU::S_WAIT_BVHCNT,
125 AMDGPU::S_WAIT_KMCNT, AMDGPU::S_WAIT_XCNT,
126 AMDGPU::S_WAIT_ASYNCCNT, AMDGPU::S_WAIT_TENSORCNT};
131 switch (
MI.getOpcode()) {
132 case AMDGPU::ASYNCMARK:
133 case AMDGPU::WAIT_ASYNCMARK:
136 return MI.isMetaInstruction();
151class WaitcntBrackets;
159class WaitcntGenerator {
161 const GCNSubtarget &ST;
162 const SIInstrInfo &TII;
163 AMDGPU::IsaVersion IV;
166 bool ExpandWaitcntProfiling =
false;
167 const AMDGPU::HardwareLimits &Limits;
170 WaitcntGenerator() =
delete;
171 WaitcntGenerator(
const WaitcntGenerator &) =
delete;
172 WaitcntGenerator(
const MachineFunction &MF,
174 const AMDGPU::HardwareLimits &Limits)
175 : ST(MF.getSubtarget<GCNSubtarget>()), TII(*ST.getInstrInfo()),
179 ExpandWaitcntProfiling(
180 MF.
getFunction().hasFnAttribute(
"amdgpu-expand-waitcnt-profiling")),
185 bool isOptNone()
const {
return OptNone; }
201 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
202 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
206 bool promoteSoftWaitCnt(MachineInstr *Waitcnt)
const;
211 virtual bool createNewWaitcnt(MachineBasicBlock &
Block,
213 AMDGPU::Waitcnt
Wait,
214 const WaitcntBrackets &ScoreBrackets) = 0;
221 assert(
E.size() == 1 &&
"Cannot handle a mask of events!");
223 if (getWaitEvents(
T) &
E)
234 virtual AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const = 0;
236 virtual ~WaitcntGenerator() =
default;
239class WaitcntGeneratorPreGFX12 final :
public WaitcntGenerator {
242 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_SAMPLER_READ_ACCESS |
243 HWEvents::VMEM_BVH_READ_ACCESS,
244 HWEvents::SMEM_ACCESS | HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS |
245 HWEvents::SQ_MESSAGE,
246 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
247 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
248 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
249 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
261 using WaitcntGenerator::WaitcntGenerator;
263 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
264 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
267 bool createNewWaitcnt(MachineBasicBlock &
Block,
269 AMDGPU::Waitcnt
Wait,
270 const WaitcntBrackets &ScoreBrackets)
override;
273 HWEvents EVs = WaitEventMaskForInstPreGFX12[
T];
279 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
282class WaitcntGeneratorGFX12Plus final :
public WaitcntGenerator {
287 HWEvents::VMEM_READ_ACCESS | HWEvents::GLOBAL_INV_ACCESS,
288 HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS,
289 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
290 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
291 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
293 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
294 HWEvents::VMEM_SAMPLER_READ_ACCESS,
295 HWEvents::VMEM_BVH_READ_ACCESS,
297 HWEvents::SMEM_ACCESS | HWEvents::SQ_MESSAGE | HWEvents::SCC_WRITE,
298 HWEvents::VMEM_GROUP | HWEvents::SMEM_GROUP,
299 HWEvents::ASYNC_ACCESS,
300 HWEvents::TENSOR_ACCESS,
301 HWEvents::VGPR_CSMACC_READ | HWEvents::VGPR_DPMACC_READ |
302 HWEvents::VGPR_TRANS_READ | HWEvents::VGPR_XDL_READ,
303 HWEvents::VGPR_CSMACC_WRITE | HWEvents::VGPR_DPMACC_WRITE |
304 HWEvents::VGPR_TRANS_WRITE | HWEvents::VGPR_XDL_WRITE,
305 HWEvents::VGPR_LDS_READ | HWEvents::VGPR_FLAT_READ |
306 HWEvents::VGPR_VMEM_READ};
309 WaitcntGeneratorGFX12Plus() =
delete;
310 WaitcntGeneratorGFX12Plus(
const MachineFunction &MF,
312 const AMDGPU::HardwareLimits &Limits,
314 : WaitcntGenerator(MF, MaxCounter, Limits), IsExpertMode(IsExpertMode) {}
317 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
318 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &
Wait,
321 bool createNewWaitcnt(MachineBasicBlock &
Block,
323 AMDGPU::Waitcnt
Wait,
324 const WaitcntBrackets &ScoreBrackets)
override;
327 return WaitEventMaskForInstGFX12Plus[
T];
330 AMDGPU::Waitcnt getAllZeroWaitcnt(
bool IncludeVSCnt)
const override;
334struct PreheaderFlushFlags {
335 bool FlushVmCnt =
false;
336 bool FlushDsCnt =
false;
339class SIInsertWaitcnts {
340 DenseMap<const Value *, MachineBasicBlock *> SLoadAddresses;
341 DenseMap<MachineBasicBlock *, PreheaderFlushFlags> PreheadersToFlush;
342 MachineLoopInfo &MLI;
343 MachinePostDominatorTree &PDT;
348 std::unique_ptr<WaitcntBrackets> Incoming;
350 BlockInfo() =
default;
351 BlockInfo(BlockInfo &&) =
default;
352 BlockInfo &operator=(BlockInfo &&) =
default;
356 MapVector<MachineBasicBlock *, BlockInfo> BlockInfos;
360 std::unique_ptr<WaitcntGenerator> WCG;
363 DenseSet<MachineInstr *> CallInsts;
364 DenseSet<MachineInstr *> ReturnInsts;
369 DenseMap<MachineInstr *, bool> EndPgmInsts;
371 AMDGPU::HardwareLimits Limits;
374 const GCNSubtarget &ST;
375 const SIInstrInfo &TII;
376 const SIRegisterInfo &TRI;
377 const MachineRegisterInfo &MRI;
380 bool IsExpertMode =
false;
383 SIInsertWaitcnts(MachineLoopInfo &MLI, MachinePostDominatorTree &PDT,
385 : MLI(MLI), PDT(PDT), AA(AA), MF(MF), ST(MF.getSubtarget<GCNSubtarget>()),
386 TII(*ST.getInstrInfo()), TRI(TII.getRegisterInfo()),
387 MRI(MF.getRegInfo()),
388 TgSplit(ST.hasTgSplitSupport() &&
391 const AMDGPU::HardwareLimits &getLimits()
const {
return Limits; }
393 PreheaderFlushFlags getPreheaderFlushFlags(MachineLoop *
ML,
394 const WaitcntBrackets &Brackets);
395 PreheaderFlushFlags isPreheaderToFlush(MachineBasicBlock &
MBB,
396 const WaitcntBrackets &ScoreBrackets);
397 bool isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const;
398 bool isDSRead(
const MachineInstr &
MI)
const;
399 bool mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const;
402 bool isAsync(
const MachineInstr &
MI)
const {
407 const MachineOperand *
Async =
408 TII.getNamedOperand(
MI, AMDGPU::OpName::IsAsync);
412 bool isNonAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
416 bool isAsyncLdsDmaWrite(
const MachineInstr &
MI)
const {
420 bool shouldUpdateAsyncMark(
const MachineInstr &
MI,
424 if (!isAsyncLdsDmaWrite(
MI))
431 bool isVmemAccess(
const MachineInstr &
MI)
const;
432 bool generateWaitcntInstBefore(MachineInstr &
MI,
433 WaitcntBrackets &ScoreBrackets,
434 MachineInstr *OldWaitcntInstr,
435 PreheaderFlushFlags FlushFlags);
436 bool generateWaitcnt(AMDGPU::Waitcnt
Wait,
438 MachineBasicBlock &
Block, WaitcntBrackets &ScoreBrackets,
439 MachineInstr *OldWaitcntInstr);
440 void updateEventWaitcntAfter(MachineInstr &Inst,
441 WaitcntBrackets *ScoreBrackets);
443 MachineBasicBlock *
Block)
const;
444 bool insertForcedWaitAfter(MachineInstr &Inst, MachineBasicBlock &
Block,
445 WaitcntBrackets &ScoreBrackets);
446 bool insertWaitcntInBlock(MachineFunction &MF, MachineBasicBlock &
Block,
447 WaitcntBrackets &ScoreBrackets);
450 bool removeRedundantSoftXcnts(MachineBasicBlock &
Block);
452 bool ExpertMode)
const;
454 return WCG->getWaitEvents(
T);
457 return WCG->getCounterFromEvent(
E);
469class WaitcntBrackets {
471 WaitcntBrackets(
const SIInsertWaitcnts *Context) : Context(Context) {
472 assert(Context->TRI.getNumRegUnits() < REGUNITS_END);
477 unsigned NumUnusedVmem = 0, NumUnusedSGPRs = 0;
478 for (
auto &[
ID, Val] : VMem) {
482 for (
auto &[
ID, Val] : SGPRs) {
487 if (NumUnusedVmem || NumUnusedSGPRs) {
488 errs() <<
"WaitcntBracket had unused entries at destruction time: "
489 << NumUnusedVmem <<
" VMem and " << NumUnusedSGPRs
490 <<
" SGPR unused entries\n";
501 return ScoreUBs[
T] - ScoreLBs[
T];
505 return getVMemScore(
ID,
T) > getScoreLB(
T);
523 return getScoreUB(
T) - getScoreLB(
T);
527 auto It = SGPRs.find(RU);
528 return It != SGPRs.end() ? It->second.get(
T) : 0;
532 auto It = VMem.find(TID);
533 return It != VMem.end() ? It->second.Scores[
T] : 0;
540 void simplifyWaitcnt(AMDGPU::Waitcnt &
Wait)
const {
543 void simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
544 AMDGPU::Waitcnt &UpdateWait)
const;
547 void simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
548 AMDGPU::Waitcnt &UpdateWait)
const;
549 void simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
550 AMDGPU::Waitcnt &UpdateWait)
const;
553 AMDGPU::Waitcnt &
Wait,
554 const MachineInstr &
MI)
const;
555 MCPhysReg determineVGPR16Dependency(
const MachineInstr &
MI,
559 AMDGPU::Waitcnt &
Wait)
const;
560 AMDGPU::Waitcnt determineAsyncWait(
unsigned N);
561 void tryClearSCCWriteEvent(MachineInstr *Inst);
563 void applyWaitcnt(
const AMDGPU::Waitcnt &
Wait);
567 void recordAsyncMark(MachineInstr &
MI);
569 HWEvents getPendingEvents()
const {
return PendingEvents; }
570 bool hasPendingEvent()
const {
return PendingEvents.
any(); }
571 bool hasPendingEvent(
HWEvents E)
const {
return PendingEvents.contains(
E); }
573 bool HasPending = (PendingEvents & Context->getWaitEvents(
T)).any();
575 "Expected pending events iff scoreboard is not empty");
580 HWEvents Events = PendingEvents & Context->getWaitEvents(
T);
582 return Events.
size() > 1;
585 bool hasPendingFlat()
const {
592 void setPendingFlat() {
597 bool hasPendingGDS()
const {
602 unsigned getPendingGDSWait()
const {
612 for (MCRegUnit RU : regunits(
Reg)) {
613 auto It = VMem.find(toVMEMID(RU));
614 if (It != VMem.end() && (It->second.VGPRPendingEvents & ~
E).any())
621 for (MCRegUnit RU : regunits(
Reg)) {
622 if (
auto It = VMem.find(toVMEMID(RU)); It != VMem.end()) {
624 if (It->second.empty())
630 void setStateOnFunctionEntryOrReturn() {
636 ArrayRef<const MachineInstr *> getLDSDMAStores()
const {
640 bool hasPointSampleAccel(
const MachineInstr &
MI)
const;
641 bool hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
644 void print(raw_ostream &)
const;
649 void purgeEmptyTrackingData();
653 return Context->getLimits().get(
T);
663 using CounterValueArray = std::array<unsigned, AMDGPU::NUM_INST_CNTS>;
666 AMDGPU::Waitcnt &
Wait)
const;
668 static bool mergeScore(
const MergeInfo &M,
unsigned &Score,
669 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;
798 static constexpr unsigned MaxAsyncMarks = 16;
802 CounterValueArray AsyncScore{};
805SIInsertWaitcnts::BlockInfo::~BlockInfo() =
default;
810 SIInsertWaitcntsLegacy() : MachineFunctionPass(ID) {}
812 bool runOnMachineFunction(MachineFunction &MF)
override;
814 StringRef getPassName()
const override {
815 return "SI insert wait instructions";
818 void getAnalysisUsage(AnalysisUsage &AU)
const override {
821 AU.
addRequired<MachinePostDominatorTreeWrapperPass>();
833 setRegScore(
Op.getReg().asMCReg(), CntTy, Score);
841bool WaitcntBrackets::hasPointSampleAccel(
const MachineInstr &
MI)
const {
846 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
856bool WaitcntBrackets::hasPointSamplePendingVmemTypes(
const MachineInstr &
MI,
858 if (!hasPointSampleAccel(
MI))
861 return hasDifferentVGPRPendingEvents(
Reg, HWEvents::VMEM_READ_ACCESS);
864void WaitcntBrackets::updateByEvent(
HWEvents E, MachineInstr &Inst) {
865 assert(
E.size() == 1 &&
"Expected singular event!");
869 unsigned UB = getScoreUB(
T);
873 Context->ST.hasVOP3PX2IncrementsVaVdstTwice()) {
885 setScoreUB(
T, CurrScore);
888 const MachineRegisterInfo &MRI =
Context->MRI;
897 if (
const auto *AddrOp =
TII.getNamedOperand(Inst, AMDGPU::OpName::addr))
901 if (
const auto *Data0 =
902 TII.getNamedOperand(Inst, AMDGPU::OpName::data0))
904 if (
const auto *Data1 =
905 TII.getNamedOperand(Inst, AMDGPU::OpName::data1))
909 Inst.
getOpcode() != AMDGPU::DS_CONSUME &&
910 Inst.
getOpcode() != AMDGPU::DS_ORDERED_COUNT) {
911 for (
const MachineOperand &
Op : Inst.
all_uses()) {
912 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
916 }
else if (
TII.isFLAT(Inst)) {
918 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
921 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
924 }
else if (
TII.isMIMG(Inst)) {
928 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
931 }
else if (
TII.isMTBUF(Inst)) {
934 }
else if (
TII.isMUBUF(Inst)) {
938 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::data),
941 }
else if (
TII.isLDSDIR(Inst)) {
943 setScoreByOperand(*
TII.getNamedOperand(Inst, AMDGPU::OpName::vdst),
946 if (
TII.isEXP(Inst)) {
951 for (MachineOperand &DefMO : Inst.
all_defs()) {
952 if (
TRI.isVGPR(MRI, DefMO.getReg())) {
957 for (
const MachineOperand &
Op : Inst.
all_uses()) {
958 if (
TRI.isVectorRegister(MRI,
Op.getReg()))
964 E == HWEvents::SMEM_GROUP ? HWEvents::VMEM_GROUP : HWEvents::SMEM_GROUP;
965 if (PendingEvents.
contains(OtherEvent)) {
970 setScoreLB(
T, getScoreUB(
T) - 1);
971 PendingEvents -= OtherEvent;
973 for (
const MachineOperand &
Op : Inst.
all_uses())
974 setScoreByOperand(
Op,
T, CurrScore);
979 for (
const MachineOperand &
Op : Inst.
operands()) {
992 setScoreByOperand(
Op,
T, CurrScore);
1004 for (
const MachineOperand &
Op : Inst.
defs()) {
1007 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
1009 if (updateVMCntOnly(Inst)) {
1018 if (hasPointSampleAccel(Inst))
1019 VGPRContext |= HWEvents::VMEM_READ_ACCESS;
1020 for (MCRegUnit RU : regunits(
Op.getReg().asMCReg()))
1021 VMem[toVMEMID(RU)].VGPRPendingEvents |= VGPRContext;
1024 setScoreByOperand(
Op,
T, CurrScore);
1027 (
TII.isDS(Inst) ||
Context->isNonAsyncLdsDmaWrite(Inst))) {
1036 if (!MemOp->isStore() ||
1041 auto AAI = MemOp->getAAInfo();
1047 if (!AAI || !AAI.Scope)
1049 for (
unsigned I = 0,
E = LDSDMAStores.
size();
I !=
E && !Slot; ++
I) {
1050 for (
const auto *MemOp : LDSDMAStores[
I]->memoperands()) {
1051 if (MemOp->isStore() && AAI == MemOp->getAAInfo()) {
1066 setVMemScore(LDSDMA_BEGIN,
T, CurrScore);
1067 if (Slot && Slot < NUM_LDSDMA)
1068 setVMemScore(LDSDMA_BEGIN + Slot,
T, CurrScore);
1071 if (
Context->shouldUpdateAsyncMark(Inst,
T)) {
1072 AsyncScore[
T] = CurrScore;
1076 setRegScore(AMDGPU::SCC,
T, CurrScore);
1077 PendingSCCWrite = &Inst;
1082void WaitcntBrackets::recordAsyncMark(MachineInstr &Inst) {
1088 AsyncMarks.push_back(AsyncScore);
1091 dbgs() <<
"recordAsyncMark:\n" << Inst;
1092 for (
const auto &Mark : AsyncMarks) {
1099void WaitcntBrackets::print(raw_ostream &OS)
const {
1103 unsigned SR = getScoreRange(
T);
1106 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM") <<
"_CNT("
1110 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM") <<
"_CNT("
1114 OS <<
" EXP_CNT(" << SR <<
"):";
1117 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS") <<
"_CNT("
1121 OS <<
" SAMPLE_CNT(" << SR <<
"):";
1124 OS <<
" BVH_CNT(" << SR <<
"):";
1127 OS <<
" KM_CNT(" << SR <<
"):";
1130 OS <<
" X_CNT(" << SR <<
"):";
1133 OS <<
" ASYNC_CNT(" << SR <<
"):";
1136 OS <<
" VA_VDST_RD(" << SR <<
"): ";
1139 OS <<
" VA_VDST_WR(" << SR <<
"): ";
1142 OS <<
" VM_VSRC(" << SR <<
"): ";
1145 OS <<
" UNKNOWN(" << SR <<
"):";
1151 unsigned LB = getScoreLB(
T);
1154 sort(SortedVMEMIDs);
1156 for (
auto ID : SortedVMEMIDs) {
1157 unsigned RegScore = VMem.at(
ID).Scores[
T];
1160 unsigned RelScore = RegScore - LB - 1;
1161 if (
ID < REGUNITS_END) {
1162 OS <<
' ' << RelScore <<
':'
1165 assert(
ID >= LDSDMA_BEGIN &&
ID < LDSDMA_END &&
1166 "Unhandled/unexpected ID value!");
1167 OS <<
' ' << RelScore <<
":LDSDMA" <<
ID;
1172 if (isSmemCounter(
T)) {
1174 sort(SortedSMEMIDs);
1175 for (
auto ID : SortedSMEMIDs) {
1176 unsigned RegScore = SGPRs.at(
ID).get(
T);
1179 unsigned RelScore = RegScore - LB - 1;
1180 OS <<
' ' << RelScore <<
':'
1186 OS <<
' ' << SCCScore <<
":scc";
1191 OS <<
"Pending Events: ";
1192 if (hasPendingEvent()) {
1193 OS << getPendingEvents();
1199 OS <<
"Async score: ";
1200 if (AsyncScore.empty())
1206 OS <<
"Async marks: " << AsyncMarks.size() <<
'\n';
1208 for (
const auto &Mark : AsyncMarks) {
1210 unsigned MarkedScore = Mark[
T];
1213 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"LOAD" :
"VM")
1214 <<
"_CNT: " << MarkedScore;
1217 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"DS" :
"LGKM")
1218 <<
"_CNT: " << MarkedScore;
1221 OS <<
" EXP_CNT: " << MarkedScore;
1224 OS <<
" " << (
ST.hasExtendedWaitCounts() ?
"STORE" :
"VS")
1225 <<
"_CNT: " << MarkedScore;
1228 OS <<
" SAMPLE_CNT: " << MarkedScore;
1231 OS <<
" BVH_CNT: " << MarkedScore;
1234 OS <<
" KM_CNT: " << MarkedScore;
1237 OS <<
" X_CNT: " << MarkedScore;
1240 OS <<
" ASYNC_CNT: " << MarkedScore;
1243 OS <<
" UNKNOWN: " << MarkedScore;
1254void WaitcntBrackets::simplifyWaitcnt(
const AMDGPU::Waitcnt &CheckWait,
1255 AMDGPU::Waitcnt &UpdateWait)
const {
1263 simplifyXcnt(CheckWait, UpdateWait);
1266 simplifyVmVsrc(CheckWait, UpdateWait);
1271 unsigned &
Count)
const {
1275 if (
Count >= getScoreRange(
T))
1279void WaitcntBrackets::simplifyWaitcnt(AMDGPU::Waitcnt &
Wait,
1281 unsigned Cnt =
Wait.get(
T);
1282 simplifyWaitcnt(
T, Cnt);
1286void WaitcntBrackets::simplifyXcnt(
const AMDGPU::Waitcnt &CheckWait,
1287 AMDGPU::Waitcnt &UpdateWait)
const {
1297 hasPendingEvent(HWEvents::SMEM_GROUP))
1303 hasPendingEvent(HWEvents::VMEM_GROUP) &&
1310void WaitcntBrackets::simplifyVmVsrc(
const AMDGPU::Waitcnt &CheckWait,
1311 AMDGPU::Waitcnt &UpdateWait)
const {
1321 return Acc |
Context->getWaitEvents(
T);
1323 HWEvents PendingVmemEvents = PendingEvents & VmemEvents;
1325 unsigned CheckCount = CheckWait.
get(
T);
1327 (CheckCount == 0 || !counterOutOfOrder(
T)) &&
1328 (PendingVmemEvents & ~
Context->getWaitEvents(
T)) == 0)
1335void WaitcntBrackets::purgeEmptyTrackingData() {
1336 VMem.remove_if([](
const auto &
P) {
return P.second.empty(); });
1337 SGPRs.remove_if([](
const auto &
P) {
return P.second.empty(); });
1341 unsigned ScoreToWait,
1342 AMDGPU::Waitcnt &
Wait)
const {
1343 const unsigned LB = getScoreLB(
T);
1344 const unsigned UB = getScoreUB(
T);
1347 if ((UB >= ScoreToWait) && (ScoreToWait > LB)) {
1349 !
Context->ST.hasFlatLgkmVMemCountInOrder()) {
1354 }
else if (counterOutOfOrder(
T)) {
1362 unsigned NeededWait = std::min(UB - ScoreToWait, getLimit(
T) - 1);
1363 Wait.add(
T, NeededWait);
1368AMDGPU::Waitcnt WaitcntBrackets::determineAsyncWait(
unsigned N) {
1370 dbgs() <<
"Need " <<
N <<
" async marks. Found " << AsyncMarks.size()
1372 for (
const auto &Mark : AsyncMarks) {
1378 if (AsyncMarks.size() == MaxAsyncMarks) {
1383 LLVM_DEBUG(
dbgs() <<
"Possible truncation. Ensuring a non-trivial wait.\n");
1384 N = std::min(
N, (
unsigned)MaxAsyncMarks - 1);
1387 AMDGPU::Waitcnt
Wait;
1388 if (AsyncMarks.size() <=
N) {
1393 size_t MarkIndex = AsyncMarks.size() -
N - 1;
1394 const auto &RequiredMark = AsyncMarks[MarkIndex];
1396 determineWaitForScore(
T, RequiredMark[
T],
Wait);
1402 dbgs() <<
"Removing " << (MarkIndex + 1)
1403 <<
" async marks after determining wait\n";
1405 AsyncMarks.erase(AsyncMarks.begin(), AsyncMarks.begin() + MarkIndex + 1);
1418MCPhysReg WaitcntBrackets::determineVGPR16Dependency(
const MachineInstr &
MI,
1422 unsigned Size =
Context->TRI.getRegSizeInBits(*RC);
1424 if (
Size != 16 || !
Context->ST.hasD16Writes32BitVgpr())
1434 AMDGPU::Waitcnt
Wait;
1435 for (MCRegUnit RU : regunits(OtherHalf))
1436 determineWaitForScore(
T, getVMemScore(toVMEMID(RU),
T),
Wait);
1439 if (!
Wait.hasWait())
1449 HWEvents Events = MIEvents & OtherHalfEvents;
1450 if (Events.
size() > 1)
1457 AMDGPU::Waitcnt &
Wait,
1458 const MachineInstr &
MI)
const {
1459 if (
Reg == AMDGPU::SCC) {
1460 determineWaitForScore(
T, SCCScore,
Wait);
1464 Reg = determineVGPR16Dependency(
MI,
T,
Reg);
1465 for (MCRegUnit RU : regunits(
Reg))
1466 determineWaitForScore(
1467 T, IsVGPR ? getVMemScore(toVMEMID(RU),
T) : getSGPRScore(RU,
T),
1474 AMDGPU::Waitcnt &
Wait)
const {
1475 assert(TID >= LDSDMA_BEGIN && TID < LDSDMA_END);
1476 determineWaitForScore(
T, getVMemScore(TID,
T),
Wait);
1479void WaitcntBrackets::tryClearSCCWriteEvent(MachineInstr *Inst) {
1482 if (PendingSCCWrite &&
1483 PendingSCCWrite->
getOpcode() == AMDGPU::S_BARRIER_SIGNAL_ISFIRST_IMM &&
1485 HWEvents SCC_WRITE_PendingEvent = HWEvents::SCC_WRITE;
1488 SCC_WRITE_PendingEvent) {
1492 PendingEvents -= SCC_WRITE_PendingEvent;
1493 PendingSCCWrite =
nullptr;
1497void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait) {
1499 applyWaitcnt(
Wait,
T);
1503 const unsigned UB = getScoreUB(
T);
1507 if (counterOutOfOrder(
T))
1509 setScoreLB(
T, std::max(getScoreLB(
T), UB -
Count));
1512 PendingEvents -=
Context->getWaitEvents(
T);
1516 hasPendingEvent(HWEvents::SMEM_GROUP)) {
1520 PendingEvents -= HWEvents::SMEM_GROUP;
1526 else if (
Count == 0)
1527 PendingEvents -= HWEvents::VMEM_GROUP;
1531void WaitcntBrackets::applyWaitcnt(
const AMDGPU::Waitcnt &
Wait,
1533 unsigned Cnt =
Wait.get(
T);
1534 applyWaitcnt(
T, Cnt);
1541 if ((
T ==
Context->SmemAccessCounter &&
1542 hasPendingEvent(HWEvents::SMEM_ACCESS)) ||
1559 static constexpr HWEvents ExtendedImageEvents =
1560 HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
1561 if (!
Context->ST.hasExtendedWaitCounts() &&
1562 (Events & ExtendedImageEvents).any()) {
1563 Events -= ExtendedImageEvents;
1564 Events |= HWEvents::VMEM_READ_ACCESS;
1570 Events -= HWEvents::GLOBAL_INV_ACCESS;
1574 return Events.
size() > 1;
1577 return hasMixedPendingEvents(
T);
1587char SIInsertWaitcntsLegacy::
ID = 0;
1592 return new SIInsertWaitcntsLegacy();
1597 int OpIdx = AMDGPU::getNamedOperandIdx(
MI.getOpcode(),
OpName);
1602 if (NewEnc == MO.
getImm())
1609bool WaitcntGenerator::promoteSoftWaitCnt(MachineInstr *Waitcnt)
const {
1623bool WaitcntGeneratorPreGFX12::applyPreexistingWaitcnt(
1624 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1626 assert(isNormalMode(MaxCounter));
1629 MachineInstr *WaitcntInstr =
nullptr;
1630 MachineInstr *WaitcntVsCntInstr =
nullptr;
1633 dbgs() <<
"PreGFX12::applyPreexistingWaitcnt at: ";
1635 dbgs() <<
"end of block\n";
1643 if (isNonWaitcntMetaInst(
II)) {
1649 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1653 if (Opcode == AMDGPU::S_WAITCNT) {
1654 unsigned IEnc =
II.getOperand(0).getImm();
1657 ScoreBrackets.simplifyWaitcnt(OldWait);
1661 if (WaitcntInstr || (!
Wait.hasWaitExceptStoreCnt() && TrySimplify)) {
1662 II.eraseFromParent();
1666 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1669 <<
"Before: " <<
Wait <<
'\n';);
1680 II.eraseFromParent();
1681 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1682 unsigned N =
II.getOperand(0).getImm();
1684 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1687 assert(Opcode == AMDGPU::S_WAITCNT_VSCNT);
1688 assert(
II.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
1691 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1697 if (WaitcntVsCntInstr || (!
Wait.hasWaitStoreCnt() && TrySimplify)) {
1698 II.eraseFromParent();
1701 WaitcntVsCntInstr = &
II;
1708 Modified |= promoteSoftWaitCnt(WaitcntInstr);
1717 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
1718 <<
"New Instr at block end: "
1719 << *WaitcntInstr <<
'\n'
1720 :
dbgs() <<
"applied pre-existing waitcnt\n"
1721 <<
"Old Instr: " << *It
1722 <<
"New Instr: " << *WaitcntInstr <<
'\n');
1725 if (WaitcntVsCntInstr) {
1729 Modified |= promoteSoftWaitCnt(WaitcntVsCntInstr);
1735 ?
dbgs() <<
"applied pre-existing waitcnt\n"
1736 <<
"New Instr at block end: " << *WaitcntVsCntInstr
1738 :
dbgs() <<
"applied pre-existing waitcnt\n"
1739 <<
"Old Instr: " << *It
1740 <<
"New Instr: " << *WaitcntVsCntInstr <<
'\n');
1748bool WaitcntGeneratorPreGFX12::createNewWaitcnt(
1750 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
1751 assert(isNormalMode(MaxCounter));
1758 if (
Wait.hasWaitExceptStoreCnt()) {
1760 if (ExpandWaitcntProfiling) {
1764 bool AnyOutOfOrder =
false;
1766 unsigned WaitCnt =
Wait.get(CT);
1767 if (WaitCnt != ~0u && ScoreBrackets.counterOutOfOrder(CT)) {
1768 AnyOutOfOrder =
true;
1773 if (AnyOutOfOrder) {
1781 unsigned WaitCnt =
Wait.get(CT);
1785 unsigned Outstanding =
1786 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
1787 EmitExpandedWaitcnt(Outstanding, WaitCnt, [&](
unsigned Count) {
1799 [[maybe_unused]]
auto SWaitInst =
1804 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1805 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1809 if (
Wait.hasWaitStoreCnt()) {
1815 unsigned Outstanding =
1818 EmitExpandedWaitcnt(
1820 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1821 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1826 [[maybe_unused]]
auto SWaitInst =
1828 .
addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1833 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
1834 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
1842WaitcntGeneratorPreGFX12::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1843 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt &&
ST.hasVscnt() ? 0 : ~0u);
1847WaitcntGeneratorGFX12Plus::getAllZeroWaitcnt(
bool IncludeVSCnt)
const {
1848 unsigned ExpertVal = IsExpertMode ? 0 : ~0
u;
1849 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt ? 0 : ~0u, 0, 0, 0,
1851 ~0u , ExpertVal, ExpertVal, ExpertVal);
1858bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt(
1859 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1861 assert(!isNormalMode(MaxCounter));
1864 MachineInstr *CombinedLoadDsCntInstr =
nullptr;
1865 MachineInstr *CombinedStoreDsCntInstr =
nullptr;
1866 MachineInstr *WaitcntDepctrInstr =
nullptr;
1870 dbgs() <<
"GFX12Plus::applyPreexistingWaitcnt at: ";
1872 dbgs() <<
"end of block\n";
1878 AMDGPU::Waitcnt RequiredWait;
1883 if (isNonWaitcntMetaInst(
II)) {
1892 bool TrySimplify = Opcode !=
II.getOpcode() && !OptNone;
1896 if (Opcode == AMDGPU::S_WAITCNT)
1899 if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) {
1901 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1906 RequiredWait = RequiredWait.combined(OldWait);
1908 if (CombinedLoadDsCntInstr ==
nullptr) {
1909 CombinedLoadDsCntInstr = &
II;
1911 II.eraseFromParent();
1914 }
else if (Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT) {
1916 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1921 RequiredWait = RequiredWait.combined(OldWait);
1923 if (CombinedStoreDsCntInstr ==
nullptr) {
1924 CombinedStoreDsCntInstr = &
II;
1926 II.eraseFromParent();
1929 }
else if (Opcode == AMDGPU::S_WAITCNT_DEPCTR) {
1931 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1932 AMDGPU::Waitcnt OldWait;
1939 ScoreBrackets.simplifyWaitcnt(OldWait);
1941 if (WaitcntDepctrInstr ==
nullptr) {
1942 WaitcntDepctrInstr = &
II;
1951 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1962 II.eraseFromParent();
1966 }
else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1969 II.eraseFromParent();
1971 }
else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1974 unsigned N =
II.getOperand(0).getImm();
1975 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
N);
1978 std::optional<AMDGPU::InstCounterType> CT =
1982 TII.getNamedOperand(
II, AMDGPU::OpName::simm16)->getImm();
1984 Wait.add(CT.value(), OldCnt);
1986 RequiredWait.add(CT.value(), OldCnt);
1988 if (WaitInstrs[CT.value()] ==
nullptr) {
1989 WaitInstrs[CT.value()] = &
II;
1991 II.eraseFromParent();
1997 ScoreBrackets.simplifyWaitcnt(
Wait.combined(RequiredWait),
Wait);
1998 Wait =
Wait.combined(RequiredWait);
2000 if (CombinedLoadDsCntInstr) {
2016 AMDGPU::OpName::simm16, NewEnc);
2017 Modified |= promoteSoftWaitCnt(CombinedLoadDsCntInstr);
2023 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2024 <<
"New Instr at block end: "
2025 << *CombinedLoadDsCntInstr <<
'\n'
2026 :
dbgs() <<
"applied pre-existing waitcnt\n"
2027 <<
"Old Instr: " << *It <<
"New Instr: "
2028 << *CombinedLoadDsCntInstr <<
'\n');
2035 if (CombinedStoreDsCntInstr) {
2040 AMDGPU::OpName::simm16, NewEnc);
2041 Modified |= promoteSoftWaitCnt(CombinedStoreDsCntInstr);
2047 LLVM_DEBUG(It.isEnd() ?
dbgs() <<
"applied pre-existing waitcnt\n"
2048 <<
"New Instr at block end: "
2049 << *CombinedStoreDsCntInstr <<
'\n'
2050 :
dbgs() <<
"applied pre-existing waitcnt\n"
2051 <<
"Old Instr: " << *It <<
"New Instr: "
2052 << *CombinedStoreDsCntInstr <<
'\n');
2082 for (MachineInstr **WI : WaitsToErase) {
2086 (*WI)->eraseFromParent();
2093 if (!WaitInstrs[CT])
2096 unsigned NewCnt =
Wait.get(CT);
2097 if (NewCnt != ~0u) {
2099 AMDGPU::OpName::simm16, NewCnt);
2100 Modified |= promoteSoftWaitCnt(WaitInstrs[CT]);
2102 ScoreBrackets.applyWaitcnt(CT, NewCnt);
2106 ?
dbgs() <<
"applied pre-existing waitcnt\n"
2107 <<
"New Instr at block end: " << *WaitInstrs[CT]
2109 :
dbgs() <<
"applied pre-existing waitcnt\n"
2110 <<
"Old Instr: " << *It
2111 <<
"New Instr: " << *WaitInstrs[CT] <<
'\n');
2118 if (WaitcntDepctrInstr) {
2122 TII.getNamedOperand(*WaitcntDepctrInstr, AMDGPU::OpName::simm16)
2142 AMDGPU::OpName::simm16, Enc);
2144 <<
"New Instr at block end: "
2145 << *WaitcntDepctrInstr <<
'\n'
2146 :
dbgs() <<
"applyPreexistingWaitcnt\n"
2147 <<
"Old Instr: " << *It <<
"New Instr: "
2148 << *WaitcntDepctrInstr <<
'\n');
2159bool WaitcntGeneratorGFX12Plus::createNewWaitcnt(
2161 AMDGPU::Waitcnt
Wait,
const WaitcntBrackets &ScoreBrackets) {
2162 assert(!isNormalMode(MaxCounter));
2169 if (ExpandWaitcntProfiling) {
2176 if (ScoreBrackets.counterOutOfOrder(CT)) {
2183 unsigned Outstanding =
2184 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
2185 EmitExpandedWaitcnt(Outstanding,
Count, [&](
unsigned Val) {
2197 MachineInstr *SWaitInst =
nullptr;
2221 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2222 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2234 [[maybe_unused]]
auto SWaitInst =
2241 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2242 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2245 if (
Wait.hasWaitDepctr()) {
2254 [[maybe_unused]]
auto SWaitInst =
2260 if (It !=
Block.instr_end())
dbgs() <<
"Old Instr: " << *It;
2261 dbgs() <<
"New Instr: " << *SWaitInst <<
'\n');
2280bool SIInsertWaitcnts::generateWaitcntInstBefore(
2281 MachineInstr &
MI, WaitcntBrackets &ScoreBrackets,
2282 MachineInstr *OldWaitcntInstr, PreheaderFlushFlags FlushFlags) {
2287 AMDGPU::Waitcnt
Wait;
2288 const unsigned Opc =
MI.getOpcode();
2291 case AMDGPU::BUFFER_WBINVL1:
2292 case AMDGPU::BUFFER_WBINVL1_SC:
2293 case AMDGPU::BUFFER_WBINVL1_VOL:
2294 case AMDGPU::BUFFER_GL0_INV:
2295 case AMDGPU::BUFFER_GL1_INV: {
2303 case AMDGPU::SI_RETURN_TO_EPILOG:
2304 case AMDGPU::SI_RETURN:
2305 case AMDGPU::SI_WHOLE_WAVE_FUNC_RETURN:
2306 case AMDGPU::S_SETPC_B64_return: {
2311 AMDGPU::Waitcnt AllZeroWait =
2312 WCG->getAllZeroWaitcnt(
false);
2317 if (
ST.hasExtendedWaitCounts() &&
2318 !ScoreBrackets.hasPendingEvent(HWEvents::VMEM_READ_ACCESS))
2323 case AMDGPU::S_ENDPGM:
2324 case AMDGPU::S_ENDPGM_SAVED: {
2335 !ScoreBrackets.hasPendingEvent(HWEvents::SCRATCH_WRITE_ACCESS);
2338 case AMDGPU::S_SENDMSG:
2339 case AMDGPU::S_SENDMSGHALT: {
2340 if (
ST.hasLegacyGeometry() &&
2355 if (
MI.modifiesRegister(AMDGPU::EXEC, &
TRI)) {
2358 if (ScoreBrackets.hasPendingEvent(HWEvents::EXP_GPR_LOCK) ||
2359 ScoreBrackets.hasPendingEvent(HWEvents::EXP_PARAM_ACCESS) ||
2360 ScoreBrackets.hasPendingEvent(HWEvents::EXP_POS_ACCESS) ||
2361 ScoreBrackets.hasPendingEvent(HWEvents::GDS_GPR_LOCK)) {
2368 if (
TII.isAlwaysGDS(
Opc) && ScoreBrackets.hasPendingGDS())
2376 Wait = AMDGPU::Waitcnt();
2378 const MachineOperand &CallAddrOp =
TII.getCalleeOperand(
MI);
2379 if (CallAddrOp.
isReg()) {
2380 ScoreBrackets.determineWaitForPhysReg(
2383 if (
const auto *RtnAddrOp =
2384 TII.getNamedOperand(
MI, AMDGPU::OpName::dst)) {
2385 ScoreBrackets.determineWaitForPhysReg(
2386 SmemAccessCounter, RtnAddrOp->getReg().asMCReg(),
Wait,
MI);
2389 }
else if (
Opc == AMDGPU::S_BARRIER_WAIT) {
2390 ScoreBrackets.tryClearSCCWriteEvent(&
MI);
2406 for (
const MachineMemOperand *Memop :
MI.memoperands()) {
2407 const Value *Ptr = Memop->getValue();
2408 if (Memop->isStore()) {
2409 if (
auto It = SLoadAddresses.
find(Ptr); It != SLoadAddresses.
end()) {
2410 Wait.add(SmemAccessCounter, 0);
2412 SLoadAddresses.
erase(It);
2415 unsigned AS = Memop->getAddrSpace();
2419 if (
TII.mayWriteLDSThroughDMA(
MI))
2423 unsigned TID = LDSDMA_BEGIN;
2424 if (Ptr && Memop->getAAInfo()) {
2425 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2426 for (
unsigned I = 0,
E = LDSDMAStores.size();
I !=
E; ++
I) {
2427 if (
MI.mayAlias(AA, *LDSDMAStores[
I],
true)) {
2428 if ((
I + 1) >= NUM_LDSDMA) {
2443 if (Memop->isStore()) {
2449 for (
const MachineOperand &
Op :
MI.operands()) {
2454 if (
Op.isTied() &&
Op.isUse() &&
TII.doesNotReadTiedSource(
MI))
2459 const bool IsVGPR =
TRI.isVectorRegister(MRI,
Op.getReg());
2466 if (
Op.isImplicit() &&
MI.mayLoadOrStore())
2484 if (
Op.isUse() || !updateVMCntOnly(
MI) ||
2485 ScoreBrackets.hasDifferentVGPRPendingEvents(
2487 ScoreBrackets.hasPointSamplePendingVmemTypes(
MI,
Reg) ||
2488 !
ST.hasVmemWriteVgprInOrder()) {
2495 ScoreBrackets.clearVGPRPendingEvents(
Reg);
2499 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2504 }
else if (
Op.getReg() == AMDGPU::SCC) {
2507 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter,
Reg,
Wait,
2511 if (
ST.hasWaitXcnt() &&
Op.isDef())
2530 if (
Opc == AMDGPU::S_BARRIER && !
ST.hasAutoWaitcntBeforeBarrier() &&
2531 !
ST.hasBackOffBarrier()) {
2532 Wait =
Wait.combined(WCG->getAllZeroWaitcnt(
true));
2539 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2544 ScoreBrackets.simplifyWaitcnt(
Wait);
2550 if (
TII.isVALU(
MI,
false)) {
2566 Wait = WCG->getAllZeroWaitcnt(
false);
2570 if (!ForceEmitWaitcnt[
T])
2575 if (FlushFlags.FlushVmCnt) {
2581 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
2587 return generateWaitcnt(
Wait,
MI.getIterator(), *
MI.getParent(), ScoreBrackets,
2591bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt
Wait,
2593 MachineBasicBlock &
Block,
2594 WaitcntBrackets &ScoreBrackets,
2595 MachineInstr *OldWaitcntInstr) {
2598 if (OldWaitcntInstr)
2602 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr,
Wait, It);
2607 MachineOperand *WaitExp =
TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2617 <<
"Update Instr: " << *It);
2620 if (WCG->createNewWaitcnt(
Block, It,
Wait, ScoreBrackets))
2625 ScoreBrackets.applyWaitcnt(
Wait);
2630bool SIInsertWaitcnts::isVmemAccess(
const MachineInstr &
MI)
const {
2631 return (
TII.isFLAT(
MI) &&
TII.mayAccessVMEMThroughFlat(
MI)) ||
2638 MachineBasicBlock *
Block)
const {
2639 auto BlockEnd =
Block->getParent()->end();
2640 auto BlockIter =
Block->getIterator();
2644 if (++BlockIter != BlockEnd) {
2645 It = BlockIter->instr_begin();
2652 if (!It->isMetaInstruction())
2660 return It->getOpcode() == AMDGPU::S_ENDPGM;
2664bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2665 MachineBasicBlock &
Block,
2666 WaitcntBrackets &ScoreBrackets) {
2667 AMDGPU::Waitcnt
Wait;
2668 bool NeedsEndPGMCheck =
false;
2676 NeedsEndPGMCheck =
true;
2679 ScoreBrackets.simplifyWaitcnt(
Wait);
2682 bool Result = generateWaitcnt(
Wait, SuccessorIt,
Block, ScoreBrackets,
2685 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &
Block)) {
2693void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2694 WaitcntBrackets *ScoreBrackets) {
2698 ScoreBrackets->updateByEvent(
E, Inst);
2700 if (
TII.isDS(Inst) &&
TII.usesLGKM_CNT(Inst)) {
2702 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2703 ScoreBrackets->setPendingGDS();
2705 }
else if (
TII.isFLAT(Inst)) {
2707 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2714 ScoreBrackets->setPendingFlat();
2716 }
else if (Inst.
isCall()) {
2719 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(
false));
2720 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2721 }
else if (
TII.isVINTERP(Inst)) {
2722 int64_t
Imm =
TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2732bool WaitcntBrackets::mergeScore(
const MergeInfo &M,
unsigned &Score,
2733 unsigned OtherScore) {
2734 unsigned MyShifted = Score <=
M.OldLB ? 0 : Score +
M.MyShift;
2735 unsigned OtherShifted =
2736 OtherScore <=
M.OtherLB ? 0 : OtherScore +
M.OtherShift;
2737 Score = std::max(MyShifted, OtherShifted);
2738 return OtherShifted > MyShifted;
2743 bool StrictDom =
false;
2747 if (AsyncMarks.empty() && OtherMarks.
empty()) {
2754 auto MaxSize = (unsigned)std::max(AsyncMarks.size(), OtherMarks.
size());
2755 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2758 if (AsyncMarks.size() > MaxSize)
2759 AsyncMarks.erase(AsyncMarks.begin(),
2760 AsyncMarks.begin() + (AsyncMarks.size() - MaxSize));
2766 constexpr CounterValueArray ZeroMark{};
2767 AsyncMarks.insert(AsyncMarks.begin(), MaxSize - AsyncMarks.size(), ZeroMark);
2770 dbgs() <<
"Before merge:\n";
2771 for (
const auto &Mark : AsyncMarks) {
2775 dbgs() <<
"Other marks:\n";
2776 for (
const auto &Mark : OtherMarks) {
2785 unsigned OtherSize = OtherMarks.size();
2786 unsigned OurSize = AsyncMarks.size();
2787 unsigned MergeCount = std::min(OtherSize, OurSize);
2791 if (MergeCount == 0)
2795 StrictDom |= mergeScore(MergeInfos[
T], AsyncMarks[OurSize - Idx][
T],
2796 OtherMarks[OtherSize - Idx][
T]);
2801 dbgs() <<
"After merge:\n";
2802 for (
const auto &Mark : AsyncMarks) {
2816bool WaitcntBrackets::merge(
const WaitcntBrackets &
Other) {
2817 bool StrictDom =
false;
2821 for (
auto K :
Other.VMem.keys())
2822 VMem.try_emplace(K);
2823 for (
auto K :
Other.SGPRs.keys())
2824 SGPRs.try_emplace(K);
2832 const HWEvents OldEvents = PendingEvents & EventsForT;
2833 const HWEvents OtherEvents =
Other.PendingEvents & EventsForT;
2834 if (!OldEvents.
contains(OtherEvents))
2836 PendingEvents |= OtherEvents;
2839 const unsigned MyPending = ScoreUBs[
T] - ScoreLBs[
T];
2840 const unsigned OtherPending =
Other.ScoreUBs[
T] -
Other.ScoreLBs[
T];
2841 const unsigned NewUB = ScoreLBs[
T] + std::max(MyPending, OtherPending);
2842 if (NewUB < ScoreLBs[
T])
2845 MergeInfo &
M = MergeInfos[
T];
2846 M.OldLB = ScoreLBs[
T];
2847 M.OtherLB =
Other.ScoreLBs[
T];
2848 M.MyShift = NewUB - ScoreUBs[
T];
2849 M.OtherShift = NewUB -
Other.ScoreUBs[
T];
2851 ScoreUBs[
T] = NewUB;
2854 StrictDom |= mergeScore(M, LastFlatLoadCnt,
Other.LastFlatLoadCnt);
2857 StrictDom |= mergeScore(M, LastFlatDsCnt,
Other.LastFlatDsCnt);
2858 StrictDom |= mergeScore(M, LastGDS,
Other.LastGDS);
2862 StrictDom |= mergeScore(M, SCCScore,
Other.SCCScore);
2863 if (
Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2864 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2865 PendingSCCWrite =
Other.PendingSCCWrite;
2866 }
else if (PendingSCCWrite !=
Other.PendingSCCWrite) {
2867 PendingSCCWrite =
nullptr;
2872 for (
auto &[RegID, Info] : VMem)
2873 StrictDom |= mergeScore(M,
Info.Scores[
T],
Other.getVMemScore(RegID,
T));
2875 if (isSmemCounter(
T)) {
2876 for (
auto &[RegID, Info] : SGPRs) {
2877 auto It =
Other.SGPRs.find(RegID);
2878 unsigned OtherScore = (It !=
Other.SGPRs.end()) ? It->second.get(
T) : 0;
2879 StrictDom |= mergeScore(M,
Info.get(
T), OtherScore);
2884 for (
auto &[TID, Info] : VMem) {
2885 if (
auto It =
Other.VMem.find(TID); It !=
Other.VMem.end()) {
2887 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
2888 StrictDom |= NewVGPRContext !=
Info.VGPRPendingEvents;
2889 Info.VGPRPendingEvents = NewVGPRContext;
2893 StrictDom |= mergeAsyncMarks(MergeInfos,
Other.AsyncMarks);
2895 StrictDom |= mergeScore(MergeInfos[
T], AsyncScore[
T],
Other.AsyncScore[
T]);
2897 purgeEmptyTrackingData();
2903 return Opcode == AMDGPU::S_WAITCNT ||
2906 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
2907 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
2908 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
2909 Opcode == AMDGPU::WAIT_ASYNCMARK ||
2913void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &
MBB,
2915 bool ExpertMode)
const {
2919 .
addImm(ExpertMode ? 2 : 0)
2937class VCCZWorkaround {
2938 const WaitcntBrackets &ScoreBrackets;
2939 const GCNSubtarget &
ST;
2940 const SIInstrInfo &
TII;
2941 const SIRegisterInfo &
TRI;
2942 bool VCCZCorruptionBug =
false;
2943 bool VCCZNotUpdatedByPartialWrites =
false;
2946 bool MustRecomputeVCCZ =
true;
2949 VCCZWorkaround(
const WaitcntBrackets &ScoreBrackets,
const GCNSubtarget &ST,
2950 const SIInstrInfo &
TII,
const SIRegisterInfo &
TRI)
2952 VCCZCorruptionBug =
ST.hasReadVCCZBug();
2953 VCCZNotUpdatedByPartialWrites = !
ST.partialVCCWritesUpdateVCCZ();
2960 bool tryRecomputeVCCZ(MachineInstr &
MI) {
2962 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
2972 MustRecomputeVCCZ |= VCCZCorruptionBug &&
TII.isSMRD(
MI);
2978 std::optional<bool> PartiallyWritesToVCCOpt;
2979 auto PartiallyWritesToVCC = [](MachineInstr &
MI) {
2980 return MI.definesRegister(AMDGPU::VCC_LO,
nullptr) ||
2981 MI.definesRegister(AMDGPU::VCC_HI,
nullptr);
2983 if (VCCZNotUpdatedByPartialWrites) {
2984 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
2987 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
2993 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
2994 !VCCZCorruptionBug) {
2996 if (!PartiallyWritesToVCCOpt)
2997 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(
MI);
2998 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
2999 MI.definesRegister(AMDGPU::VCC,
nullptr);
3002 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
3003 *PartiallyWritesToVCCOpt);
3005 MustRecomputeVCCZ =
false;
3015 TII.get(
ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
3018 MustRecomputeVCCZ =
false;
3028bool SIInsertWaitcnts::insertWaitcntInBlock(MachineFunction &MF,
3029 MachineBasicBlock &
Block,
3030 WaitcntBrackets &ScoreBrackets) {
3034 dbgs() <<
"*** Begin Block: ";
3036 ScoreBrackets.dump();
3038 VCCZWorkaround VCCZW(ScoreBrackets, ST,
TII,
TRI);
3041 MachineInstr *OldWaitcntInstr =
nullptr;
3046 Iter !=
E; ++Iter) {
3047 MachineInstr &Inst = *Iter;
3048 if (isNonWaitcntMetaInst(Inst))
3053 (IsExpertMode && Inst.
getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3054 if (!OldWaitcntInstr)
3055 OldWaitcntInstr = &Inst;
3059 PreheaderFlushFlags FlushFlags;
3060 if (
Block.getFirstTerminator() == Inst)
3061 FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3064 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3066 OldWaitcntInstr =
nullptr;
3068 if (Inst.
getOpcode() == AMDGPU::ASYNCMARK) {
3072 ScoreBrackets.recordAsyncMark(Inst);
3076 if (
TII.isSMRD(Inst)) {
3077 for (
const MachineMemOperand *Memop : Inst.
memoperands()) {
3080 if (!Memop->isInvariant()) {
3081 const Value *Ptr = Memop->getValue();
3087 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3091 Modified |= insertForcedWaitAfter(Inst,
Block, ScoreBrackets);
3095 ScoreBrackets.dump();
3100 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3105 AMDGPU::Waitcnt
Wait;
3106 if (
Block.getFirstTerminator() ==
Block.end()) {
3107 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(
Block, ScoreBrackets);
3108 if (FlushFlags.FlushVmCnt) {
3116 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(
AMDGPU::DS_CNT))
3125 dbgs() <<
"*** End Block: ";
3127 ScoreBrackets.dump();
3133bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &
Block) {
3134 if (
Block.size() <= 1)
3142 MachineInstr *LastAtomicWithSoftXcnt =
nullptr;
3146 bool IsLDS =
TII.isDS(
MI) ||
3147 (
TII.isFLAT(
MI) &&
TII.mayAccessLDSThroughFlat(
MI, TgSplit));
3148 if (!IsLDS && (
MI.mayLoad() ^
MI.mayStore()))
3149 LastAtomicWithSoftXcnt =
nullptr;
3153 MachineInstr &PrevMI = *
MI.getPrevNode();
3155 if (PrevMI.
getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3158 if (LastAtomicWithSoftXcnt) {
3162 LastAtomicWithSoftXcnt = &
MI;
3170SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &
MBB,
3171 const WaitcntBrackets &ScoreBrackets) {
3172 auto [Iterator, IsInserted] =
3175 return Iterator->second;
3179 return PreheaderFlushFlags();
3183 return PreheaderFlushFlags();
3186 Iterator->second = getPreheaderFlushFlags(Loop, ScoreBrackets);
3187 return Iterator->second;
3190 return PreheaderFlushFlags();
3193bool SIInsertWaitcnts::isVMEMOrFlatVMEM(
const MachineInstr &
MI)
const {
3195 return TII.mayAccessVMEMThroughFlat(
MI);
3199bool SIInsertWaitcnts::isDSRead(
const MachineInstr &
MI)
const {
3205bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(
const MachineInstr &
MI)
const {
3234SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *
ML,
3235 const WaitcntBrackets &Brackets) {
3236 PreheaderFlushFlags
Flags;
3237 bool HasVMemLoad =
false;
3238 bool HasVMemStore =
false;
3239 bool UsesVgprVMEMLoadedOutside =
false;
3240 bool UsesVgprDSReadOutside =
false;
3241 bool VMemInvalidated =
false;
3245 bool TrackSimpleDSOpt =
ST.hasExtendedWaitCounts();
3246 DenseSet<MCRegUnit> VgprUse;
3247 DenseSet<MCRegUnit> VgprDefVMEM;
3248 DenseSet<MCRegUnit> VgprDefDS;
3254 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3255 unsigned DSReadPosition = 0;
3256 bool IsSingleBlock =
ML->getNumBlocks() == 1;
3257 bool TrackDSFlushPoint =
ST.hasExtendedWaitCounts() && IsSingleBlock;
3258 unsigned LastDSFlushPosition = 0;
3260 for (MachineBasicBlock *
MBB :
ML->blocks()) {
3261 for (MachineInstr &
MI : *
MBB) {
3262 if (isVMEMOrFlatVMEM(
MI)) {
3263 HasVMemLoad |=
MI.mayLoad();
3264 HasVMemStore |=
MI.mayStore();
3268 if (mayStoreIncrementingDSCNT(
MI)) {
3271 if (VMemInvalidated)
3273 TrackSimpleDSOpt =
false;
3274 TrackDSFlushPoint =
false;
3276 bool IsDSRead = isDSRead(
MI);
3281 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3282 if (!TrackDSFlushPoint)
3284 if (
auto It = LastDSReadPositionMap.
find(RU);
3285 It != LastDSReadPositionMap.
end()) {
3289 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3293 for (
const MachineOperand &
Op :
MI.all_uses()) {
3294 if (
Op.isDebug() || !
TRI.isVectorRegister(MRI,
Op.getReg()))
3297 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3301 VMemInvalidated =
true;
3305 TrackSimpleDSOpt =
false;
3308 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3312 updateDSReadFlushTracking(RU);
3317 VMEMID
ID = toVMEMID(RU);
3321 UsesVgprVMEMLoadedOutside =
true;
3326 UsesVgprDSReadOutside =
true;
3331 if (isVMEMOrFlatVMEM(
MI) &&
MI.mayLoad()) {
3332 for (
const MachineOperand &
Op :
MI.all_defs()) {
3333 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3337 VMemInvalidated =
true;
3342 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3353 if (IsDSRead || TrackDSFlushPoint) {
3354 for (
const MachineOperand &
Op :
MI.all_defs()) {
3355 if (!
TRI.isVectorRegister(MRI,
Op.getReg()))
3357 for (MCRegUnit RU :
TRI.regunits(
Op.getReg().asMCReg())) {
3360 updateDSReadFlushTracking(RU);
3363 if (TrackDSFlushPoint)
3364 LastDSReadPositionMap[RU] = DSReadPosition;
3373 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3374 ((!
ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3375 (HasVMemLoad &&
ST.hasVmemWriteVgprInOrder())))
3376 Flags.FlushVmCnt =
true;
3382 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3385 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3386 bool DSFlushPointPrefetch =
3387 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3389 if (SimpleDSOpt || DSFlushPointPrefetch)
3390 Flags.FlushDsCnt =
true;
3395bool SIInsertWaitcntsLegacy::runOnMachineFunction(MachineFunction &MF) {
3396 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3398 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3400 if (
auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3401 AA = &AAR->getAAResults();
3403 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3415 if (!SIInsertWaitcnts(MLI, PDT,
AA, MF).
run())
3420 .preserve<AAManager>();
3423bool SIInsertWaitcnts::run() {
3431 if (ST.hasExtendedWaitCounts()) {
3432 IsExpertMode = ST.hasExpertSchedulingMode() &&
3441 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3446 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3450 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3454 MachineBasicBlock &EntryBB = MF.
front();
3465 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3468 if (
ST.hasExtendedWaitCounts()) {
3477 if (!
ST.hasImageInsts() &&
3483 TII.get(instrsForExtendedCounterTypes[CT]))
3496 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(
this);
3497 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3498 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3505 for (
auto *
MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3508 std::unique_ptr<WaitcntBrackets> Brackets;
3513 for (
auto BII = BlockInfos.
begin(), BIE = BlockInfos.
end(); BII != BIE;
3515 MachineBasicBlock *
MBB = BII->first;
3516 BlockInfo &BI = BII->second;
3522 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3524 *Brackets = *BI.Incoming;
3527 Brackets = std::make_unique<WaitcntBrackets>(
this);
3532 Brackets->~WaitcntBrackets();
3533 new (Brackets.get()) WaitcntBrackets(
this);
3537 if (
ST.hasWaitXcnt())
3539 Modified |= insertWaitcntInBlock(MF, *
MBB, *Brackets);
3542 if (Brackets->hasPendingEvent()) {
3543 BlockInfo *MoveBracketsToSucc =
nullptr;
3545 auto *SuccBII = BlockInfos.
find(Succ);
3546 BlockInfo &SuccBI = SuccBII->second;
3547 if (!SuccBI.Incoming) {
3548 SuccBI.Dirty =
true;
3549 if (SuccBII <= BII) {
3553 if (!MoveBracketsToSucc) {
3554 MoveBracketsToSucc = &SuccBI;
3556 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3560 dbgs() <<
"Try to merge ";
3566 if (SuccBI.Incoming->merge(*Brackets)) {
3567 SuccBI.Dirty =
true;
3568 if (SuccBII <= BII) {
3575 if (MoveBracketsToSucc)
3576 MoveBracketsToSucc->Incoming = std::move(Brackets);
3581 if (
ST.hasScalarStores()) {
3582 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3583 bool HaveScalarStores =
false;
3585 for (MachineBasicBlock &
MBB : MF) {
3586 for (MachineInstr &
MI :
MBB) {
3587 if (!HaveScalarStores &&
TII.isScalarStore(
MI))
3588 HaveScalarStores =
true;
3590 if (
MI.getOpcode() == AMDGPU::S_ENDPGM ||
3591 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3596 if (HaveScalarStores) {
3605 for (MachineBasicBlock *
MBB : EndPgmBlocks) {
3606 bool SeenDCacheWB =
false;
3610 if (
I->getOpcode() == AMDGPU::S_DCACHE_WB)
3611 SeenDCacheWB =
true;
3612 else if (
TII.isScalarStore(*
I))
3613 SeenDCacheWB =
false;
3616 if ((
I->getOpcode() == AMDGPU::S_ENDPGM ||
3617 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3633 while (
I != EntryBB.
end() &&
I->isMetaInstruction())
3635 setSchedulingMode(EntryBB,
I,
true);
3637 for (MachineInstr *
MI : CallInsts) {
3638 MachineBasicBlock &
MBB = *
MI->getParent();
3639 setSchedulingMode(
MBB,
MI,
false);
3640 setSchedulingMode(
MBB, std::next(
MI->getIterator()),
true);
3643 for (MachineInstr *
MI : ReturnInsts)
3644 setSchedulingMode(*
MI->getParent(),
MI,
false);
3655 for (
auto [
MI,
_] : EndPgmInsts) {
3657 TII.get(AMDGPU::S_ALLOC_VGPR))
3661 }
else if (!WCG->isOptNone() &&
3662 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3663 (MF.getFrameInfo().hasCalls() ||
3664 ST.getOccupancyWithNumVGPRs(
3665 TRI.getNumUsedPhysRegs(MRI, AMDGPU::VGPR_32RegClass),
3668 for (
auto [
MI, Flag] : EndPgmInsts) {
3670 if (
ST.requiresNopBeforeDeallocVGPRs()) {
3672 TII.get(AMDGPU::S_NOP))
3676 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)
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Printable print(const GCNRegPressure &RP, const GCNSubtarget *ST=nullptr, unsigned DynamicVGPRBlockSize=0)
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
constexpr auto seq_inclusive(T Begin, T End)
Iterate over an integral type from Begin to End inclusive.
static StringRef getCPU(StringRef CPU)
Processes a CPU name.
auto accumulate(R &&Range, E &&Init)
Wrapper for std::accumulate.
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
void interleaveComma(const Container &c, StreamT &os, UnaryFunctor each_fn)
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
LLVM_ABI Printable printRegUnit(MCRegUnit Unit, const TargetRegisterInfo *TRI)
Create Printable object to print register units on a raw_ostream.
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
constexpr auto equal_to(T &&Arg)
Functor variant of std::equal_to that can be used as a UnaryPredicate in functional algorithms like a...
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
char & SIInsertWaitcntsID
@ Async
"Asynchronous" unwind tables (instr precise)
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
void sort(IteratorTy Start, IteratorTy End)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
CodeGenOptLevel
Code generation optimization level.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
iterator_range(Container &&) -> iterator_range< llvm::detail::IterOfRange< Container > >
uint16_t MCPhysReg
An unsigned integer type large enough to represent all physical registers, but not necessarily virtua...
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
@ Increment
Incrementally increasing token ID.
FunctionPass * createSIInsertWaitcntsPass()
AAResults AliasAnalysis
Temporary typedef for legacy code that uses a generic AliasAnalysis pointer or reference.
MCRegisterClass TargetRegisterClass
static constexpr ValueType Default
static constexpr uint64_t encode(Fields... Values)
Represents the hardware counter limits for different wait count types.
Instruction set architecture version.