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