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