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