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