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