LLVM 24.0.0git
SIInsertWaitcnts.cpp
Go to the documentation of this file.
1//===- SIInsertWaitcnts.cpp - Insert Wait Instructions --------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9/// \file
10/// Insert wait instructions for memory reads and writes.
11///
12/// Memory reads and writes are issued asynchronously, so we need to insert
13/// S_WAITCNT instructions when we want to access any of their results or
14/// overwrite any register that's used asynchronously.
15///
16/// TODO: This pass currently keeps one timeline per hardware counter. A more
17/// finely-grained approach that keeps one timeline per event type could
18/// sometimes get away with generating weaker s_waitcnt instructions. For
19/// example, when both SMEM and LDS are in flight and we need to wait for
20/// the i-th-last LDS instruction, then an lgkmcnt(i) is actually sufficient,
21/// but the pass will currently generate a conservative lgkmcnt(0) because
22/// multiple event types are in flight.
23//
24//===----------------------------------------------------------------------===//
25
26#include "AMDGPU.h"
27#include "AMDGPUHWEvents.h"
28#include "AMDGPUWaitcntUtils.h"
29#include "GCNSubtarget.h"
37#include "llvm/IR/Dominators.h"
41
42using namespace llvm;
43
45
46#define DEBUG_TYPE "si-insert-waitcnts"
47
48namespace {
49
50template <typename EmitWaitcntFn>
51static void EmitExpandedWaitcnt(unsigned Outstanding, unsigned Target,
52 EmitWaitcntFn &&EmitWaitcnt) {
53 // Emit waitcnts from (Outstanding - 1) down to Target.
54 for (unsigned I = Outstanding - 1; I > Target && I != ~0u; --I)
55 EmitWaitcnt(I);
56 EmitWaitcnt(Target);
57}
58
59/// Integer IDs used to track vector memory locations we may have to wait on.
60/// Encoded as u16 chunks:
61///
62/// [0, REGUNITS_END ): MCRegUnit
63/// [LDSDMA_BEGIN, LDSDMA_END ) : LDS DMA IDs
64///
65/// NOTE: The choice of encoding these as "u16 chunks" is arbitrary.
66/// It gives (2 << 16) - 1 entries per category which is more than enough
67/// for all register units. MCPhysReg is u16 so we don't even support >u16
68/// physical register numbers at this time, let alone >u16 register units.
69/// In any case, an assertion in "WaitcntBrackets" ensures REGUNITS_END
70/// is enough for all register units.
71using VMEMID = uint32_t;
72
73enum : VMEMID {
74 TRACKINGID_RANGE_LEN = (1 << 16),
75
76 // Important: MCRegUnits must always be tracked starting from 0, as we
77 // need to be able to convert between a MCRegUnit and a VMEMID freely.
78 REGUNITS_BEGIN = 0,
79 REGUNITS_END = REGUNITS_BEGIN + TRACKINGID_RANGE_LEN,
80
81 // Note for LDSDMA: LDSDMA_BEGIN corresponds to the "common"
82 // entry, which is updated for all LDS DMA operations encountered.
83 // Specific LDS DMA IDs start at LDSDMA_BEGIN + 1.
84 NUM_LDSDMA = TRACKINGID_RANGE_LEN,
85 LDSDMA_BEGIN = REGUNITS_END,
86 LDSDMA_END = LDSDMA_BEGIN + NUM_LDSDMA,
87};
88
89/// Convert a MCRegUnit to a VMEMID.
90static constexpr VMEMID toVMEMID(MCRegUnit RU) {
91 return static_cast<unsigned>(RU);
92}
93
94} // namespace
95
96namespace {
97
98// Maps values of InstCounterType to the instruction that waits on that
99// counter. Only used if GCNSubtarget::hasExtendedWaitCounts()
100// returns true, and does not cover VA_VDST or VM_VSRC.
101static const unsigned
102 instrsForExtendedCounterTypes[AMDGPU::NUM_EXTENDED_INST_CNTS] = {
103 AMDGPU::S_WAIT_LOADCNT, AMDGPU::S_WAIT_DSCNT,
104 AMDGPU::S_WAIT_EXPCNT, AMDGPU::S_WAIT_STORECNT,
105 AMDGPU::S_WAIT_SAMPLECNT, AMDGPU::S_WAIT_BVHCNT,
106 AMDGPU::S_WAIT_KMCNT, AMDGPU::S_WAIT_XCNT,
107 AMDGPU::S_WAIT_ASYNCCNT, AMDGPU::S_WAIT_TENSORCNT};
108
109// ASYNCMARK and WAIT_ASYNCMARK are meta instructions that emit no hardware
110// code but still need to be processed by this pass for async vmcnt tracking.
111static bool isNonWaitcntMetaInst(const MachineInstr &MI) {
112 switch (MI.getOpcode()) {
113 case AMDGPU::ASYNCMARK:
114 case AMDGPU::WAIT_ASYNCMARK:
115 return false;
116 default:
117 return MI.isMetaInstruction();
118 }
119}
120
121static bool updateVMCntOnly(const MachineInstr &Inst) {
122 return (SIInstrInfo::isVMEM(Inst) && !SIInstrInfo::isFLAT(Inst)) ||
124}
125
126#ifndef NDEBUG
127static bool isNormalMode(AMDGPU::InstCounterType MaxCounter) {
128 return MaxCounter == AMDGPU::NUM_NORMAL_INST_CNTS;
129}
130#endif // NDEBUG
131
132class WaitcntBrackets;
133
134// This abstracts the logic for generating and updating S_WAIT* instructions
135// away from the analysis that determines where they are needed. This was
136// done because the set of counters and instructions for waiting on them
137// underwent a major shift with gfx12, sufficiently so that having this
138// abstraction allows the main analysis logic to be simpler than it would
139// otherwise have had to become.
140class WaitcntGenerator {
141protected:
142 const GCNSubtarget &ST;
143 const SIInstrInfo &TII;
144 AMDGPU::IsaVersion IV;
145 AMDGPU::InstCounterType MaxCounter;
146 bool OptNone;
147 bool ExpandWaitcntProfiling = false;
148 const AMDGPU::HardwareLimits &Limits;
149
150 // Soft waitcnts whose erasures are postponed so we can confirm if they're
151 // truly redundant or if they're waiting on a loop-carried dependency.
152 DenseSet<MachineInstr *> DeferredErasures;
153
154public:
155 WaitcntGenerator() = delete;
156 WaitcntGenerator(const WaitcntGenerator &) = delete;
157 WaitcntGenerator(const MachineFunction &MF,
158 AMDGPU::InstCounterType MaxCounter,
159 const AMDGPU::HardwareLimits &Limits)
160 : ST(MF.getSubtarget<GCNSubtarget>()), TII(*ST.getInstrInfo()),
161 IV(AMDGPU::getIsaVersion(ST.getCPU())), MaxCounter(MaxCounter),
162 OptNone(MF.getFunction().hasOptNone() ||
163 MF.getTarget().getOptLevel() == CodeGenOptLevel::None),
164 ExpandWaitcntProfiling(
165 MF.getFunction().hasFnAttribute("amdgpu-expand-waitcnt-profiling")),
166 Limits(Limits) {}
167
168 // Return true if the current function should be compiled with no
169 // optimization.
170 bool isOptNone() const { return OptNone; }
171
172 unsigned getLimit(AMDGPU::InstCounterType E) const { return Limits.get(E); }
173
174 // Edits an existing sequence of wait count instructions according
175 // to an incoming Waitcnt value, which is itself updated to reflect
176 // any new wait count instructions which may need to be generated by
177 // WaitcntGenerator::createNewWaitcnt(). It will return true if any edits
178 // were made.
179 //
180 // This editing will usually be merely updated operands, but it may also
181 // delete instructions if the incoming Wait value indicates they are not
182 // needed. It may also remove existing instructions for which a wait
183 // is needed if it can be determined that it is better to generate new
184 // instructions later, as can happen on gfx12.
185 virtual bool
186 applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
187 MachineInstr &OldWaitcntInstr, AMDGPU::Waitcnt &Wait,
189
190 bool eraseDeferredWaitcnts() {
191 bool Modified = false;
192 for (MachineInstr *MI : DeferredErasures) {
193 MI->eraseFromParent();
194 Modified = true;
195 }
196 DeferredErasures.clear();
197 return Modified;
198 }
199
200 // Transform a soft waitcnt into a normal one.
201 bool promoteSoftWaitCnt(MachineInstr *Waitcnt) const;
202
203 // Generates new wait count instructions according to the value of
204 // Wait, returning true if any new instructions were created.
205 // ScoreBrackets is used for profiling expansion.
206 virtual bool createNewWaitcnt(MachineBasicBlock &Block,
208 AMDGPU::Waitcnt Wait,
209 const WaitcntBrackets &ScoreBrackets) = 0;
210
211 // Returns the set of HWEvents that corresponds to counter \p T.
212 virtual HWEvents getWaitEvents(AMDGPU::InstCounterType T) const = 0;
213
214 /// \returns the counter that corresponds to event \p E.
215 AMDGPU::InstCounterType getCounterFromEvent(HWEvents E) const {
216 assert(E.size() == 1 && "Cannot handle a mask of events!");
217 for (auto T : AMDGPU::inst_counter_types()) {
218 if (getWaitEvents(T) & E)
219 return T;
220 }
221 llvm_unreachable("event type has no associated counter");
222 }
223
224 // Returns a new waitcnt with all counters except VScnt set to 0. If
225 // IncludeVSCnt is true, VScnt is set to 0, otherwise it is set to ~0u.
226 // AsyncCnt and TensorCnt always default to ~0u (don't wait for it). They
227 // are only updated when a call to @llvm.amdgcn.wait.asyncmark() is
228 // processed.
229 virtual AMDGPU::Waitcnt getAllZeroWaitcnt(bool IncludeVSCnt) const = 0;
230
231 virtual ~WaitcntGenerator() = default;
232};
233
234class WaitcntGeneratorPreGFX12 final : public WaitcntGenerator {
235 static constexpr const HWEvents
236 WaitEventMaskForInstWithoutVscnt[AMDGPU::NUM_INST_CNTS] = {
237 // LOAD_CNT
238 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_SAMPLER_READ_ACCESS |
239 HWEvents::VMEM_BVH_READ_ACCESS | HWEvents::VMEM_INV_ACCESS |
240 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
241 // DS_CNT
242 HWEvents::SMEM_ACCESS | HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS |
243 HWEvents::SQ_MESSAGE,
244 // EXP_CNT
245 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
246 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
247 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
251
252 // Hardware with VScnt has non-empty STORE_CNT wait events.
253 static constexpr const HWEvents
254 WaitEventMaskForInstWithVscnt[AMDGPU::NUM_INST_CNTS] = {
255 // LOAD_CNT
256 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_SAMPLER_READ_ACCESS |
257 HWEvents::VMEM_BVH_READ_ACCESS | HWEvents::VMEM_INV_ACCESS,
258 // DS_CNT
259 HWEvents::SMEM_ACCESS | HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS |
260 HWEvents::SQ_MESSAGE,
261 // EXP_CNT
262 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
263 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
264 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
265 // STORE_CNT
266 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
270
271public:
272 using WaitcntGenerator::WaitcntGenerator;
273 bool applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
274 MachineInstr &OldWaitcntInstr,
275 AMDGPU::Waitcnt &Wait,
277
278 bool createNewWaitcnt(MachineBasicBlock &Block,
280 AMDGPU::Waitcnt Wait,
281 const WaitcntBrackets &ScoreBrackets) override;
282
283 HWEvents getWaitEvents(AMDGPU::InstCounterType T) const override {
284 return ST.hasVscnt() ? WaitEventMaskForInstWithVscnt[T]
285 : WaitEventMaskForInstWithoutVscnt[T];
286 }
287
288 AMDGPU::Waitcnt getAllZeroWaitcnt(bool IncludeVSCnt) const override;
289};
290
291class WaitcntGeneratorGFX12Plus final : public WaitcntGenerator {
292protected:
293 bool IsExpertMode;
294 static constexpr const HWEvents
295 WaitEventMaskForInstGFX12Plus[AMDGPU::NUM_INST_CNTS] = {
296 HWEvents::VMEM_READ_ACCESS | HWEvents::VMEM_INV_ACCESS,
297 HWEvents::LDS_ACCESS | HWEvents::GDS_ACCESS,
298 HWEvents::EXP_GPR_LOCK | HWEvents::GDS_GPR_LOCK |
299 HWEvents::VMW_GPR_LOCK | HWEvents::EXP_PARAM_ACCESS |
300 HWEvents::EXP_POS_ACCESS | HWEvents::EXP_LDS_ACCESS,
301
302 HWEvents::VMEM_WRITE_ACCESS | HWEvents::SCRATCH_WRITE_ACCESS,
303 HWEvents::VMEM_SAMPLER_READ_ACCESS,
304 HWEvents::VMEM_BVH_READ_ACCESS,
305
306 HWEvents::SMEM_ACCESS | HWEvents::SQ_MESSAGE | HWEvents::SCC_WRITE,
307 HWEvents::VMEM_GROUP | HWEvents::SMEM_GROUP,
308 HWEvents::ASYNC_ACCESS,
309 HWEvents::TENSOR_ACCESS,
310 HWEvents::VGPR_CSMACC_READ | HWEvents::VGPR_DPMACC_READ |
311 HWEvents::VGPR_TRANS_READ | HWEvents::VGPR_XDL_READ,
312 HWEvents::VGPR_CSMACC_WRITE | HWEvents::VGPR_DPMACC_WRITE |
313 HWEvents::VGPR_TRANS_WRITE | HWEvents::VGPR_XDL_WRITE,
314 HWEvents::VGPR_LDS_READ | HWEvents::VGPR_FLAT_READ |
315 HWEvents::VGPR_VMEM_READ};
316
317public:
318 WaitcntGeneratorGFX12Plus() = delete;
319 WaitcntGeneratorGFX12Plus(const MachineFunction &MF,
320 AMDGPU::InstCounterType MaxCounter,
321 const AMDGPU::HardwareLimits &Limits,
322 bool IsExpertMode)
323 : WaitcntGenerator(MF, MaxCounter, Limits), IsExpertMode(IsExpertMode) {}
324
325 bool applyPreexistingWaitcnt(WaitcntBrackets &ScoreBrackets,
326 MachineInstr &OldWaitcntInstr,
327 AMDGPU::Waitcnt &Wait,
329
330 bool createNewWaitcnt(MachineBasicBlock &Block,
332 AMDGPU::Waitcnt Wait,
333 const WaitcntBrackets &ScoreBrackets) override;
334
335 HWEvents getWaitEvents(AMDGPU::InstCounterType T) const override {
336 return WaitEventMaskForInstGFX12Plus[T];
337 }
338
339 AMDGPU::Waitcnt getAllZeroWaitcnt(bool IncludeVSCnt) const override;
340};
341
342// Flags indicating which counters should be flushed in a loop preheader.
343struct PreheaderFlushFlags {
344 bool FlushVmCnt = false;
345 bool FlushDsCnt = false;
346};
347
348class SIInsertWaitcnts {
349 DenseMap<const Value *, MachineBasicBlock *> SLoadAddresses;
350 DenseMap<MachineBasicBlock *, PreheaderFlushFlags> PreheadersToFlush;
351 MachineLoopInfo &MLI;
352 MachinePostDominatorTree &PDT;
353 AliasAnalysis *AA = nullptr;
354 MachineFunction &MF;
355
356 struct BlockInfo {
357 std::unique_ptr<WaitcntBrackets> Incoming;
358 bool Dirty = true;
359 BlockInfo() = default;
360 BlockInfo(BlockInfo &&) = default;
361 BlockInfo &operator=(BlockInfo &&) = default;
362 ~BlockInfo();
363 };
364
365 MapVector<MachineBasicBlock *, BlockInfo> BlockInfos;
366
367 bool ForceEmitWaitcnt[AMDGPU::NUM_INST_CNTS] = {};
368
369 std::unique_ptr<WaitcntGenerator> WCG;
370
371 // Remember call and return instructions in the function.
372 DenseSet<MachineInstr *> CallInsts;
373 DenseSet<MachineInstr *> ReturnInsts;
374
375 // Remember all S_ENDPGM instructions. The boolean flag is true if there might
376 // be outstanding stores but definitely no outstanding scratch stores, to help
377 // with insertion of DEALLOC_VGPRS messages.
378 DenseMap<MachineInstr *, bool> EndPgmInsts;
379
380 AMDGPU::HardwareLimits Limits;
381
382public:
383 const GCNSubtarget &ST;
384 const SIInstrInfo &TII;
385 const SIRegisterInfo &TRI;
386 const MachineRegisterInfo &MRI;
387 AMDGPU::InstCounterType SmemAccessCounter;
388 AMDGPU::InstCounterType MaxCounter;
389 bool IsExpertMode = false;
390 const bool TgSplit;
391
392 SIInsertWaitcnts(MachineLoopInfo &MLI, MachinePostDominatorTree &PDT,
394 : MLI(MLI), PDT(PDT), AA(AA), MF(MF), ST(MF.getSubtarget<GCNSubtarget>()),
395 TII(*ST.getInstrInfo()), TRI(TII.getRegisterInfo()),
396 MRI(MF.getRegInfo()),
397 TgSplit(ST.hasTgSplitSupport() &&
398 AMDGPU::isTgSplitEnabled(MF.getFunction())) {}
399
400 const AMDGPU::HardwareLimits &getLimits() const { return Limits; }
401
402 PreheaderFlushFlags getPreheaderFlushFlags(MachineLoop *ML,
403 const WaitcntBrackets &Brackets);
404 PreheaderFlushFlags isPreheaderToFlush(MachineBasicBlock &MBB,
405 const WaitcntBrackets &ScoreBrackets);
406 bool isVMEMOrFlatVMEM(const MachineInstr &MI) const;
407 bool isDSRead(const MachineInstr &MI) const;
408 bool mayStoreIncrementingDSCNT(const MachineInstr &MI) const;
409 bool run();
410
411 bool isVmemAccess(const MachineInstr &MI) const;
412 bool generateWaitcntInstBefore(MachineInstr &MI,
413 WaitcntBrackets &ScoreBrackets,
414 MachineInstr *OldWaitcntInstr,
415 PreheaderFlushFlags FlushFlags);
416 bool generateWaitcnt(AMDGPU::Waitcnt Wait,
418 MachineBasicBlock &Block, WaitcntBrackets &ScoreBrackets,
419 MachineInstr *OldWaitcntInstr);
420 void updateEventWaitcntAfter(MachineInstr &Inst,
421 WaitcntBrackets *ScoreBrackets);
422 bool isNextENDPGM(MachineBasicBlock::instr_iterator It,
423 MachineBasicBlock *Block) const;
424 bool insertForcedWaitAfter(MachineInstr &Inst, MachineBasicBlock &Block,
425 WaitcntBrackets &ScoreBrackets);
426 bool insertWaitcntInBlock(MachineFunction &MF, MachineBasicBlock &Block,
427 WaitcntBrackets &ScoreBrackets);
428 /// Removes redundant Soft Xcnt Waitcnts in \p Block emitted by the Memory
429 /// Legalizer. Returns true if block was modified.
430 bool removeRedundantSoftXcnts(MachineBasicBlock &Block);
431 void setSchedulingMode(MachineBasicBlock &MBB, MachineBasicBlock::iterator I,
432 bool ExpertMode) const;
433 HWEvents getWaitEvents(AMDGPU::InstCounterType T) const {
434 return WCG->getWaitEvents(T);
435 }
436 AMDGPU::InstCounterType getCounterFromEvent(HWEvents E) const {
437 return WCG->getCounterFromEvent(E);
438 }
439};
440
441// This objects maintains the current score brackets of each wait counter, and
442// a per-register scoreboard for each wait counter.
443//
444// We also maintain the latest score for every event type that can change the
445// waitcnt in order to know if there are multiple types of events within
446// the brackets. When multiple types of event happen in the bracket,
447// wait count may get decreased out of order, therefore we need to put in
448// "s_waitcnt 0" before use.
449class WaitcntBrackets {
450public:
451 WaitcntBrackets(const SIInsertWaitcnts *Context) : Context(Context) {
452 assert(Context->TRI.getNumRegUnits() < REGUNITS_END);
453 }
454
455#ifndef NDEBUG
456 ~WaitcntBrackets() {
457 unsigned NumUnusedVmem = 0, NumUnusedSGPRs = 0;
458 for (auto &[ID, Val] : VMem) {
459 if (Val.empty())
460 ++NumUnusedVmem;
461 }
462 for (auto &[ID, Val] : SGPRs) {
463 if (Val.empty())
464 ++NumUnusedSGPRs;
465 }
466
467 if (NumUnusedVmem || NumUnusedSGPRs) {
468 errs() << "WaitcntBracket had unused entries at destruction time: "
469 << NumUnusedVmem << " VMem and " << NumUnusedSGPRs
470 << " SGPR unused entries\n";
471 std::abort();
472 }
473 }
474#endif
475
476 bool isSmemCounter(AMDGPU::InstCounterType T) const {
477 return T == Context->SmemAccessCounter || T == AMDGPU::X_CNT;
478 }
479
480 unsigned getOutstanding(AMDGPU::InstCounterType T) const {
481 return ScoreUBs[T] - ScoreLBs[T];
482 }
483
484 bool hasPendingVMEM(VMEMID ID, AMDGPU::InstCounterType T) const {
485 return getVMemScore(ID, T) > getScoreLB(T);
486 }
487
488 /// \Return true if we have no score entries for counter \p T.
489 bool empty(AMDGPU::InstCounterType T) const { return getScoreRange(T) == 0; }
490
491private:
492 unsigned getScoreLB(AMDGPU::InstCounterType T) const {
494 return ScoreLBs[T];
495 }
496
497 unsigned getScoreUB(AMDGPU::InstCounterType T) const {
499 return ScoreUBs[T];
500 }
501
502 unsigned getScoreRange(AMDGPU::InstCounterType T) const {
503 return getScoreUB(T) - getScoreLB(T);
504 }
505
506 unsigned getSGPRScore(MCRegUnit RU, AMDGPU::InstCounterType T) const {
507 auto It = SGPRs.find(RU);
508 return It != SGPRs.end() ? It->second.get(T) : 0;
509 }
510
511 unsigned getVMemScore(VMEMID TID, AMDGPU::InstCounterType T) const {
512 auto It = VMem.find(TID);
513 return It != VMem.end() ? It->second.Scores[T] : 0;
514 }
515
516 // Number of ordered XDL writes issued after the VA_VDST write tracked for
517 // TID. Only meaningful while that write is pending.
518 unsigned getNumOrderedXDLsAfterWrite(VMEMID TID) const {
519 return std::min(OrderedXDLCount - VMem.at(TID).OrderedXDLCountAtWrite,
520 getLimit(AMDGPU::VA_VDST_WR) - 1);
521 }
522
523public:
524 // Do some internal consistency checks.
525 void verify() {
526 // These track the same underlying hardware counter so the score ranges
527 // should be identical.
528 assert(getScoreLB(AMDGPU::VA_VDST_RD) == getScoreLB(AMDGPU::VA_VDST_WR));
529 assert(getScoreUB(AMDGPU::VA_VDST_RD) == getScoreUB(AMDGPU::VA_VDST_WR));
530 }
531
532 bool merge(const WaitcntBrackets &Other);
533
534 bool counterOutOfOrder(AMDGPU::InstCounterType T) const;
535 void simplifyWaitcnt(AMDGPU::Waitcnt &Wait) const {
536 simplifyWaitcnt(Wait, Wait);
537 }
538 void simplifyWaitcnt(const AMDGPU::Waitcnt &CheckWait,
539 AMDGPU::Waitcnt &UpdateWait) const;
540 void simplifyWaitcnt(AMDGPU::InstCounterType T, unsigned &Count) const;
541 void simplifyWaitcnt(AMDGPU::Waitcnt &Wait, AMDGPU::InstCounterType T) const;
542 void simplifyXcnt(const AMDGPU::Waitcnt &CheckWait,
543 AMDGPU::Waitcnt &UpdateWait) const;
544 void simplifyVmVsrc(const AMDGPU::Waitcnt &CheckWait,
545 AMDGPU::Waitcnt &UpdateWait) const;
546 // GLOBAL_INV/BUFFER_INV increment loadcnt/vmcnt but do not write VGPRs, so
547 // waits that only drain these events can be omitted.
548 void simplifyVmemInvOnlyWait(AMDGPU::Waitcnt &Wait) const {
549 // LOAD_CNT also includes store events on targets without VScnt.
550 HWEvents LoadEvents =
551 PendingEvents & Context->getWaitEvents(AMDGPU::LOAD_CNT);
552 if (LoadEvents == HWEvents::VMEM_INV_ACCESS)
553 Wait.clear(AMDGPU::LOAD_CNT);
554 }
555
556 void determineWaitForPhysReg(AMDGPU::InstCounterType T, MCRegister Reg,
557 AMDGPU::Waitcnt &Wait,
558 const MachineInstr &MI) const;
559 MCRegister determineVGPR16Dependency(const MachineInstr &MI,
561 MCRegister Reg) const;
562 void determineWaitForLDSDMA(AMDGPU::InstCounterType T, VMEMID TID,
563 AMDGPU::Waitcnt &Wait) const;
564 AMDGPU::Waitcnt determineAsyncWait(unsigned N, AMDGPU::AsyncStages Stages);
565 void tryClearSCCWriteEvent(MachineInstr *Inst);
566
567 void applyWaitcnt(const AMDGPU::Waitcnt &Wait);
568 void applyWaitcnt(AMDGPU::InstCounterType T, unsigned Count);
569 void applyWaitcnt(const AMDGPU::Waitcnt &Wait, AMDGPU::InstCounterType T);
570 void updateByEvent(HWEvents E, MachineInstr &MI);
571 void recordAsyncMark(MachineInstr &MI, AMDGPU::AsyncStages Stages);
572
573 HWEvents getPendingEvents() const { return PendingEvents; }
574 bool hasPendingEvent() const { return PendingEvents.any(); }
575 bool hasPendingEvent(HWEvents E) const { return PendingEvents.contains(E); }
576 bool hasPendingEvent(AMDGPU::InstCounterType T) const {
577 bool HasPending = (PendingEvents & Context->getWaitEvents(T)).any();
578 assert(HasPending == !empty(T) &&
579 "Expected pending events iff scoreboard is not empty");
580 return HasPending;
581 }
582
583 bool hasMixedPendingEvents(AMDGPU::InstCounterType T) const {
584 HWEvents Events = PendingEvents & Context->getWaitEvents(T);
585 // Return true if more than one bit is set in Events.
586 return Events.size() > 1;
587 }
588
589 bool hasPendingFlat() const {
590 return ((LastFlatDsCnt > ScoreLBs[AMDGPU::DS_CNT] &&
591 LastFlatDsCnt <= ScoreUBs[AMDGPU::DS_CNT]) ||
592 (LastFlatLoadCnt > ScoreLBs[AMDGPU::LOAD_CNT] &&
593 LastFlatLoadCnt <= ScoreUBs[AMDGPU::LOAD_CNT]));
594 }
595
596 void setPendingFlat() {
597 LastFlatLoadCnt = ScoreUBs[AMDGPU::LOAD_CNT];
598 LastFlatDsCnt = ScoreUBs[AMDGPU::DS_CNT];
599 }
600
601 bool hasPendingGDS() const {
602 return LastGDS > ScoreLBs[AMDGPU::DS_CNT] &&
603 LastGDS <= ScoreUBs[AMDGPU::DS_CNT];
604 }
605
606 unsigned getPendingGDSWait() const {
607 return std::min(getScoreUB(AMDGPU::DS_CNT) - LastGDS,
608 getLimit(AMDGPU::DS_CNT) - 1);
609 }
610
611 void setPendingGDS() { LastGDS = ScoreUBs[AMDGPU::DS_CNT]; }
612
613 // Return true if there might be pending writes to the vgpr-interval by VMEM
614 // instructions where the HWEvents in VGPRContext are not contained in E.
615 bool hasDifferentVGPRPendingEvents(MCRegister Reg, HWEvents E) const {
616 for (MCRegUnit RU : regunits(Reg)) {
617 auto It = VMem.find(toVMEMID(RU));
618 if (It != VMem.end() && (It->second.VGPRPendingEvents & ~E).any())
619 return true;
620 }
621 return false;
622 }
623
624 void clearVGPRPendingEvents(MCRegister Reg) {
625 for (MCRegUnit RU : regunits(Reg)) {
626 if (auto It = VMem.find(toVMEMID(RU)); It != VMem.end()) {
627 It->second.VGPRPendingEvents = HWEvents::NONE;
628 if (It->second.empty())
629 VMem.erase(It);
630 }
631 }
632 }
633
634 void setStateOnFunctionEntryOrReturn() {
635 setScoreUB(AMDGPU::STORE_CNT,
636 getScoreUB(AMDGPU::STORE_CNT) + getLimit(AMDGPU::STORE_CNT));
637 PendingEvents |= Context->getWaitEvents(AMDGPU::STORE_CNT);
638 }
639
640 ArrayRef<const MachineInstr *> getLDSDMAStores() const {
641 return LDSDMAStores;
642 }
643
644 bool hasPointSampleAccel(const MachineInstr &MI) const;
645 bool hasPointSamplePendingVmemTypes(const MachineInstr &MI,
646 MCRegister RU) const;
647
648 void print(raw_ostream &) const;
649 void dump() const { print(dbgs()); }
650
651 // Free up memory by removing empty entries from the DenseMap that track event
652 // scores.
653 void purgeEmptyTrackingData();
654
655private:
656 unsigned getLimit(AMDGPU::InstCounterType T) const {
657 return Context->getLimits().get(T);
658 }
659
660 struct MergeInfo {
661 unsigned OldLB;
662 unsigned OtherLB;
663 unsigned MyShift;
664 unsigned OtherShift;
665 };
666
667 using CounterValueArray = std::array<unsigned, AMDGPU::NUM_INST_CNTS>;
668
669 unsigned getWaitForScore(AMDGPU::InstCounterType T, unsigned Score) const;
670 void determineWaitForScore(AMDGPU::InstCounterType T, unsigned Score,
671 AMDGPU::Waitcnt &Wait) const;
672 void determineWaitForVAVDSTWrite(VMEMID ID, AMDGPU::Waitcnt &Wait) const;
673
674 static bool mergeScore(const MergeInfo &M, unsigned &Score,
675 unsigned OtherScore);
676 bool mergeAsyncMarks(AMDGPU::AsyncStages Stage,
677 ArrayRef<MergeInfo> MergeInfos,
678 ArrayRef<CounterValueArray> OtherMarks);
679
680 iterator_range<MCRegUnitIterator> regunits(MCRegister Reg) const {
681 assert(Reg != AMDGPU::SCC && "Shouldn't be used on SCC");
682 if (!Context->TRI.isInAllocatableClass(Reg))
683 return {{}, {}};
684 return Context->TRI.regunits(Reg);
685 }
686
687 void setScoreLB(AMDGPU::InstCounterType T, unsigned Val) {
689 ScoreLBs[T] = Val;
690 }
691
692 void setScoreUB(AMDGPU::InstCounterType T, unsigned Val) {
694 ScoreUBs[T] = Val;
695
696 if (T != AMDGPU::EXP_CNT)
697 return;
698
699 if (getScoreRange(AMDGPU::EXP_CNT) > getLimit(AMDGPU::EXP_CNT))
700 ScoreLBs[AMDGPU::EXP_CNT] =
701 ScoreUBs[AMDGPU::EXP_CNT] - getLimit(AMDGPU::EXP_CNT);
702 }
703
704 void setRegScore(MCRegister Reg, AMDGPU::InstCounterType T, unsigned Val) {
705 const SIRegisterInfo &TRI = Context->TRI;
706 if (Reg == AMDGPU::SCC) {
707 SCCScore = Val;
708 } else if (TRI.isVectorRegister(Context->MRI, Reg)) {
709 for (MCRegUnit RU : regunits(Reg)) {
710 auto &Info = VMem[toVMEMID(RU)];
711 Info.Scores[T] = Val;
712 if (T == AMDGPU::VA_VDST_WR)
713 Info.OrderedXDLCountAtWrite = OrderedXDLCount;
714 }
715 } else if (TRI.isSGPRReg(Context->MRI, Reg)) {
716 for (MCRegUnit RU : regunits(Reg))
717 SGPRs[RU].get(T) = Val;
718 } else {
719 llvm_unreachable("Register cannot be tracked/unknown register!");
720 }
721 }
722
723 void setVMemScore(VMEMID TID, AMDGPU::InstCounterType T, unsigned Val) {
724 VMem[TID].Scores[T] = Val;
725 }
726
727 void setScoreByOperand(const MachineOperand &Op,
728 AMDGPU::InstCounterType CntTy, unsigned Val);
729
730 const SIInsertWaitcnts *Context;
731
732 unsigned ScoreLBs[AMDGPU::NUM_INST_CNTS] = {0};
733 unsigned ScoreUBs[AMDGPU::NUM_INST_CNTS] = {0};
734 HWEvents PendingEvents;
735 // Remember the last flat memory operation.
736 unsigned LastFlatDsCnt = 0;
737 unsigned LastFlatLoadCnt = 0;
738 // Remember the last GDS operation.
739 unsigned LastGDS = 0;
740 // Number of VA_VDST ordered XDL writes issued so far. Only differences
741 // between values are meaningful, so unsigned wraparound is harmless.
742 unsigned OrderedXDLCount = 0;
743
744 // The score tracking logic is fragmented as follows:
745 // - VMem: VGPR RegUnits and LDS DMA IDs, see the VMEMID encoding.
746 // - SGPRs: SGPR RegUnits
747 // - SCC: Non-allocatable and not general purpose: not a SGPR.
748 //
749 // For the VMem case, if the key is within the range of LDS DMA IDs,
750 // then the corresponding index into the `LDSDMAStores` vector below is:
751 // Key - LDSDMA_BEGIN - 1
752 // This is because LDSDMA_BEGIN is a generic entry and does not have an
753 // associated MachineInstr.
754 //
755 // TODO: Could we track SCC alongside SGPRs so it's not longer a special case?
756
757 struct VMEMInfo {
758 // Scores for all instruction counters. Zero-initialized.
759 CounterValueArray Scores{};
760 // OrderedXDLCount when the VA_VDST_WR score was set. Every ordered XDL
761 // write issued since then remains outstanding while this write is pending.
762 unsigned OrderedXDLCountAtWrite = 0;
763 // For VGPRs, we need to track an additional fine-grained set of pending
764 // events.
765 HWEvents VGPRPendingEvents;
766
767 bool empty() const {
768 return all_of(Scores, equal_to(0)) && !VGPRPendingEvents;
769 }
770 };
771
772 /// Wait cnt scores for every sgpr, the DS_CNT (corresponding to LGKMcnt
773 /// pre-gfx12) or KM_CNT (gfx12+ only), and X_CNT (gfx1250) are relevant.
774 class SGPRInfo {
775 /// Either DS_CNT or KM_CNT score.
776 unsigned ScoreDsKmCnt = 0;
777 unsigned ScoreXCnt = 0;
778
779 public:
780 unsigned get(AMDGPU::InstCounterType T) const {
781 assert(
782 (T == AMDGPU::DS_CNT || T == AMDGPU::KM_CNT || T == AMDGPU::X_CNT) &&
783 "Invalid counter");
784 return T == AMDGPU::X_CNT ? ScoreXCnt : ScoreDsKmCnt;
785 }
786 unsigned &get(AMDGPU::InstCounterType T) {
787 assert(
788 (T == AMDGPU::DS_CNT || T == AMDGPU::KM_CNT || T == AMDGPU::X_CNT) &&
789 "Invalid counter");
790 return T == AMDGPU::X_CNT ? ScoreXCnt : ScoreDsKmCnt;
791 }
792
793 bool empty() const { return !ScoreDsKmCnt && !ScoreXCnt; }
794 };
795
796 DenseMap<VMEMID, VMEMInfo> VMem; // VGPR + LDS DMA
797 DenseMap<MCRegUnit, SGPRInfo> SGPRs;
798
799 // Reg score for SCC.
800 unsigned SCCScore = 0;
801 // The unique instruction that has an SCC write pending, if there is one.
802 const MachineInstr *PendingSCCWrite = nullptr;
803
804 // Store representative LDS DMA operations. The only useful info here is
805 // alias info. One store is kept per unique AAInfo.
806 SmallVector<const MachineInstr *> LDSDMAStores;
807
808 // State of all counters at each async mark encountered so far, per stage.
809 std::array<SmallVector<CounterValueArray, 0>, AMDGPU::NumAsyncStages>
810 AsyncMarks;
811
812 // But in the rare pathological case, a nest of loops that pushes marks
813 // without waiting on any mark can cause AsyncMarks to grow very large. We cap
814 // it to a reasonable limit. We can tune this later or potentially introduce a
815 // user option to control the value.
816 static constexpr unsigned MaxAsyncMarks = 16;
817
818 // Track the upper bound score for async operations that are not part of a
819 // mark yet, per stage. Initialized to all zeros.
820 std::array<CounterValueArray, AMDGPU::NumAsyncStages> AsyncScore{};
821};
822
823SIInsertWaitcnts::BlockInfo::~BlockInfo() = default;
824
825class SIInsertWaitcntsLegacy : public MachineFunctionPass {
826public:
827 static char ID;
828 SIInsertWaitcntsLegacy() : MachineFunctionPass(ID) {}
829
830 bool runOnMachineFunction(MachineFunction &MF) override;
831
832 StringRef getPassName() const override {
833 return "SI insert wait instructions";
834 }
835
836 void getAnalysisUsage(AnalysisUsage &AU) const override {
837 AU.setPreservesCFG();
838 AU.addRequired<MachineLoopInfoWrapperPass>();
839 AU.addRequired<MachinePostDominatorTreeWrapperPass>();
840 AU.addUsedIfAvailable<AAResultsWrapperPass>();
841 AU.addPreserved<AAResultsWrapperPass>();
843 }
844};
845
846} // end anonymous namespace
847
848void WaitcntBrackets::setScoreByOperand(const MachineOperand &Op,
850 unsigned Score) {
851 setRegScore(Op.getReg().asMCReg(), CntTy, Score);
852}
853
854// Return true if the subtarget is one that enables Point Sample Acceleration
855// and the MachineInstr passed in is one to which it might be applied (the
856// hardware makes this decision based on several factors, but we can't determine
857// this at compile time, so we have to assume it might be applied if the
858// instruction supports it).
859bool WaitcntBrackets::hasPointSampleAccel(const MachineInstr &MI) const {
860 if (!Context->ST.hasPointSampleAccel() || !SIInstrInfo::isMIMG(MI))
861 return false;
862
863 const AMDGPU::MIMGInfo *Info = AMDGPU::getMIMGInfo(MI.getOpcode());
864 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
866 return BaseInfo->PointSampleAccel;
867}
868
869// Return true if the subtarget enables Point Sample Acceleration, the supplied
870// MachineInstr is one to which it might be applied and the supplied interval is
871// one that has outstanding writes to vmem-types different than VMEM_NOSAMPLER
872// (this is the type that a point sample accelerated instruction effectively
873// becomes)
874bool WaitcntBrackets::hasPointSamplePendingVmemTypes(const MachineInstr &MI,
875 MCRegister Reg) const {
876 if (!hasPointSampleAccel(MI))
877 return false;
878
879 return hasDifferentVGPRPendingEvents(Reg, HWEvents::VMEM_READ_ACCESS);
880}
881
882void WaitcntBrackets::updateByEvent(HWEvents E, MachineInstr &Inst) {
883 assert(E.size() == 1 && "Expected singular event!");
884 AMDGPU::InstCounterType T = Context->getCounterFromEvent(E);
885 assert(T < Context->MaxCounter);
886
887 unsigned UB = getScoreUB(T);
888 unsigned Increment = 1;
889 if ((T == AMDGPU::VA_VDST_RD || T == AMDGPU::VA_VDST_WR) &&
891 Context->ST.hasVOP3PX2IncrementsVaVdstTwice()) {
892 // V_WMMA_SCALE instructions use VOP3PX2 encoding. Hardware treats this as
893 // two VOP3P instructions and increments VA_VDST twice.
894 Increment = 2;
895 }
896 unsigned CurrScore = UB + Increment;
897 if (CurrScore <= UB)
898 report_fatal_error("InsertWaitcnt score wraparound");
899 // PendingEvents and ScoreUB need to be update regardless if this event
900 // changes the score of a register or not.
901 // Examples including vm_cnt when buffer-store or lgkm_cnt when send-message.
902 PendingEvents |= E;
903 setScoreUB(T, CurrScore);
904
905 // Count this write before recording its own destination, so the
906 // destination does not count itself. A VOP3PX2 WMMA counts once even though
907 // it increments VA_VDST twice: only its WMMA half is an ordered XDL write.
908 if (E == HWEvents::VGPR_XDL_WRITE && Context->TII.isVAVDSTOrderedXDL(Inst))
909 ++OrderedXDLCount;
910
911 const SIRegisterInfo &TRI = Context->TRI;
912 const MachineRegisterInfo &MRI = Context->MRI;
913 const SIInstrInfo &TII = Context->TII;
914
915 if (T == AMDGPU::EXP_CNT) {
916 // Put score on the source vgprs. If this is a store, just use those
917 // specific register(s).
918 if (TII.isDS(Inst) && Inst.mayLoadOrStore()) {
919 // All GDS operations must protect their address register (same as
920 // export.)
921 if (const auto *AddrOp = TII.getNamedOperand(Inst, AMDGPU::OpName::addr))
922 setScoreByOperand(*AddrOp, AMDGPU::EXP_CNT, CurrScore);
923
924 if (Inst.mayStore()) {
925 if (const auto *Data0 =
926 TII.getNamedOperand(Inst, AMDGPU::OpName::data0))
927 setScoreByOperand(*Data0, AMDGPU::EXP_CNT, CurrScore);
928 if (const auto *Data1 =
929 TII.getNamedOperand(Inst, AMDGPU::OpName::data1))
930 setScoreByOperand(*Data1, AMDGPU::EXP_CNT, CurrScore);
931 } else if (SIInstrInfo::isAtomicRet(Inst) && !SIInstrInfo::isGWS(Inst) &&
932 Inst.getOpcode() != AMDGPU::DS_APPEND &&
933 Inst.getOpcode() != AMDGPU::DS_CONSUME &&
934 Inst.getOpcode() != AMDGPU::DS_ORDERED_COUNT) {
935 for (const MachineOperand &Op : Inst.all_uses()) {
936 if (TRI.isVectorRegister(MRI, Op.getReg()))
937 setScoreByOperand(Op, AMDGPU::EXP_CNT, CurrScore);
938 }
939 }
940 } else if (TII.isFLAT(Inst)) {
941 if (Inst.mayStore()) {
942 setScoreByOperand(*TII.getNamedOperand(Inst, AMDGPU::OpName::data),
943 AMDGPU::EXP_CNT, CurrScore);
944 } else if (SIInstrInfo::isAtomicRet(Inst)) {
945 setScoreByOperand(*TII.getNamedOperand(Inst, AMDGPU::OpName::data),
946 AMDGPU::EXP_CNT, CurrScore);
947 }
948 } else if (TII.isMIMG(Inst)) {
949 if (Inst.mayStore()) {
950 setScoreByOperand(Inst.getOperand(0), AMDGPU::EXP_CNT, CurrScore);
951 } else if (SIInstrInfo::isAtomicRet(Inst)) {
952 setScoreByOperand(*TII.getNamedOperand(Inst, AMDGPU::OpName::data),
953 AMDGPU::EXP_CNT, CurrScore);
954 }
955 } else if (TII.isMTBUF(Inst)) {
956 if (Inst.mayStore())
957 setScoreByOperand(Inst.getOperand(0), AMDGPU::EXP_CNT, CurrScore);
958 } else if (TII.isMUBUF(Inst)) {
959 if (Inst.mayStore()) {
960 setScoreByOperand(Inst.getOperand(0), AMDGPU::EXP_CNT, CurrScore);
961 } else if (SIInstrInfo::isAtomicRet(Inst)) {
962 setScoreByOperand(*TII.getNamedOperand(Inst, AMDGPU::OpName::data),
963 AMDGPU::EXP_CNT, CurrScore);
964 }
965 } else if (TII.isLDSDIR(Inst)) {
966 // LDSDIR instructions attach the score to the destination.
967 setScoreByOperand(*TII.getNamedOperand(Inst, AMDGPU::OpName::vdst),
968 AMDGPU::EXP_CNT, CurrScore);
969 } else {
970 if (TII.isEXP(Inst)) {
971 // For export the destination registers are really temps that
972 // can be used as the actual source after export patching, so
973 // we need to treat them like sources and set the EXP_CNT
974 // score.
975 for (MachineOperand &DefMO : Inst.all_defs()) {
976 if (TRI.isVGPR(MRI, DefMO.getReg())) {
977 setScoreByOperand(DefMO, AMDGPU::EXP_CNT, CurrScore);
978 }
979 }
980 }
981 for (const MachineOperand &Op : Inst.all_uses()) {
982 if (TRI.isVectorRegister(MRI, Op.getReg()))
983 setScoreByOperand(Op, AMDGPU::EXP_CNT, CurrScore);
984 }
985 }
986 } else if (T == AMDGPU::X_CNT) {
987 HWEvents OtherEvent =
988 E == HWEvents::SMEM_GROUP ? HWEvents::VMEM_GROUP : HWEvents::SMEM_GROUP;
989 if (PendingEvents.contains(OtherEvent)) {
990 // Hardware inserts an implicit xcnt between interleaved
991 // SMEM and VMEM operations. So there will never be
992 // outstanding address translations for both SMEM and
993 // VMEM at the same time.
994 setScoreLB(T, getScoreUB(T) - 1);
995 PendingEvents -= OtherEvent;
996 }
997 for (const MachineOperand &Op : Inst.all_uses())
998 setScoreByOperand(Op, T, CurrScore);
999 } else if (T == AMDGPU::VA_VDST_RD || T == AMDGPU::VA_VDST_WR ||
1000 T == AMDGPU::VM_VSRC) {
1001 // Match the score to the VGPR destination or source registers as
1002 // appropriate
1003 for (const MachineOperand &Op : Inst.operands()) {
1004 if (!Op.isReg())
1005 continue;
1006
1007 // Skip based on counter type and operand type
1008 if (T == AMDGPU::VA_VDST_RD && Op.isDef())
1009 continue; // RD tracks reads only
1010 if (T == AMDGPU::VA_VDST_WR && Op.isUse())
1011 continue; // WR tracks writes only
1012 if (T == AMDGPU::VM_VSRC && Op.isDef())
1013 continue;
1014
1015 if (TRI.isVectorRegister(Context->MRI, Op.getReg()))
1016 setScoreByOperand(Op, T, CurrScore);
1017 }
1018 } else /* LGKM_CNT || EXP_CNT || VS_CNT || NUM_INST_CNTS */ {
1019 // Match the score to the destination registers.
1020 //
1021 // Check only explicit operands. Stores, especially spill stores, include
1022 // implicit uses and defs of their super registers which would create an
1023 // artificial dependency, while these are there only for register liveness
1024 // accounting purposes.
1025 //
1026 // Special cases where implicit register defs exists, such as M0 or VCC,
1027 // but none with memory instructions.
1028 for (const MachineOperand &Op : Inst.defs()) {
1029 if (T == AMDGPU::LOAD_CNT || T == AMDGPU::SAMPLE_CNT ||
1030 T == AMDGPU::BVH_CNT) {
1031 if (!TRI.isVectorRegister(MRI, Op.getReg())) // TODO: add wrapper
1032 continue;
1033 if (updateVMCntOnly(Inst)) {
1034 // updateVMCntOnly should only leave us with VGPRs
1035 // MUBUF, MTBUF, MIMG, FlatGlobal, and FlatScratch only have VGPR/AGPR
1036 // defs.
1037 assert(TRI.isVectorRegister(MRI, Op.getReg()));
1038 HWEvents VGPRContext =
1040 // If instruction can have Point Sample Accel applied, we have to flag
1041 // this with another potential dependency
1042 if (hasPointSampleAccel(Inst))
1043 VGPRContext |= HWEvents::VMEM_READ_ACCESS;
1044 for (MCRegUnit RU : regunits(Op.getReg().asMCReg()))
1045 VMem[toVMEMID(RU)].VGPRPendingEvents |= VGPRContext;
1046 }
1047 }
1048 setScoreByOperand(Op, T, CurrScore);
1049 }
1050 if (Inst.mayStore() &&
1051 (TII.isDS(Inst) || TII.isNonAsyncLdsDmaWrite(Inst))) {
1052 // MUBUF and FLAT LDS DMA operations need a wait on vmcnt before LDS
1053 // written can be accessed. A load from LDS to VMEM does not need a wait.
1054 //
1055 // The "Slot" is the offset from LDSDMA_BEGIN. If it's non-zero, then
1056 // there is a MachineInstr in LDSDMAStores used to track this LDSDMA
1057 // store. The "Slot" is the index into LDSDMAStores + 1.
1058 size_t Slot = 0;
1059 for (const auto *MemOp : Inst.memoperands()) {
1060 if (!MemOp->isStore() ||
1061 MemOp->getAddrSpace() != AMDGPUAS::LOCAL_ADDRESS)
1062 continue;
1063 // Comparing just AA info does not guarantee memoperands are equal
1064 // in general, but this is so for LDS DMA in practice.
1065 auto AAI = MemOp->getAAInfo();
1066 // Alias scope information gives a way to definitely identify an
1067 // original memory object and practically produced in the module LDS
1068 // lowering pass. If there is no scope available we will not be able
1069 // to disambiguate LDS aliasing as after the module lowering all LDS
1070 // is squashed into a single big object.
1071 if (!AAI || !AAI.Scope)
1072 break;
1073 for (size_t I = 0, E = LDSDMAStores.size(); I != E && !Slot; ++I) {
1074 for (const auto *MemOp : LDSDMAStores[I]->memoperands()) {
1075 if (MemOp->isStore() && AAI == MemOp->getAAInfo()) {
1076 Slot = I + 1;
1077 break;
1078 }
1079 }
1080 }
1081 if (Slot)
1082 break;
1083 // The slot may not be valid because it can be >= NUM_LDSDMA which
1084 // means the scoreboard cannot track it. We still want to preserve the
1085 // MI in order to check alias information, though.
1086 LDSDMAStores.push_back(&Inst);
1087 Slot = LDSDMAStores.size();
1088 break;
1089 }
1090 setVMemScore(LDSDMA_BEGIN, T, CurrScore);
1091 if (Slot && Slot < NUM_LDSDMA)
1092 setVMemScore(LDSDMA_BEGIN + static_cast<VMEMID>(Slot), T, CurrScore);
1093 }
1094
1095 AMDGPU::AsyncStages Stage = TII.getAsyncStage(Inst);
1096 if (any(Stage))
1097 AsyncScore[AMDGPU::getAsyncStageIndex(Stage)][T] = CurrScore;
1098
1100 setRegScore(AMDGPU::SCC, T, CurrScore);
1101 PendingSCCWrite = &Inst;
1102 }
1103 }
1104}
1105
1106void WaitcntBrackets::recordAsyncMark(MachineInstr &Inst,
1107 AMDGPU::AsyncStages Stages) {
1108 // In the absence of loops, AsyncMarks can grow linearly with the program
1109 // until we encounter an ASYNCMARK_WAIT. We could drop the oldest mark above a
1110 // limit every time we push a new mark, but that seems like unnecessary work
1111 // in practical cases. We do separately truncate the array when processing a
1112 // loop, which should be sufficient.
1113 //
1114 // A mark is inserted in every stage named. Each stage gets a copy of its
1115 // corresponding score.
1116 LLVM_DEBUG(dbgs() << "recordAsyncMark(stages=" << Stages << "):\n" << Inst);
1117 for (AMDGPU::AsyncStages S : AMDGPU::asyncStages(Stages)) {
1118 unsigned SI = AMDGPU::getAsyncStageIndex(S);
1119 AsyncMarks[SI].push_back(AsyncScore[SI]);
1120 LLVM_DEBUG({
1121 dbgs() << " stage " << S << ":\n";
1122 for (const auto &Mark : AsyncMarks[SI]) {
1123 dbgs() << " ";
1124 llvm::interleaveComma(Mark, dbgs());
1125 dbgs() << '\n';
1126 }
1127 });
1128 }
1129}
1130
1131void WaitcntBrackets::print(raw_ostream &OS) const {
1132 const GCNSubtarget &ST = Context->ST;
1133
1134 for (auto T : inst_counter_types(Context->MaxCounter)) {
1135 unsigned SR = getScoreRange(T);
1136 switch (T) {
1137 case AMDGPU::LOAD_CNT:
1138 OS << " " << (ST.hasExtendedWaitCounts() ? "LOAD" : "VM") << "_CNT("
1139 << SR << "):";
1140 break;
1141 case AMDGPU::DS_CNT:
1142 OS << " " << (ST.hasExtendedWaitCounts() ? "DS" : "LGKM") << "_CNT("
1143 << SR << "):";
1144 break;
1145 case AMDGPU::EXP_CNT:
1146 OS << " EXP_CNT(" << SR << "):";
1147 break;
1148 case AMDGPU::STORE_CNT:
1149 OS << " " << (ST.hasExtendedWaitCounts() ? "STORE" : "VS") << "_CNT("
1150 << SR << "):";
1151 break;
1152 case AMDGPU::SAMPLE_CNT:
1153 OS << " SAMPLE_CNT(" << SR << "):";
1154 break;
1155 case AMDGPU::BVH_CNT:
1156 OS << " BVH_CNT(" << SR << "):";
1157 break;
1158 case AMDGPU::KM_CNT:
1159 OS << " KM_CNT(" << SR << "):";
1160 break;
1161 case AMDGPU::X_CNT:
1162 OS << " X_CNT(" << SR << "):";
1163 break;
1164 case AMDGPU::ASYNC_CNT:
1165 OS << " ASYNC_CNT(" << SR << "):";
1166 break;
1167 case AMDGPU::VA_VDST_RD:
1168 OS << " VA_VDST_RD(" << SR << "): ";
1169 break;
1170 case AMDGPU::VA_VDST_WR:
1171 OS << " VA_VDST_WR(" << SR << "): ";
1172 break;
1173 case AMDGPU::VM_VSRC:
1174 OS << " VM_VSRC(" << SR << "): ";
1175 break;
1176 default:
1177 OS << " UNKNOWN(" << SR << "):";
1178 break;
1179 }
1180
1181 if (SR != 0) {
1182 // Print vgpr scores.
1183 unsigned LB = getScoreLB(T);
1184
1185 SmallVector<VMEMID> SortedVMEMIDs(VMem.keys());
1186 sort(SortedVMEMIDs);
1187
1188 for (auto ID : SortedVMEMIDs) {
1189 unsigned RegScore = VMem.at(ID).Scores[T];
1190 if (RegScore <= LB)
1191 continue;
1192 unsigned RelScore = RegScore - LB - 1;
1193 if (ID < REGUNITS_END) {
1194 OS << ' ' << RelScore << ':'
1195 << printRegUnit(static_cast<MCRegUnit>(ID), &Context->TRI);
1196 } else {
1197 assert(ID >= LDSDMA_BEGIN && ID < LDSDMA_END &&
1198 "Unhandled/unexpected ID value!");
1199 OS << ' ' << RelScore << ":LDSDMA" << ID;
1200 }
1201 }
1202
1203 // Also need to print sgpr scores for lgkm_cnt or xcnt.
1204 if (isSmemCounter(T)) {
1205 SmallVector<MCRegUnit> SortedSMEMIDs(SGPRs.keys());
1206 sort(SortedSMEMIDs);
1207 for (auto ID : SortedSMEMIDs) {
1208 unsigned RegScore = SGPRs.at(ID).get(T);
1209 if (RegScore <= LB)
1210 continue;
1211 unsigned RelScore = RegScore - LB - 1;
1212 OS << ' ' << RelScore << ':'
1213 << printRegUnit(static_cast<MCRegUnit>(ID), &Context->TRI);
1214 }
1215 }
1216
1217 if (T == AMDGPU::KM_CNT && SCCScore > 0)
1218 OS << ' ' << SCCScore << ":scc";
1219 }
1220 OS << '\n';
1221 }
1222
1223 OS << "Pending Events: ";
1224 if (hasPendingEvent()) {
1225 OS << getPendingEvents();
1226 } else {
1227 OS << "none";
1228 }
1229 OS << '\n';
1230
1231 for (AMDGPU::AsyncStages S : AMDGPU::asyncStages(AMDGPU::AsyncStages::ALL)) {
1232 unsigned SI = AMDGPU::getAsyncStageIndex(S);
1233 const CounterValueArray &Score = AsyncScore[SI];
1234 OS << "Async score (stage " << S << "): ";
1235 if (llvm::all_of(Score, [](unsigned V) { return V == 0; }))
1236 OS << "none";
1237 else
1238 llvm::interleaveComma(Score, OS);
1239 OS << '\n';
1240
1241 OS << "Async marks (stage " << S << "): " << AsyncMarks[SI].size() << '\n';
1242
1243 for (const auto &Mark : AsyncMarks[SI]) {
1244 for (auto T : AMDGPU::inst_counter_types()) {
1245 unsigned MarkedScore = Mark[T];
1246 switch (T) {
1247 case AMDGPU::LOAD_CNT:
1248 OS << " " << (ST.hasExtendedWaitCounts() ? "LOAD" : "VM")
1249 << "_CNT: " << MarkedScore;
1250 break;
1251 case AMDGPU::DS_CNT:
1252 OS << " " << (ST.hasExtendedWaitCounts() ? "DS" : "LGKM")
1253 << "_CNT: " << MarkedScore;
1254 break;
1255 case AMDGPU::EXP_CNT:
1256 OS << " EXP_CNT: " << MarkedScore;
1257 break;
1258 case AMDGPU::STORE_CNT:
1259 OS << " " << (ST.hasExtendedWaitCounts() ? "STORE" : "VS")
1260 << "_CNT: " << MarkedScore;
1261 break;
1262 case AMDGPU::SAMPLE_CNT:
1263 OS << " SAMPLE_CNT: " << MarkedScore;
1264 break;
1265 case AMDGPU::BVH_CNT:
1266 OS << " BVH_CNT: " << MarkedScore;
1267 break;
1268 case AMDGPU::KM_CNT:
1269 OS << " KM_CNT: " << MarkedScore;
1270 break;
1271 case AMDGPU::X_CNT:
1272 OS << " X_CNT: " << MarkedScore;
1273 break;
1274 case AMDGPU::ASYNC_CNT:
1275 OS << " ASYNC_CNT: " << MarkedScore;
1276 break;
1277 default:
1278 OS << " UNKNOWN: " << MarkedScore;
1279 break;
1280 }
1281 }
1282 OS << '\n';
1283 }
1284 }
1285 OS << '\n';
1286}
1287
1288/// Simplify \p UpdateWait by removing waits that are redundant based on the
1289/// current WaitcntBrackets and any other waits specified in \p CheckWait.
1290void WaitcntBrackets::simplifyWaitcnt(const AMDGPU::Waitcnt &CheckWait,
1291 AMDGPU::Waitcnt &UpdateWait) const {
1292 simplifyWaitcnt(UpdateWait, AMDGPU::LOAD_CNT);
1293 simplifyWaitcnt(UpdateWait, AMDGPU::EXP_CNT);
1294 simplifyWaitcnt(UpdateWait, AMDGPU::DS_CNT);
1295 simplifyWaitcnt(UpdateWait, AMDGPU::STORE_CNT);
1296 simplifyWaitcnt(UpdateWait, AMDGPU::SAMPLE_CNT);
1297 simplifyWaitcnt(UpdateWait, AMDGPU::BVH_CNT);
1298 simplifyWaitcnt(UpdateWait, AMDGPU::KM_CNT);
1299 simplifyXcnt(CheckWait, UpdateWait);
1300 simplifyWaitcnt(UpdateWait, AMDGPU::VA_VDST_RD);
1301 simplifyWaitcnt(UpdateWait, AMDGPU::VA_VDST_WR);
1302 simplifyVmVsrc(CheckWait, UpdateWait);
1303 simplifyWaitcnt(UpdateWait, AMDGPU::ASYNC_CNT);
1304}
1305
1306void WaitcntBrackets::simplifyWaitcnt(AMDGPU::InstCounterType T,
1307 unsigned &Count) const {
1308 // The number of outstanding events for this type, T, can be calculated
1309 // as (UB - LB). If the current Count is greater than or equal to the number
1310 // of outstanding events, then the wait for this counter is redundant.
1311 if (Count >= getScoreRange(T))
1312 Count = ~0u;
1313}
1314
1315void WaitcntBrackets::simplifyWaitcnt(AMDGPU::Waitcnt &Wait,
1316 AMDGPU::InstCounterType T) const {
1317 unsigned Cnt = Wait.get(T);
1318 simplifyWaitcnt(T, Cnt);
1319 Wait.set(T, Cnt);
1320}
1321
1322void WaitcntBrackets::simplifyXcnt(const AMDGPU::Waitcnt &CheckWait,
1323 AMDGPU::Waitcnt &UpdateWait) const {
1324 // Try to simplify xcnt further by checking for joint kmcnt and loadcnt
1325 // optimizations. On entry to a block with multiple predescessors, there may
1326 // be pending SMEM and VMEM events active at the same time.
1327 // In such cases, only clear one active event at a time.
1328 // TODO: Revisit xcnt optimizations for gfx1250.
1329 // Wait on XCNT is redundant if we are already waiting for a load to complete.
1330 // SMEM can return out of order, so only omit XCNT wait if we are waiting till
1331 // zero.
1332 if (CheckWait.get(AMDGPU::KM_CNT) == 0 &&
1333 hasPendingEvent(HWEvents::SMEM_GROUP))
1334 UpdateWait.set(AMDGPU::X_CNT, ~0u);
1335 // If we have pending store we cannot optimize XCnt because we do not wait for
1336 // stores. VMEM loads retun in order, so if we only have loads XCnt is
1337 // decremented to the same number as LOADCnt.
1338 if (CheckWait.get(AMDGPU::LOAD_CNT) != ~0u &&
1339 hasPendingEvent(HWEvents::VMEM_GROUP) &&
1340 !hasPendingEvent(AMDGPU::STORE_CNT) &&
1341 CheckWait.get(AMDGPU::X_CNT) >= CheckWait.get(AMDGPU::LOAD_CNT))
1342 UpdateWait.set(AMDGPU::X_CNT, ~0u);
1343 simplifyWaitcnt(UpdateWait, AMDGPU::X_CNT);
1344}
1345
1346void WaitcntBrackets::simplifyVmVsrc(const AMDGPU::Waitcnt &CheckWait,
1347 AMDGPU::Waitcnt &UpdateWait) const {
1348 // Waiting for a VMEM counter (like LOAD_CNT) implies an equivalent wait for
1349 // VM_VSRC, because if the VMEM operation has completed then it must surely
1350 // have read its VGPR sources, but only if there are no other outstanding VMEM
1351 // operations that use a different counter (like SAMPLE_CNT).
1352 static constexpr AMDGPU::InstCounterType VmemCounters[] = {
1355 HWEvents VmemEvents = llvm::accumulate(
1356 VmemCounters, HWEvents(), [&](HWEvents Acc, AMDGPU::InstCounterType T) {
1357 return Acc | Context->getWaitEvents(T);
1358 });
1359 HWEvents PendingVmemEvents = PendingEvents & VmemEvents;
1360 for (AMDGPU::InstCounterType T : VmemCounters) {
1361 unsigned CheckCount = CheckWait.get(T);
1362 if (UpdateWait.get(AMDGPU::VM_VSRC) >= CheckCount &&
1363 (CheckCount == 0 || !counterOutOfOrder(T)) &&
1364 (PendingVmemEvents & ~Context->getWaitEvents(T)) == 0)
1365 UpdateWait.set(AMDGPU::VM_VSRC, ~0u);
1366 }
1367
1368 simplifyWaitcnt(UpdateWait, AMDGPU::VM_VSRC);
1369}
1370
1371void WaitcntBrackets::purgeEmptyTrackingData() {
1372 VMem.remove_if([](const auto &P) { return P.second.empty(); });
1373 SGPRs.remove_if([](const auto &P) { return P.second.empty(); });
1374}
1375
1376// Returns the wait needed for \p ScoreToWait, or ~0u if no wait is needed.
1377unsigned WaitcntBrackets::getWaitForScore(AMDGPU::InstCounterType T,
1378 unsigned ScoreToWait) const {
1379 const unsigned LB = getScoreLB(T);
1380 const unsigned UB = getScoreUB(T);
1381
1382 // If the score falls within the bracket, we need a waitcnt.
1383 if ((UB >= ScoreToWait) && (ScoreToWait > LB)) {
1384 if ((T == AMDGPU::LOAD_CNT || T == AMDGPU::DS_CNT) && hasPendingFlat() &&
1385 !Context->ST.hasFlatLgkmVMemCountInOrder()) {
1386 // If there is a pending FLAT operation, and this is a VMem or LGKM
1387 // waitcnt and the target can report early completion, then we need
1388 // to force a waitcnt 0.
1389 return 0;
1390 }
1391 if (counterOutOfOrder(T)) {
1392 // Counter can get decremented out-of-order when there
1393 // are multiple types event in the bracket. Also emit an s_wait counter
1394 // with a conservative value of 0 for the counter.
1395 return 0;
1396 }
1397 // If a counter has been maxed out avoid overflow by waiting for
1398 // MAX(CounterType) - 1 instead.
1399 return std::min(UB - ScoreToWait, getLimit(T) - 1);
1400 }
1401 return ~0u;
1402}
1403
1404void WaitcntBrackets::determineWaitForScore(AMDGPU::InstCounterType T,
1405 unsigned ScoreToWait,
1406 AMDGPU::Waitcnt &Wait) const {
1407 Wait.add(T, getWaitForScore(T, ScoreToWait));
1408}
1409
1410void WaitcntBrackets::determineWaitForVAVDSTWrite(VMEMID ID,
1411 AMDGPU::Waitcnt &Wait) const {
1412 unsigned Count =
1413 getWaitForScore(AMDGPU::VA_VDST_WR, getVMemScore(ID, AMDGPU::VA_VDST_WR));
1414 if (Count == ~0u)
1415 return;
1416
1417 // The ordered-XDL count is a wait bound independent of exact event-class
1418 // ordering. Combine the bounds for this register unit before Wait.add()
1419 // takes the minimum across all required register-unit dependencies.
1421 std::max(Count, getNumOrderedXDLsAfterWrite(ID)));
1422}
1423
1424AMDGPU::Waitcnt
1425WaitcntBrackets::determineAsyncWait(unsigned N, AMDGPU::AsyncStages Stages) {
1426 // Trim each named stage to at most N marks. The dropped marks imply waits
1427 // that get accumulated into one Waitcnt,
1428 // so a wait covering several stages is the union of their requirements.
1429 AMDGPU::Waitcnt Wait;
1430 for (AMDGPU::AsyncStages S : AMDGPU::asyncStages(Stages)) {
1431 auto &StageMarks = AsyncMarks[AMDGPU::getAsyncStageIndex(S)];
1432 LLVM_DEBUG({
1433 dbgs() << "Need " << N << " async marks in stage " << S << ". Found "
1434 << StageMarks.size() << ":\n";
1435 for (const auto &Mark : StageMarks) {
1436 llvm::interleaveComma(Mark, dbgs());
1437 dbgs() << '\n';
1438 }
1439 });
1440
1441 unsigned StageN = N;
1442 if (StageMarks.size() == MaxAsyncMarks) {
1443 // Enforcing MaxAsyncMarks here is unnecessary work because the size of
1444 // MaxAsyncMarks is linear when traversing straightline code. But we do
1445 // need to check if truncation may have occured at a merge, and adjust N
1446 // to ensure that a wait is generated.
1447 LLVM_DEBUG(
1448 dbgs() << "Possible truncation. Ensuring a non-trivial wait.\n");
1449 StageN = std::min(StageN, (unsigned)MaxAsyncMarks - 1);
1450 }
1451
1452 if (StageMarks.size() <= StageN) {
1453 LLVM_DEBUG(dbgs() << "No additional wait for async mark.\n");
1454 continue;
1455 }
1456
1457 size_t MarkIndex = StageMarks.size() - StageN - 1;
1458 const auto &RequiredMark = StageMarks[MarkIndex];
1460 determineWaitForScore(T, RequiredMark[T], Wait);
1461 }
1462
1463 // Immediately remove the waited mark and all older ones
1464 // This happens BEFORE the wait is actually inserted, which is fine
1465 // because we've already extracted the wait requirements
1466 LLVM_DEBUG({
1467 dbgs() << "Removing " << (MarkIndex + 1)
1468 << " async marks after determining wait\n";
1469 });
1470 StageMarks.erase(StageMarks.begin(), StageMarks.begin() + MarkIndex + 1);
1471 }
1472
1473 LLVM_DEBUG(dbgs() << "Waits to add: " << Wait);
1474 return Wait;
1475}
1476
1477// With D16Write32BitVgpr, D16 inst might be clobbered by events running on the
1478// other half 16bit.
1479//
1480// Replace VGPR16 to VGPR32 for wait check if:
1481// 1. MI is a VALU, and there is a wait event on the other half
1482// 2. MI is a LdSt, and there is a wait event on the other half from different
1483// order group
1484MCRegister WaitcntBrackets::determineVGPR16Dependency(const MachineInstr &MI,
1486 MCRegister Reg) const {
1487 const TargetRegisterClass *RC = Context->TRI.getPhysRegBaseClass(Reg);
1488 uint64_t Size = Context->TRI.getRegSizeInBits(*RC);
1489
1490 if (Size != 16 || !Context->ST.hasD16Writes32BitVgpr())
1491 return Reg;
1492
1493 // With D16Writes32BitVgpr, D16 Inst might clobber the whole vgpr32
1494 // check dependency on the other half
1495 Register Reg32 = Context->TRI.get32BitRegister(Reg);
1496 Register OtherHalf = Context->TRI.getSubReg(
1497 Reg32,
1498 AMDGPU::isHi16Reg(Reg, Context->TRI) ? AMDGPU::lo16 : AMDGPU::hi16);
1499
1500 AMDGPU::Waitcnt Wait;
1501 for (MCRegUnit RU : regunits(OtherHalf)) {
1502 if (T == AMDGPU::VA_VDST_WR)
1503 determineWaitForVAVDSTWrite(toVMEMID(RU), Wait);
1504 else
1505 determineWaitForScore(T, getVMemScore(toVMEMID(RU), T), Wait);
1506 }
1507
1508 // No wait on otherhalf
1509 if (!Wait.hasWait())
1510 return Reg;
1511
1512 if (Context->TII.isVALU(MI, /*AllowLDSDMA=*/false))
1513 return Reg32;
1514
1515 // If hi/lo16 mixed events
1516 HWEvents MIEvents = AMDGPU::getEventsFor(
1517 MI, Context->ST, Context->IsExpertMode, Context->TgSplit);
1518 HWEvents OtherHalfEvents = Context->getWaitEvents(T);
1519 HWEvents Events = MIEvents & OtherHalfEvents;
1520 if (Events.size() > 1)
1521 return Reg32;
1522 return Reg;
1523}
1524
1525void WaitcntBrackets::determineWaitForPhysReg(AMDGPU::InstCounterType T,
1526 MCRegister Reg,
1527 AMDGPU::Waitcnt &Wait,
1528 const MachineInstr &MI) const {
1529 if (Reg == AMDGPU::SCC) {
1530 determineWaitForScore(T, SCCScore, Wait);
1531 } else {
1532 bool IsVGPR = Context->TRI.isVectorRegister(Context->MRI, Reg);
1533 if (IsVGPR)
1534 Reg = determineVGPR16Dependency(MI, T, Reg);
1535 for (MCRegUnit RU : regunits(Reg)) {
1536 if (IsVGPR && T == AMDGPU::VA_VDST_WR) {
1537 determineWaitForVAVDSTWrite(toVMEMID(RU), Wait);
1538 } else {
1539 determineWaitForScore(
1540 T, IsVGPR ? getVMemScore(toVMEMID(RU), T) : getSGPRScore(RU, T),
1541 Wait);
1542 }
1543 }
1544 }
1545}
1546
1547void WaitcntBrackets::determineWaitForLDSDMA(AMDGPU::InstCounterType T,
1548 VMEMID TID,
1549 AMDGPU::Waitcnt &Wait) const {
1550 assert(TID >= LDSDMA_BEGIN && TID < LDSDMA_END);
1551 determineWaitForScore(T, getVMemScore(TID, T), Wait);
1552}
1553
1554void WaitcntBrackets::tryClearSCCWriteEvent(MachineInstr *Inst) {
1555 // S_BARRIER_WAIT on the same barrier guarantees that the pending write to
1556 // SCC has landed
1557 if (PendingSCCWrite &&
1558 PendingSCCWrite->getOpcode() == AMDGPU::S_BARRIER_SIGNAL_ISFIRST_IMM &&
1559 PendingSCCWrite->getOperand(0).getImm() == Inst->getOperand(0).getImm()) {
1560 HWEvents SCC_WRITE_PendingEvent = HWEvents::SCC_WRITE;
1561 // If this SCC_WRITE is the only pending KM_CNT event, clear counter.
1562 if ((PendingEvents & Context->getWaitEvents(AMDGPU::KM_CNT)) ==
1563 SCC_WRITE_PendingEvent) {
1564 setScoreLB(AMDGPU::KM_CNT, getScoreUB(AMDGPU::KM_CNT));
1565 }
1566
1567 PendingEvents -= SCC_WRITE_PendingEvent;
1568 PendingSCCWrite = nullptr;
1569 }
1570}
1571
1572void WaitcntBrackets::applyWaitcnt(const AMDGPU::Waitcnt &Wait) {
1574 unsigned Cnt;
1576 Cnt =
1577 std::min(Wait.get(AMDGPU::VA_VDST_RD), Wait.get(AMDGPU::VA_VDST_WR));
1578 } else {
1579 Cnt = Wait.get(T);
1580 }
1581 applyWaitcnt(T, Cnt);
1582 }
1583}
1584
1585void WaitcntBrackets::applyWaitcnt(AMDGPU::InstCounterType T, unsigned Count) {
1586 const unsigned UB = getScoreUB(T);
1587 if (Count >= UB)
1588 return;
1589 if (Count != 0) {
1590 if (counterOutOfOrder(T))
1591 return;
1592 setScoreLB(T, std::max(getScoreLB(T), UB - Count));
1593 } else {
1594 setScoreLB(T, UB);
1595 PendingEvents -= Context->getWaitEvents(T);
1596 }
1597
1598 if (T == AMDGPU::KM_CNT && Count == 0 &&
1599 hasPendingEvent(HWEvents::SMEM_GROUP)) {
1600 if (!hasMixedPendingEvents(AMDGPU::X_CNT))
1601 applyWaitcnt(AMDGPU::X_CNT, 0);
1602 else
1603 PendingEvents -= HWEvents::SMEM_GROUP;
1604 }
1605 if (T == AMDGPU::LOAD_CNT && hasPendingEvent(HWEvents::VMEM_GROUP) &&
1606 !hasPendingEvent(AMDGPU::STORE_CNT)) {
1607 if (!hasMixedPendingEvents(AMDGPU::X_CNT))
1608 applyWaitcnt(AMDGPU::X_CNT, Count);
1609 else if (Count == 0)
1610 PendingEvents -= HWEvents::VMEM_GROUP;
1611 }
1612}
1613
1614void WaitcntBrackets::applyWaitcnt(const AMDGPU::Waitcnt &Wait,
1616 unsigned Cnt = Wait.get(T);
1617 applyWaitcnt(T, Cnt);
1618}
1619
1620// Where there are multiple types of event in the bracket of a counter,
1621// the decrement may go out of order.
1622bool WaitcntBrackets::counterOutOfOrder(AMDGPU::InstCounterType T) const {
1623 // Scalar memory read always can go out of order.
1624 if ((T == Context->SmemAccessCounter &&
1625 hasPendingEvent(HWEvents::SMEM_ACCESS)) ||
1626 (T == AMDGPU::X_CNT && hasPendingEvent(HWEvents::SMEM_GROUP)))
1627 return true;
1628
1629 if (T == AMDGPU::LOAD_CNT) {
1630
1631 // On targets without VScnt, LOAD_CNT includes all of STORE_CNT as well.
1632 // All these events use one counter and do not go out of order with respect
1633 // to each other.
1634 if (!Context->ST.hasVscnt())
1635 return false;
1636
1637 HWEvents Events = PendingEvents & Context->getWaitEvents(T);
1638
1639 // If the target does not have extended counters, VMEM_BVH/SAMPLE_READ
1640 // events are equivalent to VMEM_READ_ACCESS. We do not go out of order in
1641 // such cases.
1642 static constexpr HWEvents ExtendedImageEvents =
1643 HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
1644 if (!Context->ST.hasExtendedWaitCounts() &&
1645 (Events & ExtendedImageEvents).any()) {
1646 Events -= ExtendedImageEvents;
1647 Events |= HWEvents::VMEM_READ_ACCESS;
1648 }
1649
1650 // GLOBAL_INV/BUFFER_INV complete in-order with VMEM_READ_ACCESS, so mixing
1651 // VMEM_INV_ACCESS with other LOAD_CNT events does not cause out-of-order
1652 // completion.
1653 Events -= HWEvents::VMEM_INV_ACCESS;
1654
1655 return Events.size() > 1;
1656 }
1657
1658 return hasMixedPendingEvents(T);
1659}
1660
1661INITIALIZE_PASS_BEGIN(SIInsertWaitcntsLegacy, DEBUG_TYPE, "SI Insert Waitcnts",
1662 false, false)
1665INITIALIZE_PASS_END(SIInsertWaitcntsLegacy, DEBUG_TYPE, "SI Insert Waitcnts",
1667
1668char SIInsertWaitcntsLegacy::ID = 0;
1669
1670char &llvm::SIInsertWaitcntsID = SIInsertWaitcntsLegacy::ID;
1671
1673 return new SIInsertWaitcntsLegacy();
1674}
1675
1676static bool updateOperandIfDifferent(MachineInstr &MI, AMDGPU::OpName OpName,
1677 unsigned NewEnc) {
1678 int OpIdx = AMDGPU::getNamedOperandIdx(MI.getOpcode(), OpName);
1679 assert(OpIdx >= 0);
1680
1681 MachineOperand &MO = MI.getOperand(OpIdx);
1682
1683 if (NewEnc == MO.getImm())
1684 return false;
1685
1686 MO.setImm(NewEnc);
1687 return true;
1688}
1689
1690bool WaitcntGenerator::promoteSoftWaitCnt(MachineInstr *Waitcnt) const {
1691 unsigned Opcode = SIInstrInfo::getNonSoftWaitcntOpcode(Waitcnt->getOpcode());
1692 if (Opcode == Waitcnt->getOpcode())
1693 return false;
1694
1695 Waitcnt->setDesc(TII.get(Opcode));
1696 return true;
1697}
1698
1699/// Combine consecutive S_WAITCNT and S_WAITCNT_VSCNT instructions that
1700/// precede \p It and follow \p OldWaitcntInstr and apply any extra waits
1701/// from \p Wait that were added by previous passes. Currently this pass
1702/// conservatively assumes that these preexisting waits are required for
1703/// correctness.
1704bool WaitcntGeneratorPreGFX12::applyPreexistingWaitcnt(
1705 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1706 AMDGPU::Waitcnt &Wait, MachineBasicBlock::instr_iterator It) {
1707 assert(isNormalMode(MaxCounter));
1708
1709 bool Modified = false;
1710 MachineInstr *WaitcntInstr = nullptr;
1711 MachineInstr *WaitcntVsCntInstr = nullptr;
1712
1713 LLVM_DEBUG({
1714 dbgs() << "PreGFX12::applyPreexistingWaitcnt at: ";
1715 if (It.isEnd())
1716 dbgs() << "end of block\n";
1717 else
1718 dbgs() << *It;
1719 });
1720
1721 for (auto &II :
1722 make_early_inc_range(make_range(OldWaitcntInstr.getIterator(), It))) {
1723 LLVM_DEBUG(dbgs() << "pre-existing iter: " << II);
1724 if (isNonWaitcntMetaInst(II)) {
1725 LLVM_DEBUG(dbgs() << "skipped meta instruction\n");
1726 continue;
1727 }
1728
1729 unsigned Opcode = SIInstrInfo::getNonSoftWaitcntOpcode(II.getOpcode());
1730 bool TrySimplify = Opcode != II.getOpcode() && !OptNone;
1731
1732 // Update required wait count. If this is a soft waitcnt (= it was added
1733 // by an earlier pass), it may be entirely removed.
1734 if (Opcode == AMDGPU::S_WAITCNT) {
1735 unsigned IEnc = static_cast<unsigned>(II.getOperand(0).getImm());
1736 AMDGPU::Waitcnt OldWait = AMDGPU::decodeWaitcnt(IV, IEnc);
1737 if (TrySimplify) {
1738 ScoreBrackets.simplifyWaitcnt(OldWait);
1739 ScoreBrackets.simplifyVmemInvOnlyWait(OldWait);
1740 }
1741 Wait = Wait.combined(OldWait);
1742
1743 // Merge consecutive waitcnt of the same type by erasing multiples.
1744 if (WaitcntInstr) {
1745 DeferredErasures.erase(&II);
1746 II.eraseFromParent();
1747 Modified = true;
1748 } else if (!Wait.hasWaitExceptStoreCnt() && TrySimplify) {
1749 // Might or might not be redundant, depending on if there's a loop
1750 // carried dependency. Stash it away so we can erase it if nothing saves
1751 // it.
1752 DeferredErasures.insert(&II);
1753 } else {
1754 WaitcntInstr = &II;
1755 DeferredErasures.erase(&II);
1756 }
1757 } else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
1758 assert(ST.hasVMemToLDSLoad());
1759 LLVM_DEBUG(dbgs() << "Processing S_WAITCNT_lds_direct: " << II
1760 << "Before: " << Wait << '\n';);
1761 ScoreBrackets.determineWaitForLDSDMA(AMDGPU::LOAD_CNT, LDSDMA_BEGIN,
1762 Wait);
1763 LLVM_DEBUG(dbgs() << "After: " << Wait << '\n';);
1764
1765 // It is possible (but unlikely) that this is the only wait instruction,
1766 // in which case, we exit this loop without a WaitcntInstr to consume
1767 // `Wait`. But that works because `Wait` was passed in by reference, and
1768 // the callee eventually calls createNewWaitcnt on it. We test this
1769 // possibility in an articial MIR test since such a situation cannot be
1770 // recreated by running the memory legalizer.
1771 II.eraseFromParent();
1772 } else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
1773 unsigned N = static_cast<unsigned>(II.getOperand(0).getImm());
1774 LLVM_DEBUG(dbgs() << "Processing WAIT_ASYNCMARK: " << II << '\n';);
1775 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
1776 N, AMDGPU::asyncStagesFromMaskOperand(II.getOperand(1).getImm()));
1777 Wait = Wait.combined(OldWait);
1778 } else {
1779 assert(Opcode == AMDGPU::S_WAITCNT_VSCNT);
1780 assert(II.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
1781
1782 unsigned OldVSCnt = static_cast<unsigned>(
1783 TII.getNamedOperand(II, AMDGPU::OpName::simm16)->getImm());
1784 if (TrySimplify)
1785 ScoreBrackets.simplifyWaitcnt(AMDGPU::STORE_CNT, OldVSCnt);
1787 std::min(Wait.get(AMDGPU::STORE_CNT), OldVSCnt));
1788
1789 if (WaitcntVsCntInstr) {
1790 DeferredErasures.erase(&II);
1791 II.eraseFromParent();
1792 Modified = true;
1793 } else if (!Wait.hasWaitStoreCnt() && TrySimplify) {
1794 // Loop-carried dependencies guard, see S_WAITCNT case above.
1795 DeferredErasures.insert(&II);
1796 } else {
1797 WaitcntVsCntInstr = &II;
1798 DeferredErasures.erase(&II);
1799 }
1800 }
1801 }
1802
1803 if (WaitcntInstr) {
1804 Modified |= updateOperandIfDifferent(*WaitcntInstr, AMDGPU::OpName::simm16,
1806 Modified |= promoteSoftWaitCnt(WaitcntInstr);
1807
1808 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::LOAD_CNT);
1809 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::EXP_CNT);
1810 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::DS_CNT);
1811 Wait.set(AMDGPU::LOAD_CNT, ~0u);
1812 Wait.set(AMDGPU::EXP_CNT, ~0u);
1813 Wait.set(AMDGPU::DS_CNT, ~0u);
1814
1815 LLVM_DEBUG(It.isEnd() ? dbgs() << "applied pre-existing waitcnt\n"
1816 << "New Instr at block end: "
1817 << *WaitcntInstr << '\n'
1818 : dbgs() << "applied pre-existing waitcnt\n"
1819 << "Old Instr: " << *It
1820 << "New Instr: " << *WaitcntInstr << '\n');
1821 }
1822
1823 if (WaitcntVsCntInstr) {
1824 Modified |=
1825 updateOperandIfDifferent(*WaitcntVsCntInstr, AMDGPU::OpName::simm16,
1826 Wait.get(AMDGPU::STORE_CNT));
1827 Modified |= promoteSoftWaitCnt(WaitcntVsCntInstr);
1828
1829 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::STORE_CNT);
1830 Wait.set(AMDGPU::STORE_CNT, ~0u);
1831
1832 LLVM_DEBUG(It.isEnd()
1833 ? dbgs() << "applied pre-existing waitcnt\n"
1834 << "New Instr at block end: " << *WaitcntVsCntInstr
1835 << '\n'
1836 : dbgs() << "applied pre-existing waitcnt\n"
1837 << "Old Instr: " << *It
1838 << "New Instr: " << *WaitcntVsCntInstr << '\n');
1839 }
1840
1841 return Modified;
1842}
1843
1844/// Generate S_WAITCNT and/or S_WAITCNT_VSCNT instructions for any
1845/// required counters in \p Wait
1846bool WaitcntGeneratorPreGFX12::createNewWaitcnt(
1847 MachineBasicBlock &Block, MachineBasicBlock::instr_iterator It,
1848 AMDGPU::Waitcnt Wait, const WaitcntBrackets &ScoreBrackets) {
1849 assert(isNormalMode(MaxCounter));
1850
1851 bool Modified = false;
1852 const DebugLoc &DL = Block.findDebugLoc(It);
1853
1854 // Waits for VMcnt, LKGMcnt and/or EXPcnt are encoded together into a
1855 // single instruction while VScnt has its own instruction.
1856 if (Wait.hasWaitExceptStoreCnt()) {
1857 // If profiling expansion is enabled, emit an expanded sequence
1858 if (ExpandWaitcntProfiling) {
1859 // Check if any of the counters to be waited on are out-of-order.
1860 // If so, fall back to normal (non-expanded) behavior since expansion
1861 // would provide misleading profiling information.
1862 bool AnyOutOfOrder = false;
1863 for (auto CT : {AMDGPU::LOAD_CNT, AMDGPU::DS_CNT, AMDGPU::EXP_CNT}) {
1864 unsigned WaitCnt = Wait.get(CT);
1865 if (WaitCnt != ~0u && ScoreBrackets.counterOutOfOrder(CT)) {
1866 AnyOutOfOrder = true;
1867 break;
1868 }
1869 }
1870
1871 if (AnyOutOfOrder) {
1872 // Fall back to non-expanded wait
1873 unsigned Enc = AMDGPU::encodeWaitcnt(IV, Wait);
1874 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT)).addImm(Enc);
1875 Modified = true;
1876 } else {
1877 // All counters are in-order, safe to expand
1878 for (auto CT : {AMDGPU::LOAD_CNT, AMDGPU::DS_CNT, AMDGPU::EXP_CNT}) {
1879 unsigned WaitCnt = Wait.get(CT);
1880 if (WaitCnt == ~0u)
1881 continue;
1882
1883 unsigned Outstanding =
1884 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
1885 EmitExpandedWaitcnt(Outstanding, WaitCnt, [&](unsigned Count) {
1886 AMDGPU::Waitcnt W;
1887 W.set(CT, Count);
1888 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT))
1890 });
1891 Modified = true;
1892 }
1893 }
1894 } else {
1895 // Normal behavior: emit single combined waitcnt
1896 unsigned Enc = AMDGPU::encodeWaitcnt(IV, Wait);
1897 [[maybe_unused]] auto SWaitInst =
1898 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT)).addImm(Enc);
1899 Modified = true;
1900
1901 LLVM_DEBUG(dbgs() << "PreGFX12::createNewWaitcnt\n";
1902 if (It != Block.instr_end()) dbgs() << "Old Instr: " << *It;
1903 dbgs() << "New Instr: " << *SWaitInst << '\n');
1904 }
1905 }
1906
1907 if (Wait.hasWaitStoreCnt()) {
1908 assert(ST.hasVscnt());
1909
1910 if (ExpandWaitcntProfiling && Wait.get(AMDGPU::STORE_CNT) != ~0u &&
1911 !ScoreBrackets.counterOutOfOrder(AMDGPU::STORE_CNT)) {
1912 // Only expand if counter is not out-of-order
1913 unsigned Outstanding =
1914 std::min(ScoreBrackets.getOutstanding(AMDGPU::STORE_CNT),
1915 getLimit(AMDGPU::STORE_CNT) - 1);
1916 EmitExpandedWaitcnt(
1917 Outstanding, Wait.get(AMDGPU::STORE_CNT), [&](unsigned Count) {
1918 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1919 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1920 .addImm(Count);
1921 });
1922 Modified = true;
1923 } else {
1924 [[maybe_unused]] auto SWaitInst =
1925 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_VSCNT))
1926 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
1928 Modified = true;
1929
1930 LLVM_DEBUG(dbgs() << "PreGFX12::createNewWaitcnt\n";
1931 if (It != Block.instr_end()) dbgs() << "Old Instr: " << *It;
1932 dbgs() << "New Instr: " << *SWaitInst << '\n');
1933 }
1934 }
1935
1936 return Modified;
1937}
1938
1939AMDGPU::Waitcnt
1940WaitcntGeneratorPreGFX12::getAllZeroWaitcnt(bool IncludeVSCnt) const {
1941 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt && ST.hasVscnt() ? 0 : ~0u);
1942}
1943
1944AMDGPU::Waitcnt
1945WaitcntGeneratorGFX12Plus::getAllZeroWaitcnt(bool IncludeVSCnt) const {
1946 unsigned ExpertVal = IsExpertMode ? 0 : ~0u;
1947 return AMDGPU::Waitcnt(0, 0, 0, IncludeVSCnt ? 0 : ~0u, 0, 0, 0,
1948 ~0u /* XCNT */, ~0u /* ASYNC_CNT */,
1949 ~0u /* TENSOR_CNT */, ExpertVal, ExpertVal, ExpertVal);
1950}
1951
1952/// Combine consecutive S_WAIT_*CNT instructions that precede \p It and
1953/// follow \p OldWaitcntInstr and apply any extra waits from \p Wait that
1954/// were added by previous passes. Currently this pass conservatively
1955/// assumes that these preexisting waits are required for correctness.
1956bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt(
1957 WaitcntBrackets &ScoreBrackets, MachineInstr &OldWaitcntInstr,
1958 AMDGPU::Waitcnt &Wait, MachineBasicBlock::instr_iterator It) {
1959 assert(!isNormalMode(MaxCounter));
1960
1961 bool Modified = false;
1962 MachineInstr *CombinedLoadDsCntInstr = nullptr;
1963 MachineInstr *CombinedStoreDsCntInstr = nullptr;
1964 MachineInstr *WaitcntDepctrInstr = nullptr;
1965 MachineInstr *WaitInstrs[AMDGPU::NUM_EXTENDED_INST_CNTS] = {};
1966
1967 LLVM_DEBUG({
1968 dbgs() << "GFX12Plus::applyPreexistingWaitcnt at: ";
1969 if (It.isEnd())
1970 dbgs() << "end of block\n";
1971 else
1972 dbgs() << *It;
1973 });
1974
1975 // Accumulate waits that should not be simplified.
1976 AMDGPU::Waitcnt RequiredWait;
1977
1978 for (auto &II :
1979 make_early_inc_range(make_range(OldWaitcntInstr.getIterator(), It))) {
1980 LLVM_DEBUG(dbgs() << "pre-existing iter: " << II);
1981 if (isNonWaitcntMetaInst(II)) {
1982 LLVM_DEBUG(dbgs() << "skipped meta instruction\n");
1983 continue;
1984 }
1985
1986 // Update required wait count. If this is a soft waitcnt (= it was added
1987 // by an earlier pass), it may be entirely removed.
1988
1989 unsigned Opcode = SIInstrInfo::getNonSoftWaitcntOpcode(II.getOpcode());
1990 bool TrySimplify = Opcode != II.getOpcode() && !OptNone;
1991
1992 // Don't crash if the programmer used legacy waitcnt intrinsics, but don't
1993 // attempt to do more than that either.
1994 if (Opcode == AMDGPU::S_WAITCNT)
1995 continue;
1996
1997 if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) {
1998 unsigned OldEnc = static_cast<unsigned>(
1999 TII.getNamedOperand(II, AMDGPU::OpName::simm16)->getImm());
2000 AMDGPU::Waitcnt OldWait = AMDGPU::decodeLoadcntDscnt(IV, OldEnc);
2001 if (TrySimplify)
2002 Wait = Wait.combined(OldWait);
2003 else
2004 RequiredWait = RequiredWait.combined(OldWait);
2005 // Keep the first wait_loadcnt, erase the rest.
2006 if (CombinedLoadDsCntInstr == nullptr) {
2007 CombinedLoadDsCntInstr = &II;
2008 } else {
2009 II.eraseFromParent();
2010 Modified = true;
2011 }
2012 } else if (Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT) {
2013 unsigned OldEnc = static_cast<unsigned>(
2014 TII.getNamedOperand(II, AMDGPU::OpName::simm16)->getImm());
2015 AMDGPU::Waitcnt OldWait = AMDGPU::decodeStorecntDscnt(IV, OldEnc);
2016 if (TrySimplify)
2017 Wait = Wait.combined(OldWait);
2018 else
2019 RequiredWait = RequiredWait.combined(OldWait);
2020 // Keep the first wait_storecnt, erase the rest.
2021 if (CombinedStoreDsCntInstr == nullptr) {
2022 CombinedStoreDsCntInstr = &II;
2023 } else {
2024 II.eraseFromParent();
2025 Modified = true;
2026 }
2027 } else if (Opcode == AMDGPU::S_WAITCNT_DEPCTR) {
2028 unsigned OldEnc = static_cast<unsigned>(
2029 TII.getNamedOperand(II, AMDGPU::OpName::simm16)->getImm());
2030 AMDGPU::Waitcnt OldWait;
2031 // Set both counters to the decoded value from the single hardware field
2032 unsigned VaVdst = AMDGPU::DepCtr::decodeFieldVaVdst(OldEnc);
2033 OldWait.set(AMDGPU::VA_VDST_RD, VaVdst);
2034 OldWait.set(AMDGPU::VA_VDST_WR, VaVdst);
2036 if (TrySimplify)
2037 ScoreBrackets.simplifyWaitcnt(OldWait);
2038 Wait = Wait.combined(OldWait);
2039 if (WaitcntDepctrInstr == nullptr) {
2040 WaitcntDepctrInstr = &II;
2041 } else {
2042 // S_WAITCNT_DEPCTR requires special care. Don't remove a
2043 // duplicate if it is waiting on things other than VA_VDST or
2044 // VM_VSRC. If that is the case, just make sure the VA_VDST and
2045 // VM_VSRC subfields of the operand are set to the "no wait"
2046 // values.
2047
2048 unsigned Enc = static_cast<unsigned>(
2049 TII.getNamedOperand(II, AMDGPU::OpName::simm16)->getImm());
2050 Enc = AMDGPU::DepCtr::encodeFieldVmVsrc(Enc, ~0u);
2051 // Encode min(VA_VDST_RD, VA_VDST_WR) into the single hardware field
2052 unsigned VaVdst = std::min(Wait.get(AMDGPU::VA_VDST_RD),
2054 Enc = AMDGPU::DepCtr::encodeFieldVaVdst(Enc, VaVdst);
2055
2056 if (Enc != (unsigned)AMDGPU::DepCtr::getDefaultDepCtrEncoding(ST)) {
2057 Modified |= updateOperandIfDifferent(II, AMDGPU::OpName::simm16, Enc);
2058 Modified |= promoteSoftWaitCnt(&II);
2059 } else {
2060 II.eraseFromParent();
2061 Modified = true;
2062 }
2063 }
2064 } else if (Opcode == AMDGPU::S_WAITCNT_lds_direct) {
2065 // Architectures higher than GFX10 do not have direct loads to
2066 // LDS, so no work required here yet.
2067 II.eraseFromParent();
2068 Modified = true;
2069 } else if (Opcode == AMDGPU::WAIT_ASYNCMARK) {
2070 // Update the Waitcnt, but don't erase the wait.asyncmark() itself. It
2071 // shows up in the assembly as a comment with the original parameter N.
2072 unsigned N = static_cast<unsigned>(II.getOperand(0).getImm());
2073 AMDGPU::Waitcnt OldWait = ScoreBrackets.determineAsyncWait(
2074 N, AMDGPU::asyncStagesFromMaskOperand(II.getOperand(1).getImm()));
2075 Wait = Wait.combined(OldWait);
2076 } else {
2077 std::optional<AMDGPU::InstCounterType> CT =
2079 assert(CT.has_value());
2080 unsigned OldCnt = static_cast<unsigned>(
2081 TII.getNamedOperand(II, AMDGPU::OpName::simm16)->getImm());
2082 if (TrySimplify)
2083 Wait.add(CT.value(), OldCnt);
2084 else
2085 RequiredWait.add(CT.value(), OldCnt);
2086 // Keep the first wait of its kind, erase the rest.
2087 if (WaitInstrs[CT.value()] == nullptr) {
2088 WaitInstrs[CT.value()] = &II;
2089 } else {
2090 II.eraseFromParent();
2091 Modified = true;
2092 }
2093 }
2094 }
2095
2096 ScoreBrackets.simplifyWaitcnt(Wait.combined(RequiredWait), Wait);
2097 Wait = Wait.combined(RequiredWait);
2098
2099 if (CombinedLoadDsCntInstr) {
2100 // Only keep an S_WAIT_LOADCNT_DSCNT if both counters actually need
2101 // to be waited for. Otherwise, let the instruction be deleted so
2102 // the appropriate single counter wait instruction can be inserted
2103 // instead, when new S_WAIT_*CNT instructions are inserted by
2104 // createNewWaitcnt(). As a side effect, resetting the wait counts will
2105 // cause any redundant S_WAIT_LOADCNT or S_WAIT_DSCNT to be removed by
2106 // the loop below that deals with single counter instructions.
2107 //
2108 // A wait for LOAD_CNT or DS_CNT implies a wait for VM_VSRC, since
2109 // instructions that have decremented LOAD_CNT or DS_CNT on completion
2110 // will have needed to wait for their register sources to be available
2111 // first.
2112 if (Wait.get(AMDGPU::LOAD_CNT) != ~0u && Wait.get(AMDGPU::DS_CNT) != ~0u) {
2113 unsigned NewEnc = AMDGPU::encodeLoadcntDscnt(IV, Wait);
2114 Modified |= updateOperandIfDifferent(*CombinedLoadDsCntInstr,
2115 AMDGPU::OpName::simm16, NewEnc);
2116 Modified |= promoteSoftWaitCnt(CombinedLoadDsCntInstr);
2117 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::LOAD_CNT);
2118 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::DS_CNT);
2119 Wait.set(AMDGPU::LOAD_CNT, ~0u);
2120 Wait.set(AMDGPU::DS_CNT, ~0u);
2121
2122 LLVM_DEBUG(It.isEnd() ? dbgs() << "applied pre-existing waitcnt\n"
2123 << "New Instr at block end: "
2124 << *CombinedLoadDsCntInstr << '\n'
2125 : dbgs() << "applied pre-existing waitcnt\n"
2126 << "Old Instr: " << *It << "New Instr: "
2127 << *CombinedLoadDsCntInstr << '\n');
2128 } else {
2129 CombinedLoadDsCntInstr->eraseFromParent();
2130 Modified = true;
2131 }
2132 }
2133
2134 if (CombinedStoreDsCntInstr) {
2135 // Similarly for S_WAIT_STORECNT_DSCNT.
2136 if (Wait.get(AMDGPU::STORE_CNT) != ~0u && Wait.get(AMDGPU::DS_CNT) != ~0u) {
2137 unsigned NewEnc = AMDGPU::encodeStorecntDscnt(IV, Wait);
2138 Modified |= updateOperandIfDifferent(*CombinedStoreDsCntInstr,
2139 AMDGPU::OpName::simm16, NewEnc);
2140 Modified |= promoteSoftWaitCnt(CombinedStoreDsCntInstr);
2141 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::STORE_CNT);
2142 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::DS_CNT);
2143 Wait.set(AMDGPU::STORE_CNT, ~0u);
2144 Wait.set(AMDGPU::DS_CNT, ~0u);
2145
2146 LLVM_DEBUG(It.isEnd() ? dbgs() << "applied pre-existing waitcnt\n"
2147 << "New Instr at block end: "
2148 << *CombinedStoreDsCntInstr << '\n'
2149 : dbgs() << "applied pre-existing waitcnt\n"
2150 << "Old Instr: " << *It << "New Instr: "
2151 << *CombinedStoreDsCntInstr << '\n');
2152 } else {
2153 CombinedStoreDsCntInstr->eraseFromParent();
2154 Modified = true;
2155 }
2156 }
2157
2158 // Look for an opportunity to convert existing S_WAIT_LOADCNT,
2159 // S_WAIT_STORECNT and S_WAIT_DSCNT into new S_WAIT_LOADCNT_DSCNT
2160 // or S_WAIT_STORECNT_DSCNT. This is achieved by selectively removing
2161 // instructions so that createNewWaitcnt() will create new combined
2162 // instructions to replace them.
2163
2164 if (Wait.get(AMDGPU::DS_CNT) != ~0u) {
2165 // This is a vector of addresses in WaitInstrs pointing to instructions
2166 // that should be removed if they are present.
2168
2169 // If it's known that both DScnt and either LOADcnt or STOREcnt (but not
2170 // both) need to be waited for, ensure that there are no existing
2171 // individual wait count instructions for these.
2172
2173 if (Wait.get(AMDGPU::LOAD_CNT) != ~0u) {
2174 WaitsToErase.push_back(&WaitInstrs[AMDGPU::LOAD_CNT]);
2175 WaitsToErase.push_back(&WaitInstrs[AMDGPU::DS_CNT]);
2176 } else if (Wait.get(AMDGPU::STORE_CNT) != ~0u) {
2177 WaitsToErase.push_back(&WaitInstrs[AMDGPU::STORE_CNT]);
2178 WaitsToErase.push_back(&WaitInstrs[AMDGPU::DS_CNT]);
2179 }
2180
2181 for (MachineInstr **WI : WaitsToErase) {
2182 if (!*WI)
2183 continue;
2184
2185 DeferredErasures.erase(*WI);
2186 (*WI)->eraseFromParent();
2187 *WI = nullptr;
2188 Modified = true;
2189 }
2190 }
2191
2193 if (!WaitInstrs[CT])
2194 continue;
2195
2196 unsigned NewCnt = Wait.get(CT);
2197 if (NewCnt != ~0u) {
2198 Modified |= updateOperandIfDifferent(*WaitInstrs[CT],
2199 AMDGPU::OpName::simm16, NewCnt);
2200 Modified |= promoteSoftWaitCnt(WaitInstrs[CT]);
2201 DeferredErasures.erase(WaitInstrs[CT]);
2202
2203 ScoreBrackets.applyWaitcnt(CT, NewCnt);
2204 Wait.clear(CT);
2205
2206 LLVM_DEBUG(It.isEnd()
2207 ? dbgs() << "applied pre-existing waitcnt\n"
2208 << "New Instr at block end: " << *WaitInstrs[CT]
2209 << '\n'
2210 : dbgs() << "applied pre-existing waitcnt\n"
2211 << "Old Instr: " << *It
2212 << "New Instr: " << *WaitInstrs[CT] << '\n');
2213 } else {
2214 // Defer erasure in case there's a loop-carried dependency.
2215 DeferredErasures.insert(WaitInstrs[CT]);
2216 }
2217 }
2218
2219 if (WaitcntDepctrInstr) {
2220 // Get the encoded Depctr immediate and override the VA_VDST and VM_VSRC
2221 // subfields with the new required values.
2222 unsigned Enc = static_cast<unsigned>(
2223 TII.getNamedOperand(*WaitcntDepctrInstr, AMDGPU::OpName::simm16)
2224 ->getImm());
2226 // Encode min(VA_VDST_RD, VA_VDST_WR) into the single hardware field
2227 unsigned VaVdst =
2228 std::min(Wait.get(AMDGPU::VA_VDST_RD), Wait.get(AMDGPU::VA_VDST_WR));
2229 Enc = AMDGPU::DepCtr::encodeFieldVaVdst(Enc, VaVdst);
2230
2231 ScoreBrackets.applyWaitcnt(AMDGPU::VA_VDST_RD, VaVdst);
2232 ScoreBrackets.applyWaitcnt(AMDGPU::VA_VDST_WR, VaVdst);
2233 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::VM_VSRC);
2234 Wait.set(AMDGPU::VA_VDST_RD, ~0u);
2235 Wait.set(AMDGPU::VA_VDST_WR, ~0u);
2236 Wait.set(AMDGPU::VM_VSRC, ~0u);
2237
2238 // If that new encoded Depctr immediate would actually still wait
2239 // for anything, update the instruction's operand. Otherwise it can
2240 // just be deleted.
2241 if (Enc != (unsigned)AMDGPU::DepCtr::getDefaultDepCtrEncoding(ST)) {
2242 Modified |= updateOperandIfDifferent(*WaitcntDepctrInstr,
2243 AMDGPU::OpName::simm16, Enc);
2244 LLVM_DEBUG(It.isEnd() ? dbgs() << "applyPreexistingWaitcnt\n"
2245 << "New Instr at block end: "
2246 << *WaitcntDepctrInstr << '\n'
2247 : dbgs() << "applyPreexistingWaitcnt\n"
2248 << "Old Instr: " << *It << "New Instr: "
2249 << *WaitcntDepctrInstr << '\n');
2250 } else {
2251 WaitcntDepctrInstr->eraseFromParent();
2252 Modified = true;
2253 }
2254 }
2255
2256 return Modified;
2257}
2258
2259/// Generate S_WAIT_*CNT instructions for any required counters in \p Wait
2260bool WaitcntGeneratorGFX12Plus::createNewWaitcnt(
2261 MachineBasicBlock &Block, MachineBasicBlock::instr_iterator It,
2262 AMDGPU::Waitcnt Wait, const WaitcntBrackets &ScoreBrackets) {
2263 assert(!isNormalMode(MaxCounter));
2264
2265 bool Modified = false;
2266 const DebugLoc &DL = Block.findDebugLoc(It);
2267
2268 // For GFX12+, we use separate wait instructions, which makes expansion
2269 // simpler
2270 if (ExpandWaitcntProfiling) {
2272 unsigned Count = Wait.get(CT);
2273 if (Count == ~0u)
2274 continue;
2275
2276 // Skip expansion for out-of-order counters - emit normal wait instead
2277 if (ScoreBrackets.counterOutOfOrder(CT)) {
2278 BuildMI(Block, It, DL, TII.get(instrsForExtendedCounterTypes[CT]))
2279 .addImm(Count);
2280 Modified = true;
2281 continue;
2282 }
2283
2284 unsigned Outstanding =
2285 std::min(ScoreBrackets.getOutstanding(CT), getLimit(CT) - 1);
2286 EmitExpandedWaitcnt(Outstanding, Count, [&](unsigned Val) {
2287 BuildMI(Block, It, DL, TII.get(instrsForExtendedCounterTypes[CT]))
2288 .addImm(Val);
2289 });
2290 Modified = true;
2291 }
2292 return Modified;
2293 }
2294
2295 // Normal behavior (no expansion)
2296 // Check for opportunities to use combined wait instructions.
2297 if (Wait.get(AMDGPU::DS_CNT) != ~0u) {
2298 MachineInstr *SWaitInst = nullptr;
2299
2300 if (Wait.get(AMDGPU::LOAD_CNT) != ~0u) {
2301 unsigned Enc = AMDGPU::encodeLoadcntDscnt(IV, Wait);
2302
2303 SWaitInst = BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAIT_LOADCNT_DSCNT))
2304 .addImm(Enc);
2305
2306 Wait.set(AMDGPU::LOAD_CNT, ~0u);
2307 Wait.set(AMDGPU::DS_CNT, ~0u);
2308 } else if (Wait.get(AMDGPU::STORE_CNT) != ~0u) {
2309 unsigned Enc = AMDGPU::encodeStorecntDscnt(IV, Wait);
2310
2311 SWaitInst = BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAIT_STORECNT_DSCNT))
2312 .addImm(Enc);
2313
2314 Wait.set(AMDGPU::STORE_CNT, ~0u);
2315 Wait.set(AMDGPU::DS_CNT, ~0u);
2316 }
2317
2318 if (SWaitInst) {
2319 Modified = true;
2320
2321 LLVM_DEBUG(dbgs() << "GFX12Plus::createNewWaitcnt\n";
2322 if (It != Block.instr_end()) dbgs() << "Old Instr: " << *It;
2323 dbgs() << "New Instr: " << *SWaitInst << '\n');
2324 }
2325 }
2326
2327 // Generate an instruction for any remaining counter that needs
2328 // waiting for.
2329
2331 unsigned Count = Wait.get(CT);
2332 if (Count == ~0u)
2333 continue;
2334
2335 [[maybe_unused]] auto SWaitInst =
2336 BuildMI(Block, It, DL, TII.get(instrsForExtendedCounterTypes[CT]))
2337 .addImm(Count);
2338
2339 Modified = true;
2340
2341 LLVM_DEBUG(dbgs() << "GFX12Plus::createNewWaitcnt\n";
2342 if (It != Block.instr_end()) dbgs() << "Old Instr: " << *It;
2343 dbgs() << "New Instr: " << *SWaitInst << '\n');
2344 }
2345
2346 if (Wait.hasWaitDepctr()) {
2347 assert(IsExpertMode);
2348 unsigned Enc =
2350 // Encode min(VA_VDST_RD, VA_VDST_WR) into the single hardware field
2351 unsigned VaVdst =
2352 std::min(Wait.get(AMDGPU::VA_VDST_RD), Wait.get(AMDGPU::VA_VDST_WR));
2353 Enc = AMDGPU::DepCtr::encodeFieldVaVdst(Enc, VaVdst);
2354
2355 [[maybe_unused]] auto SWaitInst =
2356 BuildMI(Block, It, DL, TII.get(AMDGPU::S_WAITCNT_DEPCTR)).addImm(Enc);
2357
2358 Modified = true;
2359
2360 LLVM_DEBUG(dbgs() << "generateWaitcnt\n";
2361 if (It != Block.instr_end()) dbgs() << "Old Instr: " << *It;
2362 dbgs() << "New Instr: " << *SWaitInst << '\n');
2363 }
2364
2365 return Modified;
2366}
2367
2368/// Generate s_waitcnt instruction to be placed before cur_Inst.
2369/// Instructions of a given type are returned in order,
2370/// but instructions of different types can complete out of order.
2371/// We rely on this in-order completion
2372/// and simply assign a score to the memory access instructions.
2373/// We keep track of the active "score bracket" to determine
2374/// if an access of a memory read requires an s_waitcnt
2375/// and if so what the value of each counter is.
2376/// The "score bracket" is bound by the lower bound and upper bound
2377/// scores (*_score_LB and *_score_ub respectively).
2378/// If FlushFlags.FlushVmCnt is true, we want to flush the vmcnt counter here.
2379/// If FlushFlags.FlushDsCnt is true, we want to flush the dscnt counter here
2380/// (GFX12+ only, where DS_CNT is a separate counter).
2381bool SIInsertWaitcnts::generateWaitcntInstBefore(
2382 MachineInstr &MI, WaitcntBrackets &ScoreBrackets,
2383 MachineInstr *OldWaitcntInstr, PreheaderFlushFlags FlushFlags) {
2384 LLVM_DEBUG(dbgs() << "\n*** GenerateWaitcntInstBefore: "; MI.print(dbgs()););
2385
2386 assert(!isNonWaitcntMetaInst(MI));
2387
2388 AMDGPU::Waitcnt Wait;
2389 const unsigned Opc = MI.getOpcode();
2390
2391 switch (Opc) {
2392 case AMDGPU::BUFFER_WBINVL1:
2393 case AMDGPU::BUFFER_WBINVL1_SC:
2394 case AMDGPU::BUFFER_WBINVL1_VOL:
2395 case AMDGPU::BUFFER_GL0_INV:
2396 case AMDGPU::BUFFER_GL1_INV: {
2397 // FIXME: This should have already been handled by the memory legalizer.
2398 // Removing this currently doesn't affect any lit tests, but we need to
2399 // verify that nothing was relying on this. The number of buffer invalidates
2400 // being handled here should not be expanded.
2401 Wait.set(AMDGPU::LOAD_CNT, 0);
2402 break;
2403 }
2404 case AMDGPU::SI_RETURN_TO_EPILOG:
2405 case AMDGPU::SI_RETURN:
2406 case AMDGPU::SI_WHOLE_WAVE_FUNC_RETURN:
2407 case AMDGPU::S_SETPC_B64_return: {
2408 // All waits must be resolved at call return.
2409 // NOTE: this could be improved with knowledge of all call sites or
2410 // with knowledge of the called routines.
2411 ReturnInsts.insert(&MI);
2412 AMDGPU::Waitcnt AllZeroWait =
2413 WCG->getAllZeroWaitcnt(/*IncludeVSCnt=*/false);
2414 ScoreBrackets.simplifyVmemInvOnlyWait(AllZeroWait);
2415 Wait = AllZeroWait;
2416 break;
2417 }
2418 case AMDGPU::S_ENDPGM:
2419 case AMDGPU::S_ENDPGM_SAVED: {
2420 // In dynamic VGPR mode, we want to release the VGPRs before the wave exits.
2421 // Technically the hardware will do this on its own if we don't, but that
2422 // might cost extra cycles compared to doing it explicitly.
2423 // When not in dynamic VGPR mode, identify S_ENDPGM instructions which may
2424 // have to wait for outstanding VMEM stores. In this case it can be useful
2425 // to send a message to explicitly release all VGPRs before the stores have
2426 // completed, but it is only safe to do this if there are no outstanding
2427 // scratch stores.
2428 EndPgmInsts[&MI] =
2429 !ScoreBrackets.empty(AMDGPU::STORE_CNT) &&
2430 !ScoreBrackets.hasPendingEvent(HWEvents::SCRATCH_WRITE_ACCESS);
2431 break;
2432 }
2433 case AMDGPU::S_SENDMSG:
2434 case AMDGPU::S_SENDMSGHALT: {
2435 if (ST.hasLegacyGeometry() &&
2436 ((MI.getOperand(0).getImm() & AMDGPU::SendMsg::ID_MASK_PreGFX11_) ==
2438 // Resolve vm waits before gs-done.
2439 Wait.set(AMDGPU::LOAD_CNT, 0);
2440 break;
2441 }
2442 [[fallthrough]];
2443 }
2444 default: {
2445
2446 // Export & GDS instructions do not read the EXEC mask until after the
2447 // export is granted (which can occur well after the instruction is issued).
2448 // The shader program must flush all EXP operations on the export-count
2449 // before overwriting the EXEC mask.
2450 if (MI.modifiesRegister(AMDGPU::EXEC, &TRI)) {
2451 // Export and GDS are tracked individually, either may trigger a waitcnt
2452 // for EXEC.
2453 if (ScoreBrackets.hasPendingEvent(HWEvents::EXP_GPR_LOCK) ||
2454 ScoreBrackets.hasPendingEvent(HWEvents::EXP_PARAM_ACCESS) ||
2455 ScoreBrackets.hasPendingEvent(HWEvents::EXP_POS_ACCESS) ||
2456 ScoreBrackets.hasPendingEvent(HWEvents::GDS_GPR_LOCK)) {
2457 Wait.set(AMDGPU::EXP_CNT, 0);
2458 }
2459 }
2460
2461 // Wait for any pending GDS instruction to complete before any
2462 // "Always GDS" instruction.
2463 if (TII.isAlwaysGDS(Opc) && ScoreBrackets.hasPendingGDS())
2464 Wait.add(AMDGPU::DS_CNT, ScoreBrackets.getPendingGDSWait());
2465
2466 if (MI.isCall()) {
2467 // The function is going to insert a wait on everything in its prolog.
2468 // This still needs to be careful if the call target is a load (e.g. a GOT
2469 // load). We also need to check WAW dependency with saved PC.
2470 CallInsts.insert(&MI);
2471 Wait = AMDGPU::Waitcnt();
2472
2473 const MachineOperand &CallAddrOp = TII.getCalleeOperand(MI);
2474 if (CallAddrOp.isReg()) {
2475 ScoreBrackets.determineWaitForPhysReg(
2476 SmemAccessCounter, CallAddrOp.getReg().asMCReg(), Wait, MI);
2477
2478 if (const auto *RtnAddrOp =
2479 TII.getNamedOperand(MI, AMDGPU::OpName::dst)) {
2480 ScoreBrackets.determineWaitForPhysReg(
2481 SmemAccessCounter, RtnAddrOp->getReg().asMCReg(), Wait, MI);
2482 }
2483 }
2484 } else if (Opc == AMDGPU::S_BARRIER_WAIT) {
2485 ScoreBrackets.tryClearSCCWriteEvent(&MI);
2486 } else {
2487 // FIXME: Should not be relying on memoperands.
2488 // Look at the source operands of every instruction to see if
2489 // any of them results from a previous memory operation that affects
2490 // its current usage. If so, an s_waitcnt instruction needs to be
2491 // emitted.
2492 // If the source operand was defined by a load, add the s_waitcnt
2493 // instruction.
2494 //
2495 // Two cases are handled for destination operands:
2496 // 1) If the destination operand was defined by a load, add the s_waitcnt
2497 // instruction to guarantee the right WAW order.
2498 // 2) If a destination operand that was used by a recent export/store ins,
2499 // add s_waitcnt on exp_cnt to guarantee the WAR order.
2500
2501 for (const MachineMemOperand *Memop : MI.memoperands()) {
2502 const Value *Ptr = Memop->getValue();
2503 if (Memop->isStore()) {
2504 if (auto It = SLoadAddresses.find(Ptr); It != SLoadAddresses.end()) {
2505 Wait.add(SmemAccessCounter, 0);
2506 if (PDT.dominates(MI.getParent(), It->second))
2507 SLoadAddresses.erase(It);
2508 }
2509 }
2510 unsigned AS = Memop->getAddrSpace();
2512 continue;
2513 // No need to wait before load from VMEM to LDS.
2514 if (TII.mayWriteLDSThroughDMA(MI))
2515 continue;
2516
2517 // LOAD_CNT is only relevant to vgpr or LDS.
2518 VMEMID TID = LDSDMA_BEGIN;
2519 if (Ptr && Memop->getAAInfo()) {
2520 const auto &LDSDMAStores = ScoreBrackets.getLDSDMAStores();
2521 for (size_t I = 0, E = LDSDMAStores.size(); I != E; ++I) {
2522 if (MI.mayAlias(AA, *LDSDMAStores[I], true)) {
2523 size_t Slot = I + 1;
2524 if (Slot >= NUM_LDSDMA) {
2525 // We didn't have enough slot to track this LDS DMA store, it
2526 // has been tracked using the common RegNo (FIRST_LDS_VGPR).
2527 ScoreBrackets.determineWaitForLDSDMA(AMDGPU::LOAD_CNT, TID,
2528 Wait);
2529 break;
2530 }
2531
2532 ScoreBrackets.determineWaitForLDSDMA(
2533 AMDGPU::LOAD_CNT, TID + static_cast<VMEMID>(Slot), Wait);
2534 }
2535 }
2536 } else {
2537 ScoreBrackets.determineWaitForLDSDMA(AMDGPU::LOAD_CNT, TID, Wait);
2538 }
2539 if (Memop->isStore()) {
2540 ScoreBrackets.determineWaitForLDSDMA(AMDGPU::EXP_CNT, TID, Wait);
2541 }
2542 }
2543
2544 // Loop over use and def operands.
2545 for (const MachineOperand &Op : MI.operands()) {
2546 if (!Op.isReg())
2547 continue;
2548
2549 // If the instruction does not read tied source, skip the operand.
2550 if (Op.isTied() && Op.isUse() && TII.doesNotReadTiedSource(MI))
2551 continue;
2552
2553 MCRegister Reg = Op.getReg().asMCReg();
2554
2555 const bool IsVGPR = TRI.isVectorRegister(MRI, Op.getReg());
2556 if (IsVGPR) {
2557 // Implicit VGPR defs and uses are never a part of the memory
2558 // instructions description and usually present to account for
2559 // super-register liveness.
2560 // TODO: Most of the other instructions also have implicit uses
2561 // for the liveness accounting only.
2562 if (Op.isImplicit() && MI.mayLoadOrStore())
2563 continue;
2564
2565 ScoreBrackets.determineWaitForPhysReg(AMDGPU::VA_VDST_WR, Reg, Wait,
2566 MI);
2567 if (Op.isDef()) {
2568 ScoreBrackets.determineWaitForPhysReg(AMDGPU::VA_VDST_RD, Reg, Wait,
2569 MI);
2570 ScoreBrackets.determineWaitForPhysReg(AMDGPU::VM_VSRC, Reg, Wait,
2571 MI);
2572 }
2573
2574 // RAW always needs an s_waitcnt. WAW needs an s_waitcnt unless the
2575 // previous write and this write are the same type of VMEM
2576 // instruction, in which case they are (in some architectures)
2577 // guaranteed to write their results in order anyway.
2578 // Additionally check instructions where Point Sample Acceleration
2579 // might be applied.
2580 if (Op.isUse() || !updateVMCntOnly(MI) ||
2581 ScoreBrackets.hasDifferentVGPRPendingEvents(
2583 ScoreBrackets.hasPointSamplePendingVmemTypes(MI, Reg) ||
2584 !ST.hasVmemWriteVgprInOrder()) {
2585 ScoreBrackets.determineWaitForPhysReg(AMDGPU::LOAD_CNT, Reg, Wait,
2586 MI);
2587 ScoreBrackets.determineWaitForPhysReg(AMDGPU::SAMPLE_CNT, Reg, Wait,
2588 MI);
2589 ScoreBrackets.determineWaitForPhysReg(AMDGPU::BVH_CNT, Reg, Wait,
2590 MI);
2591 ScoreBrackets.clearVGPRPendingEvents(Reg);
2592 }
2593
2594 if (Op.isDef() ||
2595 ScoreBrackets.hasPendingEvent(HWEvents::EXP_LDS_ACCESS)) {
2596 ScoreBrackets.determineWaitForPhysReg(AMDGPU::EXP_CNT, Reg, Wait,
2597 MI);
2598 }
2599 ScoreBrackets.determineWaitForPhysReg(AMDGPU::DS_CNT, Reg, Wait, MI);
2600 } else if (Op.getReg() == AMDGPU::SCC) {
2601 ScoreBrackets.determineWaitForPhysReg(AMDGPU::KM_CNT, Reg, Wait, MI);
2602 } else {
2603 ScoreBrackets.determineWaitForPhysReg(SmemAccessCounter, Reg, Wait,
2604 MI);
2605 }
2606
2607 if (ST.hasWaitXcnt() && ST.isXNACKEnabled() && Op.isDef())
2608 ScoreBrackets.determineWaitForPhysReg(AMDGPU::X_CNT, Reg, Wait, MI);
2609 }
2610 }
2611 }
2612 }
2613
2614 // Ensure safety against exceptions from outstanding memory operations while
2615 // waiting for a barrier:
2616 //
2617 // * Some subtargets safely handle backing off the barrier in hardware
2618 // when an exception occurs.
2619 // * Some subtargets have an implicit S_WAITCNT 0 before barriers, so that
2620 // there can be no outstanding memory operations during the wait.
2621 // * Subtargets with split barriers don't need to back off the barrier; it
2622 // is up to the trap handler to preserve the user barrier state correctly.
2623 //
2624 // In all other cases, ensure safety by ensuring that there are no outstanding
2625 // memory operations.
2626 if (Opc == AMDGPU::S_BARRIER && !ST.hasAutoWaitcntBeforeBarrier() &&
2627 !ST.hasBackOffBarrier()) {
2628 Wait = Wait.combined(WCG->getAllZeroWaitcnt(/*IncludeVSCnt=*/true));
2629 }
2630
2631 // TODO: Remove this work-around, enable the assert for Bug 457939
2632 // after fixing the scheduler. Also, the Shader Compiler code is
2633 // independent of target.
2634 if (SIInstrInfo::isCBranchVCCZRead(MI) && ST.hasReadVCCZBug() &&
2635 ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS)) {
2636 Wait.set(AMDGPU::DS_CNT, 0);
2637 }
2638
2639 // Verify that the wait is actually needed.
2640 ScoreBrackets.simplifyWaitcnt(Wait);
2641
2642 // It is only necessary to insert an S_WAITCNT_DEPCTR instruction that
2643 // waits on VA_VDST if the instruction it would precede is not a VALU
2644 // instruction, since hardware handles VALU->VGPR->VALU hazards in
2645 // expert scheduling mode.
2646 if (TII.isVALU(MI, /*AllowLDSDMA=*/false)) {
2647 Wait.set(AMDGPU::VA_VDST_RD, ~0u);
2648 Wait.set(AMDGPU::VA_VDST_WR, ~0u);
2649 }
2650
2651 // Since the translation for VMEM addresses occur in-order, we can apply the
2652 // XCnt if the current instruction is of VMEM type and has a memory
2653 // dependency with another VMEM instruction in flight.
2654 if (Wait.get(AMDGPU::X_CNT) != ~0u && isVmemAccess(MI)) {
2655 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::X_CNT);
2656 Wait.set(AMDGPU::X_CNT, ~0u);
2657 }
2658
2659 // When forcing emit, we need to skip terminators because that would break the
2660 // terminators of the MBB if we emit a waitcnt between terminators.
2661 if (ST.getCLOpts().waitcnt_forcezero && !MI.isTerminator())
2662 Wait = WCG->getAllZeroWaitcnt(/*IncludeVSCnt=*/false);
2663
2664 // If we force waitcnt then update Wait accordingly.
2666 if (!ForceEmitWaitcnt[T])
2667 continue;
2668 Wait.set(T, 0);
2669 }
2670
2671 if (FlushFlags.FlushVmCnt) {
2674 Wait.set(T, 0);
2675 }
2676
2677 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(AMDGPU::DS_CNT))
2678 Wait.set(AMDGPU::DS_CNT, 0);
2679
2680 if (ST.getCLOpts().waitcnt_load_forcezero &&
2681 Wait.get(AMDGPU::LOAD_CNT) != ~0u)
2682 Wait.set(AMDGPU::LOAD_CNT, 0);
2683
2684 return generateWaitcnt(Wait, MI.getIterator(), *MI.getParent(), ScoreBrackets,
2685 OldWaitcntInstr);
2686}
2687
2688bool SIInsertWaitcnts::generateWaitcnt(AMDGPU::Waitcnt Wait,
2690 MachineBasicBlock &Block,
2691 WaitcntBrackets &ScoreBrackets,
2692 MachineInstr *OldWaitcntInstr) {
2693 bool Modified = false;
2694
2695 if (OldWaitcntInstr)
2696 // Try to merge the required wait with preexisting waitcnt instructions.
2697 // Also erase redundant waitcnt.
2698 Modified =
2699 WCG->applyPreexistingWaitcnt(ScoreBrackets, *OldWaitcntInstr, Wait, It);
2700
2701 // ExpCnt can be merged into VINTERP.
2702 if (Wait.get(AMDGPU::EXP_CNT) != ~0u && It != Block.instr_end() &&
2704 MachineOperand *WaitExp = TII.getNamedOperand(*It, AMDGPU::OpName::waitexp);
2705 if (Wait.get(AMDGPU::EXP_CNT) < WaitExp->getImm()) {
2706 WaitExp->setImm(Wait.get(AMDGPU::EXP_CNT));
2707 Modified = true;
2708 }
2709 // Apply ExpCnt before resetting it, so applyWaitcnt below sees all counts.
2710 ScoreBrackets.applyWaitcnt(Wait, AMDGPU::EXP_CNT);
2711 Wait.set(AMDGPU::EXP_CNT, ~0u);
2712
2713 LLVM_DEBUG(dbgs() << "generateWaitcnt\n"
2714 << "Update Instr: " << *It);
2715 }
2716
2717 if (WCG->createNewWaitcnt(Block, It, Wait, ScoreBrackets))
2718 Modified = true;
2719
2720 // Any counts that could have been applied to any existing waitcnt
2721 // instructions will have been done so, now deal with any remaining.
2722 ScoreBrackets.applyWaitcnt(Wait);
2723
2724 return Modified;
2725}
2726
2727bool SIInsertWaitcnts::isVmemAccess(const MachineInstr &MI) const {
2728 return (TII.isFLAT(MI) && TII.mayAccessVMEMThroughFlat(MI)) ||
2729 (TII.isVMEM(MI) && !AMDGPU::getMUBUFIsBufferInv(MI.getOpcode()));
2730}
2731
2732// Return true if the next instruction is S_ENDPGM, following fallthrough
2733// blocks if necessary.
2734bool SIInsertWaitcnts::isNextENDPGM(MachineBasicBlock::instr_iterator It,
2735 MachineBasicBlock *Block) const {
2736 auto BlockEnd = Block->getParent()->end();
2737 auto BlockIter = Block->getIterator();
2738
2739 while (true) {
2740 if (It.isEnd()) {
2741 if (++BlockIter != BlockEnd) {
2742 It = BlockIter->instr_begin();
2743 continue;
2744 }
2745
2746 return false;
2747 }
2748
2749 if (!It->isMetaInstruction())
2750 break;
2751
2752 It++;
2753 }
2754
2755 assert(!It.isEnd());
2756
2757 return It->getOpcode() == AMDGPU::S_ENDPGM;
2758}
2759
2760// Add a wait after an instruction if architecture requirements mandate one.
2761bool SIInsertWaitcnts::insertForcedWaitAfter(MachineInstr &Inst,
2762 MachineBasicBlock &Block,
2763 WaitcntBrackets &ScoreBrackets) {
2764 AMDGPU::Waitcnt Wait;
2765 bool NeedsEndPGMCheck = false;
2766
2767 if (ST.isPreciseMemoryEnabled() && Inst.mayLoadOrStore())
2768 Wait = WCG->getAllZeroWaitcnt(Inst.mayStore() &&
2770
2771 if (TII.isAlwaysGDS(Inst.getOpcode())) {
2772 Wait.set(AMDGPU::DS_CNT, 0);
2773 NeedsEndPGMCheck = true;
2774 }
2775
2776 ScoreBrackets.simplifyWaitcnt(Wait);
2777
2778 auto SuccessorIt = next_nodbg(Inst.getIterator(), Block.instr_end());
2779 bool Result = generateWaitcnt(Wait, SuccessorIt, Block, ScoreBrackets,
2780 /*OldWaitcntInstr=*/nullptr);
2781
2782 if (Result && NeedsEndPGMCheck && isNextENDPGM(SuccessorIt, &Block)) {
2783 BuildMI(Block, SuccessorIt, Inst.getDebugLoc(), TII.get(AMDGPU::S_NOP))
2784 .addImm(0);
2785 }
2786
2787 return Result;
2788}
2789
2790void SIInsertWaitcnts::updateEventWaitcntAfter(MachineInstr &Inst,
2791 WaitcntBrackets *ScoreBrackets) {
2792
2793 HWEvents InstEvents = AMDGPU::getEventsFor(Inst, ST, IsExpertMode, TgSplit);
2794 for (HWEvents E : InstEvents)
2795 ScoreBrackets->updateByEvent(E, Inst);
2796
2797 if (TII.isDS(Inst) && TII.usesLGKM_CNT(Inst)) {
2798 if (TII.isAlwaysGDS(Inst.getOpcode()) ||
2799 TII.hasModifiersSet(Inst, AMDGPU::OpName::gds)) {
2800 ScoreBrackets->setPendingGDS();
2801 }
2802 } else if (TII.isFLAT(Inst)) {
2803 if (Inst.mayLoadOrStore() && TII.mayAccessVMEMThroughFlat(Inst) &&
2804 TII.mayAccessLDSThroughFlat(Inst, TgSplit) &&
2805 !SIInstrInfo::isLDSDMA(Inst)) {
2806 // Async/LDSDMA operations have FLAT encoding but do not actually use flat
2807 // pointers. They do have two operands that each access global and LDS,
2808 // thus making it appear at this point that they are using a flat pointer.
2809 // Filter them out, and for the rest, generate a dependency on flat
2810 // pointers so that both VM and LGKM counters are flushed.
2811 ScoreBrackets->setPendingFlat();
2812 }
2813 } else if (Inst.isCall()) {
2814 // Act as a wait on everything, but AsyncCnt and TensorCnt are never
2815 // included in such blanket waits.
2816 ScoreBrackets->applyWaitcnt(WCG->getAllZeroWaitcnt(/*IncludeVSCnt=*/false));
2817 ScoreBrackets->setStateOnFunctionEntryOrReturn();
2818 } else if (TII.isVINTERP(Inst)) {
2819 int64_t Imm = TII.getNamedOperand(Inst, AMDGPU::OpName::waitexp)->getImm();
2820 ScoreBrackets->applyWaitcnt(AMDGPU::EXP_CNT, static_cast<unsigned>(Imm));
2821 }
2822
2823 // Set XCNT to zero in the bracket for instructions that implicitly drain
2824 // XCNT.
2825 if (ST.hasWaitXcnt() && ST.isXNACKEnabled() && SIInstrInfo::isXcntDrain(Inst))
2826 ScoreBrackets->applyWaitcnt(AMDGPU::X_CNT, 0);
2827}
2828
2829bool WaitcntBrackets::mergeScore(const MergeInfo &M, unsigned &Score,
2830 unsigned OtherScore) {
2831 unsigned MyShifted = Score <= M.OldLB ? 0 : Score + M.MyShift;
2832 unsigned OtherShifted =
2833 OtherScore <= M.OtherLB ? 0 : OtherScore + M.OtherShift;
2834 Score = std::max(MyShifted, OtherShifted);
2835 return OtherShifted > MyShifted;
2836}
2837
2838bool WaitcntBrackets::mergeAsyncMarks(AMDGPU::AsyncStages Stage,
2839 ArrayRef<MergeInfo> MergeInfos,
2840 ArrayRef<CounterValueArray> OtherMarks) {
2841 bool StrictDom = false;
2842 auto &StageMarks = AsyncMarks[AMDGPU::getAsyncStageIndex(Stage)];
2843
2844 LLVM_DEBUG(dbgs() << "Merging async marks of stage " << Stage << " ...");
2845 // Early exit: nothing to merge when both sides are empty.
2846 if (StageMarks.empty() && OtherMarks.empty()) {
2847 LLVM_DEBUG(dbgs() << " nothing to merge\n");
2848 return false;
2849 }
2850 LLVM_DEBUG(dbgs() << '\n');
2851
2852 // Determine maximum length needed after merging
2853 auto MaxSize = (unsigned)std::max(StageMarks.size(), OtherMarks.size());
2854 MaxSize = std::min(MaxSize, MaxAsyncMarks);
2855
2856 // Keep only the most recent marks within our limit.
2857 if (StageMarks.size() > MaxSize)
2858 StageMarks.erase(StageMarks.begin(),
2859 StageMarks.begin() + (StageMarks.size() - MaxSize));
2860
2861 // Pad with zero-filled marks if our list is shorter. Zero represents "no
2862 // pending async operations at this checkpoint" and acts as the identity
2863 // element for max() during merging. We pad at the beginning since the marks
2864 // need to be aligned in most-recent order.
2865 constexpr CounterValueArray ZeroMark{};
2866 StageMarks.insert(StageMarks.begin(), MaxSize - StageMarks.size(), ZeroMark);
2867
2868 LLVM_DEBUG({
2869 dbgs() << "Before merge:\n";
2870 for (const auto &Mark : StageMarks) {
2871 llvm::interleaveComma(Mark, dbgs());
2872 dbgs() << '\n';
2873 }
2874 dbgs() << "Other marks:\n";
2875 for (const auto &Mark : OtherMarks) {
2876 llvm::interleaveComma(Mark, dbgs());
2877 dbgs() << '\n';
2878 }
2879 });
2880
2881 // Merge element-wise using the existing mergeScore function and the
2882 // appropriate MergeInfo for each counter type. Rebase and merge EVERY
2883 // surviving mark into the new (widened) frame, aligned by recency from the
2884 // back. A mark with no counterpart on the other side merges against the
2885 // zero/identity mark: mergeScore still applies MyShift, so the mark is
2886 // re-expressed in the new frame instead of being left with a stale
2887 // (pre-merge) score.
2888 const size_t OtherSize = OtherMarks.size();
2889 const size_t OurSize = StageMarks.size();
2890 // After the both-empty early-return above, max(StageMarks, OtherMarks) >= 1,
2891 // and the erase/pad steps normalize StageMarks to exactly MaxSize. Hence
2892 // OurSize == MaxSize >= 1 (as long as MaxAsyncMarks != 0), so the
2893 // seq_inclusive(1, OurSize) below never trips its "Begin <= End" assertion
2894 // the way seq_inclusive(1, MergeCount) could when OtherSize == 0.
2895 assert(OurSize >= 1 &&
2896 "StageMarks padded to MaxSize >= 1 (needs MaxAsyncMarks != 0)");
2897
2898 for (auto Idx : seq_inclusive<size_t>(1, OurSize)) {
2899 const CounterValueArray &OtherMark =
2900 Idx <= OtherSize ? OtherMarks[OtherSize - Idx] : ZeroMark;
2901 for (auto T : inst_counter_types(Context->MaxCounter))
2902 StrictDom |=
2903 mergeScore(MergeInfos[T], StageMarks[OurSize - Idx][T], OtherMark[T]);
2904 }
2905
2906 LLVM_DEBUG({
2907 dbgs() << "After merge:\n";
2908 for (const auto &Mark : StageMarks) {
2909 llvm::interleaveComma(Mark, dbgs());
2910 dbgs() << '\n';
2911 }
2912 });
2913
2914 return StrictDom;
2915}
2916
2917/// Merge the pending events and associater score brackets of \p Other into
2918/// this brackets status.
2919///
2920/// Returns whether the merge resulted in a change that requires tighter waits
2921/// (i.e. the merged brackets strictly dominate the original brackets).
2922bool WaitcntBrackets::merge(const WaitcntBrackets &Other) {
2923 bool StrictDom = false;
2924
2925 // Check if "other" has keys we don't have, and create default entries for
2926 // those. If they remain empty after merging, we will clean it up after.
2927 for (auto K : Other.VMem.keys())
2928 VMem.try_emplace(K);
2929 for (auto K : Other.SGPRs.keys())
2930 SGPRs.try_emplace(K);
2931
2932 // Array to store MergeInfo for each counter type
2933 MergeInfo MergeInfos[AMDGPU::NUM_INST_CNTS];
2934
2935 for (auto T : inst_counter_types(Context->MaxCounter)) {
2936 // Merge event flags for this counter
2937 const HWEvents &EventsForT = Context->getWaitEvents(T);
2938 const HWEvents OldEvents = PendingEvents & EventsForT;
2939 const HWEvents OtherEvents = Other.PendingEvents & EventsForT;
2940 if (!OldEvents.contains(OtherEvents))
2941 StrictDom = true;
2942 PendingEvents |= OtherEvents;
2943
2944 // Merge scores for this counter
2945 const unsigned MyPending = ScoreUBs[T] - ScoreLBs[T];
2946 const unsigned OtherPending = Other.ScoreUBs[T] - Other.ScoreLBs[T];
2947 const unsigned NewUB = ScoreLBs[T] + std::max(MyPending, OtherPending);
2948 if (NewUB < ScoreLBs[T])
2949 report_fatal_error("waitcnt score overflow");
2950
2951 MergeInfo &M = MergeInfos[T];
2952 M.OldLB = ScoreLBs[T];
2953 M.OtherLB = Other.ScoreLBs[T];
2954 M.MyShift = NewUB - ScoreUBs[T];
2955 M.OtherShift = NewUB - Other.ScoreUBs[T];
2956
2957 ScoreUBs[T] = NewUB;
2958
2959 if (T == AMDGPU::LOAD_CNT)
2960 StrictDom |= mergeScore(M, LastFlatLoadCnt, Other.LastFlatLoadCnt);
2961
2962 if (T == AMDGPU::DS_CNT) {
2963 StrictDom |= mergeScore(M, LastFlatDsCnt, Other.LastFlatDsCnt);
2964 StrictDom |= mergeScore(M, LastGDS, Other.LastGDS);
2965 }
2966
2967 if (T == AMDGPU::KM_CNT) {
2968 StrictDom |= mergeScore(M, SCCScore, Other.SCCScore);
2969 if (Other.hasPendingEvent(HWEvents::SCC_WRITE)) {
2970 if (!(OldEvents & HWEvents::SCC_WRITE)) {
2971 PendingSCCWrite = Other.PendingSCCWrite;
2972 } else if (PendingSCCWrite != Other.PendingSCCWrite) {
2973 PendingSCCWrite = nullptr;
2974 }
2975 }
2976 }
2977
2978 for (auto &[RegID, Info] : VMem) {
2979 unsigned OtherScore = Other.getVMemScore(RegID, T);
2980 if (T == AMDGPU::VA_VDST_WR) {
2981 // A fence count is usable only when every path carrying the dependency
2982 // has at least that many later ordered XDL writes. Keep this path's
2983 // OrderedXDLCount and rebase each write onto it.
2984 bool MyPending = Info.Scores[T] > M.OldLB;
2985 bool OtherPending = OtherScore > M.OtherLB;
2986 unsigned MyCount = MyPending ? getNumOrderedXDLsAfterWrite(RegID) : 0;
2987 unsigned OtherCount =
2988 OtherPending ? Other.getNumOrderedXDLsAfterWrite(RegID) : 0;
2989 unsigned NewCount = MyPending && OtherPending
2990 ? std::min(MyCount, OtherCount)
2991 : (MyPending ? MyCount : OtherCount);
2992 StrictDom |= MyPending && NewCount < MyCount;
2993 Info.OrderedXDLCountAtWrite = OrderedXDLCount - NewCount;
2994 }
2995 StrictDom |= mergeScore(M, Info.Scores[T], OtherScore);
2996 }
2997
2998 if (isSmemCounter(T)) {
2999 for (auto &[RegID, Info] : SGPRs) {
3000 auto It = Other.SGPRs.find(RegID);
3001 unsigned OtherScore = (It != Other.SGPRs.end()) ? It->second.get(T) : 0;
3002 StrictDom |= mergeScore(M, Info.get(T), OtherScore);
3003 }
3004 }
3005 }
3006
3007 for (auto &[TID, Info] : VMem) {
3008 if (auto It = Other.VMem.find(TID); It != Other.VMem.end()) {
3009 HWEvents NewVGPRContext =
3010 Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
3011 StrictDom |= NewVGPRContext != Info.VGPRPendingEvents;
3012 Info.VGPRPendingEvents = NewVGPRContext;
3013 }
3014 }
3015
3016 for (AMDGPU::AsyncStages S : AMDGPU::asyncStages(AMDGPU::AsyncStages::ALL)) {
3017 unsigned SI = AMDGPU::getAsyncStageIndex(S);
3018 StrictDom |= mergeAsyncMarks(S, MergeInfos, Other.AsyncMarks[SI]);
3019 for (auto T : inst_counter_types(Context->MaxCounter))
3020 StrictDom |=
3021 mergeScore(MergeInfos[T], AsyncScore[SI][T], Other.AsyncScore[SI][T]);
3022 }
3023
3024 purgeEmptyTrackingData();
3025 return StrictDom;
3026}
3027
3028static bool isWaitInstr(MachineInstr &Inst) {
3029 unsigned Opcode = SIInstrInfo::getNonSoftWaitcntOpcode(Inst.getOpcode());
3030 return Opcode == AMDGPU::S_WAITCNT ||
3031 (Opcode == AMDGPU::S_WAITCNT_VSCNT && Inst.getOperand(0).isReg() &&
3032 Inst.getOperand(0).getReg() == AMDGPU::SGPR_NULL) ||
3033 Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT ||
3034 Opcode == AMDGPU::S_WAIT_STORECNT_DSCNT ||
3035 Opcode == AMDGPU::S_WAITCNT_lds_direct ||
3036 Opcode == AMDGPU::WAIT_ASYNCMARK ||
3037 AMDGPU::counterTypeForInstr(Opcode).has_value();
3038}
3039
3040void SIInsertWaitcnts::setSchedulingMode(MachineBasicBlock &MBB,
3042 bool ExpertMode) const {
3045 BuildMI(MBB, I, DebugLoc(), TII.get(AMDGPU::S_SETREG_IMM32_B32))
3046 .addImm(ExpertMode ? 2 : 0)
3047 .addImm(EncodedReg);
3048}
3049
3050namespace {
3051// TODO: Remove this work-around after fixing the scheduler.
3052// There are two reasons why vccz might be incorrect; see ST.hasReadVCCZBug()
3053// and ST.partialVCCWritesUpdateVCCZ().
3054// i. VCCZBug: There is a hardware bug on CI/SI where SMRD instruction may
3055// corrupt vccz bit, so when we detect that an instruction may read from
3056// a corrupt vccz bit, we need to:
3057// 1. Insert s_waitcnt lgkm(0) to wait for all outstanding SMRD
3058// operations to complete.
3059// 2. Recompute the correct value of vccz by writing the current value
3060// of vcc back to vcc.
3061// ii. Partial writes to vcc don't update vccz, so we need to recompute the
3062// correct value of vccz by reading vcc and writing it back to vcc.
3063// No waitcnt is needed in this case.
3064class VCCZWorkaround {
3065 const WaitcntBrackets &ScoreBrackets;
3066 const GCNSubtarget &ST;
3067 const SIInstrInfo &TII;
3068 const SIRegisterInfo &TRI;
3069 bool VCCZCorruptionBug = false;
3070 bool VCCZNotUpdatedByPartialWrites = false;
3071 /// vccz could be incorrect at a basic block boundary if a predecessor wrote
3072 /// to vcc and then issued an smem load, so initialize to true.
3073 bool MustRecomputeVCCZ = true;
3074
3075public:
3076 VCCZWorkaround(const WaitcntBrackets &ScoreBrackets, const GCNSubtarget &ST,
3077 const SIInstrInfo &TII, const SIRegisterInfo &TRI)
3078 : ScoreBrackets(ScoreBrackets), ST(ST), TII(TII), TRI(TRI) {
3079 VCCZCorruptionBug = ST.hasReadVCCZBug();
3080 VCCZNotUpdatedByPartialWrites = !ST.partialVCCWritesUpdateVCCZ();
3081 }
3082 /// If \p MI reads vccz and we must recompute it based on MustRecomputeVCCZ,
3083 /// then emit a vccz recompute instruction before \p MI. This needs to be
3084 /// called on every instruction in the basic block because it also tracks the
3085 /// state and updates MustRecomputeVCCZ accordingly. Returns true if it
3086 /// modified the IR.
3087 bool tryRecomputeVCCZ(MachineInstr &MI) {
3088 // No need to run this if neither bug is present.
3089 if (!VCCZCorruptionBug && !VCCZNotUpdatedByPartialWrites)
3090 return false;
3091
3092 // If MI is an SMEM and it can corrupt vccz on this target, then we need
3093 // both to emit a waitcnt and to recompute vccz.
3094 // But we don't actually emit a waitcnt here. This is done in
3095 // generateWaitcntInstBefore() because it tracks all the necessary waitcnt
3096 // state, and can either skip emitting a waitcnt if there is already one in
3097 // the IR, or emit an "optimized" combined waitcnt.
3098 // If this is an smem read, it could complete and clobber vccz at any time.
3099 MustRecomputeVCCZ |= VCCZCorruptionBug && TII.isSMRD(MI);
3100
3101 // If the target partial vcc writes don't update vccz, and MI is such an
3102 // instruction then we must recompute vccz.
3103 // Note: We are using PartiallyWritesToVCCOpt optional to avoid calling
3104 // `definesRegister()` more than needed, because it's not very cheap.
3105 std::optional<bool> PartiallyWritesToVCCOpt;
3106 auto PartiallyWritesToVCC = [](MachineInstr &MI) {
3107 return MI.definesRegister(AMDGPU::VCC_LO, /*TRI=*/nullptr) ||
3108 MI.definesRegister(AMDGPU::VCC_HI, /*TRI=*/nullptr);
3109 };
3110 if (VCCZNotUpdatedByPartialWrites) {
3111 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(MI);
3112 // If this is a partial VCC write but won't update vccz, then we must
3113 // recompute vccz.
3114 MustRecomputeVCCZ |= *PartiallyWritesToVCCOpt;
3115 }
3116
3117 // If MI is a vcc write with no pending smem, or there is a pending smem
3118 // but the target does not suffer from the vccz corruption bug, then we
3119 // don't need to recompute vccz as this write will recompute it anyway.
3120 if (!ScoreBrackets.hasPendingEvent(HWEvents::SMEM_ACCESS) ||
3121 !VCCZCorruptionBug) {
3122 // Compute PartiallyWritesToVCCOpt if we haven't done so already.
3123 if (!PartiallyWritesToVCCOpt)
3124 PartiallyWritesToVCCOpt = PartiallyWritesToVCC(MI);
3125 bool FullyWritesToVCC = !*PartiallyWritesToVCCOpt &&
3126 MI.definesRegister(AMDGPU::VCC, /*TRI=*/nullptr);
3127 // If we write to the full vcc or we write partially and the target
3128 // updates vccz on partial writes, then vccz will be updated correctly.
3129 bool UpdatesVCCZ = FullyWritesToVCC || (!VCCZNotUpdatedByPartialWrites &&
3130 *PartiallyWritesToVCCOpt);
3131 if (UpdatesVCCZ)
3132 MustRecomputeVCCZ = false;
3133 }
3134
3135 // If MI is a branch that reads VCCZ then emit a waitcnt and a vccz
3136 // restore instruction if either is needed.
3137 if (SIInstrInfo::isCBranchVCCZRead(MI) && MustRecomputeVCCZ) {
3138 // Recompute the vccz bit. Any time a value is written to vcc, the vccz
3139 // bit is updated, so we can restore the bit by reading the value of vcc
3140 // and then writing it back to the register.
3141 BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
3142 TII.get(ST.isWave32() ? AMDGPU::S_MOV_B32 : AMDGPU::S_MOV_B64),
3143 TRI.getVCC())
3144 .addReg(TRI.getVCC());
3145 MustRecomputeVCCZ = false;
3146 return true;
3147 }
3148 return false;
3149 }
3150};
3151
3152} // namespace
3153
3154// Generate s_waitcnt instructions where needed.
3155bool SIInsertWaitcnts::insertWaitcntInBlock(MachineFunction &MF,
3156 MachineBasicBlock &Block,
3157 WaitcntBrackets &ScoreBrackets) {
3158 bool Modified = false;
3159
3160 LLVM_DEBUG({
3161 dbgs() << "*** Begin Block: ";
3162 Block.printName(dbgs());
3163 ScoreBrackets.dump();
3164 });
3165 VCCZWorkaround VCCZW(ScoreBrackets, ST, TII, TRI);
3166
3167 // Walk over the instructions.
3168 MachineInstr *OldWaitcntInstr = nullptr;
3169
3170 // NOTE: We may append instrs after Inst while iterating.
3171 ScoreBrackets.verify();
3172 for (MachineBasicBlock::instr_iterator Iter = Block.instr_begin(),
3173 E = Block.instr_end();
3174 Iter != E; ++Iter) {
3175 MachineInstr &Inst = *Iter;
3176 if (isNonWaitcntMetaInst(Inst))
3177 continue;
3178 // Track pre-existing waitcnts that were added in earlier iterations or by
3179 // the memory legalizer.
3180 if (isWaitInstr(Inst) ||
3181 (IsExpertMode && Inst.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR)) {
3182 if (!OldWaitcntInstr)
3183 OldWaitcntInstr = &Inst;
3184 continue;
3185 }
3186
3187 PreheaderFlushFlags FlushFlags;
3188 if (Block.getFirstTerminator() == Inst)
3189 FlushFlags = isPreheaderToFlush(Block, ScoreBrackets);
3190
3191 // Generate an s_waitcnt instruction to be placed before Inst, if needed.
3192 Modified |= generateWaitcntInstBefore(Inst, ScoreBrackets, OldWaitcntInstr,
3193 FlushFlags);
3194 OldWaitcntInstr = nullptr;
3195
3196 if (Inst.getOpcode() == AMDGPU::ASYNCMARK) {
3197 // Asyncmarks record the current wait state and so should not allow
3198 // waitcnts that occur after them to be merged into waitcnts that occur
3199 // before.
3200 ScoreBrackets.recordAsyncMark(Inst, AMDGPU::asyncStagesFromMaskOperand(
3201 Inst.getOperand(0).getImm()));
3202 continue;
3203 }
3204
3205 if (TII.isSMRD(Inst)) {
3206 for (const MachineMemOperand *Memop : Inst.memoperands()) {
3207 // No need to handle invariant loads when avoiding WAR conflicts, as
3208 // there cannot be a vector store to the same memory location.
3209 if (!Memop->isInvariant()) {
3210 const Value *Ptr = Memop->getValue();
3211 SLoadAddresses.insert(std::pair(Ptr, Inst.getParent()));
3212 }
3213 }
3214 }
3215
3216 updateEventWaitcntAfter(Inst, &ScoreBrackets);
3217
3218 // Note: insertForcedWaitAfter() may add instrs after Iter that need to be
3219 // visited by the loop.
3220 Modified |= insertForcedWaitAfter(Inst, Block, ScoreBrackets);
3221
3222 LLVM_DEBUG({
3223 Inst.print(dbgs());
3224 ScoreBrackets.dump();
3225 });
3226
3227 // If the target suffers from the vccz bugs, this may emit the necessary
3228 // vccz recompute instruction before \p Inst if needed.
3229 Modified |= VCCZW.tryRecomputeVCCZ(Inst);
3230
3231 ScoreBrackets.verify();
3232 }
3233
3234 // Flush counters at the end of the block if needed (for preheaders with no
3235 // terminator).
3236 AMDGPU::Waitcnt Wait;
3237 if (Block.getFirstTerminator() == Block.end()) {
3238 PreheaderFlushFlags FlushFlags = isPreheaderToFlush(Block, ScoreBrackets);
3239 if (FlushFlags.FlushVmCnt) {
3240 if (ScoreBrackets.hasPendingEvent(AMDGPU::LOAD_CNT))
3241 Wait.set(AMDGPU::LOAD_CNT, 0);
3242 if (ScoreBrackets.hasPendingEvent(AMDGPU::SAMPLE_CNT))
3243 Wait.set(AMDGPU::SAMPLE_CNT, 0);
3244 if (ScoreBrackets.hasPendingEvent(AMDGPU::BVH_CNT))
3245 Wait.set(AMDGPU::BVH_CNT, 0);
3246 }
3247 if (FlushFlags.FlushDsCnt && ScoreBrackets.hasPendingEvent(AMDGPU::DS_CNT))
3248 Wait.set(AMDGPU::DS_CNT, 0);
3249 }
3250
3251 // Combine or remove any redundant waitcnts at the end of the block.
3252 Modified |= generateWaitcnt(Wait, Block.instr_end(), Block, ScoreBrackets,
3253 OldWaitcntInstr);
3254
3255 LLVM_DEBUG({
3256 dbgs() << "*** End Block: ";
3257 Block.printName(dbgs());
3258 ScoreBrackets.dump();
3259 });
3260
3261 return Modified;
3262}
3263
3264bool SIInsertWaitcnts::removeRedundantSoftXcnts(MachineBasicBlock &Block) {
3265 if (Block.size() <= 1)
3266 return false;
3267 // The Memory Legalizer conservatively inserts a soft xcnt before each
3268 // atomic RMW operation. However, for sequences of back-to-back atomic
3269 // RMWs, only the first s_wait_xcnt insertion is necessary. Optimize away
3270 // the redundant soft xcnts.
3271 bool Modified = false;
3272 // Remember the last atomic with a soft xcnt right before it.
3273 MachineInstr *LastAtomicWithSoftXcnt = nullptr;
3274
3275 for (MachineInstr &MI : drop_begin(Block)) {
3276 // Ignore last atomic if non-LDS VMEM and SMEM.
3277 bool IsLDS = TII.isDS(MI) ||
3278 (TII.isFLAT(MI) && TII.mayAccessLDSThroughFlat(MI, TgSplit));
3279 if (!IsLDS && (MI.mayLoad() ^ MI.mayStore()))
3280 LastAtomicWithSoftXcnt = nullptr;
3281
3282 bool IsAtomicRMW =
3283 SIInstrFlags::isMaybeAtomic(MI) && MI.mayLoad() && MI.mayStore();
3284 MachineInstr &PrevMI = *MI.getPrevNode();
3285 // This is an atomic with a soft xcnt.
3286 if (PrevMI.getOpcode() == AMDGPU::S_WAIT_XCNT_soft && IsAtomicRMW) {
3287 // If we have already found an atomic with a soft xcnt, remove this soft
3288 // xcnt as it's redundant.
3289 if (LastAtomicWithSoftXcnt) {
3290 PrevMI.eraseFromParent();
3291 Modified = true;
3292 }
3293 LastAtomicWithSoftXcnt = &MI;
3294 }
3295 }
3296 return Modified;
3297}
3298
3299// Return flags indicating which counters should be flushed in the preheader.
3300PreheaderFlushFlags
3301SIInsertWaitcnts::isPreheaderToFlush(MachineBasicBlock &MBB,
3302 const WaitcntBrackets &ScoreBrackets) {
3303 auto [Iterator, IsInserted] =
3304 PreheadersToFlush.try_emplace(&MBB, PreheaderFlushFlags());
3305 if (!IsInserted)
3306 return Iterator->second;
3307
3308 MachineBasicBlock *Succ = MBB.getSingleSuccessor();
3309 if (!Succ)
3310 return PreheaderFlushFlags();
3311
3312 MachineLoop *Loop = MLI.getLoopFor(Succ);
3313 if (!Loop)
3314 return PreheaderFlushFlags();
3315
3316 if (Loop->getLoopPreheader() == &MBB) {
3317 Iterator->second = getPreheaderFlushFlags(Loop, ScoreBrackets);
3318 return Iterator->second;
3319 }
3320
3321 return PreheaderFlushFlags();
3322}
3323
3324bool SIInsertWaitcnts::isVMEMOrFlatVMEM(const MachineInstr &MI) const {
3326 return TII.mayAccessVMEMThroughFlat(MI);
3327 return SIInstrInfo::isVMEM(MI);
3328}
3329
3330bool SIInsertWaitcnts::isDSRead(const MachineInstr &MI) const {
3331 return SIInstrInfo::isDS(MI) && MI.mayLoad() && !MI.mayStore();
3332}
3333
3334// Check if instruction is a store to LDS that is counted via DSCNT
3335// (where that counter exists).
3336bool SIInsertWaitcnts::mayStoreIncrementingDSCNT(const MachineInstr &MI) const {
3337 return MI.mayStore() && SIInstrInfo::isDS(MI);
3338}
3339
3340// Return flags indicating which counters should be flushed in the preheader of
3341// the given loop. We currently decide to flush in the following situations:
3342// For VMEM (FlushVmCnt):
3343// 1. The loop contains vmem store(s), no vmem load and at least one use of a
3344// vgpr containing a value that is loaded outside of the loop. (Only on
3345// targets with no vscnt counter).
3346// 2. The loop contains vmem load(s), but the loaded values are not used in the
3347// loop, and at least one use of a vgpr containing a value that is loaded
3348// outside of the loop.
3349// For DS (FlushDsCnt, GFX12+ only):
3350// 3. The loop contains no DS reads, and at least one use of a vgpr containing
3351// a value that is DS read outside of the loop.
3352// 4. The loop contains DS read(s), loaded values are not used in the same
3353// iteration but in the next iteration (prefetch pattern), and at least one
3354// use of a vgpr containing a value that is DS read outside of the loop.
3355// Flushing in preheader reduces wait overhead if the wait requirement in
3356// iteration 1 would otherwise be more strict (but unfortunately preheader
3357// flush decision is taken before knowing that).
3358// 5. (Single-block loops only) The loop has DS prefetch reads with flush point
3359// tracking. Some DS reads may be used in the same iteration (creating
3360// "flush points"), but others remain unflushed at the backedge. When a DS
3361// read is consumed in the same iteration, it and all prior reads are
3362// "flushed" (FIFO order). No DS writes are allowed in the loop.
3363// TODO: Find a way to extend to multi-block loops.
3364PreheaderFlushFlags
3365SIInsertWaitcnts::getPreheaderFlushFlags(MachineLoop *ML,
3366 const WaitcntBrackets &Brackets) {
3367 PreheaderFlushFlags Flags;
3368 bool HasVMemLoad = false;
3369 bool HasVMemStore = false;
3370 bool UsesVgprVMEMLoadedOutside = false;
3371 bool UsesVgprDSReadOutside = false;
3372 bool VMemInvalidated = false;
3373 // DS optimization only applies to GFX12+ where DS_CNT is separate.
3374 // Tracking status for "no DS read in loop" or "pure DS prefetch
3375 // (use only in next iteration)".
3376 bool TrackSimpleDSOpt = ST.hasExtendedWaitCounts();
3377 DenseSet<MCRegUnit> VgprUse;
3378 DenseSet<MCRegUnit> VgprDefVMEM;
3379 DenseSet<MCRegUnit> VgprDefDS;
3380
3381 // Track DS reads for prefetch pattern with flush points (single-block only).
3382 // Keeps track of the last DS read (position counted from the top of the loop)
3383 // to each VGPR. Read is considered consumed (and thus needs flushing) if
3384 // the dest register has a use or is overwritten (by any later opertions).
3385 DenseMap<MCRegUnit, unsigned> LastDSReadPositionMap;
3386 unsigned DSReadPosition = 0;
3387 bool IsSingleBlock = ML->getNumBlocks() == 1;
3388 bool TrackDSFlushPoint = ST.hasExtendedWaitCounts() && IsSingleBlock;
3389 unsigned LastDSFlushPosition = 0;
3390
3391 for (MachineBasicBlock *MBB : ML->blocks()) {
3392 for (MachineInstr &MI : *MBB) {
3393 if (isVMEMOrFlatVMEM(MI)) {
3394 HasVMemLoad |= MI.mayLoad();
3395 HasVMemStore |= MI.mayStore();
3396 }
3397 // TODO: Can we relax DSStore check? There may be cases where
3398 // these DS stores are drained prior to the end of MBB (or loop).
3399 if (mayStoreIncrementingDSCNT(MI)) {
3400 // Early exit if none of the optimizations are feasible.
3401 // Otherwise, set tracking status appropriately and continue.
3402 if (VMemInvalidated)
3403 return Flags;
3404 TrackSimpleDSOpt = false;
3405 TrackDSFlushPoint = false;
3406 }
3407 bool IsDSRead = isDSRead(MI);
3408 if (IsDSRead)
3409 ++DSReadPosition;
3410
3411 // Helper: if RU has a pending DS read, update LastDSFlushPosition
3412 auto updateDSReadFlushTracking = [&](MCRegUnit RU) {
3413 if (!TrackDSFlushPoint)
3414 return;
3415 if (auto It = LastDSReadPositionMap.find(RU);
3416 It != LastDSReadPositionMap.end()) {
3417 // RU defined by DSRead is used or overwritten. Need to complete
3418 // the read, if not already implied by a later DSRead (to any RU)
3419 // needing to complete in FIFO order.
3420 LastDSFlushPosition = std::max(LastDSFlushPosition, It->second);
3421 }
3422 };
3423
3424 for (const MachineOperand &Op : MI.all_uses()) {
3425 if (Op.isDebug() || !TRI.isVectorRegister(MRI, Op.getReg()))
3426 continue;
3427 // Vgpr use
3428 for (MCRegUnit RU : TRI.regunits(Op.getReg().asMCReg())) {
3429 // If we find a register that is loaded inside the loop, 1. and 2.
3430 // are invalidated.
3431 if (VgprDefVMEM.contains(RU))
3432 VMemInvalidated = true;
3433
3434 // Check for DS reads used inside the loop
3435 if (VgprDefDS.contains(RU))
3436 TrackSimpleDSOpt = false;
3437
3438 // Early exit if all optimizations are invalidated
3439 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3440 return Flags;
3441
3442 // Check for flush points (DS read used in same iteration)
3443 updateDSReadFlushTracking(RU);
3444
3445 VgprUse.insert(RU);
3446 // Check if this register has a pending VMEM load from outside the
3447 // loop (value loaded outside and used inside).
3448 VMEMID ID = toVMEMID(RU);
3449 if (Brackets.hasPendingVMEM(ID, AMDGPU::LOAD_CNT) ||
3450 Brackets.hasPendingVMEM(ID, AMDGPU::SAMPLE_CNT) ||
3451 Brackets.hasPendingVMEM(ID, AMDGPU::BVH_CNT))
3452 UsesVgprVMEMLoadedOutside = true;
3453 // Check if loaded outside the loop via DS (not VMEM/FLAT).
3454 // Only consider it a DS read if there's no pending VMEM load for
3455 // this register, since FLAT can set both counters.
3456 else if (Brackets.hasPendingVMEM(ID, AMDGPU::DS_CNT))
3457 UsesVgprDSReadOutside = true;
3458 }
3459 }
3460
3461 // VMem load vgpr def
3462 if (isVMEMOrFlatVMEM(MI) && MI.mayLoad()) {
3463 for (const MachineOperand &Op : MI.all_defs()) {
3464 for (MCRegUnit RU : TRI.regunits(Op.getReg().asMCReg())) {
3465 // If we find a register that is loaded inside the loop, 1. and 2.
3466 // are invalidated.
3467 if (VgprUse.contains(RU))
3468 VMemInvalidated = true;
3469 VgprDefVMEM.insert(RU);
3470 }
3471 }
3472 // Early exit if all optimizations are invalidated
3473 if (VMemInvalidated && !TrackSimpleDSOpt && !TrackDSFlushPoint)
3474 return Flags;
3475 }
3476
3477 // DS read vgpr def
3478 // Note: Unlike VMEM, we DON'T invalidate when VgprUse.contains(RegNo).
3479 // If USE comes before DEF, it's the prefetch pattern (use value from
3480 // previous iteration, read for next iteration). We should still flush
3481 // in preheader so iteration 1 doesn't need to wait inside the loop.
3482 // Only invalidate when DEF comes before USE (same-iteration consumption,
3483 // checked above when processing uses).
3484 if (IsDSRead || TrackDSFlushPoint) {
3485 for (const MachineOperand &Op : MI.all_defs()) {
3486 if (!TRI.isVectorRegister(MRI, Op.getReg()))
3487 continue;
3488 for (MCRegUnit RU : TRI.regunits(Op.getReg().asMCReg())) {
3489 // Check for overwrite of pending DS read (flush point) by any
3490 // instruction
3491 updateDSReadFlushTracking(RU);
3492 if (IsDSRead) {
3493 VgprDefDS.insert(RU);
3494 if (TrackDSFlushPoint)
3495 LastDSReadPositionMap[RU] = DSReadPosition;
3496 }
3497 }
3498 }
3499 }
3500 }
3501 }
3502
3503 // VMEM flush decision
3504 if (!VMemInvalidated && UsesVgprVMEMLoadedOutside &&
3505 ((!ST.hasVscnt() && HasVMemStore && !HasVMemLoad) ||
3506 (HasVMemLoad && ST.hasVmemWriteVgprInOrder())))
3507 Flags.FlushVmCnt = true;
3508
3509 // DS flush decision:
3510 // Simple DS Opt: flush if loop uses DS read values from outside
3511 // and either has no DS reads in the loop, or DS reads whose results
3512 // are not used in the loop.
3513 bool SimpleDSOpt = TrackSimpleDSOpt && UsesVgprDSReadOutside;
3514 // Prefetch with flush points: some DS reads used in same iteration,
3515 // but unflushed reads remain at backedge
3516 bool HasUnflushedDSReads = DSReadPosition > LastDSFlushPosition;
3517 bool DSFlushPointPrefetch =
3518 TrackDSFlushPoint && UsesVgprDSReadOutside && HasUnflushedDSReads;
3519
3520 if (SimpleDSOpt || DSFlushPointPrefetch)
3521 Flags.FlushDsCnt = true;
3522
3523 return Flags;
3524}
3525
3526bool SIInsertWaitcntsLegacy::runOnMachineFunction(MachineFunction &MF) {
3527 auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI();
3528 auto &PDT =
3529 getAnalysis<MachinePostDominatorTreeWrapperPass>().getPostDomTree();
3530 AliasAnalysis *AA = nullptr;
3531 if (auto *AAR = getAnalysisIfAvailable<AAResultsWrapperPass>())
3532 AA = &AAR->getAAResults();
3533
3534 return SIInsertWaitcnts(MLI, PDT, AA, MF).run();
3535}
3536
3537PreservedAnalyses
3540 auto &MLI = MFAM.getResult<MachineLoopAnalysis>(MF);
3541 auto &PDT = MFAM.getResult<MachinePostDominatorTreeAnalysis>(MF);
3543 .getManager()
3544 .getCachedResult<AAManager>(MF.getFunction());
3545
3546 if (!SIInsertWaitcnts(MLI, PDT, AA, MF).run())
3547 return PreservedAnalyses::all();
3548
3551 .preserve<AAManager>();
3552}
3553
3554bool SIInsertWaitcnts::run() {
3556
3558
3559 // Initialize hardware limits first, as they're needed by the generators.
3560 Limits = AMDGPU::HardwareLimits(IV);
3561
3562 if (ST.hasExtendedWaitCounts()) {
3563 IsExpertMode = AMDGPU::isExpertSchedulingMode(ST, MF.getFunction());
3564 MaxCounter = IsExpertMode ? AMDGPU::NUM_EXPERT_INST_CNTS
3566 // Initialize WCG per MF. It contains state that depends on MF attributes.
3567 WCG = std::make_unique<WaitcntGeneratorGFX12Plus>(MF, MaxCounter, Limits,
3568 IsExpertMode);
3569 } else {
3570 MaxCounter = AMDGPU::NUM_NORMAL_INST_CNTS;
3571 // Initialize WCG per MF. It contains state that depends on MF attributes.
3572 WCG = std::make_unique<WaitcntGeneratorPreGFX12>(
3573 MF, AMDGPU::NUM_NORMAL_INST_CNTS, Limits);
3574 }
3575
3576 SmemAccessCounter = getCounterFromEvent(HWEvents::SMEM_ACCESS);
3577
3578 bool Modified = false;
3579
3580 MachineBasicBlock &EntryBB = MF.front();
3581
3582 if (!MFI->isEntryFunction() &&
3583 !MF.getFunction().hasFnAttribute(Attribute::Naked)) {
3584 // Wait for any outstanding memory operations that the input registers may
3585 // depend on. We can't track them and it's better to do the wait after the
3586 // costly call sequence.
3587
3588 // TODO: Could insert earlier and schedule more liberally with operations
3589 // that only use caller preserved registers.
3591 while (I != EntryBB.end() && I->isMetaInstruction())
3592 ++I;
3593
3594 if (ST.hasExtendedWaitCounts()) {
3595 BuildMI(EntryBB, I, DebugLoc(), TII.get(AMDGPU::S_WAIT_LOADCNT_DSCNT))
3596 .addImm(0);
3598 if (CT == AMDGPU::LOAD_CNT || CT == AMDGPU::DS_CNT ||
3599 CT == AMDGPU::STORE_CNT || CT == AMDGPU::X_CNT ||
3601 continue;
3602
3603 if (!ST.hasImageInsts() &&
3604 (CT == AMDGPU::EXP_CNT || CT == AMDGPU::SAMPLE_CNT ||
3605 CT == AMDGPU::BVH_CNT))
3606 continue;
3607
3608 if (!ST.hasAnyBVHInsts() && CT == AMDGPU::BVH_CNT)
3609 continue;
3610
3611 BuildMI(EntryBB, I, DebugLoc(),
3612 TII.get(instrsForExtendedCounterTypes[CT]))
3613 .addImm(0);
3614 }
3615 if (IsExpertMode) {
3616 unsigned Enc = AMDGPU::DepCtr::encodeFieldVaVdst(0, ST);
3618 BuildMI(EntryBB, I, DebugLoc(), TII.get(AMDGPU::S_WAITCNT_DEPCTR))
3619 .addImm(Enc);
3620 }
3621 } else {
3622 BuildMI(EntryBB, I, DebugLoc(), TII.get(AMDGPU::S_WAITCNT)).addImm(0);
3623 }
3624
3625 auto NonKernelInitialState = std::make_unique<WaitcntBrackets>(this);
3626 NonKernelInitialState->setStateOnFunctionEntryOrReturn();
3627 BlockInfos[&EntryBB].Incoming = std::move(NonKernelInitialState);
3628
3629 Modified = true;
3630 }
3631
3632 // Keep iterating over the blocks in reverse post order, inserting and
3633 // updating s_waitcnt where needed, until a fix point is reached.
3634 for (auto *MBB : ReversePostOrderTraversal<MachineFunction *>(&MF))
3635 BlockInfos.try_emplace(MBB);
3636
3637 std::unique_ptr<WaitcntBrackets> Brackets;
3638 bool Repeat;
3639 do {
3640 Repeat = false;
3641
3642 for (auto BII = BlockInfos.begin(), BIE = BlockInfos.end(); BII != BIE;
3643 ++BII) {
3644 MachineBasicBlock *MBB = BII->first;
3645 BlockInfo &BI = BII->second;
3646 if (!BI.Dirty)
3647 continue;
3648
3649 if (BI.Incoming) {
3650 if (!Brackets)
3651 Brackets = std::make_unique<WaitcntBrackets>(*BI.Incoming);
3652 else
3653 *Brackets = *BI.Incoming;
3654 } else {
3655 if (!Brackets) {
3656 Brackets = std::make_unique<WaitcntBrackets>(this);
3657 } else {
3658 // Reinitialize in-place. N.B. do not do this by assigning from a
3659 // temporary because the WaitcntBrackets class is large and it could
3660 // cause this function to use an unreasonable amount of stack space.
3661 Brackets->~WaitcntBrackets();
3662 new (Brackets.get()) WaitcntBrackets(this);
3663 }
3664 }
3665
3666 if (ST.hasWaitXcnt() && ST.isXNACKEnabled())
3667 Modified |= removeRedundantSoftXcnts(*MBB);
3668 Modified |= insertWaitcntInBlock(MF, *MBB, *Brackets);
3669 BI.Dirty = false;
3670
3671 if (Brackets->hasPendingEvent()) {
3672 BlockInfo *MoveBracketsToSucc = nullptr;
3673 for (MachineBasicBlock *Succ : MBB->successors()) {
3674 auto *SuccBII = BlockInfos.find(Succ);
3675 BlockInfo &SuccBI = SuccBII->second;
3676 if (!SuccBI.Incoming) {
3677 SuccBI.Dirty = true;
3678 if (SuccBII <= BII) {
3679 LLVM_DEBUG(dbgs() << "Repeat on backedge without merge\n");
3680 Repeat = true;
3681 }
3682 if (!MoveBracketsToSucc) {
3683 MoveBracketsToSucc = &SuccBI;
3684 } else {
3685 SuccBI.Incoming = std::make_unique<WaitcntBrackets>(*Brackets);
3686 }
3687 } else {
3688 LLVM_DEBUG({
3689 dbgs() << "Try to merge ";
3690 MBB->printName(dbgs());
3691 dbgs() << " into ";
3692 Succ->printName(dbgs());
3693 dbgs() << '\n';
3694 });
3695 if (SuccBI.Incoming->merge(*Brackets)) {
3696 SuccBI.Dirty = true;
3697 if (SuccBII <= BII) {
3698 LLVM_DEBUG(dbgs() << "Repeat on backedge with merge\n");
3699 Repeat = true;
3700 }
3701 }
3702 }
3703 }
3704 if (MoveBracketsToSucc)
3705 MoveBracketsToSucc->Incoming = std::move(Brackets);
3706 }
3707 }
3708 } while (Repeat);
3709
3710 Modified |= WCG->eraseDeferredWaitcnts();
3711
3712 if (ST.hasScalarStores()) {
3713 SmallVector<MachineBasicBlock *, 4> EndPgmBlocks;
3714 bool HaveScalarStores = false;
3715
3716 for (MachineBasicBlock &MBB : MF) {
3717 for (MachineInstr &MI : MBB) {
3718 if (!HaveScalarStores && TII.isScalarStore(MI))
3719 HaveScalarStores = true;
3720
3721 if (MI.getOpcode() == AMDGPU::S_ENDPGM ||
3722 MI.getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG)
3723 EndPgmBlocks.push_back(&MBB);
3724 }
3725 }
3726
3727 if (HaveScalarStores) {
3728 // If scalar writes are used, the cache must be flushed or else the next
3729 // wave to reuse the same scratch memory can be clobbered.
3730 //
3731 // Insert s_dcache_wb at wave termination points if there were any scalar
3732 // stores, and only if the cache hasn't already been flushed. This could
3733 // be improved by looking across blocks for flushes in postdominating
3734 // blocks from the stores but an explicitly requested flush is probably
3735 // very rare.
3736 for (MachineBasicBlock *MBB : EndPgmBlocks) {
3737 bool SeenDCacheWB = false;
3738
3739 for (MachineBasicBlock::iterator I = MBB->begin(), E = MBB->end();
3740 I != E; ++I) {
3741 if (I->getOpcode() == AMDGPU::S_DCACHE_WB)
3742 SeenDCacheWB = true;
3743 else if (TII.isScalarStore(*I))
3744 SeenDCacheWB = false;
3745
3746 // FIXME: It would be better to insert this before a waitcnt if any.
3747 if ((I->getOpcode() == AMDGPU::S_ENDPGM ||
3748 I->getOpcode() == AMDGPU::SI_RETURN_TO_EPILOG) &&
3749 !SeenDCacheWB) {
3750 Modified = true;
3751 BuildMI(*MBB, I, I->getDebugLoc(), TII.get(AMDGPU::S_DCACHE_WB));
3752 }
3753 }
3754 }
3755 }
3756 }
3757
3758 if (IsExpertMode) {
3759 // Enable expert scheduling on function entry. To satisfy ABI requirements
3760 // and to allow calls between function with different expert scheduling
3761 // settings, disable it around calls and before returns.
3762
3764 while (I != EntryBB.end() && I->isMetaInstruction())
3765 ++I;
3766 setSchedulingMode(EntryBB, I, true);
3767
3768 for (MachineInstr *MI : CallInsts) {
3769 MachineBasicBlock &MBB = *MI->getParent();
3770 setSchedulingMode(MBB, MI, false);
3771 setSchedulingMode(MBB, std::next(MI->getIterator()), true);
3772 }
3773
3774 for (MachineInstr *MI : ReturnInsts)
3775 setSchedulingMode(*MI->getParent(), MI, false);
3776
3777 Modified = true;
3778 }
3779
3780 // Deallocate the VGPRs before previously identified S_ENDPGM instructions.
3781 // This is done in different ways depending on how the VGPRs were allocated
3782 // (i.e. whether we're in dynamic VGPR mode or not).
3783 // Skip deallocation if kernel is waveslot limited vs VGPR limited. A short
3784 // waveslot limited kernel runs slower with the deallocation.
3785 if (!WCG->isOptNone() && MFI->isDynamicVGPREnabled()) {
3786 for (auto [MI, _] : EndPgmInsts) {
3787 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
3788 TII.get(AMDGPU::S_ALLOC_VGPR))
3789 .addImm(0);
3790 Modified = true;
3791 }
3792 } else if (!WCG->isOptNone() &&
3793 ST.getGeneration() >= AMDGPUSubtarget::GFX11 &&
3794 (MF.getFrameInfo().hasCalls() ||
3795 ST.getOccupancyWithNumVGPRs(
3796 TRI.getNumUsedPhysRegs(MRI, AMDGPU::VGPR_32RegClass),
3797 /*IsDynamicVGPR=*/false) < ST.getMaxWavesPerEU())) {
3798 for (auto [MI, Flag] : EndPgmInsts) {
3799 if (Flag) {
3800 if (ST.requiresNopBeforeDeallocVGPRs()) {
3801 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
3802 TII.get(AMDGPU::S_NOP))
3803 .addImm(0);
3804 }
3805 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
3806 TII.get(AMDGPU::S_SENDMSG))
3808 Modified = true;
3809 }
3810 }
3811 }
3812
3813 if (MFI->isEntryFunction()) {
3814 MachineBasicBlock::iterator InsertPt = EntryBB.begin();
3815
3816 if (ST.hasWaitXcnt() && ST.isXNACKEnabled()) {
3817 // Set REPLAY_MODE (bit 25) in MODE register to enable multi-group XNACK
3818 // replay. This aligns hardware behavior with the compiler's s_wait_xcnt
3819 // insertion logic, which assumes multi-group mode by default.
3820 uint64_t RegEncoding =
3822 BuildMI(EntryBB, InsertPt, DebugLoc(),
3823 TII.get(AMDGPU::S_SETREG_IMM32_B32))
3824 .addImm(1)
3825 .addImm(RegEncoding);
3826 Modified = true;
3827 }
3828
3829 if (ST.hasRequiresInitialUnclausedVmem()) {
3830 // Hardware entrypoints must begin with a specific sequence:
3831 // S_MOV_B64 S[64:65], 0
3832 // V_NOP
3833 // GLOBAL_PREFETCH_B8 V0, S[64:65] SCOPE:SCOPE_SE TH:TH_LOAD_RT
3834 BuildMI(EntryBB, InsertPt, DebugLoc(), TII.get(AMDGPU::S_MOV_B64),
3835 AMDGPU::SGPR64_SGPR65)
3836 .addImm(0);
3837 BuildMI(EntryBB, InsertPt, DebugLoc(), TII.get(AMDGPU::V_NOP_e32));
3838 BuildMI(EntryBB, InsertPt, DebugLoc(),
3839 TII.get(AMDGPU::GLOBAL_PREFETCH_B8_SADDR))
3840 .addReg(AMDGPU::SGPR64_SGPR65)
3841 .addReg(AMDGPU::VGPR0, RegState::Undef)
3842 .addImm(0)
3844 Modified = true;
3845 }
3846 }
3847
3848 return Modified;
3849}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Shared AMDGPU asyncmark stage definitions.
uint64_t uint64_t
uint64_t Imm
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
AMD GCN specific subclass of TargetSubtarget.
#define DEBUG_TYPE
const HexagonInstrInfo * TII
static bool isOptNone(const MachineFunction &MF)
#define _
IRTranslator LLVM IR MI
static LoopDeletionResult merge(LoopDeletionResult A, LoopDeletionResult B)
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define T
static bool isReg(const MCInst &MI, unsigned OpNo)
uint64_t IntrinsicInst * II
#define P(N)
ppc ctr loops verify
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
MachineRegisterInfo & MRI
static bool updateOperandIfDifferent(MachineInstr &MI, AMDGPU::OpName OpName, unsigned NewEnc)
static bool isWaitInstr(MachineInstr &Inst)
AMDGPU::HWEvents HWEvents
#define LLVM_DEBUG(...)
Definition Debug.h:119
static Function * getFunction(FunctionType *Ty, const Twine &Name, Module *M)
static const uint32_t IV[8]
Definition blake3_impl.h:83
A manager for alias analyses.
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:
Definition Pass.cpp:280
size_t size() const
Get the array size.
Definition ArrayRef.h:141
bool empty() const
Check if the array is empty.
Definition ArrayRef.h:136
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:766
iterator end()
Definition DenseMap.h:686
bool erase(const KeyT &Val)
Definition DenseMap.h:930
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition DenseMap.h:827
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
Definition DenseMap.h:856
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.
Definition Pass.h:312
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
Definition Function.cpp:731
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
mop_range operands()
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)
int64_t getImm() const
bool isReg() const
isReg - Tests if this is a MO_Register operand.
Register getReg() const
getReg - Returns the register number.
iterator begin()
Definition MapVector.h:68
iterator find(const KeyT &Key)
Definition MapVector.h:157
iterator end()
Definition MapVector.h:70
std::pair< iterator, bool > try_emplace(const KeyT &Key, Ts &&...Args)
Definition MapVector.h:119
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
MCRegister asMCReg() const
Utility to check-convert this value to a MCRegister.
Definition Register.h:107
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...
void push_back(const T &Elt)
Target - Wrapper for Target specific information.
std::pair< iterator, bool > insert(const ValueT &V)
Definition DenseSet.h:210
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
Definition DenseSet.h:183
self_iterator getIterator()
Definition ilist_node.h:123
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Abstract Attribute helper functions.
Definition Attributor.h:165
@ 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)
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)
Definition SIDefines.h:322
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
bool empty() const
Definition BasicBlock.h:101
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.
Definition STLExtras.h:316
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.
Definition STLExtras.h:1755
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.
@ Wait
Definition Threading.h:60
constexpr auto seq_inclusive(T Begin, T End)
Iterate over an integral type from Begin to End inclusive.
Definition Sequence.h:361
static StringRef getCPU(StringRef CPU)
Processes a CPU name.
auto accumulate(R &&Range, E &&Init)
Wrapper for std::accumulate.
Definition STLExtras.h:1718
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)
Definition STLExtras.h:2329
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...
Definition STLExtras.h:649
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...
Definition STLExtras.h:2189
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
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)
Definition STLExtras.h:1652
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
CodeGenOptLevel
Code generation optimization level.
Definition CodeGen.h:280
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 > >
@ Other
Any other memory.
Definition ModRef.h:68
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
Definition InstrProf.h:145
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
@ Increment
Incrementally increasing token ID.
Definition AllocToken.h:26
FunctionPass * createSIInsertWaitcntsPass()
AAResults AliasAnalysis
Temporary typedef for legacy code that uses a generic AliasAnalysis pointer or reference.
MCRegisterClass TargetRegisterClass
Definition FastISel.h:58
#define N
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.