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