LLVM 24.0.0git
GCNHazardRecognizer.cpp
Go to the documentation of this file.
1//===-- GCNHazardRecognizers.cpp - GCN Hazard Recognizer Impls ------------===//
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// This file implements hazard recognizers for scheduling on GCN processors.
10//
11//===----------------------------------------------------------------------===//
12
13#include "GCNHazardRecognizer.h"
14#include "AMDGPUTargetMachine.h"
15#include "AMDGPUWaitcntUtils.h"
16#include "GCNSubtarget.h"
18#include "llvm/ADT/Statistic.h"
23#include "llvm/Support/Debug.h"
25
26using namespace llvm;
27
28#define DEBUG_TYPE "gcn-hazard-recognizer"
29// Opt-in debug type for the per-candidate co-execution slot traces, which are
30// far too noisy for the normal debug output. Pass both types to get everything.
31#define DEBUG_TYPE_VERBOSE "gcn-hazard-recognizer-verbose"
32
33STATISTIC(NumWMMANopsHoisted,
34 "Number of WMMA hazard V_NOPs hoisted from loops");
35STATISTIC(NumWMMAHoistingBailed,
36 "Number of WMMA hazards where V_NOP hoisting was not possible");
37
38namespace {
39
40struct MFMAPaddingRatioParser : public cl::parser<unsigned> {
41 MFMAPaddingRatioParser(cl::Option &O) : cl::parser<unsigned>(O) {}
42
43 bool parse(cl::Option &O, StringRef ArgName, StringRef Arg, unsigned &Value) {
44 if (Arg.getAsInteger(0, Value))
45 return O.error("'" + Arg + "' value invalid for uint argument!");
46
47 if (Value > 100)
48 return O.error("'" + Arg + "' value must be in the range [0, 100]!");
49
50 return false;
51 }
52};
53
54} // end anonymous namespace
55
57 MFMAPaddingRatio("amdgpu-mfma-padding-ratio", cl::init(0), cl::Hidden,
58 cl::desc("Fill a percentage of the latency between "
59 "neighboring MFMA with s_nops."));
60
61// This is intended for debugging purposes only.
63 NopPadding("amdgpu-snop-padding", cl::init(0), cl::Hidden,
64 cl::desc("Insert a s_nop x before every instruction"));
65
67 "amdgpu-wmma-vnop-hoisting", cl::init(true), cl::Hidden,
68 cl::desc("Hoist WMMA hazard V_NOPs from loops to preheaders"));
69
70//===----------------------------------------------------------------------===//
71// Hazard Recognizer Implementation
72//===----------------------------------------------------------------------===//
73
75 const GCNSubtarget &ST);
76
79 MachineLoopInfo *MLI)
80 : Mode(Mode), CurrCycleInstr(nullptr), MF(MF),
81 ST(MF.getSubtarget<GCNSubtarget>()), TII(*ST.getInstrInfo()),
82 TRI(TII.getRegisterInfo()), TSchedModel(TII.getSchedModel()), MLI(MLI),
83 ClauseUses(TRI.getNumRegUnits()), ClauseDefs(TRI.getNumRegUnits()) {
84 MaxLookAhead = MF.getRegInfo().isPhysRegUsed(AMDGPU::AGPR0) ? 19 : 5;
85 RunLdsBranchVmemWARHazardFixup = shouldRunLdsBranchVmemWARHazardFixup(MF, ST);
87 if (isPreRA())
88 dbgs() << " PreRA hazard recognizer: " << MF.getName() << "\n";
89 });
90}
91
95
97 // Dump any active co-execution window that did not complete naturally
98 // (e.g. region ended before the window expired).
100 if (CurrentCoExecStage.has_value()) {
101 unsigned Stage = *CurrentCoExecStage;
102 if (Stage < AMDGPU::MaxCoExecStages)
103 CoExecWindowLog[Stage] = ActiveCoExecInfo.Pattern[Stage];
104 dbgs() << " CoExec window ended at stage " << Stage << ":\n";
105 dumpCoExecWindow();
106 }
107 });
108}
109
111 EmittedInstrs.clear();
112 EmittedVALUInstrs.clear();
113 HasPendingWMMACoexecHazard = false;
114 if (isSchedulerMode())
115 schedulerReset();
116}
117
118void GCNHazardRecognizer::schedulerReset() {
119 LLVM_DEBUG({
120 if (CurrentCoExecStage.has_value() || CyclesUntilTRANS > 0 ||
121 CyclesUntilVALU > 0)
122 dbgs() << " Scheduler Reset: clearing co-exec window, TRANS="
123 << CyclesUntilTRANS << ", VALU=" << CyclesUntilVALU << "\n";
124 });
125 CurrentCoExecStage = std::nullopt;
126 CoExecWindowStartCycle = 0;
127 CyclesUntilTRANS = 0;
128 CyclesUntilVALU = 0;
129 ActiveCoExecInfo = AMDGPU::CoExecInfo();
130 CoExecWindowLog.fill('.');
131}
132
133void GCNHazardRecognizer::dumpCoExecWindow() const {
134 unsigned W = ActiveCoExecInfo.TotalWindow;
135 if (W == 0)
136 return;
137
138 // Print the stage numbers row.
139 dbgs() << " Stages: ";
140 for (unsigned I = 0; I < W; ++I)
141 dbgs() << I % 10 << ' ';
142 dbgs() << '\n';
143
144 // Print the pattern row.
145 dbgs() << " Slots: ";
146 for (unsigned I = 0; I < W; ++I)
147 dbgs() << ActiveCoExecInfo.Pattern[I] << ' ';
148 dbgs() << '\n';
149
150 // Print the scheduled row.
151 dbgs() << " Scheduled: ";
152 for (unsigned I = 0; I < W; ++I)
153 dbgs() << CoExecWindowLog[I] << ' ';
154 dbgs() << '\n';
155}
156
157void GCNHazardRecognizer::schedulerAdvanceCycle() {
158 // Record what happened at the current stage of the co-exec window.
159 if (CurrentCoExecStage.has_value()) {
160 unsigned Stage = *CurrentCoExecStage;
161 if (Stage < AMDGPU::MaxCoExecStages) {
162 if (CurrCycleInstr)
163 CoExecWindowLog[Stage] = ActiveCoExecInfo.Pattern[Stage];
164 else
165 CoExecWindowLog[Stage] = '-';
166 }
167 }
168
169 LLVM_DEBUG({
170 bool HasState = CurrentCoExecStage.has_value() || CyclesUntilTRANS > 0 ||
171 CyclesUntilVALU > 0;
172 if (HasState) {
173 dbgs() << " Scheduler AdvanceCycle:";
174 if (CurrentCoExecStage.has_value()) {
175 unsigned Stage = *CurrentCoExecStage;
176 unsigned Next = Stage + 1;
177 if (Next >= ActiveCoExecInfo.TotalWindow)
178 dbgs() << " stage " << Stage << "->expired";
179 else
180 dbgs() << " stage " << Stage << "->" << Next;
181 }
182 if (CyclesUntilTRANS > 0)
183 dbgs() << " TRANS=" << CyclesUntilTRANS << "->"
184 << (CyclesUntilTRANS - 1);
185 if (CyclesUntilVALU > 0)
186 dbgs() << " VALU=" << CyclesUntilVALU << "->" << (CyclesUntilVALU - 1);
187 dbgs() << "\n";
188 }
189 });
190
191 // Decrement hazard counters.
192 if (CyclesUntilTRANS > 0)
193 --CyclesUntilTRANS;
194 if (CyclesUntilVALU > 0)
195 --CyclesUntilVALU;
196
197 // Advance WMMA co-execution window.
198 if (CurrentCoExecStage.has_value()) {
199 unsigned Stage = *CurrentCoExecStage + 1;
200 if (Stage >= ActiveCoExecInfo.TotalWindow) {
201 // Window expired.
202 LLVM_DEBUG({
203 dbgs() << " CoExec window complete:\n";
204 dumpCoExecWindow();
205 });
206 CurrentCoExecStage = std::nullopt;
207 } else {
208 CurrentCoExecStage = Stage;
209 }
210 }
211}
212
213bool GCNHazardRecognizer::hasCoExecWindowModel() const {
214 // The co-execution slot patterns returned by getCoExecInfo() are derived from
215 // gfx1250 timings, so the window model is restricted to gfx1250 for now.
216 // gfx1251 and gfx12.5-generic report the same co-execution hazard features
217 // but have different WMMA latencies, so they need their own slot patterns
218 // before they can be modeled here.
219 if (ST.hasWMMACoexecutionHazards() && ST.hasTransCoexecutionHazard() &&
221 return true;
222
223 if (ST.hasGFX950Insts() &&
224 AMDGPU::getSchedStrategy(MF.getFunction()) == "coexec")
225 return true;
226
227 return false;
228}
229
230void GCNHazardRecognizer::updateWMMAWindowState(const MachineInstr &MI) {
231 if (!hasCoExecWindowModel())
232 return;
233
236 return;
237
238 // If a previous window was still active, dump it before starting a new one.
239 // Record the current stage (filled by this new WMMA) before dumping.
240 LLVM_DEBUG({
241 if (CurrentCoExecStage.has_value()) {
242 unsigned Stage = *CurrentCoExecStage;
243 if (Stage < AMDGPU::MaxCoExecStages)
244 CoExecWindowLog[Stage] = ActiveCoExecInfo.Pattern[Stage];
245 dbgs() << " CoExec window interrupted at stage " << Stage << ":\n";
246 dumpCoExecWindow();
247 }
248 });
249
250 // Start a new co-execution window.
251 ActiveCoExecInfo = AMDGPU::getCoExecInfo(MI, TII);
252 CurrentCoExecStage = 0;
253 CoExecWindowLog.fill('.');
254
255 LLVM_DEBUG(dbgs() << " WMMA window started: " << ActiveCoExecInfo.Pattern
256 << " (window=" << ActiveCoExecInfo.TotalWindow << ")\n"
257 << " " << MI);
258}
259
260void GCNHazardRecognizer::updateTRANSState(const MachineInstr &MI) {
261 if (!hasCoExecWindowModel())
262 return;
264 return;
265
266 // Back-to-back TRANS instructions have a 1-cycle hazard.
267 // This is checked via checkTRANSHazard() and does not create a co-exec
268 // window. The TRANS shadow slot allows anything except TRANS and
269 // multi-cycle VALU.
270 // Set to 2: bumpCycle advances to the next pick's cycle (decrementing
271 // by 1 via AdvanceCycle) before the next instruction's hazard check, so
272 // the counter is observed at 1 there. That 1-cycle stall lets the
273 // strategy pick a non-TRANS, non-multi-cycle-VALU candidate to fill the
274 // shadow slot.
275 CyclesUntilTRANS = 2;
276 LLVM_DEBUG(dbgs() << " TRANS hazard set: CyclesUntilTRANS=2\n");
277}
278
279void GCNHazardRecognizer::updateMultiCycleVALUState(const MachineInstr &MI) {
280 if (!hasCoExecWindowModel())
281 return;
282 // Multi-cycle VALU (CVT, etc.) blocks subsequent VALU for repeat rate cycles.
283 if (!SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true))
284 return;
285
286 // Skip WMMA, MFMA, and TRANS - they have their own tracking.
289 return;
290
291 unsigned RepeatRate = TII.getRepeatRate(MI);
292 if (RepeatRate > 1) {
293 // bumpCycle's AdvanceCycle decrements once before the next pick's
294 // hazard check (same convention as CyclesUntilTRANS), so to expose
295 // RepeatRate-1 cycles of shadow we must seed with RepeatRate.
296 CyclesUntilVALU = RepeatRate;
297 LLVM_DEBUG(dbgs() << " Multi-cycle VALU: repeat=" << RepeatRate
298 << ", CyclesUntilVALU=" << CyclesUntilVALU << "\n");
299 }
300}
301
307
308unsigned GCNHazardRecognizer::checkTRANSHazard(const MachineInstr &MI) const {
309 if (!CyclesUntilTRANS)
310 return 0;
311
312 // Only TRANS and multi-cycle VALU are blocked by the TRANS shadow.
314 return CyclesUntilTRANS;
315
316 if (SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) &&
318 TII.getRepeatRate(MI) > 1)
319 return CyclesUntilTRANS;
320
321 return 0;
322}
323
324unsigned
325GCNHazardRecognizer::checkMultiCycleVALUHazard(const MachineInstr &MI) const {
326 if (!CyclesUntilVALU)
327 return 0;
328
329 // Multi-cycle VALU blocks anything on the VALU pipe - VALU, WMMA, SWMMAC,
330 // and TRANS - for RepeatRate-1 cycles. Only off-pipe instructions (MEM,
331 // SALU, control) can fill the shadow.
332 if (!SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) &&
335 return 0;
336
337 return CyclesUntilVALU;
338}
339
340unsigned
341GCNHazardRecognizer::checkWMMACoexecSlot(const MachineInstr &MI) const {
342 // No hazard if not in a WMMA window.
343 if (!CurrentCoExecStage.has_value())
344 return 0;
345
346 unsigned Stage = *CurrentCoExecStage;
348 unsigned StallCycles = ActiveCoExecInfo.getStallCycles(InstMask, Stage);
349
350 // No stall required if the instruction can co-execute at the current stage.
351 if (StallCycles == 0)
352 return 0;
353
354 // Stall for the required number of cycles until the next allowed stage.
355 unsigned NextStage = Stage + StallCycles;
356 if (NextStage < ActiveCoExecInfo.TotalWindow) {
359 dbgs() << " CoExec stall: stage=" << Stage << "("
360 << AMDGPU::getStageTypeName(ActiveCoExecInfo.getType(Stage))
361 << ") mask=" << AMDGPU::getCoExecMaskName(InstMask)
362 << " -> stall " << StallCycles << " (next allowed=" << NextStage
363 << ")\n"
364 << " " << MI);
365 return StallCycles;
366 }
367
368 // No compatible slot in window - stall until window ends.
371 dbgs() << " CoExec stall: stage=" << Stage << "("
372 << AMDGPU::getStageTypeName(ActiveCoExecInfo.getType(Stage))
373 << ") mask=" << AMDGPU::getCoExecMaskName(InstMask) << " -> stall "
374 << StallCycles << " (window ends)\n"
375 << " " << MI);
376 return StallCycles;
377}
378
379unsigned
380GCNHazardRecognizer::checkMultiShadowHazard(const MachineInstr &MI) const {
381 // This models a VALU caught in both a WMMA and a TRANS shadow.
382 if (!hasCoExecWindowModel())
383 return 0;
384
385 // No hazard if not in a WMMA window.
386 if (!CurrentCoExecStage.has_value())
387 return 0;
388
389 if (!CyclesUntilTRANS)
390 return 0;
391
392 if (!SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) ||
394 return 0;
395
396 // We have a VALU instruction that is under both a TRANS and WMMA shadow.
397 // We need to wait for at least one to clear.
398
399 unsigned LookAheadStage = *CurrentCoExecStage + CyclesUntilTRANS;
401 return CyclesUntilTRANS +
402 ActiveCoExecInfo.getStallCycles(InstMask, LookAheadStage);
403}
404
405void GCNHazardRecognizer::schedulerEmitInstruction(MachineInstr *MI) {
406 LLVM_DEBUG({
407 bool InWindow = CurrentCoExecStage.has_value();
408 bool HasActiveState =
409 InWindow || CyclesUntilTRANS > 0 || CyclesUntilVALU > 0;
410 if (HasActiveState) {
411 if (InWindow) {
412 unsigned Stage = *CurrentCoExecStage;
413 dbgs() << " Stage " << Stage << "("
414 << AMDGPU::getStageTypeName(ActiveCoExecInfo.getType(Stage))
415 << ") Emit ["
417 << "]: " << *MI;
418 } else {
419 dbgs() << " Emit ["
421 << "]: " << *MI;
422 }
423 }
424 });
426 bool HasActiveState = CurrentCoExecStage.has_value() ||
427 CyclesUntilTRANS > 0 || CyclesUntilVALU > 0;
428 if (!HasActiveState)
429 dbgs() << " Emit ["
431 << "]: " << *MI;
432 });
433 updateWMMAWindowState(*MI);
434 updateTRANSState(*MI);
435 updateMultiCycleVALUState(*MI);
436}
437
441
443 CurrCycleInstr = MI;
444 if (isSchedulerMode())
445 schedulerEmitInstruction(MI);
446}
447
448static bool isDivFMas(unsigned Opcode) {
449 return Opcode == AMDGPU::V_DIV_FMAS_F32_e64 || Opcode == AMDGPU::V_DIV_FMAS_F64_e64;
450}
451
452static bool isSGetReg(unsigned Opcode) {
453 return Opcode == AMDGPU::S_GETREG_B32 || Opcode == AMDGPU::S_GETREG_B32_const;
454}
455
456static bool isSSetReg(unsigned Opcode) {
457 switch (Opcode) {
458 case AMDGPU::S_SETREG_B32:
459 case AMDGPU::S_SETREG_B32_mode:
460 case AMDGPU::S_SETREG_IMM32_B32:
461 case AMDGPU::S_SETREG_IMM32_B32_mode:
462 return true;
463 }
464 return false;
465}
466
467static bool isRWLane(unsigned Opcode) {
468 return Opcode == AMDGPU::V_READLANE_B32 || Opcode == AMDGPU::V_WRITELANE_B32;
469}
470
471static bool isRFE(unsigned Opcode) {
472 return Opcode == AMDGPU::S_RFE_B64;
473}
474
475static bool isSMovRel(unsigned Opcode) {
476 switch (Opcode) {
477 case AMDGPU::S_MOVRELS_B32:
478 case AMDGPU::S_MOVRELS_B64:
479 case AMDGPU::S_MOVRELD_B32:
480 case AMDGPU::S_MOVRELD_B64:
481 return true;
482 default:
483 return false;
484 }
485}
486
488 const MachineInstr &MI) {
489 if (TII.isAlwaysGDS(MI.getOpcode()))
490 return true;
491
492 switch (MI.getOpcode()) {
493 case AMDGPU::S_SENDMSG:
494 case AMDGPU::S_SENDMSGHALT:
495 case AMDGPU::S_TTRACEDATA:
496 return true;
497 // These DS opcodes don't support GDS.
498 case AMDGPU::DS_NOP:
499 case AMDGPU::DS_PERMUTE_B32:
500 case AMDGPU::DS_BPERMUTE_B32:
501 return false;
502 default:
503 if (TII.isDS(MI.getOpcode())) {
504 int GDS = AMDGPU::getNamedOperandIdx(MI.getOpcode(),
505 AMDGPU::OpName::gds);
506 if (MI.getOperand(GDS).getImm())
507 return true;
508 }
509 return false;
510 }
511}
512
513static bool isPermlane(const MachineInstr &MI) {
514 unsigned Opcode = MI.getOpcode();
515 return Opcode == AMDGPU::V_PERMLANE16_B32_e64 ||
516 Opcode == AMDGPU::V_PERMLANE64_B32 ||
517 Opcode == AMDGPU::V_PERMLANEX16_B32_e64 ||
518 Opcode == AMDGPU::V_PERMLANE16_VAR_B32_e64 ||
519 Opcode == AMDGPU::V_PERMLANEX16_VAR_B32_e64 ||
520 Opcode == AMDGPU::V_PERMLANE16_SWAP_B32_e32 ||
521 Opcode == AMDGPU::V_PERMLANE16_SWAP_B32_e64 ||
522 Opcode == AMDGPU::V_PERMLANE32_SWAP_B32_e32 ||
523 Opcode == AMDGPU::V_PERMLANE32_SWAP_B32_e64 ||
524 Opcode == AMDGPU::V_PERMLANE_BCAST_B32_e64 ||
525 Opcode == AMDGPU::V_PERMLANE_UP_B32_e64 ||
526 Opcode == AMDGPU::V_PERMLANE_DOWN_B32_e64 ||
527 Opcode == AMDGPU::V_PERMLANE_XOR_B32_e64 ||
528 Opcode == AMDGPU::V_PERMLANE_IDX_GEN_B32_e64;
529}
530
531static bool isLdsDma(const MachineInstr &MI) {
533}
534
535static unsigned getHWReg(const SIInstrInfo *TII, const MachineInstr &RegInstr) {
536 const MachineOperand *RegOp = TII->getNamedOperand(RegInstr,
537 AMDGPU::OpName::simm16);
538 return std::get<0>(AMDGPU::Hwreg::HwregEncoding::decode(RegOp->getImm()));
539}
540
543 MachineInstr *MI = SU->getInstr();
544 // If we are not in "HazardRecognizerMode" and therefore not being run from
545 // the scheduler, track possible stalls from hazards but don't insert noops.
547
548 if (MI->isBundle())
549 return NoHazard;
550
551 // Check co-execution slot hazards and pipeline stalls in scheduler modes.
552 if (isSchedulerMode()) {
553 if (checkMultiShadowHazard(*MI) > 0)
554 return Hazard;
555 if (checkWMMACoexecSlot(*MI) > 0)
556 return Hazard;
557 if (checkTRANSHazard(*MI) > 0)
558 return Hazard;
559 if (checkMultiCycleVALUHazard(*MI) > 0)
560 return Hazard;
561 // The remaining checks are all defined by register dependences.
562 if (!hasPhysRegs())
563 return NoHazard;
564 }
565
566 if (SIInstrInfo::isSMRD(*MI) && checkSMRDHazards(MI) > 0)
567 return HazardType;
568
569 if (ST.hasNSAtoVMEMBug() && checkNSAtoVMEMHazard(MI) > 0)
570 return HazardType;
571
572 if (checkFPAtomicToDenormModeHazard(MI) > 0)
573 return HazardType;
574
575 // Hazards which cannot be mitigated with S_NOPs.
576 if (!isHazardRecognizerMode()) {
577 if (checkWMMACoexecutionHazards(MI) > 0) {
578 HasPendingWMMACoexecHazard = true;
579 return Hazard;
580 }
581 }
582
583 if (ST.hasNoDataDepHazard())
584 return NoHazard;
585
586 if (SIInstrInfo::isVMEM(*MI) && checkVMEMHazards(MI) > 0)
587 return HazardType;
588
589 if (SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true) &&
590 checkVALUHazards(MI) > 0)
591 return HazardType;
592
593 if (SIInstrInfo::isDPP(*MI) && checkDPPHazards(MI) > 0)
594 return HazardType;
595
596 if (isDivFMas(MI->getOpcode()) && checkDivFMasHazards(MI) > 0)
597 return HazardType;
598
599 if (isRWLane(MI->getOpcode()) && checkRWLaneHazards(MI) > 0)
600 return HazardType;
601
602 if ((SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true) ||
605 checkMAIVALUHazards(MI) > 0)
606 return HazardType;
607
608 if (isSGetReg(MI->getOpcode()) && checkGetRegHazards(MI) > 0)
609 return HazardType;
610
611 if (isSSetReg(MI->getOpcode()) && checkSetRegHazards(MI) > 0)
612 return HazardType;
613
614 if (isRFE(MI->getOpcode()) && checkRFEHazards(MI) > 0)
615 return HazardType;
616
617 if (((ST.hasReadM0MovRelInterpHazard() &&
618 (TII.isVINTRP(*MI) || isSMovRel(MI->getOpcode()) ||
619 MI->getOpcode() == AMDGPU::DS_WRITE_ADDTID_B32 ||
620 MI->getOpcode() == AMDGPU::DS_READ_ADDTID_B32)) ||
621 (ST.hasReadM0SendMsgHazard() && isSendMsgTraceDataOrGDS(TII, *MI)) ||
622 (ST.hasReadM0LdsDmaHazard() && isLdsDma(*MI)) ||
623 (ST.hasReadM0LdsDirectHazard() &&
624 MI->readsRegister(AMDGPU::LDS_DIRECT, /*TRI=*/nullptr))) &&
625 checkReadM0Hazards(MI) > 0)
626 return HazardType;
627
628 if (SIInstrInfo::isMAI(*MI) && checkMAIHazards(MI) > 0)
629 return HazardType;
630
632 checkMAILdStHazards(MI) > 0)
633 return HazardType;
634
635 if (ST.hasPermlaneForwardingHazard() && isPermlane(*MI) &&
636 checkPermlaneHazards(MI) > 0)
637 return HazardType;
638
639 if (MI->isInlineAsm() && checkInlineAsmHazards(MI) > 0)
640 return HazardType;
641
642 return NoHazard;
643}
644
646 unsigned Quantity) {
647 while (Quantity > 0) {
648 unsigned Arg = std::min(Quantity, 8u);
649 Quantity -= Arg;
650 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII.get(AMDGPU::S_NOP))
651 .addImm(Arg - 1);
652 }
653}
654
655unsigned
656GCNHazardRecognizer::getMFMAPipelineWaitStates(const MachineInstr &MI) const {
657 const MCSchedClassDesc *SC = TSchedModel.resolveSchedClass(&MI);
658 assert(TSchedModel.getWriteProcResBegin(SC) !=
659 TSchedModel.getWriteProcResEnd(SC));
660 return TSchedModel.getWriteProcResBegin(SC)->ReleaseAtCycle;
661}
662
663void GCNHazardRecognizer::processBundle() {
664 MachineBasicBlock::instr_iterator MI = std::next(CurrCycleInstr->getIterator());
665 MachineBasicBlock::instr_iterator E = CurrCycleInstr->getParent()->instr_end();
666 // Check bundled MachineInstr's for hazards.
667 for (; MI != E && MI->isInsideBundle(); ++MI) {
668 CurrCycleInstr = &*MI;
669 unsigned WaitStates = PreEmitNoopsCommon(CurrCycleInstr);
670
672 fixHazards(CurrCycleInstr);
673
674 insertNoopsInBundle(CurrCycleInstr, TII, WaitStates);
675 }
676
677 // It’s unnecessary to track more than MaxLookAhead instructions. Since we
678 // include the bundled MI directly after, only add a maximum of
679 // (MaxLookAhead - 1) noops to EmittedInstrs.
680 for (unsigned i = 0, e = std::min(WaitStates, MaxLookAhead - 1); i < e; ++i)
681 EmittedInstrs.push_front(nullptr);
682
683 EmittedInstrs.push_front(CurrCycleInstr);
684 EmittedInstrs.resize(MaxLookAhead);
685 }
686 CurrCycleInstr = nullptr;
687}
688
689void GCNHazardRecognizer::runOnInstruction(MachineInstr *MI) {
691
692 unsigned NumPreNoops = PreEmitNoops(MI);
693 EmitNoops(NumPreNoops);
694 if (MI->isInsideBundle())
695 insertNoopsInBundle(MI, TII, NumPreNoops);
696 else
697 TII.insertNoops(*MI->getParent(), MachineBasicBlock::iterator(MI),
698 NumPreNoops);
700 AdvanceCycle();
701}
702
705 CurrCycleInstr = MI;
706 unsigned W = PreEmitNoopsCommon(MI);
707 fixHazards(MI);
708 CurrCycleInstr = nullptr;
709 return std::max(W, NopPadding.getValue());
710}
711
713 unsigned W = 0;
714
715 // Check co-execution slot hazards and pipeline stalls in scheduler modes.
716 if (isSchedulerMode()) {
717 W = checkWMMACoexecSlot(*MI);
718 W = std::max(W, checkTRANSHazard(*MI));
719 W = std::max(W, checkMultiCycleVALUHazard(*MI));
720 W = std::max(W, checkMultiShadowHazard(*MI));
721 // The remaining checks are all defined by register dependences.
722 if (!hasPhysRegs())
723 return W;
724 }
725
726 return std::max(W, PreEmitNoopsCommon(MI));
727}
728
730 if (MI->isBundle())
731 return 0;
732
733 int WaitStates = 0;
734
736 return std::max(WaitStates, checkSMRDHazards(MI));
737
738 if (ST.hasNSAtoVMEMBug())
739 WaitStates = std::max(WaitStates, checkNSAtoVMEMHazard(MI));
740
741 WaitStates = std::max(WaitStates, checkFPAtomicToDenormModeHazard(MI));
742
743 if (ST.hasNoDataDepHazard())
744 return WaitStates;
745
747 WaitStates = std::max(WaitStates, checkVMEMHazards(MI));
748
749 if (SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true))
750 WaitStates = std::max(WaitStates, checkVALUHazards(MI));
751
753 WaitStates = std::max(WaitStates, checkDPPHazards(MI));
754
755 if (isDivFMas(MI->getOpcode()))
756 WaitStates = std::max(WaitStates, checkDivFMasHazards(MI));
757
758 if (isRWLane(MI->getOpcode()))
759 WaitStates = std::max(WaitStates, checkRWLaneHazards(MI));
760
761 if (SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true) ||
764 WaitStates = std::max(WaitStates, checkMAIVALUHazards(MI));
765
766 if (MI->isInlineAsm())
767 return std::max(WaitStates, checkInlineAsmHazards(MI));
768
769 if (isSGetReg(MI->getOpcode()))
770 return std::max(WaitStates, checkGetRegHazards(MI));
771
772 if (isSSetReg(MI->getOpcode()))
773 return std::max(WaitStates, checkSetRegHazards(MI));
774
775 if (isRFE(MI->getOpcode()))
776 return std::max(WaitStates, checkRFEHazards(MI));
777
778 if ((ST.hasReadM0MovRelInterpHazard() &&
779 (TII.isVINTRP(*MI) || isSMovRel(MI->getOpcode()) ||
780 MI->getOpcode() == AMDGPU::DS_WRITE_ADDTID_B32 ||
781 MI->getOpcode() == AMDGPU::DS_READ_ADDTID_B32)) ||
782 (ST.hasReadM0SendMsgHazard() && isSendMsgTraceDataOrGDS(TII, *MI)) ||
783 (ST.hasReadM0LdsDmaHazard() && isLdsDma(*MI)) ||
784 (ST.hasReadM0LdsDirectHazard() &&
785 MI->readsRegister(AMDGPU::LDS_DIRECT, /*TRI=*/nullptr)))
786 return std::max(WaitStates, checkReadM0Hazards(MI));
787
789 return std::max(WaitStates, checkMAIHazards(MI));
790
792 return std::max(WaitStates, checkMAILdStHazards(MI));
793
794 if (ST.hasPermlaneForwardingHazard() && isPermlane(*MI))
795 return std::max(WaitStates, checkPermlaneHazards(MI));
796
797 return WaitStates;
798}
799
801 EmittedInstrs.push_front(nullptr);
802}
803
805 if (isSchedulerMode())
806 schedulerAdvanceCycle();
807
808 // When the scheduler detects a stall, it will call AdvanceCycle() without
809 // emitting any instructions.
810 if (!CurrCycleInstr) {
811 EmittedInstrs.push_front(nullptr);
812
813 if (HasPendingWMMACoexecHazard)
814 EmittedVALUInstrs.push_front(nullptr);
815 return;
816 }
817
818 HasPendingWMMACoexecHazard = false;
819
820 if (CurrCycleInstr->isBundle()) {
821 processBundle();
822 return;
823 }
824
825 unsigned NumWaitStates = TII.getNumWaitStates(*CurrCycleInstr);
826 if (!NumWaitStates) {
827 CurrCycleInstr = nullptr;
828 return;
829 }
830
831 // Keep track of emitted instructions
832 EmittedInstrs.push_front(CurrCycleInstr);
833
834 bool IsVALUOrWMMA =
835 SIInstrInfo::isVALU(*CurrCycleInstr, /*AllowLDSDMA=*/true) ||
836 SIInstrInfo::isWMMA(*CurrCycleInstr) ||
837 SIInstrInfo::isSWMMAC(*CurrCycleInstr);
838 if (IsVALUOrWMMA) {
839 EmittedVALUInstrs.push_front(CurrCycleInstr);
840 } else {
841 // A pending WMMA co-execution hazard optimistically records stall cycles as
842 // future V_NOPs. If the scheduler instead stalls for a different
843 // (S_NOP-resolvable) hazard and schedules a non-VALU into those cycles,
844 // they will not resolve the VALU-pipe hazard, so drop them here.
845 while (!EmittedVALUInstrs.empty() && EmittedVALUInstrs.front() == nullptr)
846 EmittedVALUInstrs.pop_front();
847 }
848
849 // Add a nullptr for each additional wait state after the first. Make sure
850 // not to add more than getMaxLookAhead() items to the list, since we
851 // truncate the list to that size right after this loop.
852 for (unsigned i = 1, e = std::min(NumWaitStates, getMaxLookAhead());
853 i < e; ++i) {
854 EmittedInstrs.push_front(nullptr);
855 }
856
857 // getMaxLookahead() is the largest number of wait states we will ever need
858 // to insert, so there is no point in keeping track of more than that many
859 // wait states.
860 EmittedInstrs.resize(getMaxLookAhead());
861 if (EmittedVALUInstrs.size() > MaxVALULookAhead)
862 EmittedVALUInstrs.resize(MaxVALULookAhead);
863
864 CurrCycleInstr = nullptr;
865}
866
869 "Bottom-up scheduling shouldn't run in hazard recognizer mode");
870}
871
872//===----------------------------------------------------------------------===//
873// Helper Functions
874//===----------------------------------------------------------------------===//
875
877
878// Search for a hazard in a block and its predecessors.
879template <typename StateT>
880static bool
881hasHazard(StateT InitialState,
882 function_ref<HazardFnResult(StateT &, const MachineInstr &)> IsHazard,
883 function_ref<void(StateT &, const MachineInstr &)> UpdateState,
884 const MachineBasicBlock *InitialMBB,
886 struct StateMapKey {
888 size_t Idx;
889 static bool isEqual(const StateMapKey &LHS, const StateMapKey &RHS) {
890 return LHS.States == RHS.States && LHS.Idx == RHS.Idx;
891 }
892 };
893 struct StateMapKeyTraits : DenseMapInfo<StateMapKey> {
894 static unsigned getHashValue(const StateMapKey &Key) {
895 return StateT::getHashValue((*Key.States)[Key.Idx]);
896 }
897 static unsigned getHashValue(const StateT &State) {
898 return StateT::getHashValue(State);
899 }
900 static bool isEqual(const StateMapKey &LHS, const StateMapKey &RHS) {
901 return StateT::isEqual((*LHS.States)[LHS.Idx], (*RHS.States)[RHS.Idx]);
902 }
903 static bool isEqual(const StateT &LHS, const StateMapKey &RHS) {
904 return StateT::isEqual(LHS, (*RHS.States)[RHS.Idx]);
905 }
906 };
907
910
912 const MachineBasicBlock *MBB = InitialMBB;
913 StateT State = InitialState;
914
916 unsigned WorkIdx = 0;
917 for (;;) {
918 bool Expired = false;
919 for (auto E = MBB->instr_rend(); I != E; ++I) {
920 // No need to look at parent BUNDLE instructions.
921 if (I->isBundle())
922 continue;
923
924 auto Result = IsHazard(State, *I);
925 if (Result == HazardFound)
926 return true;
927 if (Result == HazardExpired) {
928 Expired = true;
929 break;
930 }
931
932 if (I->isInlineAsm() || I->isMetaInstruction())
933 continue;
934
935 UpdateState(State, *I);
936 }
937
938 if (!Expired) {
939 size_t StateIdx = States.size();
940 StateMapKey Key = {&States, StateIdx};
941 auto Insertion = StateMap.insert_as(std::pair(Key, StateIdx), State);
942 if (Insertion.second) {
943 States.emplace_back(State);
944 } else {
945 StateIdx = Insertion.first->second;
946 }
947 for (MachineBasicBlock *Pred : MBB->predecessors())
948 Worklist.insert(std::pair(Pred, StateIdx));
949 }
950
951 if (WorkIdx == Worklist.size())
952 break;
953
954 size_t StateIdx;
955 std::tie(MBB, StateIdx) = Worklist[WorkIdx++];
956 State = States[StateIdx];
957 I = MBB->instr_rbegin();
958 }
959
960 return false;
961}
962
963// Number of trailing terminators of \p Pred not executed on the edge to \p MBB:
964// control leaves at the first terminator branching to \p MBB, so the ones after
965// it are dead on that edge. A fallthrough executes them all.
967 const MachineBasicBlock *MBB) {
968 const SIInstrInfo &TII =
969 *Pred.getParent()->getSubtarget<GCNSubtarget>().getInstrInfo();
970 unsigned Unexecuted = 0, Passed = 0;
971 for (auto I = Pred.instr_rbegin(), E = Pred.instr_rend();
972 I != E && I->isTerminator(); ++I, ++Passed) {
973 // Only an explicit MBB destination identifies this edge; the last match is
974 // the earliest such branch in program order.
975 if (I->isBranch() && I->getNumOperands() > 0 && I->getOperand(0).isMBB() &&
976 TII.getBranchDestBlock(*I) == MBB)
977 Unexecuted = Passed;
978 }
979 return Unexecuted;
980}
981
982// Wait states \p MI adds to a distance. Inline asm may be empty, so it adds
983// none, which is the worst case.
984static unsigned
987 return MI.isInlineAsm() ? 0 : GetNumWaitStates(MI);
988}
989
990namespace {
991
992// Answers GCNHazardRecognizer::getMax*WindowDeficit, which documents the
993// result, by walking the CFG backwards from the instruction being checked,
994// counting distances with GetNumWaitStates. That needs the instruction in its
995// final place, so hazard recognizer mode only. Each object answers one query.
996class WindowDeficitSearch {
997public:
998 WindowDeficitSearch(
999 function_ref<std::optional<int>(const MachineInstr &)> WindowFor,
1000 int MaxWindow,
1003 : WindowFor(WindowFor), GetNumWaitStates(GetNumWaitStates),
1004 MaxWindow(MaxWindow) {}
1005
1006 int run(const MachineInstr &MI);
1007
1008private:
1009 // A block to scan upwards from Start, which is Distance away from the
1010 // instruction being checked. In a predecessor, Start is the last instruction
1011 // above the terminators, and Distance includes the ones the edge executes.
1012 struct Arrival {
1013 const MachineBasicBlock *MBB;
1015 int Distance;
1016 };
1017
1018 void arrive(const MachineBasicBlock *MBB,
1020 int Distance);
1021 void scan(Arrival A);
1022
1023 const function_ref<std::optional<int>(const MachineInstr &)> WindowFor;
1024 const GCNHazardRecognizer::GetNumWaitStatesFn GetNumWaitStates;
1025 const int MaxWindow;
1026 int Deficit = 0;
1027 // Pending[D] holds the arrivals at distance D, and Best the smallest distance
1028 // each block has been queued at. Buckets are emptied in increasing distance,
1029 // so the first arrival scanned at a block is the shortest way into it, and
1030 // any later one is stale.
1032 DenseMap<const MachineBasicBlock *, int> Best;
1033};
1034
1035} // end anonymous namespace
1036
1037void WindowDeficitSearch::arrive(
1038 const MachineBasicBlock *MBB,
1040 // No window exceeds MaxWindow, so nothing this far back can beat the deficit
1041 // already found.
1042 if (MaxWindow - Distance <= Deficit)
1043 return;
1044
1045 if (Pending.empty())
1046 Pending.resize(MaxWindow);
1047
1048 auto [It, Inserted] = Best.try_emplace(MBB, Distance);
1049 if (!Inserted) {
1050 if (It->second <= Distance)
1051 return;
1052 It->second = Distance;
1053 }
1054
1055 assert(Distance < MaxWindow && "arrival outside the window");
1056 Pending[Distance].push_back({MBB, Start, Distance});
1057}
1058
1059// \p A is a copy because arrive() may append to the bucket it came from, which
1060// can move that bucket's elements.
1061void WindowDeficitSearch::scan(Arrival A) {
1062 // A shorter way into this block has been queued since.
1063 if (Best.lookup(A.MBB) < A.Distance)
1064 return;
1065
1066 int Distance = A.Distance;
1067 for (auto I = A.Start, E = A.MBB->instr_rend(); I != E; ++I) {
1068 if (I->isBundle())
1069 continue;
1070
1071 if (std::optional<int> Window = WindowFor(*I)) {
1072 assert(*Window >= 0 && *Window <= MaxWindow && "window out of bounds");
1073 Deficit = std::max(Deficit, *Window - Distance);
1074 }
1075
1076 Distance += getCountedWaitStates(*I, GetNumWaitStates);
1077
1078 if (Distance >= MaxWindow)
1079 return;
1080 }
1081
1082 for (const MachineBasicBlock *Pred : A.MBB->predecessors()) {
1083 auto Start = Pred->instr_rbegin(), End = Pred->instr_rend();
1084 for (unsigned N = countUnexecutedTerminators(*Pred, A.MBB); N; --N)
1085 ++Start;
1086
1087 int Executed = 0;
1088 for (; Start != End && Start->isTerminator(); ++Start) {
1089 assert(!WindowFor(*Start) && "terminators must not ask for a window");
1090 Executed += getCountedWaitStates(*Start, GetNumWaitStates);
1091 }
1092
1093 arrive(Pred, Start, Distance + Executed);
1094 }
1095}
1096
1097int WindowDeficitSearch::run(const MachineInstr &MI) {
1098 // Only the part above MI is scanned here, so the block stays out of Best: a
1099 // backedge into it must still scan it from its end.
1100 scan({MI.getParent(), std::next(MI.getReverseIterator()), 0});
1101
1102 // Distances never shrink along a path, so a scan only queues arrivals at the
1103 // distance being swept or further, and one pass over the buckets is enough.
1104 // The inner loop re-reads the size because an edge that adds nothing appends
1105 // to the bucket in hand; Best keeps a cycle that adds nothing from looping.
1106 for (int Distance = 0; Distance < static_cast<int>(Pending.size());
1107 ++Distance)
1108 for (unsigned I = 0; I != Pending[Distance].size(); ++I)
1109 scan(Pending[Distance][I]);
1110
1111 return Deficit;
1112}
1113
1114// Returns a minimum wait states since \p I walking all predecessors.
1115// Only scans until \p IsExpired does not return true.
1116// Can only be run in a hazard recognizer mode.
1117static int
1119 const MachineBasicBlock *MBB,
1121 int WaitStates, GCNHazardRecognizer::IsExpiredFn IsExpired,
1125 for (auto E = MBB->instr_rend(); I != E; ++I) {
1126 // Don't add WaitStates for parent BUNDLE instructions.
1127 if (I->isBundle())
1128 continue;
1129
1130 if (I->isMetaInstruction())
1131 continue;
1132
1133 if (IsHazard(*I))
1134 return WaitStates;
1135
1136 if (I->isInlineAsm())
1137 continue;
1138
1139 WaitStates += GetNumWaitStates(*I);
1140
1141 if (IsExpired(*I, WaitStates))
1142 return std::numeric_limits<int>::max();
1143 }
1144
1145 int MinWaitStates = std::numeric_limits<int>::max();
1146 for (MachineBasicBlock *Pred : MBB->predecessors()) {
1147 if (!Visited.insert(Pred).second)
1148 continue;
1149
1150 int W = getWaitStatesSince(IsHazard, Pred, Pred->instr_rbegin(), WaitStates,
1151 IsExpired, Visited, GetNumWaitStates);
1152
1153 MinWaitStates = std::min(MinWaitStates, W);
1154 }
1155
1156 return MinWaitStates;
1157}
1158
1159static int
1161 const MachineInstr *MI,
1166 return getWaitStatesSince(IsHazard, MI->getParent(),
1167 std::next(MI->getReverseIterator()), 0, IsExpired,
1168 Visited, GetNumWaitStates);
1169}
1170
1171int GCNHazardRecognizer::getMaxWindowDeficit(int MaxWindow,
1172 WindowForFn WindowFor) const {
1174 return WindowDeficitSearch(WindowFor, MaxWindow).run(*CurrCycleInstr);
1175
1176 // EmittedInstrs is capped and can be shorter than the widest window, which
1177 // costs scheduling quality only: the standalone pass still pads.
1178 int Deficit = 0, Distance = 0;
1179 for (MachineInstr *MI : EmittedInstrs) {
1180 if (MI) {
1181 if (std::optional<int> Window = WindowFor(*MI)) {
1182 assert(*Window >= 0 && *Window <= MaxWindow && "window out of bounds");
1183 Deficit = std::max(Deficit, *Window - Distance);
1184 }
1185 }
1186
1187 Distance +=
1189
1190 if (Distance >= MaxWindow)
1191 break;
1192 }
1193 return Deficit;
1194}
1195
1196int GCNHazardRecognizer::getWaitStatesSince(
1197 IsHazardFn IsHazard, int Limit, GetNumWaitStatesFn GetNumWaitStates) const {
1198 if (isHazardRecognizerMode()) {
1199 auto IsExpiredFn = [Limit](const MachineInstr &, int WaitStates) {
1200 return WaitStates >= Limit;
1201 };
1202 return ::getWaitStatesSince(IsHazard, CurrCycleInstr, IsExpiredFn,
1203 GetNumWaitStates);
1204 }
1205
1206 int WaitStates = 0;
1207 for (MachineInstr *MI : EmittedInstrs) {
1208 if (MI) {
1209 if (IsHazard(*MI))
1210 return WaitStates;
1211
1212 if (MI->isInlineAsm())
1213 continue;
1214 }
1215 WaitStates += MI ? GetNumWaitStates(*MI) : 1;
1216
1217 if (WaitStates >= Limit)
1218 break;
1219 }
1220 return std::numeric_limits<int>::max();
1221}
1222
1223int GCNHazardRecognizer::getWaitStatesSince(IsHazardFn IsHazard,
1224 int Limit) const {
1225 return getWaitStatesSince(IsHazard, Limit, SIInstrInfo::getNumWaitStates);
1226}
1227
1228int GCNHazardRecognizer::getMaxVALUWindowDeficit(int MaxWindow,
1229 WindowForFn WindowFor) const {
1230 if (isHazardRecognizerMode()) {
1231 auto GetVALUWaitStates = [](const MachineInstr &MI) -> unsigned {
1232 return SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) ? 1 : 0;
1233 };
1234 return WindowDeficitSearch(WindowFor, MaxWindow, GetVALUWaitStates)
1235 .run(*CurrCycleInstr);
1236 }
1237
1238 // EmittedVALUInstrs is capped at MaxVALULookAhead, so a MaxWindow beyond that
1239 // window could miss a hazard. Keep the cap in sync with the wait-state
1240 // tables.
1241 assert(MaxWindow <= static_cast<int>(MaxVALULookAhead) &&
1242 "MaxWindow exceeds the EmittedVALUInstrs lookahead window");
1243 int WaitStates = 0;
1244 for (MachineInstr *MI : EmittedVALUInstrs) {
1245 if (MI) {
1246 if (std::optional<int> Window = WindowFor(*MI)) {
1247 assert(*Window >= 0 && *Window <= MaxWindow && "window out of bounds");
1248 return *Window - WaitStates;
1249 }
1250 }
1251
1252 ++WaitStates;
1253
1254 if (WaitStates >= MaxWindow)
1255 break;
1256 }
1257 return 0;
1258}
1259
1260int GCNHazardRecognizer::getWaitStatesSinceDef(unsigned Reg,
1261 IsHazardFn IsHazardDef,
1262 int Limit) const {
1263 const SIRegisterInfo *TRI = ST.getRegisterInfo();
1264
1265 auto IsHazardFn = [IsHazardDef, TRI, Reg](const MachineInstr &MI) {
1266 return IsHazardDef(MI) && MI.modifiesRegister(Reg, TRI);
1267 };
1268
1269 return getWaitStatesSince(IsHazardFn, Limit);
1270}
1271
1272int GCNHazardRecognizer::getWaitStatesSinceSetReg(IsHazardFn IsHazard,
1273 int Limit) const {
1274 auto IsHazardFn = [IsHazard](const MachineInstr &MI) {
1275 return isSSetReg(MI.getOpcode()) && IsHazard(MI);
1276 };
1277
1278 return getWaitStatesSince(IsHazardFn, Limit);
1279}
1280
1281//===----------------------------------------------------------------------===//
1282// No-op Hazard Detection
1283//===----------------------------------------------------------------------===//
1284
1285static void addRegUnits(const SIRegisterInfo &TRI, BitVector &BV,
1286 MCRegister Reg) {
1287 for (MCRegUnit Unit : TRI.regunits(Reg))
1288 BV.set(static_cast<unsigned>(Unit));
1289}
1290
1293 BitVector &DefSet, BitVector &UseSet) {
1294 for (const MachineOperand &Op : Ops) {
1295 if (Op.isReg())
1296 addRegUnits(TRI, Op.isDef() ? DefSet : UseSet, Op.getReg().asMCReg());
1297 }
1298}
1299
1300void GCNHazardRecognizer::addClauseInst(const MachineInstr &MI) const {
1301 addRegsToSet(TRI, MI.operands(), ClauseDefs, ClauseUses);
1302}
1303
1305 return !SIInstrInfo::isSMRD(*MI);
1306}
1307
1309 return !SIInstrInfo::isVMEM(*MI);
1310}
1311
1312int GCNHazardRecognizer::checkSoftClauseHazards(MachineInstr *MEM) const {
1313 // SMEM soft clause are only present on VI+, and only matter if xnack is
1314 // enabled.
1315 if (!ST.isXNACKEnabled())
1316 return 0;
1317
1318 bool IsSMRD = TII.isSMRD(*MEM);
1319
1320 resetClause();
1321
1322 // A soft-clause is any group of consecutive SMEM instructions. The
1323 // instructions in this group may return out of order and/or may be
1324 // replayed (i.e. the same instruction issued more than once).
1325 //
1326 // In order to handle these situations correctly we need to make sure that
1327 // when a clause has more than one instruction, no instruction in the clause
1328 // writes to a register that is read by another instruction in the clause
1329 // (including itself). If we encounter this situation, we need to break the
1330 // clause by inserting a non SMEM instruction.
1331
1332 for (MachineInstr *MI : EmittedInstrs) {
1333 // When we hit a non-SMEM instruction then we have passed the start of the
1334 // clause and we can stop.
1335 if (!MI)
1336 break;
1337
1339 break;
1340
1341 addClauseInst(*MI);
1342 }
1343
1344 if (ClauseDefs.none())
1345 return 0;
1346
1347 // We need to make sure not to put loads and stores in the same clause if they
1348 // use the same address. For now, just start a new clause whenever we see a
1349 // store.
1350 if (MEM->mayStore())
1351 return 1;
1352
1353 addClauseInst(*MEM);
1354
1355 // If the set of defs and uses intersect then we cannot add this instruction
1356 // to the clause, so we have a hazard.
1357 return ClauseDefs.anyCommon(ClauseUses) ? 1 : 0;
1358}
1359
1360int GCNHazardRecognizer::checkSMRDHazards(MachineInstr *SMRD) const {
1361 int WaitStatesNeeded = 0;
1362
1363 WaitStatesNeeded = checkSoftClauseHazards(SMRD);
1364
1365 // This SMRD hazard only affects SI.
1366 if (!ST.hasSMRDReadVALUDefHazard())
1367 return WaitStatesNeeded;
1368
1369 // A read of an SGPR by SMRD instruction requires 4 wait states when the
1370 // SGPR was written by a VALU instruction.
1371 int SmrdSgprWaitStates = 4;
1372 auto IsHazardDefFn = [this](const MachineInstr &MI) {
1373 return TII.isVALU(MI, /*AllowLDSDMA=*/true);
1374 };
1375 auto IsBufferHazardDefFn = [this](const MachineInstr &MI) {
1376 return TII.isSALU(MI);
1377 };
1378
1379 bool IsBufferSMRD = TII.isBufferSMRD(*SMRD);
1380
1381 for (const MachineOperand &Use : SMRD->uses()) {
1382 if (!Use.isReg())
1383 continue;
1384 int WaitStatesNeededForUse =
1385 SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn,
1386 SmrdSgprWaitStates);
1387 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
1388
1389 // This fixes what appears to be undocumented hardware behavior in SI where
1390 // s_mov writing a descriptor and s_buffer_load_dword reading the descriptor
1391 // needs some number of nops in between. We don't know how many we need, but
1392 // let's use 4. This wasn't discovered before probably because the only
1393 // case when this happens is when we expand a 64-bit pointer into a full
1394 // descriptor and use s_buffer_load_dword instead of s_load_dword, which was
1395 // probably never encountered in the closed-source land.
1396 if (IsBufferSMRD) {
1397 int WaitStatesNeededForUse =
1398 SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(),
1399 IsBufferHazardDefFn,
1400 SmrdSgprWaitStates);
1401 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
1402 }
1403 }
1404
1405 return WaitStatesNeeded;
1406}
1407
1408int GCNHazardRecognizer::checkVMEMHazards(MachineInstr *VMEM) const {
1409 if (!ST.hasVMEMReadSGPRVALUDefHazard())
1410 return 0;
1411
1412 int WaitStatesNeeded = checkSoftClauseHazards(VMEM);
1413
1414 // A read of an SGPR by a VMEM instruction requires 5 wait states when the
1415 // SGPR was written by a VALU Instruction.
1416 const int VmemSgprWaitStates = 5;
1417 auto IsHazardDefFn = [this](const MachineInstr &MI) {
1418 return TII.isVALU(MI, /*AllowLDSDMA=*/true);
1419 };
1420 for (const MachineOperand &Use : VMEM->uses()) {
1421 if (!Use.isReg() || TRI.isVectorRegister(MF.getRegInfo(), Use.getReg()))
1422 continue;
1423
1424 int WaitStatesNeededForUse =
1425 VmemSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn,
1426 VmemSgprWaitStates);
1427 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
1428 }
1429 return WaitStatesNeeded;
1430}
1431
1432int GCNHazardRecognizer::checkDPPHazards(MachineInstr *DPP) const {
1433 const SIRegisterInfo *TRI = ST.getRegisterInfo();
1434 const SIInstrInfo *TII = ST.getInstrInfo();
1435
1436 // Check for DPP VGPR read after VALU VGPR write and EXEC write.
1437 int DppVgprWaitStates = 2;
1438 int DppExecWaitStates = 5;
1439 int WaitStatesNeeded = 0;
1440 auto IsHazardDefFn = [TII](const MachineInstr &MI) {
1441 return TII->isVALU(MI, /*AllowLDSDMA=*/true);
1442 };
1443
1444 for (const MachineOperand &Use : DPP->uses()) {
1445 if (!Use.isReg() || !TRI->isVGPR(MF.getRegInfo(), Use.getReg()))
1446 continue;
1447 int WaitStatesNeededForUse =
1448 DppVgprWaitStates - getWaitStatesSinceDef(
1449 Use.getReg(),
1450 [](const MachineInstr &) { return true; },
1451 DppVgprWaitStates);
1452 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
1453 }
1454
1455 WaitStatesNeeded = std::max(
1456 WaitStatesNeeded,
1457 DppExecWaitStates - getWaitStatesSinceDef(AMDGPU::EXEC, IsHazardDefFn,
1458 DppExecWaitStates));
1459
1460 return WaitStatesNeeded;
1461}
1462
1463int GCNHazardRecognizer::checkDivFMasHazards(MachineInstr *DivFMas) const {
1464 const SIInstrInfo *TII = ST.getInstrInfo();
1465
1466 // v_div_fmas requires 4 wait states after a write to vcc from a VALU
1467 // instruction.
1468 const int DivFMasWaitStates = 4;
1469 auto IsHazardDefFn = [TII](const MachineInstr &MI) {
1470 return TII->isVALU(MI, /*AllowLDSDMA=*/true);
1471 };
1472 int WaitStatesNeeded = getWaitStatesSinceDef(AMDGPU::VCC, IsHazardDefFn,
1473 DivFMasWaitStates);
1474
1475 return DivFMasWaitStates - WaitStatesNeeded;
1476}
1477
1478int GCNHazardRecognizer::checkGetRegHazards(MachineInstr *GetRegInstr) const {
1479 const SIInstrInfo *TII = ST.getInstrInfo();
1480 unsigned GetRegHWReg = getHWReg(TII, *GetRegInstr);
1481
1482 const int GetRegWaitStates = 2;
1483 auto IsHazardFn = [TII, GetRegHWReg](const MachineInstr &MI) {
1484 return GetRegHWReg == getHWReg(TII, MI);
1485 };
1486 int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn, GetRegWaitStates);
1487
1488 return GetRegWaitStates - WaitStatesNeeded;
1489}
1490
1491int GCNHazardRecognizer::checkSetRegHazards(MachineInstr *SetRegInstr) const {
1492 const SIInstrInfo *TII = ST.getInstrInfo();
1493 unsigned HWReg = getHWReg(TII, *SetRegInstr);
1494
1495 const int SetRegWaitStates = ST.getSetRegWaitStates();
1496 auto IsHazardFn = [TII, HWReg](const MachineInstr &MI) {
1497 return HWReg == getHWReg(TII, MI);
1498 };
1499 int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn, SetRegWaitStates);
1500 return SetRegWaitStates - WaitStatesNeeded;
1501}
1502
1503int GCNHazardRecognizer::createsVALUHazard(const MachineInstr &MI) const {
1504 if (!MI.mayStore())
1505 return -1;
1506
1507 const SIInstrInfo *TII = ST.getInstrInfo();
1508 unsigned Opcode = MI.getOpcode();
1509 const MCInstrDesc &Desc = MI.getDesc();
1510
1511 int VDataIdx = AMDGPU::getNamedOperandIdx(Opcode, AMDGPU::OpName::vdata);
1512 int VDataRCID = -1;
1513 if (VDataIdx != -1)
1514 VDataRCID = TII->getOpRegClassID(Desc.operands()[VDataIdx]);
1515
1516 if (TII->isMUBUF(MI) || TII->isMTBUF(MI)) {
1517 // There is no hazard if the instruction does not use vector regs
1518 // (like wbinvl1)
1519 if (VDataIdx == -1)
1520 return -1;
1521 if (AMDGPU::getRegBitWidth(VDataRCID) > 64) {
1522 // When SOFFSET-dependent wide-store windows apply, the BUFFER_STORE
1523 // source-vgpr WAR hazard exists for every SOFFSET shape; the wait-state
1524 // count differs by SOFFSET and is computed in checkVALUHazardsHelper.
1525 // Otherwise the hazard only exists if soffset is not an SGPR.
1526 if (ST.hasVDecCoExecHazard())
1527 return VDataIdx;
1528 const MachineOperand *SOffset =
1529 TII->getNamedOperand(MI, AMDGPU::OpName::soffset);
1530 if (!SOffset || !SOffset->isReg())
1531 return VDataIdx;
1532 }
1533 }
1534
1535 // MIMG instructions create a hazard if they don't use a 256-bit T# and
1536 // the store size is greater than 8 bytes and they have more than two bits
1537 // of their dmask set.
1538 // All our MIMG definitions use a 256-bit T#, so we can skip checking for them.
1539 if (TII->isMIMG(MI)) {
1540 int SRsrcIdx = AMDGPU::getNamedOperandIdx(Opcode, AMDGPU::OpName::srsrc);
1541 assert(SRsrcIdx != -1 && AMDGPU::getRegBitWidth(TII->getOpRegClassID(
1542 Desc.operands()[SRsrcIdx])) == 256);
1543 (void)SRsrcIdx;
1544 }
1545
1546 if (TII->isFLAT(MI)) {
1547 // There is no hazard if the instruction does not use vector regs
1548 if (VDataIdx == -1)
1549 return -1;
1550
1551 if (AMDGPU::getRegBitWidth(VDataRCID) > 64)
1552 return VDataIdx;
1553 }
1554
1555 return -1;
1556}
1557
1558int GCNHazardRecognizer::checkUniformWindowVALUHazardsHelper(
1559 Register Reg) const {
1560 // Wide stores need a single wait-state bubble before a VALU that overwrites
1561 // store data. createsVALUHazard already excludes MUBUF/MTBUF stores with an
1562 // SGPR SOFFSET.
1563 const SIRegisterInfo *TRI = ST.getRegisterInfo();
1564
1565 auto IsHazard = [&](const MachineInstr &MI) {
1566 int DataIdx = createsVALUHazard(MI);
1567 return DataIdx >= 0 &&
1568 TRI->regsOverlap(MI.getOperand(DataIdx).getReg(), Reg);
1569 };
1570
1571 return std::max(0, 1 - getWaitStatesSince(IsHazard, /*Limit=*/1));
1572}
1573
1574int GCNHazardRecognizer::checkSOFFSETWindowVALUHazardsHelper(
1575 Register Reg) const {
1576 // The required wait-state window depends on the producer's SOFFSET shape:
1577 // - MUBUF/MTBUF wide store with sgpr SOFFSET: 1 wait state.
1578 // - MUBUF/MTBUF wide store with literal/absent SOFFSET, and FLAT wide
1579 // store: 2 wait states.
1580 // The 1-cycle sgpr-SOFFSET window was measured on gfx950.
1581 const SIRegisterInfo *TRI = ST.getRegisterInfo();
1582 const SIInstrInfo *TII = ST.getInstrInfo();
1583
1584 int WaitStatesNeeded = 0;
1585
1586 // Scan each wait-state window separately and take the max padding needed.
1587 // getWaitStatesSince supplies the minimum distance to a producer over paths.
1588 for (int Window = 1; Window <= 2; ++Window) {
1589 auto IsHazard = [&](const MachineInstr &MI) {
1590 int DataIdx = createsVALUHazard(MI);
1591 if (DataIdx < 0 ||
1592 !TRI->regsOverlap(MI.getOperand(DataIdx).getReg(), Reg))
1593 return false;
1594
1595 // Window 1 matches every hazard producer. Window 2 excludes BUF stores
1596 // with an SGPR SOFFSET, which only require a single wait state.
1597 if (Window == 1 || !TII->isBUF(MI))
1598 return true;
1599
1600 const MachineOperand *SOffset =
1601 TII->getNamedOperand(MI, AMDGPU::OpName::soffset);
1602 return !SOffset || !SOffset->isReg();
1603 };
1604 WaitStatesNeeded = std::max(WaitStatesNeeded,
1605 Window - getWaitStatesSince(IsHazard, Window));
1606 }
1607
1608 return WaitStatesNeeded;
1609}
1610
1611int GCNHazardRecognizer::checkVALUHazardsHelper(
1612 const MachineOperand &Def, const MachineRegisterInfo &MRI) const {
1613 // Helper to check for the hazard where VMEM instructions that store more
1614 // than 8 bytes can have their store data overwritten by the next
1615 // instruction.
1616 const SIRegisterInfo *TRI = ST.getRegisterInfo();
1617
1618 if (!TRI->isVectorRegister(MRI, Def.getReg()))
1619 return 0;
1620
1621 if (ST.hasVDecCoExecHazard())
1622 return checkSOFFSETWindowVALUHazardsHelper(Def.getReg());
1623
1624 return checkUniformWindowVALUHazardsHelper(Def.getReg());
1625}
1626
1627/// Dest sel forwarding issue occurs if additional logic is needed to swizzle /
1628/// pack the computed value into correct bit position of the dest register. This
1629/// occurs if we have SDWA with dst_sel != DWORD or if we have op_sel with
1630/// dst_sel that is not aligned to the register. This function analayzes the \p
1631/// MI and \returns an operand with dst forwarding issue, or nullptr if
1632/// none exists.
1633static const MachineOperand *
1635 if (!SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/false))
1636 return nullptr;
1637
1638 const SIInstrInfo *TII = ST.getInstrInfo();
1639
1640 unsigned Opcode = MI.getOpcode();
1641
1642 // There are three different types of instructions
1643 // which produce forwarded dest: 1. SDWA with dst_sel != DWORD, 2. VOP3
1644 // which write hi bits (e.g. op_sel[3] == 1), and 3. FP8DstSelInst
1645 // (instructions with dest byte sel, e.g. CVT_SR_BF8_F32) and
1646 // op_sel[3:2]
1647 // != 0
1648 if (SIInstrInfo::isSDWA(MI)) {
1649 // Type 1: SDWA with dst_sel != DWORD
1650 if (auto *DstSel = TII->getNamedOperand(MI, AMDGPU::OpName::dst_sel))
1651 if (DstSel->getImm() != AMDGPU::SDWA::DWORD)
1652 return TII->getNamedOperand(MI, AMDGPU::OpName::vdst);
1653 }
1654
1655 AMDGPU::FPType IsFP4OrFP8ConvOpc = AMDGPU::getFPDstSelType(Opcode);
1656 if (AMDGPU::hasNamedOperand(Opcode, AMDGPU::OpName::op_sel)) {
1657 // Type 2: VOP3 which write the hi bits. DST_OP_SEL is a destination
1658 // selector in the VOP3 layout; VOP3P uses this bit for source op_sel_hi.
1660 (TII->getNamedImmOperand(MI, AMDGPU::OpName::src0_modifiers) &
1662 return TII->getNamedOperand(MI, AMDGPU::OpName::vdst);
1663
1664 // Type 2b: VOP3P mix instructions with a tied destination (MIXLO/MIXHI)
1665 // write only one half of vdst and preserve the other half, so treat them
1666 // like a VOP3 destination op_sel write regardless of their source
1667 // op_sel_hi bits.
1668 if (SIInstrInfo::isVOP3P(MI) &&
1669 AMDGPU::hasNamedOperand(Opcode, AMDGPU::OpName::vdst_in))
1670 return TII->getNamedOperand(MI, AMDGPU::OpName::vdst);
1671
1672 // Type 3: FP8DstSelInst with op_sel[3:2] != 0)
1673 if (IsFP4OrFP8ConvOpc == AMDGPU::FPType::FP8 &&
1674 (TII->getNamedImmOperand(MI, AMDGPU::OpName::src2_modifiers) &
1676 return TII->getNamedOperand(MI, AMDGPU::OpName::vdst);
1677 }
1678
1679 // Special case: nop is required for all the opsel values for fp4 sr variant
1680 // cvt scale instructions
1681 if (IsFP4OrFP8ConvOpc == AMDGPU::FPType::FP4)
1682 return TII->getNamedOperand(MI, AMDGPU::OpName::vdst);
1683
1684 return nullptr;
1685}
1686
1687/// Checks whether the provided \p MI "consumes" the operand with a Dest sel
1688/// fowarding issue \p Dst . We may "consume" the Dst via a standard explicit
1689/// RAW, or through irregular ways (e.g implicit RAW, certain types of WAW)
1691 const MachineOperand *Dst,
1692 const SIRegisterInfo *TRI) {
1693 // We must consider implicit reads of the VALU. SDWA with dst_sel and
1694 // UNUSED_PRESERVE will implicitly read the result from forwarded dest,
1695 // and we must account for that hazard.
1696 // We also must account for WAW hazards. In particular, WAW with dest
1697 // preserve semantics (e.g. VOP3 with op_sel, VOP2 &&
1698 // !zeroesHigh16BitsOfDest) will read the forwarded dest for parity
1699 // check for ECC. Without accounting for this hazard, the ECC will be
1700 // wrong.
1701 // TODO: limit to RAW (including implicit reads) + problematic WAW (i.e.
1702 // complete zeroesHigh16BitsOfDest)
1703 for (auto &Operand : VALU->operands()) {
1704 if (Operand.isReg() && TRI->regsOverlap(Dst->getReg(), Operand.getReg())) {
1705 return true;
1706 }
1707 }
1708 return false;
1709}
1710
1711int GCNHazardRecognizer::checkVALUHazards(MachineInstr *VALU) const {
1712 int WaitStatesNeeded = 0;
1713
1714 if (ST.hasTransForwardingHazard() && !SIInstrInfo::isTRANS(*VALU)) {
1715 const int TransDefWaitstates = 1;
1716
1717 auto IsTransDefFn = [this, VALU](const MachineInstr &MI) {
1719 return false;
1720 const SIRegisterInfo *TRI = ST.getRegisterInfo();
1721 const SIInstrInfo *TII = ST.getInstrInfo();
1722 Register Def = TII->getNamedOperand(MI, AMDGPU::OpName::vdst)->getReg();
1723
1724 for (const MachineOperand &Use : VALU->explicit_uses()) {
1725 if (Use.isReg() && TRI->regsOverlap(Def, Use.getReg()))
1726 return true;
1727 }
1728
1729 return false;
1730 };
1731
1732 int WaitStatesNeededForDef =
1733 TransDefWaitstates -
1734 getWaitStatesSince(IsTransDefFn, TransDefWaitstates);
1735 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForDef);
1736 }
1737
1738 if (ST.hasDstSelForwardingHazard() || ST.hasCvtScaleForwardingHazard()) {
1739 const int Shift16DefWaitstates = 1;
1740
1741 auto IsShift16BitDefFn = [this, VALU](const MachineInstr &ProducerMI) {
1742 const SIRegisterInfo *TRI = ST.getRegisterInfo();
1743 const MachineOperand *ForwardedDst =
1744 getDstSelForwardingOperand(ProducerMI, ST);
1745 if (ForwardedDst) {
1746 return consumesDstSelForwardingOperand(VALU, ForwardedDst, TRI);
1747 }
1748
1749 if (ProducerMI.isInlineAsm()) {
1750 // Assume inline asm has dst forwarding hazard
1751 for (auto &Def : ProducerMI.all_defs()) {
1752 if (consumesDstSelForwardingOperand(VALU, &Def, TRI))
1753 return true;
1754 }
1755 }
1756
1757 return false;
1758 };
1759
1760 int WaitStatesNeededForDef =
1761 Shift16DefWaitstates -
1762 getWaitStatesSince(IsShift16BitDefFn, Shift16DefWaitstates);
1763 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForDef);
1764 }
1765
1766 if (ST.hasVDecCoExecHazard()) {
1767 const int VALUWriteSGPRVALUReadWaitstates = 2;
1768 const int VALUWriteEXECRWLane = 4;
1769 const int VALUWriteVGPRReadlaneRead = 1;
1770
1771 const SIRegisterInfo *TRI = ST.getRegisterInfo();
1772 const MachineRegisterInfo &MRI = MF.getRegInfo();
1774 auto IsVALUDefSGPRFn = [&UseReg, TRI](const MachineInstr &MI) {
1775 if (!SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true))
1776 return false;
1777 return MI.modifiesRegister(UseReg, TRI);
1778 };
1779
1780 for (const MachineOperand &Use : VALU->explicit_uses()) {
1781 if (!Use.isReg())
1782 continue;
1783
1784 UseReg = Use.getReg();
1785 if (TRI->isSGPRReg(MRI, UseReg)) {
1786 int WaitStatesNeededForDef =
1787 VALUWriteSGPRVALUReadWaitstates -
1788 getWaitStatesSince(IsVALUDefSGPRFn,
1789 VALUWriteSGPRVALUReadWaitstates);
1790 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForDef);
1791 }
1792 }
1793
1794 if (VALU->readsRegister(AMDGPU::VCC, TRI)) {
1795 UseReg = AMDGPU::VCC;
1796 int WaitStatesNeededForDef =
1797 VALUWriteSGPRVALUReadWaitstates -
1798 getWaitStatesSince(IsVALUDefSGPRFn, VALUWriteSGPRVALUReadWaitstates);
1799 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForDef);
1800 }
1801
1802 switch (VALU->getOpcode()) {
1803 case AMDGPU::V_READLANE_B32:
1804 case AMDGPU::V_READFIRSTLANE_B32: {
1805 MachineOperand *Src = TII.getNamedOperand(*VALU, AMDGPU::OpName::src0);
1806 UseReg = Src->getReg();
1807 int WaitStatesNeededForDef =
1808 VALUWriteVGPRReadlaneRead -
1809 getWaitStatesSince(IsVALUDefSGPRFn, VALUWriteVGPRReadlaneRead);
1810 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForDef);
1811 }
1812 [[fallthrough]];
1813 case AMDGPU::V_WRITELANE_B32: {
1814 UseReg = AMDGPU::EXEC;
1815 int WaitStatesNeededForDef =
1816 VALUWriteEXECRWLane -
1817 getWaitStatesSince(IsVALUDefSGPRFn, VALUWriteEXECRWLane);
1818 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForDef);
1819 break;
1820 }
1821 default:
1822 break;
1823 }
1824 }
1825
1826 // This checks for the hazard where VMEM instructions that store more than
1827 // 8 bytes can have there store data over written by the next instruction.
1828 if (!ST.has12DWordStoreHazard())
1829 return WaitStatesNeeded;
1830
1831 const MachineRegisterInfo &MRI = MF.getRegInfo();
1832
1833 for (const MachineOperand &Def : VALU->defs()) {
1834 WaitStatesNeeded = std::max(WaitStatesNeeded, checkVALUHazardsHelper(Def, MRI));
1835 }
1836
1837 return WaitStatesNeeded;
1838}
1839
1840int GCNHazardRecognizer::checkInlineAsmHazards(MachineInstr *IA) const {
1841 // This checks for hazards associated with inline asm statements.
1842 // Since inline asms can contain just about anything, we use this
1843 // to call/leverage other check*Hazard routines. Note that
1844 // this function doesn't attempt to address all possible inline asm
1845 // hazards (good luck), but is a collection of what has been
1846 // problematic thus far.
1847
1848 // see checkVALUHazards()
1849 if (!ST.has12DWordStoreHazard() && !ST.hasDstSelForwardingHazard() &&
1850 !ST.hasCvtScaleForwardingHazard())
1851 return 0;
1852
1853 const MachineRegisterInfo &MRI = MF.getRegInfo();
1854 int WaitStatesNeeded = 0;
1855
1856 for (const MachineOperand &Op :
1858 if (Op.isReg() && Op.isDef()) {
1859 if (!TRI.isVectorRegister(MRI, Op.getReg()))
1860 continue;
1861
1862 if (ST.has12DWordStoreHazard()) {
1863 WaitStatesNeeded =
1864 std::max(WaitStatesNeeded, checkVALUHazardsHelper(Op, MRI));
1865 }
1866 }
1867 }
1868
1869 if (ST.hasDstSelForwardingHazard()) {
1870 const int Shift16DefWaitstates = 1;
1871
1872 auto IsShift16BitDefFn = [this, &IA](const MachineInstr &ProducerMI) {
1873 const MachineOperand *Dst = getDstSelForwardingOperand(ProducerMI, ST);
1874 // Assume inline asm reads the dst
1875 if (Dst)
1876 return IA->modifiesRegister(Dst->getReg(), &TRI) ||
1877 IA->readsRegister(Dst->getReg(), &TRI);
1878
1879 if (ProducerMI.isInlineAsm()) {
1880 // If MI is inline asm, assume it has dst forwarding hazard
1881 for (auto &Def : ProducerMI.all_defs()) {
1882 if (IA->modifiesRegister(Def.getReg(), &TRI) ||
1883 IA->readsRegister(Def.getReg(), &TRI)) {
1884 return true;
1885 }
1886 }
1887 }
1888
1889 return false;
1890 };
1891
1892 int WaitStatesNeededForDef =
1893 Shift16DefWaitstates -
1894 getWaitStatesSince(IsShift16BitDefFn, Shift16DefWaitstates);
1895 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForDef);
1896 }
1897
1898 return WaitStatesNeeded;
1899}
1900
1901int GCNHazardRecognizer::checkRWLaneHazards(MachineInstr *RWLane) const {
1902 const SIInstrInfo *TII = ST.getInstrInfo();
1903 const SIRegisterInfo *TRI = ST.getRegisterInfo();
1904 const MachineRegisterInfo &MRI = MF.getRegInfo();
1905
1906 const MachineOperand *LaneSelectOp =
1907 TII->getNamedOperand(*RWLane, AMDGPU::OpName::src1);
1908
1909 if (!LaneSelectOp->isReg() || !TRI->isSGPRReg(MRI, LaneSelectOp->getReg()))
1910 return 0;
1911
1912 Register LaneSelectReg = LaneSelectOp->getReg();
1913 auto IsHazardFn = [TII](const MachineInstr &MI) {
1914 return TII->isVALU(MI, /*AllowLDSDMA=*/true);
1915 };
1916
1917 const int RWLaneWaitStates = 4;
1918 int WaitStatesSince = getWaitStatesSinceDef(LaneSelectReg, IsHazardFn,
1919 RWLaneWaitStates);
1920 return RWLaneWaitStates - WaitStatesSince;
1921}
1922
1923int GCNHazardRecognizer::checkRFEHazards(MachineInstr *RFE) const {
1924 if (!ST.hasRFEHazards())
1925 return 0;
1926
1927 const SIInstrInfo *TII = ST.getInstrInfo();
1928
1929 const int RFEWaitStates = 1;
1930
1931 auto IsHazardFn = [TII](const MachineInstr &MI) {
1932 return getHWReg(TII, MI) == AMDGPU::Hwreg::ID_TRAPSTS;
1933 };
1934 int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn, RFEWaitStates);
1935 return RFEWaitStates - WaitStatesNeeded;
1936}
1937
1938int GCNHazardRecognizer::checkReadM0Hazards(MachineInstr *MI) const {
1939 const SIInstrInfo *TII = ST.getInstrInfo();
1940 const int ReadM0WaitStates = 1;
1941 auto IsHazardFn = [TII](const MachineInstr &MI) { return TII->isSALU(MI); };
1942 return ReadM0WaitStates -
1943 getWaitStatesSinceDef(AMDGPU::M0, IsHazardFn, ReadM0WaitStates);
1944}
1945
1946void GCNHazardRecognizer::emitVNops(MachineBasicBlock &MBB,
1948 int WaitStatesNeeded, bool IsHoisting) {
1949 const DebugLoc &DL = IsHoisting ? DebugLoc() : InsertPt->getDebugLoc();
1950 for (int I = 0; I < WaitStatesNeeded; ++I)
1951 BuildMI(MBB, InsertPt, DL, TII.get(AMDGPU::V_NOP_e32));
1952}
1953
1954void GCNHazardRecognizer::fixHazards(MachineInstr *MI) {
1955 fixVMEMtoScalarWriteHazards(MI);
1956 fixVcmpxPermlaneHazards(MI);
1957 fixSMEMtoVectorWriteHazards(MI);
1958 fixVcmpxExecWARHazard(MI);
1959 fixLdsBranchVmemWARHazard(MI);
1960 if (ST.hasLdsDirect()) {
1961 fixLdsDirectVALUHazard(MI);
1962 fixLdsDirectVMEMHazard(MI);
1963 }
1964 fixVALUPartialForwardingHazard(MI);
1965 fixVALUTransUseHazard(MI);
1966 fixVALUTransCoexecutionHazards(MI);
1967 fixWMMAHazards(MI); // fall-through if co-execution is enabled.
1968 fixWMMACoexecutionHazards(MI);
1969 fixShift64HighRegBug(MI);
1970 fixVALUMaskWriteHazard(MI);
1971 fixRequiredExportPriority(MI);
1972 if (ST.hasVPermPk16Hazard())
1973 fixVPermPk16Hazard(MI);
1974 if (ST.requiresWaitIdleBeforeGetReg())
1975 fixGetRegWaitIdle(MI);
1976 if (ST.hasDsAtomicAsyncBarrierArriveB64PipeBug())
1977 fixDsAtomicAsyncBarrierArriveB64(MI);
1978 if (ST.hasScratchBaseForwardingHazard())
1979 fixScratchBaseForwardingHazard(MI);
1980 if (ST.setRegModeNeedsVNOPs())
1981 fixSetRegMode(MI);
1982 if (ST.hasNeedsTDMDrain())
1983 fixTDM(MI);
1984}
1985
1987 const MachineInstr &MI) {
1988 return (TII.isVOPC(MI) ||
1989 (MI.isCompare() && (TII.isVOP3(MI) || TII.isSDWA(MI)))) &&
1990 MI.modifiesRegister(AMDGPU::EXEC, &TRI);
1991}
1992
1993bool GCNHazardRecognizer::fixVcmpxPermlaneHazards(MachineInstr *MI) {
1994 if (!ST.hasVcmpxPermlaneHazard() || !isPermlane(*MI))
1995 return false;
1996
1997 const SIInstrInfo *TII = ST.getInstrInfo();
1998 const SIRegisterInfo *TRI = ST.getRegisterInfo();
1999 auto IsHazardFn = [TII, TRI](const MachineInstr &MI) {
2000 return isVCmpXWritesExec(*TII, *TRI, MI);
2001 };
2002
2003 auto IsExpiredFn = [](const MachineInstr &MI, int) {
2004 unsigned Opc = MI.getOpcode();
2005 return SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) &&
2006 Opc != AMDGPU::V_NOP_e32 && Opc != AMDGPU::V_NOP_e64 &&
2007 Opc != AMDGPU::V_NOP_sdwa;
2008 };
2009
2010 if (::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn) ==
2011 std::numeric_limits<int>::max())
2012 return false;
2013
2014 // V_NOP will be discarded by SQ.
2015 // Use V_MOV_B32 v?, v?. Register must be alive so use src0 of V_PERMLANE*
2016 // which is always a VGPR and available.
2017 auto *Src0 = TII->getNamedOperand(*MI, AMDGPU::OpName::src0);
2018 Register Reg = Src0->getReg();
2019 bool IsUndef = Src0->isUndef();
2020 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
2021 TII->get(AMDGPU::V_MOV_B32_e32))
2024
2025 return true;
2026}
2027
2028bool GCNHazardRecognizer::fixVMEMtoScalarWriteHazards(MachineInstr *MI) {
2029 if (!ST.hasVMEMtoScalarWriteHazard())
2030 return false;
2031 assert(!ST.hasExtendedWaitCounts());
2032
2034 return false;
2035
2036 if (MI->getNumDefs() == 0)
2037 return false;
2038
2039 const SIRegisterInfo *TRI = ST.getRegisterInfo();
2040
2041 auto IsHazardFn = [TRI, MI](const MachineInstr &I) {
2043 return false;
2044
2045 for (const MachineOperand &Def : MI->defs()) {
2046 const MachineOperand *Op =
2047 I.findRegisterUseOperand(Def.getReg(), TRI, false);
2048 if (!Op)
2049 continue;
2050 return true;
2051 }
2052 return false;
2053 };
2054
2055 auto IsExpiredFn = [](const MachineInstr &MI, int) {
2056 return SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) ||
2057 (MI.getOpcode() == AMDGPU::S_WAITCNT &&
2058 !MI.getOperand(0).getImm()) ||
2059 (MI.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR &&
2061 static_cast<unsigned>(MI.getOperand(0).getImm())) == 0);
2062 };
2063
2064 if (::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn) ==
2065 std::numeric_limits<int>::max())
2066 return false;
2067
2068 const SIInstrInfo *TII = ST.getInstrInfo();
2069 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
2070 TII->get(AMDGPU::S_WAITCNT_DEPCTR))
2072 return true;
2073}
2074
2075bool GCNHazardRecognizer::fixSMEMtoVectorWriteHazards(MachineInstr *MI) {
2076 if (!ST.hasSMEMtoVectorWriteHazard())
2077 return false;
2078 assert(!ST.hasExtendedWaitCounts());
2079
2080 if (!SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true))
2081 return false;
2082
2083 AMDGPU::OpName SDSTName;
2084 switch (MI->getOpcode()) {
2085 case AMDGPU::V_READLANE_B32:
2086 case AMDGPU::V_READFIRSTLANE_B32:
2087 SDSTName = AMDGPU::OpName::vdst;
2088 break;
2089 default:
2090 SDSTName = AMDGPU::OpName::sdst;
2091 break;
2092 }
2093
2094 const SIInstrInfo *TII = ST.getInstrInfo();
2095 const SIRegisterInfo *TRI = ST.getRegisterInfo();
2096 const AMDGPU::IsaVersion IV = AMDGPU::getIsaVersion(ST.getCPU());
2097 const MachineOperand *SDST = TII->getNamedOperand(*MI, SDSTName);
2098 if (!SDST) {
2099 for (const auto &MO : MI->implicit_operands()) {
2100 if (MO.isDef() && TRI->isSGPRClass(TRI->getPhysRegBaseClass(MO.getReg()))) {
2101 SDST = &MO;
2102 break;
2103 }
2104 }
2105 }
2106
2107 if (!SDST)
2108 return false;
2109
2110 const Register SDSTReg = SDST->getReg();
2111 auto IsHazardFn = [SDSTReg, TRI](const MachineInstr &I) {
2112 return SIInstrInfo::isSMRD(I) && I.readsRegister(SDSTReg, TRI);
2113 };
2114
2115 auto IsExpiredFn = [TII, IV](const MachineInstr &MI, int) {
2116 if (TII->isSALU(MI)) {
2117 switch (MI.getOpcode()) {
2118 case AMDGPU::S_SETVSKIP:
2119 case AMDGPU::S_VERSION:
2120 case AMDGPU::S_WAITCNT_VSCNT:
2121 case AMDGPU::S_WAITCNT_VMCNT:
2122 case AMDGPU::S_WAITCNT_EXPCNT:
2123 // These instructions cannot not mitigate the hazard.
2124 return false;
2125 case AMDGPU::S_WAITCNT_LGKMCNT:
2126 // Reducing lgkmcnt count to 0 always mitigates the hazard.
2127 return (MI.getOperand(1).getImm() == 0) &&
2128 (MI.getOperand(0).getReg() == AMDGPU::SGPR_NULL);
2129 case AMDGPU::S_WAITCNT: {
2130 const int64_t Imm = MI.getOperand(0).getImm();
2131 AMDGPU::Waitcnt Decoded =
2132 AMDGPU::decodeWaitcnt(IV, static_cast<unsigned>(Imm));
2133 // DsCnt corresponds to LGKMCnt here.
2134 return Decoded.get(AMDGPU::DS_CNT) == 0;
2135 }
2136 default:
2137 assert((!SIInstrInfo::isWaitcnt(MI.getOpcode()) ||
2138 MI.getOpcode() == AMDGPU::S_WAIT_IDLE) &&
2139 "unexpected wait count instruction");
2140 // SOPP instructions cannot mitigate the hazard.
2141 if (TII->isSOPP(MI))
2142 return false;
2143 // At this point the SALU can be assumed to mitigate the hazard
2144 // because either:
2145 // (a) it is independent of the at risk SMEM (breaking chain),
2146 // or
2147 // (b) it is dependent on the SMEM, in which case an appropriate
2148 // s_waitcnt lgkmcnt _must_ exist between it and the at risk
2149 // SMEM instruction.
2150 return true;
2151 }
2152 }
2153 return false;
2154 };
2155
2156 if (::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn) ==
2157 std::numeric_limits<int>::max())
2158 return false;
2159
2160 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
2161 TII->get(AMDGPU::S_MOV_B32), AMDGPU::SGPR_NULL)
2162 .addImm(0);
2163 return true;
2164}
2165
2166bool GCNHazardRecognizer::fixVcmpxExecWARHazard(MachineInstr *MI) {
2167 if (!ST.hasVcmpxExecWARHazard())
2168 return false;
2169 assert(!ST.hasExtendedWaitCounts());
2170
2171 if (!SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true))
2172 return false;
2173
2174 const SIRegisterInfo *TRI = ST.getRegisterInfo();
2175 if (!MI->modifiesRegister(AMDGPU::EXEC, TRI))
2176 return false;
2177
2178 auto IsHazardFn = [TRI](const MachineInstr &I) {
2179 if (SIInstrInfo::isVALU(I, /*AllowLDSDMA=*/true))
2180 return false;
2181 return I.readsRegister(AMDGPU::EXEC, TRI);
2182 };
2183
2184 const SIInstrInfo *TII = ST.getInstrInfo();
2185 auto IsExpiredFn = [TII, TRI](const MachineInstr &MI, int) {
2186 if (SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true)) {
2187 if (TII->getNamedOperand(MI, AMDGPU::OpName::sdst))
2188 return true;
2189 for (auto MO : MI.implicit_operands())
2190 if (MO.isDef() && TRI->isSGPRClass(TRI->getPhysRegBaseClass(MO.getReg())))
2191 return true;
2192 }
2193 if (MI.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR &&
2195 static_cast<unsigned>(MI.getOperand(0).getImm())) == 0)
2196 return true;
2197 return false;
2198 };
2199
2200 if (::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn) ==
2201 std::numeric_limits<int>::max())
2202 return false;
2203
2204 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
2205 TII->get(AMDGPU::S_WAITCNT_DEPCTR))
2207 return true;
2208}
2209
2211 const GCNSubtarget &ST) {
2212 if (!ST.hasLdsBranchVmemWARHazard())
2213 return false;
2214
2215 // Check if the necessary condition for the hazard is met: both LDS and VMEM
2216 // instructions need to appear in the same function.
2217 bool HasLds = false;
2218 bool HasVmem = false;
2219 for (auto &MBB : MF) {
2220 for (auto &MI : MBB) {
2222 HasVmem |= SIInstrInfo::isVMEM(MI);
2223 if (HasLds && HasVmem)
2224 return true;
2225 }
2226 }
2227 return false;
2228}
2229
2231 return I.getOpcode() == AMDGPU::S_WAITCNT_VSCNT &&
2232 I.getOperand(0).getReg() == AMDGPU::SGPR_NULL &&
2233 !I.getOperand(1).getImm();
2234}
2235
2236bool GCNHazardRecognizer::fixLdsBranchVmemWARHazard(MachineInstr *MI) {
2237 if (!RunLdsBranchVmemWARHazardFixup)
2238 return false;
2239
2240 assert(ST.hasLdsBranchVmemWARHazard());
2241 assert(!ST.hasExtendedWaitCounts());
2242
2243 auto IsHazardInst = [](const MachineInstr &MI) {
2245 return 1;
2247 return 2;
2248 return 0;
2249 };
2250
2251 auto InstType = IsHazardInst(*MI);
2252 if (!InstType)
2253 return false;
2254
2255 auto IsExpiredFn = [&IsHazardInst](const MachineInstr &I, int) {
2256 return IsHazardInst(I) || isStoreCountWaitZero(I);
2257 };
2258
2259 auto IsHazardFn = [InstType, &IsHazardInst](const MachineInstr &I) {
2260 if (!I.isBranch())
2261 return false;
2262
2263 auto IsHazardFn = [InstType, IsHazardInst](const MachineInstr &I) {
2264 auto InstType2 = IsHazardInst(I);
2265 return InstType2 && InstType != InstType2;
2266 };
2267
2268 auto IsExpiredFn = [InstType, &IsHazardInst](const MachineInstr &I, int) {
2269 auto InstType2 = IsHazardInst(I);
2270 if (InstType == InstType2)
2271 return true;
2272
2273 return isStoreCountWaitZero(I);
2274 };
2275
2276 return ::getWaitStatesSince(IsHazardFn, &I, IsExpiredFn) !=
2277 std::numeric_limits<int>::max();
2278 };
2279
2280 if (::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn) ==
2281 std::numeric_limits<int>::max())
2282 return false;
2283
2284 const SIInstrInfo *TII = ST.getInstrInfo();
2285 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
2286 TII->get(AMDGPU::S_WAITCNT_VSCNT))
2287 .addReg(AMDGPU::SGPR_NULL, RegState::Undef)
2288 .addImm(0);
2289
2290 return true;
2291}
2292
2293bool GCNHazardRecognizer::fixLdsDirectVALUHazard(MachineInstr *MI) {
2295 return false;
2296
2297 const int NoHazardWaitStates = 15;
2298 const MachineOperand *VDST = TII.getNamedOperand(*MI, AMDGPU::OpName::vdst);
2299 const Register VDSTReg = VDST->getReg();
2300
2301 bool VisitedTrans = false;
2302 auto IsHazardFn = [this, VDSTReg, &VisitedTrans](const MachineInstr &I) {
2303 if (!SIInstrInfo::isVALU(I, /*AllowLDSDMA=*/true))
2304 return false;
2305 VisitedTrans = VisitedTrans || SIInstrInfo::isTRANS(I);
2306 // Cover both WAR and WAW
2307 return I.readsRegister(VDSTReg, &TRI) || I.modifiesRegister(VDSTReg, &TRI);
2308 };
2309 auto IsExpiredFn = [&](const MachineInstr &I, int WaitStates) {
2310 if (WaitStates >= NoHazardWaitStates)
2311 return true;
2312 // Instructions which cause va_vdst==0 expire hazard
2315 };
2316 auto GetWaitStatesFn = [](const MachineInstr &MI) {
2317 return SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) ? 1 : 0;
2318 };
2319
2320 DenseSet<const MachineBasicBlock *> Visited;
2321 auto Count = ::getWaitStatesSince(IsHazardFn, MI->getParent(),
2322 std::next(MI->getReverseIterator()), 0,
2323 IsExpiredFn, Visited, GetWaitStatesFn);
2324
2325 // Transcendentals can execute in parallel to other VALUs.
2326 // This makes va_vdst count unusable with a mixture of VALU and TRANS.
2327 if (VisitedTrans)
2328 Count = 0;
2329
2330 MachineOperand *WaitVdstOp =
2331 TII.getNamedOperand(*MI, AMDGPU::OpName::waitvdst);
2332 WaitVdstOp->setImm(std::min(Count, NoHazardWaitStates));
2333
2334 return true;
2335}
2336
2337bool GCNHazardRecognizer::fixLdsDirectVMEMHazard(MachineInstr *MI) {
2339 return false;
2340
2341 const MachineOperand *VDST = TII.getNamedOperand(*MI, AMDGPU::OpName::vdst);
2342 const Register VDSTReg = VDST->getReg();
2343
2344 auto IsHazardFn = [this, VDSTReg](const MachineInstr &I) {
2346 return false;
2347 return I.readsRegister(VDSTReg, &TRI) || I.modifiesRegister(VDSTReg, &TRI);
2348 };
2349 bool LdsdirCanWait = ST.hasLdsWaitVMSRC();
2350 // TODO: On GFX12 the hazard should expire on S_WAIT_LOADCNT/SAMPLECNT/BVHCNT
2351 // according to the type of VMEM instruction.
2352 auto IsExpiredFn = [this, LdsdirCanWait](const MachineInstr &I, int) {
2353 return SIInstrInfo::isVALU(I, /*AllowLDSDMA=*/true) ||
2355 (I.getOpcode() == AMDGPU::S_WAITCNT && !I.getOperand(0).getImm()) ||
2356 (I.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR &&
2358 static_cast<unsigned>(I.getOperand(0).getImm())) == 0) ||
2359 (LdsdirCanWait && SIInstrInfo::isLDSDIR(I) &&
2360 !TII.getNamedOperand(I, AMDGPU::OpName::waitvsrc)->getImm());
2361 };
2362
2363 if (::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn) ==
2364 std::numeric_limits<int>::max())
2365 return false;
2366
2367 if (LdsdirCanWait) {
2368 TII.getNamedOperand(*MI, AMDGPU::OpName::waitvsrc)->setImm(0);
2369 } else {
2370 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
2371 TII.get(AMDGPU::S_WAITCNT_DEPCTR))
2373 }
2374
2375 return true;
2376}
2377
2378bool GCNHazardRecognizer::fixVALUPartialForwardingHazard(MachineInstr *MI) {
2379 if (!ST.hasVALUPartialForwardingHazard())
2380 return false;
2381 assert(!ST.hasExtendedWaitCounts());
2382
2383 if (!ST.isWave64() || !SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true))
2384 return false;
2385
2386 SmallSetVector<Register, 4> SrcVGPRs;
2387
2388 for (const MachineOperand &Use : MI->explicit_uses()) {
2389 if (Use.isReg() && TRI.isVGPR(MF.getRegInfo(), Use.getReg()))
2390 SrcVGPRs.insert(Use.getReg());
2391 }
2392
2393 // Only applies with >= 2 unique VGPR sources
2394 if (SrcVGPRs.size() <= 1)
2395 return false;
2396
2397 // Look for the following pattern:
2398 // Va <- VALU [PreExecPos]
2399 // intv1
2400 // Exec <- SALU [ExecPos]
2401 // intv2
2402 // Vb <- VALU [PostExecPos]
2403 // intv3
2404 // MI Va, Vb (WaitState = 0)
2405 //
2406 // Where:
2407 // intv1 + intv2 <= 2 VALUs
2408 // intv3 <= 4 VALUs
2409 //
2410 // If found, insert an appropriate S_WAITCNT_DEPCTR before MI.
2411
2412 const int Intv1plus2MaxVALUs = 2;
2413 const int Intv3MaxVALUs = 4;
2414 const int IntvMaxVALUs = 6;
2415 const int NoHazardVALUWaitStates = IntvMaxVALUs + 2;
2416
2417 struct StateType {
2418 SmallDenseMap<Register, int, 4> DefPos;
2419 int ExecPos = std::numeric_limits<int>::max();
2420 int VALUs = 0;
2421
2422 static unsigned getHashValue(const StateType &State) {
2423 hash_code H = hash_combine(State.ExecPos, State.VALUs);
2424 for (const auto &[Reg, Pos] : State.DefPos)
2425 H = hash_combine(H, Reg, Pos);
2426 return static_cast<unsigned>(H);
2427 }
2428 static bool isEqual(const StateType &LHS, const StateType &RHS) {
2429 return LHS.DefPos == RHS.DefPos && LHS.ExecPos == RHS.ExecPos &&
2430 LHS.VALUs == RHS.VALUs;
2431 }
2432 };
2433
2434 StateType State;
2435
2436 // This overloads expiry testing with all the hazard detection
2437 auto IsHazardFn = [&, this](StateType &State, const MachineInstr &I) {
2438 // Too many VALU states have passed
2439 if (State.VALUs > NoHazardVALUWaitStates)
2440 return HazardExpired;
2441
2442 // Instructions which cause va_vdst==0 expire hazard
2445 (I.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR &&
2447 static_cast<unsigned>(I.getOperand(0).getImm())) == 0))
2448 return HazardExpired;
2449
2450 // Track registers writes
2451 bool Changed = false;
2452 if (SIInstrInfo::isVALU(I, /*AllowLDSDMA=*/true)) {
2453 for (Register Src : SrcVGPRs) {
2454 if (!State.DefPos.count(Src) && I.modifiesRegister(Src, &TRI)) {
2455 State.DefPos[Src] = State.VALUs;
2456 Changed = true;
2457 }
2458 }
2459 } else if (SIInstrInfo::isSALU(I)) {
2460 if (State.ExecPos == std::numeric_limits<int>::max()) {
2461 if (!State.DefPos.empty() && I.modifiesRegister(AMDGPU::EXEC, &TRI)) {
2462 State.ExecPos = State.VALUs;
2463 Changed = true;
2464 }
2465 }
2466 }
2467
2468 // Early expiration: too many VALUs in intv3
2469 if (State.VALUs > Intv3MaxVALUs && State.DefPos.empty())
2470 return HazardExpired;
2471
2472 // Only evaluate state if something changed
2473 if (!Changed)
2474 return NoHazardFound;
2475
2476 // Determine positions of VALUs pre/post exec change
2477 if (State.ExecPos == std::numeric_limits<int>::max())
2478 return NoHazardFound;
2479
2480 int PreExecPos = std::numeric_limits<int>::max();
2481 int PostExecPos = std::numeric_limits<int>::max();
2482
2483 for (auto Entry : State.DefPos) {
2484 int DefVALUs = Entry.second;
2485 if (DefVALUs != std::numeric_limits<int>::max()) {
2486 if (DefVALUs >= State.ExecPos)
2487 PreExecPos = std::min(PreExecPos, DefVALUs);
2488 else
2489 PostExecPos = std::min(PostExecPos, DefVALUs);
2490 }
2491 }
2492
2493 // Need a VALUs post exec change
2494 if (PostExecPos == std::numeric_limits<int>::max())
2495 return NoHazardFound;
2496
2497 // Too many VALUs in intv3?
2498 int Intv3VALUs = PostExecPos;
2499 if (Intv3VALUs > Intv3MaxVALUs)
2500 return HazardExpired;
2501
2502 // Too many VALUs in intv2?
2503 int Intv2VALUs = (State.ExecPos - PostExecPos) - 1;
2504 if (Intv2VALUs > Intv1plus2MaxVALUs)
2505 return HazardExpired;
2506
2507 // Need a VALUs pre exec change
2508 if (PreExecPos == std::numeric_limits<int>::max())
2509 return NoHazardFound;
2510
2511 // Too many VALUs in intv1?
2512 int Intv1VALUs = PreExecPos - State.ExecPos;
2513 if (Intv1VALUs > Intv1plus2MaxVALUs)
2514 return HazardExpired;
2515
2516 // Too many VALUs in intv1 + intv2
2517 if (Intv1VALUs + Intv2VALUs > Intv1plus2MaxVALUs)
2518 return HazardExpired;
2519
2520 return HazardFound;
2521 };
2522 auto UpdateStateFn = [](StateType &State, const MachineInstr &MI) {
2523 if (SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true))
2524 State.VALUs += 1;
2525 };
2526
2527 if (!hasHazard<StateType>(State, IsHazardFn, UpdateStateFn, MI->getParent(),
2528 std::next(MI->getReverseIterator())))
2529 return false;
2530
2531 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
2532 TII.get(AMDGPU::S_WAITCNT_DEPCTR))
2534
2535 return true;
2536}
2537
2538bool GCNHazardRecognizer::fixVALUTransUseHazard(MachineInstr *MI) {
2539 if (!ST.hasVALUTransUseHazard())
2540 return false;
2541 assert(!ST.hasExtendedWaitCounts());
2542
2543 if (!SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true))
2544 return false;
2545
2546 SmallSet<Register, 4> SrcVGPRs;
2547
2548 for (const MachineOperand &Use : MI->explicit_uses()) {
2549 if (Use.isReg() && TRI.isVGPR(MF.getRegInfo(), Use.getReg()))
2550 SrcVGPRs.insert(Use.getReg());
2551 }
2552
2553 // Look for the following pattern:
2554 // Va <- TRANS VALU
2555 // intv
2556 // MI Va (WaitState = 0)
2557 //
2558 // Where:
2559 // intv <= 5 VALUs / 1 TRANS
2560 //
2561 // If found, insert an appropriate S_WAITCNT_DEPCTR before MI.
2562
2563 const int IntvMaxVALUs = 5;
2564 const int IntvMaxTRANS = 1;
2565
2566 struct StateType {
2567 int VALUs = 0;
2568 int TRANS = 0;
2569
2570 static unsigned getHashValue(const StateType &State) {
2571 return static_cast<unsigned>(hash_combine(State.VALUs, State.TRANS));
2572 }
2573 static bool isEqual(const StateType &LHS, const StateType &RHS) {
2574 return LHS.VALUs == RHS.VALUs && LHS.TRANS == RHS.TRANS;
2575 }
2576 };
2577
2578 StateType State;
2579
2580 // This overloads expiry testing with all the hazard detection
2581 auto IsHazardFn = [&, this](StateType &State, const MachineInstr &I) {
2582 // Too many VALU states have passed
2583 if (State.VALUs > IntvMaxVALUs || State.TRANS > IntvMaxTRANS)
2584 return HazardExpired;
2585
2586 // Instructions which cause va_vdst==0 expire hazard
2589 (I.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR &&
2591 static_cast<unsigned>(I.getOperand(0).getImm())) == 0))
2592 return HazardExpired;
2593
2594 // Track registers writes
2595 if (SIInstrInfo::isTRANS(I)) {
2596 for (Register Src : SrcVGPRs) {
2597 if (I.modifiesRegister(Src, &TRI)) {
2598 return HazardFound;
2599 }
2600 }
2601 }
2602
2603 return NoHazardFound;
2604 };
2605 auto UpdateStateFn = [](StateType &State, const MachineInstr &MI) {
2606 if (SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true))
2607 State.VALUs += 1;
2609 State.TRANS += 1;
2610 };
2611
2612 if (!hasHazard<StateType>(State, IsHazardFn, UpdateStateFn, MI->getParent(),
2613 std::next(MI->getReverseIterator())))
2614 return false;
2615
2616 // Hazard is observed - insert a wait on va_dst counter to ensure hazard is
2617 // avoided.
2618 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
2619 TII.get(AMDGPU::S_WAITCNT_DEPCTR))
2621
2622 return true;
2623}
2624
2625bool GCNHazardRecognizer::fixVALUTransCoexecutionHazards(MachineInstr *MI) {
2626 if (!ST.hasTransCoexecutionHazard() || // Coexecution disabled.
2627 !SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true) ||
2629 return false;
2630
2631 const SIInstrInfo *TII = ST.getInstrInfo();
2632 const SIRegisterInfo *TRI = ST.getRegisterInfo();
2633
2634 auto IsTransHazardFn = [MI, TII, TRI](const MachineInstr &I) {
2635 if (!SIInstrInfo::isTRANS(I))
2636 return false;
2637
2638 // RAW: Trans(I) writes, VALU(MI) reads.
2639 Register TransDef = TII->getNamedOperand(I, AMDGPU::OpName::vdst)->getReg();
2640 for (const MachineOperand &ValuUse : MI->explicit_uses()) {
2641 if (ValuUse.isReg() && TRI->regsOverlap(TransDef, ValuUse.getReg()))
2642 return true;
2643 }
2644
2645 // WAR: Trans(I) reads, VALU(MI) writes. Use modifiesRegister for
2646 // multi-dest VALUs.
2647 for (const MachineOperand &TransUse : I.explicit_uses()) {
2648 if (TransUse.isReg() && MI->modifiesRegister(TransUse.getReg(), TRI))
2649 return true;
2650 }
2651
2652 return false;
2653 };
2654
2655 auto IsExpiredFn = [](const MachineInstr &I, int) {
2656 return SIInstrInfo::isVALU(I, /*AllowLDSDMA=*/true);
2657 };
2658
2659 const int HasVALU = std::numeric_limits<int>::max();
2660 if (::getWaitStatesSince(IsTransHazardFn, MI, IsExpiredFn) == HasVALU)
2661 return false;
2662
2663 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(AMDGPU::V_NOP_e32));
2664 return true;
2665}
2666
2667bool GCNHazardRecognizer::fixWMMAHazards(MachineInstr *MI) {
2669 return false;
2670
2671 const SIInstrInfo *TII = ST.getInstrInfo();
2672 const SIRegisterInfo *TRI = ST.getRegisterInfo();
2673
2674 auto IsHazardFn = [MI, TII, TRI, this](const MachineInstr &I) {
2676 return false;
2677
2678 // Src0(matrix A) or Src1(matrix B) of the current wmma instruction overlaps
2679 // with the dest(matrix D) of the previous wmma.
2680 const Register CurSrc0Reg =
2681 TII->getNamedOperand(*MI, AMDGPU::OpName::src0)->getReg();
2682 const Register CurSrc1Reg =
2683 TII->getNamedOperand(*MI, AMDGPU::OpName::src1)->getReg();
2684
2685 const Register PrevDstReg =
2686 TII->getNamedOperand(I, AMDGPU::OpName::vdst)->getReg();
2687
2688 if (TRI->regsOverlap(PrevDstReg, CurSrc0Reg) ||
2689 TRI->regsOverlap(PrevDstReg, CurSrc1Reg)) {
2690 return true;
2691 }
2692
2693 // GFX12+ allows overlap of matrix C with PrevDstReg (hardware will stall)
2694 // but Index can't overlap with PrevDstReg.
2695 if (AMDGPU::isGFX12Plus(ST)) {
2696 if (SIInstrInfo::isSWMMAC(*MI)) {
2697 const Register CurIndex =
2698 TII->getNamedOperand(*MI, AMDGPU::OpName::src2)->getReg();
2699 if (TRI->regsOverlap(PrevDstReg, CurIndex))
2700 return true;
2701 }
2702 return false;
2703 }
2704
2705 return false;
2706 };
2707
2708 auto IsExpiredFn = [](const MachineInstr &I, int) {
2709 return SIInstrInfo::isVALU(I, /*AllowLDSDMA=*/true);
2710 };
2711
2712 if (::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn) ==
2713 std::numeric_limits<int>::max())
2714 return false;
2715
2716 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII->get(AMDGPU::V_NOP_e32));
2717
2718 return true;
2719}
2720
2722 return SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/false) &&
2724}
2725
2726// Classify XDL WMMA instructions into co-execution hazard categories
2727// (Refer to SPG 4.6.12.1), mainly based on instruction latency.
2728//
2729// Category 0: WMMA with Latency 8
2730// WMMA_*F16, WMMA_*BF16
2731// WMMA_*_16X16X128_{FP8,BF8}
2732// WMMA_*F8F6F4 if SRCA & SRCB are not both F4
2733//
2734// Category 1: WMMA Latency 16
2735// WMMA_IU8
2736//
2737// Category 2: SWMMAC with Latency 8
2738// SWMMAC_*F16, SWMMAC_*BF16,
2739// SWMMAC_*FP8FP8
2740// SWMMAC_*BF8FP8
2741// SWMMAC_*FP8BF8
2742// SWMMAC_*BF8BF8
2743//
2744// Category 3: SWMMAC with Latency 16
2745// SWMMAC_IU8
2746//
2747// Category 4: 16 Pass GFX1251 WMMA with latency 16
2748// V_WMMA_*_16X16X32_{F16,BF16}
2749// V_WMMA_{F32,F16}_16X16X64_{FP8,BF8}*
2750// V_WMMA_F32_16x16x128_F8F6F4 (F4 only)
2751// V_SWMMAC_*_16X16X64_{F16,BF16}
2752// V_SWMMAC_{F32,F16}_16X16X128_{FP8,BF8}*
2753//
2754// Category 5: 32 Pass GFX1251 WMMA with latency 32
2755// V_WMMA_F32_16x16x128_F8F6F4 (not all F4)
2756// V_WMMA_{F32,F16}_16X16X128_{FP8,BF8}*
2757// V_WMMA_F32_32X16X128_F4
2758// V_WMMA_I32_16X16X64_IU8
2759// V_WMMA_I32_16X16X64_IU8
2760//
2761// Category 6: gfx1250 WMMA with Latency 4 (one co-execution slot)
2762// WMMA_*_16X16X64_{FP8,BF8}
2763// WMMA_*F8F6F4 if SRCA & SRCB are both F4
2765 const SIInstrInfo *TII,
2766 const TargetSchedModel &SchedModel,
2767 const GCNSubtarget &ST) {
2768 assert(TII->isXDLWMMA(MI) && "must be xdl wmma");
2769 bool IsSWMMAC = SIInstrInfo::isSWMMAC(MI);
2770 bool IsLowestRateWMMA = ST.hasGFX125xLowestRateWMMA();
2771 unsigned Category = 0;
2772
2773 unsigned Latency = SchedModel.computeInstrLatency(&MI);
2774 switch (Latency) {
2775 case 4:
2776 // Dense 4-cycle WMMA (gfx1250 16x16x64 FP8/BF8 and f8f6f4 with both
2777 // inputs F4). One co-execution slot; there is no 4-cycle SWMMAC.
2778 assert(!IsSWMMAC && "no 4-cycle SWMMAC expected");
2779 Category = 6;
2780 break;
2781 case 8:
2782 Category = IsSWMMAC ? 2 : 0;
2783 break;
2784 case 16:
2785 Category = IsLowestRateWMMA ? 4 : (IsSWMMAC ? 3 : 1);
2786 break;
2787 case 32:
2788 assert(IsLowestRateWMMA && "latency 32 is not expected");
2789 Category = 5;
2790 break;
2791 default:
2792 llvm_unreachable("unexpected xdl wmma latency");
2793 } // end switch.
2794
2795 return Category;
2796}
2797
2798int GCNHazardRecognizer::checkWMMACoexecutionHazards(MachineInstr *MI) const {
2799 if (!ST.hasWMMACoexecutionHazards())
2800 return 0;
2801
2802 const SIInstrInfo *TII = ST.getInstrInfo();
2803 if (!TII->isXDLWMMA(*MI) && !isCoexecutableVALUInst(*MI))
2804 return 0;
2805
2806 // WaitStates here is the number of V_NOPs or unrelated VALU instructions must
2807 // be in between the first WMMA and the second instruction to cover the hazard
2808 // (WMMAWaitStates if the second is also a WMMA, VALUWaitStates if the second
2809 // is a VALU). Refer to SPG 4.6.12.1. "Requirements for WMMA data hazards" for
2810 // numbers, which depends on the category of the first WMMA.
2811 const int WMMAWaitStates[] = {5, 9, 3, 5, 9, 17, 2};
2812 const int VALUWaitStates[] = {4, 8, 2, 4, 8, 16, 1};
2813 const bool IsWMMA = TII->isXDLWMMA(*MI);
2814 const bool IsLowestRateWMMA = ST.hasGFX125xLowestRateWMMA();
2815 ArrayRef<int> RequiredWaitStates =
2816 IsWMMA ? ArrayRef<int>(WMMAWaitStates) : ArrayRef<int>(VALUWaitStates);
2817 const int MaxWaitStates =
2818 IsWMMA ? (IsLowestRateWMMA ? 17 : 9) : (IsLowestRateWMMA ? 16 : 8);
2819
2820 auto WindowFor = [=](const MachineInstr &I) -> std::optional<int> {
2821 if (!TII->isXDLWMMA(I))
2822 return std::nullopt;
2823 if (IsWMMA ? !hasWMMAToWMMARegOverlap(I, *MI)
2824 : !hasWMMAToVALURegOverlap(I, *MI))
2825 return std::nullopt;
2826 return RequiredWaitStates[getWMMAHazardInstInCategory(I, TII, TSchedModel,
2827 ST)];
2828 };
2829
2830 return getMaxVALUWindowDeficit(MaxWaitStates, WindowFor);
2831}
2832
2833bool GCNHazardRecognizer::hasWMMAToWMMARegOverlap(
2834 const MachineInstr &WMMA, const MachineInstr &MI) const {
2835 Register D0 = TII.getNamedOperand(WMMA, AMDGPU::OpName::vdst)->getReg();
2836 Register A1 = TII.getNamedOperand(MI, AMDGPU::OpName::src0)->getReg();
2837 Register B1 = TII.getNamedOperand(MI, AMDGPU::OpName::src1)->getReg();
2838
2839 // WMMA0 writes (D0), WMMA1 reads (A1/B1/Idx1).
2840 if (TRI.regsOverlap(D0, A1) || TRI.regsOverlap(D0, B1))
2841 return true;
2842
2844 Register Idx1 = TII.getNamedOperand(MI, AMDGPU::OpName::src2)->getReg();
2845 if (TRI.regsOverlap(D0, Idx1))
2846 return true;
2847 }
2848 return false;
2849}
2850
2851bool GCNHazardRecognizer::hasWMMAToVALURegOverlap(
2852 const MachineInstr &WMMA, const MachineInstr &MI) const {
2853 // WMMA writes, VALU reads.
2854 Register D0 = TII.getNamedOperand(WMMA, AMDGPU::OpName::vdst)->getReg();
2855 for (const MachineOperand &ValuUse : MI.explicit_uses()) {
2856 if (ValuUse.isReg() && TRI.regsOverlap(D0, ValuUse.getReg()))
2857 return true;
2858 }
2859
2860 // WMMA reads or writes, VALU writes.
2861 Register A0 = TII.getNamedOperand(WMMA, AMDGPU::OpName::src0)->getReg();
2862 Register B0 = TII.getNamedOperand(WMMA, AMDGPU::OpName::src1)->getReg();
2863 SmallVector<Register, 4> WMMARegs({D0, A0, B0});
2864
2865 const MachineOperand *Src2 = TII.getNamedOperand(WMMA, AMDGPU::OpName::src2);
2866 if (Src2->isReg())
2867 WMMARegs.push_back(Src2->getReg());
2868
2869 for (const MachineOperand &ValuDef : MI.defs()) {
2870 Register VDstReg = ValuDef.getReg();
2871 for (Register WMMAReg : WMMARegs) {
2872 if (TRI.regsOverlap(VDstReg, WMMAReg))
2873 return true;
2874 }
2875 }
2876 return false;
2877}
2878
2879bool GCNHazardRecognizer::isCoexecutionHazardFor(const MachineInstr &I,
2880 const MachineInstr &MI) const {
2881 // I is the potential WMMA hazard source, MI is the instruction being checked
2882 // for hazard.
2883 if (!TII.isXDLWMMA(I))
2884 return false;
2885
2886 // Dispatch based on MI type
2887 if (TII.isXDLWMMA(MI))
2888 return hasWMMAToWMMARegOverlap(I, MI);
2890 return hasWMMAToVALURegOverlap(I, MI);
2891
2892 return false;
2893}
2894
2895bool GCNHazardRecognizer::hasWMMAHazardInLoop(MachineLoop *L, MachineInstr *MI,
2896 bool IncludeSubloops) {
2897 // Scan loop for any WMMA that hazards MI.
2898 // TODO: Avoid full loop scan when WMMA is beyond VALU distance.
2899 for (MachineBasicBlock *MBB : L->getBlocks()) {
2900 if (!IncludeSubloops && MLI->getLoopFor(MBB) != L)
2901 continue;
2902 for (MachineInstr &I : *MBB) {
2903 if (&I == MI)
2904 continue;
2905 if (isCoexecutionHazardFor(I, *MI))
2906 return true;
2907 }
2908 }
2909 return false;
2910}
2911
2912bool GCNHazardRecognizer::tryHoistWMMAVnopsFromLoop(MachineInstr *MI,
2913 int WaitStatesNeeded) {
2914 if (!MLI)
2915 return false;
2916
2917 MachineLoop *L = MLI->getLoopFor(MI->getParent());
2918 if (!L) {
2919 ++NumWMMAHoistingBailed;
2920 return false;
2921 }
2922
2923 // If innermost loop has WMMA hazard, we can't hoist at all
2924 if (hasWMMAHazardInLoop(L, MI)) {
2925 ++NumWMMAHoistingBailed;
2926 return false;
2927 }
2928
2929 // Find outermost loop with no internal hazard
2930 MachineLoop *TargetLoop = L;
2931 while (MachineLoop *Parent = TargetLoop->getParentLoop()) {
2932 if (hasWMMAHazardInLoop(Parent, MI, false))
2933 break; // Parent has hazard in its own blocks, stop here
2934 TargetLoop = Parent; // Safe to hoist further out
2935 }
2936
2937 // Need valid preheader to insert V_NOPs
2938 MachineBasicBlock *Preheader = TargetLoop->getLoopPreheader();
2939 if (!Preheader) {
2940 ++NumWMMAHoistingBailed;
2941 return false;
2942 }
2943
2944 LLVM_DEBUG(dbgs() << "WMMA V_NOP Hoisting: Moving " << WaitStatesNeeded
2945 << " V_NOPs from loop to " << printMBBReference(*Preheader)
2946 << "\n");
2947
2948 emitVNops(*Preheader, Preheader->getFirstTerminator(), WaitStatesNeeded,
2949 /*IsHoisting=*/true);
2950 NumWMMANopsHoisted += WaitStatesNeeded;
2951 return true;
2952}
2953
2954bool GCNHazardRecognizer::fixWMMACoexecutionHazards(MachineInstr *MI) {
2955 int WaitStatesNeeded = checkWMMACoexecutionHazards(MI);
2956 if (WaitStatesNeeded <= 0)
2957 return false;
2958
2959 if (EnableWMMAVnopHoisting && tryHoistWMMAVnopsFromLoop(MI, WaitStatesNeeded))
2960 return true;
2961
2962 emitVNops(*MI->getParent(), MI->getIterator(), WaitStatesNeeded);
2963 return true;
2964}
2965
2966bool GCNHazardRecognizer::fixShift64HighRegBug(MachineInstr *MI) {
2967 if (!ST.hasShift64HighRegBug())
2968 return false;
2969 assert(!ST.hasExtendedWaitCounts());
2970
2971 switch (MI->getOpcode()) {
2972 default:
2973 return false;
2974 case AMDGPU::V_LSHLREV_B64_e64:
2975 case AMDGPU::V_LSHRREV_B64_e64:
2976 case AMDGPU::V_ASHRREV_I64_e64:
2977 break;
2978 }
2979
2980 MachineOperand *Amt = TII.getNamedOperand(*MI, AMDGPU::OpName::src0);
2981 if (!Amt->isReg())
2982 return false;
2983
2984 Register AmtReg = Amt->getReg();
2985 const MachineRegisterInfo &MRI = MF.getRegInfo();
2986 // Check if this is a last VGPR in the allocation block.
2987 if (!TRI.isVGPR(MRI, AmtReg) || ((AmtReg - AMDGPU::VGPR0) & 7) != 7)
2988 return false;
2989
2990 if (AmtReg != AMDGPU::VGPR255 && MRI.isPhysRegUsed(AmtReg + 1))
2991 return false;
2992
2993 assert(ST.needsAlignedVGPRs());
2994 static_assert(AMDGPU::VGPR0 + 1 == AMDGPU::VGPR1);
2995
2996 const DebugLoc &DL = MI->getDebugLoc();
2997 MachineBasicBlock *MBB = MI->getParent();
2998 MachineOperand *Src1 = TII.getNamedOperand(*MI, AMDGPU::OpName::src1);
2999
3000 // In:
3001 //
3002 // Dst = shiftrev64 Amt, Src1
3003 //
3004 // if Dst!=Src1 then avoid the bug with:
3005 //
3006 // Dst.sub0 = Amt
3007 // Dst = shift64 Dst.sub0, Src1
3008
3009 Register DstReg = MI->getOperand(0).getReg();
3010 if (!Src1->isReg() || Src1->getReg() != DstReg) {
3011 Register DstLo = TRI.getSubReg(DstReg, AMDGPU::sub0);
3012 runOnInstruction(
3013 BuildMI(*MBB, MI, DL, TII.get(AMDGPU::V_MOV_B32_e32), DstLo).add(*Amt));
3014 Amt->setReg(DstLo);
3015 Amt->setIsKill(true);
3016 return true;
3017 }
3018
3019 bool Overlapped = MI->modifiesRegister(AmtReg, &TRI);
3020 Register NewReg;
3021 for (MCRegister Reg : Overlapped ? AMDGPU::VReg_64_Align2RegClass
3022 : AMDGPU::VGPR_32RegClass) {
3023 if (!MI->modifiesRegister(Reg, &TRI) && !MI->readsRegister(Reg, &TRI)) {
3024 NewReg = Reg;
3025 break;
3026 }
3027 }
3028
3029 Register NewAmt = Overlapped ? (Register)TRI.getSubReg(NewReg, AMDGPU::sub1)
3030 : NewReg;
3031 Register NewAmtLo;
3032
3033 if (Overlapped)
3034 NewAmtLo = TRI.getSubReg(NewReg, AMDGPU::sub0);
3035
3036 // Insert a full wait count because found register might be pending a wait.
3037 BuildMI(*MBB, MI, DL, TII.get(AMDGPU::S_WAITCNT))
3038 .addImm(0);
3039
3040 // Insert V_SWAP_B32 instruction(s) and run hazard recognizer on them.
3041 if (Overlapped)
3042 runOnInstruction(
3043 BuildMI(*MBB, MI, DL, TII.get(AMDGPU::V_SWAP_B32), NewAmtLo)
3044 .addDef(AmtReg - 1)
3045 .addReg(AmtReg - 1, RegState::Undef)
3046 .addReg(NewAmtLo, RegState::Undef));
3047 runOnInstruction(BuildMI(*MBB, MI, DL, TII.get(AMDGPU::V_SWAP_B32), NewAmt)
3048 .addDef(AmtReg)
3049 .addReg(AmtReg, RegState::Undef)
3050 .addReg(NewAmt, RegState::Undef));
3051
3052 // Instructions emitted after the current instruction will be processed by the
3053 // parent loop of the hazard recognizer in a natural way.
3054 BuildMI(*MBB, std::next(MI->getIterator()), DL, TII.get(AMDGPU::V_SWAP_B32),
3055 AmtReg)
3056 .addDef(NewAmt)
3057 .addReg(NewAmt)
3058 .addReg(AmtReg);
3059 if (Overlapped)
3060 BuildMI(*MBB, std::next(MI->getIterator()), DL, TII.get(AMDGPU::V_SWAP_B32),
3061 AmtReg - 1)
3062 .addDef(NewAmtLo)
3063 .addReg(NewAmtLo)
3064 .addReg(AmtReg - 1);
3065
3066 // Re-running hazard recognizer on the modified instruction is not necessary,
3067 // inserted V_SWAP_B32 has already both read and write new registers so
3068 // hazards related to these register has already been handled.
3069 Amt->setReg(NewAmt);
3070 Amt->setIsKill(false);
3071 // We do not update liveness, so verifier may see it as undef.
3072 Amt->setIsUndef();
3073 if (Overlapped) {
3074 MI->getOperand(0).setReg(NewReg);
3075 Src1->setReg(NewReg);
3076 Src1->setIsKill(false);
3077 Src1->setIsUndef();
3078 }
3079
3080 return true;
3081}
3082
3083int GCNHazardRecognizer::checkNSAtoVMEMHazard(MachineInstr *MI) const {
3084 int NSAtoVMEMWaitStates = 1;
3085
3086 if (!ST.hasNSAtoVMEMBug())
3087 return 0;
3088
3090 return 0;
3091
3092 const SIInstrInfo *TII = ST.getInstrInfo();
3093 const auto *Offset = TII->getNamedOperand(*MI, AMDGPU::OpName::offset);
3094 if (!Offset || (Offset->getImm() & 6) == 0)
3095 return 0;
3096
3097 auto IsHazardFn = [TII](const MachineInstr &I) {
3098 if (!SIInstrInfo::isMIMG(I))
3099 return false;
3100 const AMDGPU::MIMGInfo *Info = AMDGPU::getMIMGInfo(I.getOpcode());
3101 return Info->MIMGEncoding == AMDGPU::MIMGEncGfx10NSA &&
3102 TII->getInstSizeInBytes(I) >= 16;
3103 };
3104
3105 return NSAtoVMEMWaitStates - getWaitStatesSince(IsHazardFn, 1);
3106}
3107
3108int GCNHazardRecognizer::checkFPAtomicToDenormModeHazard(
3109 MachineInstr *MI) const {
3110 int FPAtomicToDenormModeWaitStates = 3;
3111
3112 if (!ST.hasFPAtomicToDenormModeHazard())
3113 return 0;
3114 assert(!ST.hasExtendedWaitCounts());
3115
3116 if (MI->getOpcode() != AMDGPU::S_DENORM_MODE)
3117 return 0;
3118
3119 auto IsHazardFn = [](const MachineInstr &I) {
3120 if (!SIInstrInfo::isVMEM(I))
3121 return false;
3122 return SIInstrInfo::isFPAtomic(I);
3123 };
3124
3125 auto IsExpiredFn = [](const MachineInstr &MI, int WaitStates) {
3126 if (WaitStates >= 3 || SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true))
3127 return true;
3128
3129 return SIInstrInfo::isWaitcnt(MI.getOpcode());
3130 };
3131
3132 return FPAtomicToDenormModeWaitStates -
3133 ::getWaitStatesSince(IsHazardFn, MI, IsExpiredFn);
3134}
3135
3136int GCNHazardRecognizer::checkMAIHazards(MachineInstr *MI) const {
3138
3139 return ST.hasGFX90AInsts() ? checkMAIHazards90A(MI) : checkMAIHazards908(MI);
3140}
3141
3142int GCNHazardRecognizer::checkMFMAPadding(MachineInstr *MI) const {
3143 // Early exit if no padding is requested.
3144 if (MFMAPaddingRatio == 0)
3145 return 0;
3146
3147 const SIMachineFunctionInfo *MFI = MF.getInfo<SIMachineFunctionInfo>();
3148 if (!SIInstrInfo::isMFMA(*MI) || MFI->getOccupancy() < 2)
3149 return 0;
3150
3151 int NeighborMFMALatency = 0;
3152 auto IsNeighboringMFMA = [&NeighborMFMALatency,
3153 this](const MachineInstr &MI) {
3154 if (!SIInstrInfo::isMFMA(MI))
3155 return false;
3156
3157 NeighborMFMALatency = this->getMFMAPipelineWaitStates(MI);
3158 return true;
3159 };
3160
3161 const int MaxMFMAPipelineWaitStates = 16;
3162 int WaitStatesSinceNeighborMFMA =
3163 getWaitStatesSince(IsNeighboringMFMA, MaxMFMAPipelineWaitStates);
3164
3165 int NeighborMFMAPaddingNeeded =
3166 (NeighborMFMALatency * MFMAPaddingRatio / 100) -
3167 WaitStatesSinceNeighborMFMA;
3168
3169 return std::max(0, NeighborMFMAPaddingNeeded);
3170}
3171
3172int GCNHazardRecognizer::checkMAIHazards908(MachineInstr *MI) const {
3173 int WaitStatesNeeded = 0;
3174 unsigned Opc = MI->getOpcode();
3175
3176 auto IsVALUFn = [](const MachineInstr &MI) {
3177 return SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) || MI.isInlineAsm();
3178 };
3179
3180 if (Opc != AMDGPU::V_ACCVGPR_READ_B32_e64) { // MFMA or v_accvgpr_write
3181 const int LegacyVALUWritesVGPRWaitStates = 2;
3182 const int VALUWritesExecWaitStates = 4;
3183 const int MaxWaitStates = 4;
3184
3185 int WaitStatesNeededForUse = VALUWritesExecWaitStates -
3186 getWaitStatesSinceDef(AMDGPU::EXEC, IsVALUFn, MaxWaitStates);
3187 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3188
3189 if (WaitStatesNeeded < MaxWaitStates) {
3190 for (const MachineOperand &Use : MI->explicit_uses()) {
3191 const int MaxWaitStates = 2;
3192
3193 if (!Use.isReg() || !TRI.isVGPR(MF.getRegInfo(), Use.getReg()))
3194 continue;
3195
3196 int WaitStatesNeededForUse = LegacyVALUWritesVGPRWaitStates -
3197 getWaitStatesSinceDef(Use.getReg(), IsVALUFn, MaxWaitStates);
3198 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3199
3200 if (WaitStatesNeeded == MaxWaitStates)
3201 break;
3202 }
3203 }
3204 }
3205
3206 for (const MachineOperand &Op : MI->explicit_operands()) {
3207 if (!Op.isReg() || !TRI.isAGPR(MF.getRegInfo(), Op.getReg()))
3208 continue;
3209
3210 if (Op.isDef() && Opc != AMDGPU::V_ACCVGPR_WRITE_B32_e64)
3211 continue;
3212
3213 const int MFMAWritesAGPROverlappedSrcABWaitStates = 4;
3214 const int MFMAWritesAGPROverlappedSrcCWaitStates = 2;
3215 const int MFMA4x4WritesAGPRAccVgprReadWaitStates = 4;
3216 const int MFMA16x16WritesAGPRAccVgprReadWaitStates = 10;
3217 const int MFMA32x32WritesAGPRAccVgprReadWaitStates = 18;
3218 const int MFMA4x4WritesAGPRAccVgprWriteWaitStates = 1;
3219 const int MFMA16x16WritesAGPRAccVgprWriteWaitStates = 7;
3220 const int MFMA32x32WritesAGPRAccVgprWriteWaitStates = 15;
3221 const int MaxWaitStates = 18;
3222 Register Reg = Op.getReg();
3223 unsigned HazardDefLatency = 0;
3224
3225 auto IsOverlappedMFMAFn = [Reg, &HazardDefLatency,
3226 this](const MachineInstr &MI) {
3227 if (!SIInstrInfo::isMFMA(MI))
3228 return false;
3229 Register DstReg = MI.getOperand(0).getReg();
3230 if (DstReg == Reg)
3231 return false;
3232 HazardDefLatency =
3233 std::max(HazardDefLatency, TSchedModel.computeInstrLatency(&MI));
3234 return TRI.regsOverlap(DstReg, Reg);
3235 };
3236
3237 int WaitStatesSinceDef = getWaitStatesSinceDef(Reg, IsOverlappedMFMAFn,
3238 MaxWaitStates);
3239 int NeedWaitStates = MFMAWritesAGPROverlappedSrcABWaitStates;
3240 int SrcCIdx = AMDGPU::getNamedOperandIdx(Opc, AMDGPU::OpName::src2);
3241 int OpNo = Op.getOperandNo();
3242 if (OpNo == SrcCIdx) {
3243 NeedWaitStates = MFMAWritesAGPROverlappedSrcCWaitStates;
3244 } else if (Opc == AMDGPU::V_ACCVGPR_READ_B32_e64) {
3245 switch (HazardDefLatency) {
3246 case 2: NeedWaitStates = MFMA4x4WritesAGPRAccVgprReadWaitStates;
3247 break;
3248 case 8: NeedWaitStates = MFMA16x16WritesAGPRAccVgprReadWaitStates;
3249 break;
3250 case 16: [[fallthrough]];
3251 default: NeedWaitStates = MFMA32x32WritesAGPRAccVgprReadWaitStates;
3252 break;
3253 }
3254 } else if (Opc == AMDGPU::V_ACCVGPR_WRITE_B32_e64) {
3255 switch (HazardDefLatency) {
3256 case 2: NeedWaitStates = MFMA4x4WritesAGPRAccVgprWriteWaitStates;
3257 break;
3258 case 8: NeedWaitStates = MFMA16x16WritesAGPRAccVgprWriteWaitStates;
3259 break;
3260 case 16: [[fallthrough]];
3261 default: NeedWaitStates = MFMA32x32WritesAGPRAccVgprWriteWaitStates;
3262 break;
3263 }
3264 }
3265
3266 int WaitStatesNeededForUse = NeedWaitStates - WaitStatesSinceDef;
3267 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3268
3269 if (WaitStatesNeeded == MaxWaitStates)
3270 return WaitStatesNeeded; // Early exit.
3271
3272 auto IsAccVgprWriteFn = [Reg, this](const MachineInstr &MI) {
3273 if (MI.getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64)
3274 return false;
3275 Register DstReg = MI.getOperand(0).getReg();
3276 return TRI.regsOverlap(Reg, DstReg);
3277 };
3278
3279 const int AccVGPRWriteMFMAReadSrcCWaitStates = 1;
3280 const int AccVGPRWriteMFMAReadSrcABWaitStates = 3;
3281 const int AccVGPRWriteAccVgprReadWaitStates = 3;
3282 NeedWaitStates = AccVGPRWriteMFMAReadSrcABWaitStates;
3283 if (OpNo == SrcCIdx)
3284 NeedWaitStates = AccVGPRWriteMFMAReadSrcCWaitStates;
3285 else if (Opc == AMDGPU::V_ACCVGPR_READ_B32_e64)
3286 NeedWaitStates = AccVGPRWriteAccVgprReadWaitStates;
3287
3288 WaitStatesNeededForUse = NeedWaitStates -
3289 getWaitStatesSinceDef(Reg, IsAccVgprWriteFn, MaxWaitStates);
3290 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3291
3292 if (WaitStatesNeeded == MaxWaitStates)
3293 return WaitStatesNeeded; // Early exit.
3294 }
3295
3296 if (Opc == AMDGPU::V_ACCVGPR_WRITE_B32_e64) {
3297 const int MFMA4x4ReadSrcCAccVgprWriteWaitStates = 0;
3298 const int MFMA16x16ReadSrcCAccVgprWriteWaitStates = 5;
3299 const int MFMA32x32ReadSrcCAccVgprWriteWaitStates = 13;
3300 const int MaxWaitStates = 13;
3301 Register DstReg = MI->getOperand(0).getReg();
3302 unsigned HazardDefLatency = 0;
3303
3304 auto IsSrcCMFMAFn = [DstReg, &HazardDefLatency,
3305 this](const MachineInstr &MI) {
3306 if (!SIInstrInfo::isMFMA(MI))
3307 return false;
3308 Register Reg = TII.getNamedOperand(MI, AMDGPU::OpName::src2)->getReg();
3309 HazardDefLatency =
3310 std::max(HazardDefLatency, TSchedModel.computeInstrLatency(&MI));
3311 return TRI.regsOverlap(Reg, DstReg);
3312 };
3313
3314 int WaitStatesSince = getWaitStatesSince(IsSrcCMFMAFn, MaxWaitStates);
3315 int NeedWaitStates;
3316 switch (HazardDefLatency) {
3317 case 2: NeedWaitStates = MFMA4x4ReadSrcCAccVgprWriteWaitStates;
3318 break;
3319 case 8: NeedWaitStates = MFMA16x16ReadSrcCAccVgprWriteWaitStates;
3320 break;
3321 case 16: [[fallthrough]];
3322 default: NeedWaitStates = MFMA32x32ReadSrcCAccVgprWriteWaitStates;
3323 break;
3324 }
3325
3326 int WaitStatesNeededForUse = NeedWaitStates - WaitStatesSince;
3327 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3328 }
3329
3330 // Pad neighboring MFMA with noops for better inter-wave performance.
3331 WaitStatesNeeded = std::max(WaitStatesNeeded, checkMFMAPadding(MI));
3332
3333 return WaitStatesNeeded;
3334}
3335
3336/// One MFMA can be written with up to four opcodes that differ only in how vdst
3337/// and src2 are encoded: both are either AGPRs or VGPRs, and the mac form ties
3338/// vdst to src2 instead of taking them as separate operands. \returns the AGPR,
3339/// non-mac opcode, so that every form of the same MFMA maps to one value.
3340static unsigned getMFMANonMacAGPRFormOp(unsigned Opc) {
3341 if (int NonMacOp = AMDGPU::getMFMAEarlyClobberOp(Opc); NonMacOp != -1)
3342 Opc = NonMacOp;
3343 if (int AGPROp = AMDGPU::getAGPRFormOp(Opc); AGPROp != -1)
3344 Opc = AGPROp;
3345 return Opc;
3346}
3347
3348/// \returns true if \p Opc0 and \p Opc1 are the same MFMA, ignoring the mac
3349/// form and whether vdst/src2 are AGPRs or VGPRs.
3350static bool isSameMFMA(unsigned Opc0, unsigned Opc1) {
3352}
3353
3354static int
3356 bool IsGFX950) {
3357 // xdl def cycles | gfx940 | gfx950
3358 // 2 pass | 3 4
3359 // 4 pass | 5 6
3360 // 8 pass | 9 10
3361 // 16 pass | 17 18
3362 return NumPasses + 1 + IsGFX950;
3363}
3364
3365static int
3367 bool IsGFX950) {
3368 // xdl def cycles | gfx940 | gfx950
3369 // 2 pass | 3 3
3370 // 4 pass | 5 6
3371 // 8 pass | 9 10
3372 // 16 pass | 17 18
3373 return NumPasses + 1 + (NumPasses != 2 && IsGFX950);
3374}
3375
3376static int
3378 // 2 pass -> 2
3379 // 4 pass -> 4
3380 // 8 pass -> 8
3381 // 16 pass -> 16
3382 return NumPasses;
3383}
3384
3385static int
3387 // 2 pass -> 4
3388 // 4 pass -> 6
3389 // 8 pass -> 10
3390 // 16 pass -> 18
3391 return NumPasses + 2;
3392}
3393
3395 bool IsGFX950) {
3396 // xdl def cycles | gfx942 | gfx950
3397 // 2 pass | 5 5
3398 // 4 pass | 7 8
3399 // 8 pass | 11 12
3400 // 16 pass | 19 20
3401 return NumPasses + 3 + (NumPasses != 2 && IsGFX950);
3402}
3403
3404int GCNHazardRecognizer::getMFMAOverlappedSrcCWaitStates(
3405 const MachineInstr *Reader, const MachineInstr *Writer) const {
3406 constexpr int SMFMA4x4WritesVGPROverlappedSMFMASrcCWaitStates = 2;
3407 constexpr int SMFMA16x16WritesVGPROverlappedSMFMASrcCWaitStates = 8;
3408 constexpr int SMFMA32x32WritesVGPROverlappedSMFMASrcCWaitStates = 16;
3409 constexpr int SMFMA4x4WritesVGPROverlappedDMFMASrcCWaitStates = 3;
3410 constexpr int SMFMA16x16WritesVGPROverlappedDMFMASrcCWaitStates = 9;
3411 constexpr int SMFMA32x32WritesVGPROverlappedDMFMASrcCWaitStates = 17;
3412 constexpr int DMFMA16x16WritesVGPROverlappedSrcCWaitStates = 9;
3413 constexpr int GFX950_DMFMA16x16WritesVGPROverlappedSrcCWaitStates = 17;
3414 constexpr int DMFMA4x4WritesVGPROverlappedSrcCWaitStates = 4;
3415
3416 // An XDL read of a non-XDL result needs no wait states. DGEMM is never XDL,
3417 // so this also covers the f64 writers handled below.
3418 if (TII.isXDL(*Reader) && !TII.isXDL(*Writer))
3419 return 0;
3420
3421 switch (Writer->getOpcode()) {
3422 case AMDGPU::V_MFMA_F64_16X16X4F64_e64:
3423 case AMDGPU::V_MFMA_F64_16X16X4F64_vgprcd_e64:
3424 case AMDGPU::V_MFMA_F64_16X16X4F64_mac_e64:
3425 case AMDGPU::V_MFMA_F64_16X16X4F64_mac_vgprcd_e64:
3426 return ST.hasGFX950Insts()
3427 ? GFX950_DMFMA16x16WritesVGPROverlappedSrcCWaitStates
3428 : DMFMA16x16WritesVGPROverlappedSrcCWaitStates;
3429 case AMDGPU::V_MFMA_F64_4X4X4F64_e64:
3430 case AMDGPU::V_MFMA_F64_4X4X4F64_vgprcd_e64:
3431 return DMFMA4x4WritesVGPROverlappedSrcCWaitStates;
3432 default:
3433 break;
3434 }
3435
3436 int NumPasses = TSchedModel.computeInstrLatency(Writer);
3437 if (ST.hasGFX940Insts()) {
3438 if (!TII.isXDL(*Writer))
3440 NumPasses);
3441 return TII.isXDL(*Reader)
3443 NumPasses, ST.hasGFX950Insts())
3445 NumPasses, ST.hasGFX950Insts());
3446 }
3447
3448 bool IsDGEMM = SIInstrInfo::isDGEMM(Reader->getOpcode());
3449 switch (NumPasses) {
3450 case 2:
3451 return IsDGEMM ? SMFMA4x4WritesVGPROverlappedDMFMASrcCWaitStates
3452 : SMFMA4x4WritesVGPROverlappedSMFMASrcCWaitStates;
3453 case 8:
3454 return IsDGEMM ? SMFMA16x16WritesVGPROverlappedDMFMASrcCWaitStates
3455 : SMFMA16x16WritesVGPROverlappedSMFMASrcCWaitStates;
3456 case 16:
3457 return IsDGEMM ? SMFMA32x32WritesVGPROverlappedDMFMASrcCWaitStates
3458 : SMFMA32x32WritesVGPROverlappedSMFMASrcCWaitStates;
3459 default:
3460 llvm_unreachable("unexpected number of passes");
3461 }
3462}
3463
3464int GCNHazardRecognizer::getMFMAReadWaitStates(const MachineInstr &Consumer,
3465 const MachineInstr &Producer,
3466 Register Reg,
3467 bool IsSrcC) const {
3468 unsigned Opc = Consumer.getOpcode();
3469 const int SMFMA4x4WritesVGPROverlappedSrcABWaitStates = 5;
3470 const int SMFMA16x16WritesVGPROverlappedSrcABWaitStates = 11;
3471 const int SMFMA32x32WritesVGPROverlappedSrcABWaitStates = 19;
3472 const int DMFMA4x4WritesVGPROverlappedMFMASrcABWaitStates = 6;
3473 const int DMFMA16x16WritesVGPROverlappedMFMASrcABWaitStates = 11;
3474 const int GFX950_DMFMA16x16WritesVGPROverlappedMFMASrcABWaitStates = 19;
3475 const int DMFMA4x4WritesVGPRFullSrcCWaitStates = 4;
3476 const int GFX940_SMFMA4x4WritesVGPRFullSrcCWaitStates = 2;
3477
3478 bool FullReg = Producer.getOperand(0).getReg() == Reg;
3479 unsigned Opc1 = Producer.getOpcode();
3480 int NeedWaitStates = 0;
3481 if (IsSrcC) {
3482 if (!SIInstrInfo::isDGEMM(Opc) &&
3483 (!ST.hasGFX940Insts() && SIInstrInfo::isDGEMM(Opc1))) {
3484 NeedWaitStates = 0;
3485 } else if (FullReg) {
3486 if ((Opc == AMDGPU::V_MFMA_F64_4X4X4F64_e64 ||
3487 Opc == AMDGPU::V_MFMA_F64_4X4X4F64_vgprcd_e64) &&
3488 (Opc1 == AMDGPU::V_MFMA_F64_4X4X4F64_e64 ||
3489 Opc1 == AMDGPU::V_MFMA_F64_4X4X4F64_vgprcd_e64))
3490 NeedWaitStates = DMFMA4x4WritesVGPRFullSrcCWaitStates;
3491 else if (ST.hasGFX940Insts() &&
3492 TSchedModel.computeInstrLatency(&Producer) == 2)
3493 NeedWaitStates = GFX940_SMFMA4x4WritesVGPRFullSrcCWaitStates;
3494
3495 // The accumulator forwarding path that allows zero wait states is only
3496 // available while the chain stays on a single MFMA. Two different MFMAs
3497 // sharing an accumulator need the wait states of a partial overlap.
3498 if (ST.hasGFX940Insts() && !isSameMFMA(Opc, Opc1)) {
3499 NeedWaitStates =
3500 std::max(NeedWaitStates,
3501 getMFMAOverlappedSrcCWaitStates(&Consumer, &Producer));
3502 }
3503 } else {
3504 NeedWaitStates = getMFMAOverlappedSrcCWaitStates(&Consumer, &Producer);
3505 }
3506 } else {
3507 switch (Opc1) {
3508 case AMDGPU::V_MFMA_F64_16X16X4F64_e64:
3509 case AMDGPU::V_MFMA_F64_16X16X4F64_vgprcd_e64:
3510 case AMDGPU::V_MFMA_F64_16X16X4F64_mac_e64:
3511 case AMDGPU::V_MFMA_F64_16X16X4F64_mac_vgprcd_e64:
3512 NeedWaitStates =
3513 ST.hasGFX950Insts()
3514 ? GFX950_DMFMA16x16WritesVGPROverlappedMFMASrcABWaitStates
3515 : DMFMA16x16WritesVGPROverlappedMFMASrcABWaitStates;
3516 break;
3517 case AMDGPU::V_MFMA_F64_4X4X4F64_e64:
3518 case AMDGPU::V_MFMA_F64_4X4X4F64_vgprcd_e64:
3519 NeedWaitStates = DMFMA4x4WritesVGPROverlappedMFMASrcABWaitStates;
3520 break;
3521 default:
3522 int NumPasses = TSchedModel.computeInstrLatency(&Producer);
3523
3524 if (ST.hasGFX940Insts()) {
3525 NeedWaitStates =
3526 TII.isXDL(Producer)
3528 NumPasses, ST.hasGFX950Insts())
3530 NumPasses);
3531 break;
3532 }
3533
3534 switch (NumPasses) {
3535 case 2:
3536 NeedWaitStates = SMFMA4x4WritesVGPROverlappedSrcABWaitStates;
3537 break;
3538 case 4:
3539 llvm_unreachable("unexpected number of passes for mfma");
3540 case 8:
3541 NeedWaitStates = SMFMA16x16WritesVGPROverlappedSrcABWaitStates;
3542 break;
3543 case 16:
3544 default:
3545 NeedWaitStates = SMFMA32x32WritesVGPROverlappedSrcABWaitStates;
3546 }
3547 }
3548 }
3549 return NeedWaitStates;
3550}
3551
3552int GCNHazardRecognizer::checkMAIHazards90A(MachineInstr *MI) const {
3553 int WaitStatesNeeded = 0;
3554 unsigned Opc = MI->getOpcode();
3555
3556 auto IsLegacyVALUFn = [](const MachineInstr &MI) {
3557 return SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) &&
3559 };
3560
3561 auto IsLegacyVALUNotDotFn = [](const MachineInstr &MI) {
3562 return SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) &&
3564 };
3565
3566 if (!SIInstrInfo::isMFMA(*MI))
3567 return WaitStatesNeeded;
3568
3569 const int VALUWritesExecWaitStates = 4;
3570 int WaitStatesNeededForUse = VALUWritesExecWaitStates -
3571 getWaitStatesSinceDef(AMDGPU::EXEC, IsLegacyVALUFn,
3572 VALUWritesExecWaitStates);
3573 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3574
3575 int SrcCIdx = AMDGPU::getNamedOperandIdx(Opc, AMDGPU::OpName::src2);
3576
3577 // Loop for both DGEMM and S/HGEMM 2nd instruction.
3578 for (const MachineOperand &Use : MI->explicit_uses()) {
3579 const int LegacyVALUNotDotWritesVGPRWaitStates = 2;
3580 const int MaxWaitStates =
3582 16, ST.hasGFX950Insts());
3583
3584 if (!Use.isReg())
3585 continue;
3586 Register Reg = Use.getReg();
3587
3588 WaitStatesNeededForUse = LegacyVALUNotDotWritesVGPRWaitStates -
3589 getWaitStatesSinceDef(Reg, IsLegacyVALUNotDotFn, MaxWaitStates);
3590 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3591
3592 int OpNo = Use.getOperandNo();
3593 WaitStatesNeeded = std::max(
3594 WaitStatesNeeded,
3595 getMaxWindowDeficit(
3596 MaxWaitStates, [&](const MachineInstr &P) -> std::optional<int> {
3597 if (!SIInstrInfo::isMFMA(P) ||
3598 !TRI.regsOverlap(P.getOperand(0).getReg(), Reg))
3599 return std::nullopt;
3600 return getMFMAReadWaitStates(*MI, P, Reg, OpNo == SrcCIdx);
3601 }));
3602
3603 if (WaitStatesNeeded == MaxWaitStates)
3604 break;
3605 }
3606
3607 // Pad neighboring MFMA with noops for better inter-wave performance.
3608 WaitStatesNeeded = std::max(WaitStatesNeeded, checkMFMAPadding(MI));
3609
3610 return WaitStatesNeeded;
3611}
3612
3613int GCNHazardRecognizer::checkMAILdStHazards(MachineInstr *MI) const {
3614 // On gfx90a+ relevant hazards are checked in checkMAIVALUHazards()
3615 if (!ST.hasMAIInsts() || ST.hasGFX90AInsts())
3616 return 0;
3617
3618 int WaitStatesNeeded = 0;
3619
3620 auto IsAccVgprReadFn = [](const MachineInstr &MI) {
3621 return MI.getOpcode() == AMDGPU::V_ACCVGPR_READ_B32_e64;
3622 };
3623
3624 for (const MachineOperand &Op : MI->explicit_uses()) {
3625 if (!Op.isReg() || !TRI.isVGPR(MF.getRegInfo(), Op.getReg()))
3626 continue;
3627
3628 Register Reg = Op.getReg();
3629
3630 const int AccVgprReadLdStWaitStates = 2;
3631 const int VALUWriteAccVgprRdWrLdStDepVALUWaitStates = 1;
3632 const int MaxWaitStates = 2;
3633
3634 int WaitStatesNeededForUse = AccVgprReadLdStWaitStates -
3635 getWaitStatesSinceDef(Reg, IsAccVgprReadFn, MaxWaitStates);
3636 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3637
3638 if (WaitStatesNeeded == MaxWaitStates)
3639 return WaitStatesNeeded; // Early exit.
3640
3641 auto IsVALUAccVgprRdWrCheckFn = [Reg, this](const MachineInstr &MI) {
3642 if (MI.getOpcode() != AMDGPU::V_ACCVGPR_READ_B32_e64 &&
3643 MI.getOpcode() != AMDGPU::V_ACCVGPR_WRITE_B32_e64)
3644 return false;
3645 auto IsVALUFn = [](const MachineInstr &MI) {
3646 return SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true) &&
3648 };
3649 return getWaitStatesSinceDef(Reg, IsVALUFn, 2 /*MaxWaitStates*/) <
3650 std::numeric_limits<int>::max();
3651 };
3652
3653 WaitStatesNeededForUse = VALUWriteAccVgprRdWrLdStDepVALUWaitStates -
3654 getWaitStatesSince(IsVALUAccVgprRdWrCheckFn, MaxWaitStates);
3655 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3656 }
3657
3658 return WaitStatesNeeded;
3659}
3660
3661int GCNHazardRecognizer::checkPermlaneHazards(MachineInstr *MI) const {
3662 assert(!ST.hasVcmpxPermlaneHazard() &&
3663 "this is a different vcmpx+permlane hazard");
3664 const SIRegisterInfo *TRI = ST.getRegisterInfo();
3665 const SIInstrInfo *TII = ST.getInstrInfo();
3666
3667 auto IsVCmpXWritesExecFn = [TII, TRI](const MachineInstr &MI) {
3668 return isVCmpXWritesExec(*TII, *TRI, MI);
3669 };
3670
3671 auto IsVALUFn = [](const MachineInstr &MI) {
3672 return SIInstrInfo::isVALU(MI, /*AllowLDSDMA=*/true);
3673 };
3674
3675 const int VCmpXWritesExecWaitStates = 4;
3676 const int VALUWritesVDstWaitStates = 2;
3677 int WaitStatesNeeded = 0;
3678
3679 for (const MachineOperand &Op : MI->explicit_uses()) {
3680 if (!Op.isReg() || !TRI->isVGPR(MF.getRegInfo(), Op.getReg()))
3681 continue;
3682 Register Reg = Op.getReg();
3683
3684 int WaitStatesSinceDef =
3685 VALUWritesVDstWaitStates -
3686 getWaitStatesSinceDef(Reg, IsVALUFn,
3687 /*MaxWaitStates=*/VALUWritesVDstWaitStates);
3688 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesSinceDef);
3689 if (WaitStatesNeeded >= VALUWritesVDstWaitStates)
3690 break;
3691 }
3692
3693 int VCmpXHazardWaits =
3694 VCmpXWritesExecWaitStates -
3695 getWaitStatesSince(IsVCmpXWritesExecFn, VCmpXWritesExecWaitStates);
3696
3697 WaitStatesNeeded = std::max(WaitStatesNeeded, VCmpXHazardWaits);
3698 return WaitStatesNeeded;
3699}
3700
3702 // 2 pass -> 4
3703 // 4 pass -> 6
3704 // 8 pass -> 10
3705 // 16 pass -> 18
3706 return NumPasses + 2;
3707}
3708
3710 bool IsGFX950) {
3711 // xdl def cycles | gfx942 | gfx950
3712 // 2 pass | 5 5
3713 // 4 pass | 7 8
3714 // 8 pass | 11 12
3715 // 16 pass | 19 20
3716 return NumPasses + 3 + (NumPasses != 2 && IsGFX950);
3717}
3718
3720 bool IsGFX950) {
3721 // xdl def cycles | gfx942 | gfx950
3722 // 2 pass | 5 5
3723 // 4 pass | 7 8
3724 // 8 pass | 11 12
3725 // 16 pass | 19 20
3726 return NumPasses + 3 + (NumPasses != 2 && IsGFX950);
3727}
3728
3730 // 2 pass -> 4
3731 // 4 pass -> 6
3732 // 8 pass -> 10
3733 // 16 pass -> 18
3734 return NumPasses + 2;
3735}
3736
3737int GCNHazardRecognizer::checkMAIVALUHazards(MachineInstr *MI) const {
3738 if (!ST.hasGFX90AInsts())
3739 return 0;
3740
3741 auto IsDGEMMFn = [](const MachineInstr &MI) -> bool {
3742 return SIInstrInfo::isDGEMM(MI.getOpcode());
3743 };
3744
3745 // This is checked in checkMAIHazards90A()
3746 if (SIInstrInfo::isMFMA(*MI))
3747 return 0;
3748
3749 const MachineRegisterInfo &MRI = MF.getRegInfo();
3750
3751 int WaitStatesNeeded = 0;
3752
3753 bool IsMem = SIInstrInfo::isVMEM(*MI) || SIInstrInfo::isDS(*MI);
3754 bool IsMemOrExport = IsMem || SIInstrInfo::isEXP(*MI);
3755 bool IsVALU = SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true);
3756
3757 unsigned Reg;
3758 auto IsMFMAWriteFn = [&Reg, this](const MachineInstr &MI) {
3759 if (!SIInstrInfo::isMFMA(MI) ||
3760 !TRI.regsOverlap(MI.getOperand(0).getReg(), Reg))
3761 return false;
3762 return true;
3763 };
3764
3765 const MachineInstr *DOT = nullptr;
3766 auto IsDotWriteFn = [&Reg, &DOT, this](const MachineInstr &MI) {
3767 if (!SIInstrInfo::isDOT(MI) ||
3768 !TRI.regsOverlap(MI.getOperand(0).getReg(), Reg))
3769 return false;
3770 DOT = &MI;
3771 return true;
3772 };
3773
3774 bool DGEMMAfterVALUWrite = false;
3775 auto IsDGEMMHazard = [&DGEMMAfterVALUWrite, this](const MachineInstr &MI) {
3776 // Found DGEMM on reverse traversal to def.
3777 if (SIInstrInfo::isDGEMM(MI.getOpcode()))
3778 DGEMMAfterVALUWrite = true;
3779
3780 // Only hazard if register is defined by a VALU and a DGEMM is found after
3781 // after the def.
3782 if (!TII.isVALU(MI, /*AllowLDSDMA=*/true) || !DGEMMAfterVALUWrite)
3783 return false;
3784
3785 return true;
3786 };
3787
3788 int SrcCIdx = AMDGPU::getNamedOperandIdx(MI->getOpcode(),
3789 AMDGPU::OpName::src2);
3790
3791 if (IsMemOrExport || IsVALU) {
3792 const int SMFMA4x4WriteVgprVALUMemExpReadWaitStates = 5;
3793 const int SMFMA16x16WriteVgprVALUMemExpReadWaitStates = 11;
3794 const int SMFMA32x32WriteVgprVALUMemExpReadWaitStates = 19;
3795 const int DMFMA4x4WriteVgprMemExpReadWaitStates = 9;
3796 const int DMFMA16x16WriteVgprMemExpReadWaitStates = 18;
3797 const int DMFMA4x4WriteVgprVALUReadWaitStates = 6;
3798 const int DMFMA16x16WriteVgprVALUReadWaitStates = 11;
3799 const int GFX950_DMFMA16x16WriteVgprVALUReadWaitStates = 19;
3800 const int DotWriteSameDotReadSrcAB = 3;
3801 const int DotWriteDifferentVALURead = 3;
3802 const int DMFMABetweenVALUWriteVMEMRead = 2;
3803 const int MaxWaitStates =
3805 ST.hasGFX950Insts());
3806
3807 for (const MachineOperand &Use : MI->explicit_uses()) {
3808 if (!Use.isReg())
3809 continue;
3810 Reg = Use.getReg();
3811
3812 DOT = nullptr;
3813 int WaitStatesSinceDef = getWaitStatesSinceDef(Reg, IsDotWriteFn,
3814 MaxWaitStates);
3815 if (DOT) {
3816 int NeedWaitStates = 0;
3817 if (DOT->getOpcode() == MI->getOpcode()) {
3818 if (&Use - &MI->getOperand(0) != SrcCIdx)
3819 NeedWaitStates = DotWriteSameDotReadSrcAB;
3820 } else {
3821 NeedWaitStates = DotWriteDifferentVALURead;
3822 }
3823
3824 int WaitStatesNeededForUse = NeedWaitStates - WaitStatesSinceDef;
3825 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3826 }
3827
3828 // Workaround for HW data hazard bug observed only in GFX90A. When there
3829 // is a DGEMM instruction in-between a VALU and a VMEM instruction it
3830 // causes the SQ to incorrectly not insert two wait states between the two
3831 // instructions needed to avoid data hazard.
3832 if (IsMem && ST.hasGFX90AInsts() && !ST.hasGFX940Insts()) {
3833 DGEMMAfterVALUWrite = false;
3834 if (TRI.isVectorRegister(MRI, Reg)) {
3835 int WaitStatesNeededForUse =
3836 DMFMABetweenVALUWriteVMEMRead -
3837 getWaitStatesSinceDef(Reg, IsDGEMMHazard,
3838 DMFMABetweenVALUWriteVMEMRead);
3839
3840 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3841 }
3842 }
3843
3844 auto MFMAWindow = [&](const MachineInstr &Producer) {
3845 int NumPasses = TSchedModel.computeInstrLatency(&Producer);
3846 int NeedWaitStates = MaxWaitStates;
3847
3848 if (SIInstrInfo::isDGEMM(Producer.getOpcode())) {
3849 switch (NumPasses) {
3850 case 4:
3851 NeedWaitStates = IsMemOrExport
3852 ? DMFMA4x4WriteVgprMemExpReadWaitStates
3853 : DMFMA4x4WriteVgprVALUReadWaitStates;
3854 break;
3855 case 8:
3856 case 16:
3857 NeedWaitStates =
3858 IsMemOrExport
3859 ? DMFMA16x16WriteVgprMemExpReadWaitStates
3860 : (ST.hasGFX950Insts()
3861 ? GFX950_DMFMA16x16WriteVgprVALUReadWaitStates
3862 : DMFMA16x16WriteVgprVALUReadWaitStates);
3863 break;
3864 default:
3865 llvm_unreachable("unexpected dgemm");
3866 }
3867 } else if (ST.hasGFX940Insts()) {
3868 NeedWaitStates =
3869 TII.isXDL(Producer)
3871 NumPasses, ST.hasGFX950Insts())
3873 NumPasses);
3874 } else {
3875 switch (NumPasses) {
3876 case 2:
3877 NeedWaitStates = SMFMA4x4WriteVgprVALUMemExpReadWaitStates;
3878 break;
3879 case 8:
3880 NeedWaitStates = SMFMA16x16WriteVgprVALUMemExpReadWaitStates;
3881 break;
3882 case 16:
3883 NeedWaitStates = SMFMA32x32WriteVgprVALUMemExpReadWaitStates;
3884 break;
3885 default:
3886 llvm_unreachable("unexpected number of passes for mfma");
3887 }
3888 }
3889
3890 assert(NeedWaitStates <= MaxWaitStates &&
3891 "hazard requirement exceeds the scan window");
3892 return NeedWaitStates;
3893 };
3894
3895 WaitStatesNeeded = std::max(
3896 WaitStatesNeeded,
3897 getMaxWindowDeficit(MaxWaitStates,
3898 [&](const MachineInstr &P) -> std::optional<int> {
3899 if (!IsMFMAWriteFn(P))
3900 return std::nullopt;
3901 return MFMAWindow(P);
3902 }));
3903
3904 if (WaitStatesNeeded >= MaxWaitStates)
3905 break;
3906 }
3907 }
3908
3909 unsigned Opc = MI->getOpcode();
3910 const int DMFMAToFMA64WaitStates = 2;
3911 if ((Opc == AMDGPU::V_FMA_F64_e64 ||
3912 Opc == AMDGPU::V_FMAC_F64_e32 || Opc == AMDGPU::V_FMAC_F64_e64 ||
3913 Opc == AMDGPU::V_FMAC_F64_dpp) &&
3914 WaitStatesNeeded < DMFMAToFMA64WaitStates) {
3915 int WaitStatesNeededForUse = DMFMAToFMA64WaitStates -
3916 getWaitStatesSince(IsDGEMMFn, DMFMAToFMA64WaitStates);
3917 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
3918 }
3919
3920 if (!IsVALU && !IsMemOrExport)
3921 return WaitStatesNeeded;
3922
3923 for (const MachineOperand &Def : MI->defs()) {
3924 const int SMFMA4x4WriteVgprVALUWawWaitStates = 5;
3925 const int SMFMA16x16WriteVgprVALUWawWaitStates = 11;
3926 const int SMFMA32x32WriteVgprVALUWawWaitStates = 19;
3927 const int SMFMA4x4ReadVgprVALUWarWaitStates = 1;
3928 const int GFX940_XDL4PassReadVgprVALUWarWaitStates = 3;
3929 const int SMFMA16x16ReadVgprVALUWarWaitStates = 7;
3930 const int SMFMA32x32ReadVgprVALUWarWaitStates = 15;
3931 const int DMFMA4x4WriteVgprVALUWriteWaitStates = 6;
3932 const int DMFMA16x16WriteVgprVALUWriteWaitStates = 11;
3933 const int DotWriteDifferentVALUWrite = 3;
3934 const int MaxWaitStates =
3935 GFX940_XDL_N_PassWriteVgprVALUWawWaitStates(16, ST.hasGFX950Insts());
3936 const int MaxWarWaitStates = 15;
3937
3938 Reg = Def.getReg();
3939
3940 DOT = nullptr;
3941 int WaitStatesSinceDef = getWaitStatesSinceDef(Reg, IsDotWriteFn,
3942 MaxWaitStates);
3943 if (DOT && DOT->getOpcode() != MI->getOpcode())
3944 WaitStatesNeeded = std::max(WaitStatesNeeded, DotWriteDifferentVALUWrite -
3945 WaitStatesSinceDef);
3946
3947 auto MFMAWawWindow = [&](const MachineInstr &Producer) {
3948 int NeedWaitStates = MaxWaitStates;
3949 int NumPasses = TSchedModel.computeInstrLatency(&Producer);
3950
3951 if (SIInstrInfo::isDGEMM(Producer.getOpcode())) {
3952 switch (NumPasses) {
3953 case 4:
3954 NeedWaitStates = DMFMA4x4WriteVgprVALUWriteWaitStates;
3955 break;
3956 case 8:
3957 case 16:
3958 NeedWaitStates = DMFMA16x16WriteVgprVALUWriteWaitStates;
3959 break;
3960 default:
3961 llvm_unreachable("unexpected number of cycles for dgemm");
3962 }
3963 } else if (ST.hasGFX940Insts()) {
3964 NeedWaitStates =
3965 TII.isXDL(Producer)
3967 NumPasses, ST.hasGFX950Insts())
3969 } else {
3970 switch (NumPasses) {
3971 case 2:
3972 NeedWaitStates = SMFMA4x4WriteVgprVALUWawWaitStates;
3973 break;
3974 case 8:
3975 NeedWaitStates = SMFMA16x16WriteVgprVALUWawWaitStates;
3976 break;
3977 case 16:
3978 NeedWaitStates = SMFMA32x32WriteVgprVALUWawWaitStates;
3979 break;
3980 default:
3981 llvm_unreachable("Unexpected number of passes for mfma");
3982 }
3983 }
3984
3985 assert(NeedWaitStates <= MaxWaitStates &&
3986 "hazard requirement exceeds the scan window");
3987 return NeedWaitStates;
3988 };
3989
3990 WaitStatesNeeded = std::max(
3991 WaitStatesNeeded,
3992 getMaxWindowDeficit(MaxWaitStates,
3993 [&](const MachineInstr &P) -> std::optional<int> {
3994 if (!IsMFMAWriteFn(P))
3995 return std::nullopt;
3996 return MFMAWawWindow(P);
3997 }));
3998
3999 if (WaitStatesNeeded >= MaxWaitStates)
4000 break;
4001
4002 // Wait states this write needs after an MFMA that read Reg as srcC.
4003 auto SMFMAReadAsCWindow =
4004 [&](const MachineInstr &Reader) -> std::optional<int> {
4005 if (!SIInstrInfo::isMFMA(Reader) ||
4006 SIInstrInfo::isDGEMM(Reader.getOpcode()) ||
4007 !Reader.readsRegister(Reg, &TRI))
4008 return std::nullopt;
4009
4010 if (ST.hasGFX940Insts() && !TII.isXDL(Reader))
4011 return std::nullopt;
4012
4013 const MachineOperand *SrcC =
4014 TII.getNamedOperand(Reader, AMDGPU::OpName::src2);
4015 assert(SrcC && "an MFMA must have a src2 operand");
4016 if (!SrcC->isReg() || !TRI.regsOverlap(SrcC->getReg(), Reg))
4017 return std::nullopt;
4018
4019 switch (TSchedModel.computeInstrLatency(&Reader)) {
4020 case 2:
4021 return SMFMA4x4ReadVgprVALUWarWaitStates;
4022 case 4:
4023 assert(ST.hasGFX940Insts() && "4-pass XDL is gfx940 or later");
4024 return GFX940_XDL4PassReadVgprVALUWarWaitStates;
4025 case 8:
4026 return SMFMA16x16ReadVgprVALUWarWaitStates;
4027 case 16:
4028 [[fallthrough]];
4029 default:
4030 return SMFMA32x32ReadVgprVALUWarWaitStates;
4031 }
4032 };
4033
4034 WaitStatesNeeded =
4035 std::max(WaitStatesNeeded,
4036 getMaxWindowDeficit(MaxWarWaitStates, SMFMAReadAsCWindow));
4037 }
4038
4039 return WaitStatesNeeded;
4040}
4041
4043 if (!SU->isInstr())
4044 return false;
4045
4046 const MachineInstr *MAI = nullptr;
4047
4048 auto IsMFMAFn = [&MAI](const MachineInstr &MI) {
4049 MAI = nullptr;
4051 MAI = &MI;
4052 return MAI != nullptr;
4053 };
4054
4055 MachineInstr *MI = SU->getInstr();
4056 if (IsMFMAFn(*MI)) {
4057 int W = getWaitStatesSince(IsMFMAFn, 16);
4058 if (MAI)
4059 return W < (int)TSchedModel.computeInstrLatency(MAI);
4060 }
4061
4062 return false;
4063}
4064
4065// Adjust global offsets for instructions bundled with S_GETPC_B64 after
4066// insertion of a new instruction.
4067static void updateGetPCBundle(MachineInstr *NewMI) {
4068 if (!NewMI->isBundled())
4069 return;
4070
4071 // Find start of bundle.
4072 auto I = NewMI->getIterator();
4073 while (I->isBundledWithPred())
4074 I--;
4075 if (I->isBundle())
4076 I++;
4077
4078 // Bail if this is not an S_GETPC bundle.
4079 if (I->getOpcode() != AMDGPU::S_GETPC_B64)
4080 return;
4081
4082 // Update offsets of any references in the bundle.
4083 const unsigned NewBytes = 4;
4084 assert(NewMI->getOpcode() == AMDGPU::S_WAITCNT_DEPCTR &&
4085 "Unexpected instruction insertion in bundle");
4086 auto NextMI = std::next(NewMI->getIterator());
4087 auto End = NewMI->getParent()->end();
4088 while (NextMI != End && NextMI->isBundledWithPred()) {
4089 for (auto &Operand : NextMI->operands()) {
4090 if (Operand.isGlobal())
4091 Operand.setOffset(Operand.getOffset() + NewBytes);
4092 }
4093 NextMI++;
4094 }
4095}
4096
4097bool GCNHazardRecognizer::fixVALUMaskWriteHazard(MachineInstr *MI) {
4098 if (!ST.hasVALUMaskWriteHazard())
4099 return false;
4100 assert(!ST.hasExtendedWaitCounts());
4101
4102 if (!ST.isWave64())
4103 return false;
4104
4105 const bool IsSALU = SIInstrInfo::isSALU(*MI);
4106 const bool IsVALU = SIInstrInfo::isVALU(*MI, /*AllowLDSDMA=*/true);
4107 if (!IsSALU && !IsVALU)
4108 return false;
4109
4110 // The hazard sequence is three instructions:
4111 // 1. VALU reads SGPR as mask
4112 // 2. VALU/SALU writes SGPR
4113 // 3. VALU/SALU reads SGPR
4114 // The hazard can expire if the distance between 2 and 3 is sufficient,
4115 // or (2) is VALU and (3) is SALU.
4116 // In practice this happens <10% of the time, hence always assume the hazard
4117 // exists if (1) and (2) are present to avoid searching all SGPR reads.
4118
4119 const SIRegisterInfo *TRI = ST.getRegisterInfo();
4120 const MachineRegisterInfo &MRI = MF.getRegInfo();
4121
4122 auto IgnoreableSGPR = [](const Register Reg) {
4123 switch (Reg) {
4124 case AMDGPU::EXEC:
4125 case AMDGPU::EXEC_LO:
4126 case AMDGPU::EXEC_HI:
4127 case AMDGPU::M0:
4128 case AMDGPU::SGPR_NULL:
4129 case AMDGPU::SGPR_NULL64:
4130 case AMDGPU::SCC:
4131 return true;
4132 default:
4133 return false;
4134 }
4135 };
4136 auto IsVCC = [](const Register Reg) {
4137 return Reg == AMDGPU::VCC || Reg == AMDGPU::VCC_LO || Reg == AMDGPU::VCC_HI;
4138 };
4139
4140 struct StateType {
4141 SmallSet<Register, 2> HazardSGPRs;
4142
4143 static unsigned getHashValue(const StateType &State) {
4144 return static_cast<unsigned>(hash_combine_range(State.HazardSGPRs));
4145 }
4146 static bool isEqual(const StateType &LHS, const StateType &RHS) {
4147 return LHS.HazardSGPRs == RHS.HazardSGPRs;
4148 }
4149 };
4150
4151 StateType InitialState;
4152
4153 // Look for SGPR write.
4154 MachineOperand *HazardDef = nullptr;
4155 for (MachineOperand &Op : MI->all_defs()) {
4156 Register Reg = Op.getReg();
4157 if (IgnoreableSGPR(Reg))
4158 continue;
4159 if (!IsVCC(Reg)) {
4160 if (Op.isImplicit())
4161 continue;
4162 if (!TRI->isSGPRReg(MRI, Reg))
4163 continue;
4164 }
4165
4166 HazardDef = &Op;
4167 break;
4168 }
4169
4170 if (!HazardDef)
4171 return false;
4172
4173 // Setup to track writes to individual SGPRs
4174 const Register HazardReg = HazardDef->getReg();
4175 if (AMDGPU::SReg_32RegClass.contains(HazardReg)) {
4176 InitialState.HazardSGPRs.insert(HazardReg);
4177 } else {
4178 assert(AMDGPU::SReg_64RegClass.contains(HazardReg));
4179 InitialState.HazardSGPRs.insert(TRI->getSubReg(HazardReg, AMDGPU::sub0));
4180 InitialState.HazardSGPRs.insert(TRI->getSubReg(HazardReg, AMDGPU::sub1));
4181 }
4182
4183 auto IsHazardFn = [&](StateType &State, const MachineInstr &I) {
4184 if (State.HazardSGPRs.empty())
4185 return HazardExpired;
4186
4187 switch (I.getOpcode()) {
4188 case AMDGPU::V_ADDC_U32_e32:
4189 case AMDGPU::V_ADDC_U32_dpp:
4190 case AMDGPU::V_CNDMASK_B16_t16_e32:
4191 case AMDGPU::V_CNDMASK_B16_fake16_e32:
4192 case AMDGPU::V_CNDMASK_B16_t16_dpp:
4193 case AMDGPU::V_CNDMASK_B16_fake16_dpp:
4194 case AMDGPU::V_CNDMASK_B32_e32:
4195 case AMDGPU::V_CNDMASK_B32_dpp:
4196 case AMDGPU::V_DIV_FMAS_F32_e64:
4197 case AMDGPU::V_DIV_FMAS_F64_e64:
4198 case AMDGPU::V_SUBB_U32_e32:
4199 case AMDGPU::V_SUBB_U32_dpp:
4200 case AMDGPU::V_SUBBREV_U32_e32:
4201 case AMDGPU::V_SUBBREV_U32_dpp: {
4202 // These implicitly read VCC as mask source.
4203 return IsVCC(HazardReg) ? HazardFound : NoHazardFound;
4204 }
4205 case AMDGPU::V_ADDC_U32_e64:
4206 case AMDGPU::V_ADDC_U32_e64_dpp:
4207 case AMDGPU::V_CNDMASK_B16_t16_e64:
4208 case AMDGPU::V_CNDMASK_B16_fake16_e64:
4209 case AMDGPU::V_CNDMASK_B16_t16_e64_dpp:
4210 case AMDGPU::V_CNDMASK_B16_fake16_e64_dpp:
4211 case AMDGPU::V_CNDMASK_B32_e64:
4212 case AMDGPU::V_CNDMASK_B32_e64_dpp:
4213 case AMDGPU::V_SUBB_U32_e64:
4214 case AMDGPU::V_SUBB_U32_e64_dpp:
4215 case AMDGPU::V_SUBBREV_U32_e64:
4216 case AMDGPU::V_SUBBREV_U32_e64_dpp: {
4217 // Only check mask register overlaps.
4218 const MachineOperand *SSRCOp = TII.getNamedOperand(I, AMDGPU::OpName::src2);
4219 assert(SSRCOp);
4220 bool Result = TRI->regsOverlap(SSRCOp->getReg(), HazardReg);
4221 return Result ? HazardFound : NoHazardFound;
4222 }
4223 default:
4224 return NoHazardFound;
4225 }
4226 };
4227
4228 auto UpdateStateFn = [&](StateType &State, const MachineInstr &I) {
4229 // Update tracking of SGPR writes.
4230 for (auto &Op : I.all_defs()) {
4231 Register Reg = Op.getReg();
4232 if (IgnoreableSGPR(Reg))
4233 continue;
4234 if (!IsVCC(Reg)) {
4235 if (Op.isImplicit())
4236 continue;
4237 if (!TRI->isSGPRReg(MRI, Reg))
4238 continue;
4239 }
4240
4241 // Stop tracking any SGPRs with writes on the basis that they will
4242 // already have an appropriate wait inserted afterwards.
4244 for (Register SGPR : State.HazardSGPRs) {
4245 if (Reg == SGPR || TRI->regsOverlap(Reg, SGPR))
4246 Found.push_back(SGPR);
4247 }
4248 for (Register SGPR : Found)
4249 State.HazardSGPRs.erase(SGPR);
4250 }
4251 };
4252
4253 // Check for hazard
4254 if (!hasHazard<StateType>(InitialState, IsHazardFn, UpdateStateFn,
4255 MI->getParent(),
4256 std::next(MI->getReverseIterator())))
4257 return false;
4258
4259 // Compute counter mask
4260 unsigned DepCtr =
4261 IsVALU ? (IsVCC(HazardReg) ? AMDGPU::DepCtr::encodeFieldVaVcc(0, ST)
4262 : AMDGPU::DepCtr::encodeFieldVaSdst(0, ST))
4263 : AMDGPU::DepCtr::encodeFieldSaSdst(0, ST);
4264
4265 // Add s_waitcnt_depctr after SGPR write.
4266 auto NextMI = std::next(MI->getIterator());
4267 auto NewMI = BuildMI(*MI->getParent(), NextMI, MI->getDebugLoc(),
4268 TII.get(AMDGPU::S_WAITCNT_DEPCTR))
4269 .addImm(DepCtr);
4270
4271 // SALU write may be s_getpc in a bundle.
4272 updateGetPCBundle(NewMI);
4273
4274 return true;
4275}
4276
4277static bool ensureEntrySetPrio(MachineFunction *MF, int Priority,
4278 const SIInstrInfo &TII) {
4279 MachineBasicBlock &EntryMBB = MF->front();
4280 if (EntryMBB.begin() != EntryMBB.end()) {
4281 auto &EntryMI = *EntryMBB.begin();
4282 if (EntryMI.getOpcode() == AMDGPU::S_SETPRIO &&
4283 EntryMI.getOperand(0).getImm() >= Priority)
4284 return false;
4285 }
4286
4287 BuildMI(EntryMBB, EntryMBB.begin(), DebugLoc(), TII.get(AMDGPU::S_SETPRIO))
4288 .addImm(Priority);
4289 return true;
4290}
4291
4292bool GCNHazardRecognizer::fixRequiredExportPriority(MachineInstr *MI) {
4293 if (!ST.hasRequiredExportPriority())
4294 return false;
4295
4296 // Assume the following shader types will never have exports,
4297 // and avoid adding or adjusting S_SETPRIO.
4298 MachineBasicBlock *MBB = MI->getParent();
4299 MachineFunction *MF = MBB->getParent();
4300 auto CC = MF->getFunction().getCallingConv();
4301 switch (CC) {
4306 return false;
4307 default:
4308 break;
4309 }
4310
4311 const int MaxPriority = 3;
4312 const int NormalPriority = 2;
4313 const int PostExportPriority = 0;
4314
4315 auto It = MI->getIterator();
4316 switch (MI->getOpcode()) {
4317 case AMDGPU::S_ENDPGM:
4318 case AMDGPU::S_ENDPGM_SAVED:
4319 case AMDGPU::S_ENDPGM_ORDERED_PS_DONE:
4320 case AMDGPU::SI_RETURN_TO_EPILOG:
4321 // Ensure shader with calls raises priority at entry.
4322 // This ensures correct priority if exports exist in callee.
4323 if (MF->getFrameInfo().hasCalls())
4324 return ensureEntrySetPrio(MF, NormalPriority, TII);
4325 return false;
4326 case AMDGPU::S_SETPRIO: {
4327 // Raise minimum priority unless in workaround.
4328 auto &PrioOp = MI->getOperand(0);
4329 int Prio = static_cast<int>(PrioOp.getImm());
4330 bool InWA = (Prio == PostExportPriority) &&
4331 (It != MBB->begin() && TII.isEXP(*std::prev(It)));
4332 if (InWA || Prio >= NormalPriority)
4333 return false;
4334 PrioOp.setImm(std::min(Prio + NormalPriority, MaxPriority));
4335 return true;
4336 }
4337 default:
4338 if (!TII.isEXP(*MI))
4339 return false;
4340 break;
4341 }
4342
4343 // Check entry priority at each export (as there will only be a few).
4344 // Note: amdgpu_gfx can only be a callee, so defer to caller setprio.
4345 bool Changed = false;
4347 Changed = ensureEntrySetPrio(MF, NormalPriority, TII);
4348
4349 auto NextMI = std::next(It);
4350 bool EndOfShader = false;
4351 if (NextMI != MBB->end()) {
4352 // Only need WA at end of sequence of exports.
4353 if (TII.isEXP(*NextMI))
4354 return Changed;
4355 // Assume appropriate S_SETPRIO after export means WA already applied.
4356 if (NextMI->getOpcode() == AMDGPU::S_SETPRIO &&
4357 NextMI->getOperand(0).getImm() == PostExportPriority)
4358 return Changed;
4359 EndOfShader = NextMI->getOpcode() == AMDGPU::S_ENDPGM;
4360 }
4361
4362 const DebugLoc &DL = MI->getDebugLoc();
4363
4364 // Lower priority.
4365 BuildMI(*MBB, NextMI, DL, TII.get(AMDGPU::S_SETPRIO))
4366 .addImm(PostExportPriority);
4367
4368 if (!EndOfShader) {
4369 // Wait for exports to complete.
4370 BuildMI(*MBB, NextMI, DL, TII.get(AMDGPU::S_WAITCNT_EXPCNT))
4371 .addReg(AMDGPU::SGPR_NULL)
4372 .addImm(0);
4373 }
4374
4375 BuildMI(*MBB, NextMI, DL, TII.get(AMDGPU::S_NOP)).addImm(0);
4376 BuildMI(*MBB, NextMI, DL, TII.get(AMDGPU::S_NOP)).addImm(0);
4377
4378 if (!EndOfShader) {
4379 // Return to normal (higher) priority.
4380 BuildMI(*MBB, NextMI, DL, TII.get(AMDGPU::S_SETPRIO))
4381 .addImm(NormalPriority);
4382 }
4383
4384 return true;
4385}
4386
4387// Advance past meta instructions (debug values, labels, CFI, KILL, etc.) to the
4388// next instruction that actually issues. Unlike skipDebugInstructionsForward /
4389// next_nodbg, this skips the full isMetaInstruction() set.
4393 while (I != End && I->isMetaInstruction())
4394 ++I;
4395 return I;
4396}
4397
4398bool GCNHazardRecognizer::fixVPermPk16Hazard(MachineInstr *MI) {
4399 // Requirement #1 of 2:
4400 // The cross-wave entry-block mitigation is delegated to the mandatory
4401 // unclaused-VMEM entry prologue (GLOBAL_PREFETCH_B8 + V_NOP).
4402 assert(ST.hasRequiresInitialUnclausedVmem() &&
4403 "V_PERM_PK16-hazard subtarget must provide the unclaused-VMEM entry "
4404 "prologue to satisfy the cross-wave entry mitigation");
4405
4406 if (!SIInstrInfo::isVPermPk16(MI->getOpcode()))
4407 return false;
4408
4409 MachineBasicBlock *MBB = MI->getParent();
4410
4411 // Requirement #2 of 2:
4412 // V_PERM_PK16 must be immediately followed by a safe instruction.
4414 skipMetaInstructionsForward(std::next(MI->getIterator()), MBB->end());
4415 if (NextI != MBB->end() && TII.isVPermPk16SafeInstr(*NextI))
4416 return false;
4417
4418 // EXEC is guaranteed non-zero here: V_PERM_PK16 reports unwanted effects
4419 // when EXEC is empty, so s_cbranch_execz over this region is retained.
4420 // A plain V_NOP is therefore a real VALU nop and clears the hazard.
4421 BuildMI(*MBB, NextI, MI->getDebugLoc(), TII.get(AMDGPU::V_NOP_e32));
4422 return true;
4423}
4424
4425bool GCNHazardRecognizer::fixGetRegWaitIdle(MachineInstr *MI) {
4426 if (!isSGetReg(MI->getOpcode()))
4427 return false;
4428
4429 const SIInstrInfo *TII = ST.getInstrInfo();
4430 switch (getHWReg(TII, *MI)) {
4431 default:
4432 return false;
4437 break;
4438 }
4439
4440 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
4441 TII->get(AMDGPU::S_WAITCNT_DEPCTR))
4442 .addImm(0);
4443 return true;
4444}
4445
4446bool GCNHazardRecognizer::fixDsAtomicAsyncBarrierArriveB64(MachineInstr *MI) {
4447 if (MI->getOpcode() != AMDGPU::DS_ATOMIC_ASYNC_BARRIER_ARRIVE_B64)
4448 return false;
4449
4450 const SIInstrInfo *TII = ST.getInstrInfo();
4451 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
4452 TII->get(AMDGPU::S_WAITCNT_DEPCTR))
4454 BuildMI(*MI->getParent(), std::next(MI->getIterator()), MI->getDebugLoc(),
4455 TII->get(AMDGPU::S_WAITCNT_DEPCTR))
4457
4458 return true;
4459}
4460
4461bool GCNHazardRecognizer::fixScratchBaseForwardingHazard(MachineInstr *MI) {
4462 // No reason to check this in pre-RA scheduling, SGPRs have to be allocated
4463 // for hazard to trigger.
4465 return false;
4466
4467 const SIRegisterInfo *TRI = ST.getRegisterInfo();
4468 const SIInstrInfo *TII = ST.getInstrInfo();
4469 // Hazard expires after 10 SGPR writes by SALU or 8 SGPR writes by VALU.
4470 const int FlatScrBaseWaitStates = 10;
4471
4472 bool ReadsFlatScrLo =
4473 MI->readsRegister(AMDGPU::SRC_FLAT_SCRATCH_BASE_LO, TRI);
4474 bool ReadsFlatScrHi =
4475 MI->readsRegister(AMDGPU::SRC_FLAT_SCRATCH_BASE_HI, TRI);
4476 if (isSGetReg(MI->getOpcode())) {
4477 switch (getHWReg(TII, *MI)) {
4478 default:
4479 break;
4481 ReadsFlatScrLo = true;
4482 break;
4484 ReadsFlatScrHi = true;
4485 break;
4486 }
4487 }
4488
4489 const MachineRegisterInfo &MRI = MF.getRegInfo();
4490
4491 auto IsRegDefHazard = [&](Register Reg) -> bool {
4492 DenseSet<const MachineBasicBlock *> Visited;
4493 auto IsHazardFn = [TRI, Reg](const MachineInstr &MI) {
4494 return MI.modifiesRegister(Reg, TRI);
4495 };
4496
4497 // This literally abuses the idea of waitstates. Instead of waitstates it
4498 // returns 1 for SGPR written and 0 otherwise.
4499 auto IsSGPRDef = [TII, TRI, &MRI](const MachineInstr &MI) -> unsigned {
4500 if (!TII->isSALU(MI) && !TII->isVALU(MI, /*AllowLDSDMA=*/true))
4501 return 0;
4502 for (const MachineOperand &MO : MI.all_defs()) {
4503 if (TRI->isSGPRReg(MRI, MO.getReg()))
4504 return 1;
4505 }
4506 return 0;
4507 };
4508
4509 auto IsExpiredFn = [=](const MachineInstr &MI, int SgprWrites) {
4510 if (MI.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR) {
4511 unsigned Wait = static_cast<unsigned>(MI.getOperand(0).getImm());
4514 return true;
4515 }
4516 return SgprWrites >= FlatScrBaseWaitStates;
4517 };
4518
4519 return ::getWaitStatesSince(
4520 IsHazardFn, MI->getParent(), std::next(MI->getReverseIterator()),
4521 0, IsExpiredFn, Visited, IsSGPRDef) < FlatScrBaseWaitStates;
4522 };
4523
4524 if ((!ReadsFlatScrLo || MRI.isConstantPhysReg(AMDGPU::SGPR102) ||
4525 !IsRegDefHazard(AMDGPU::SGPR102)) &&
4526 (!ReadsFlatScrHi || MRI.isConstantPhysReg(AMDGPU::SGPR103) ||
4527 !IsRegDefHazard(AMDGPU::SGPR103)))
4528 return false;
4529
4530 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
4531 TII->get(AMDGPU::S_WAITCNT_DEPCTR))
4534 return true;
4535}
4536
4537bool GCNHazardRecognizer::fixSetRegMode(MachineInstr *MI) {
4538 if (!isSSetReg(MI->getOpcode()) ||
4539 MI->getOperand(1).getImm() != AMDGPU::Hwreg::ID_MODE)
4540 return false;
4541
4542 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII.get(AMDGPU::V_NOP_e32));
4543 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(), TII.get(AMDGPU::V_NOP_e32));
4544 return true;
4545}
4546
4547bool GCNHazardRecognizer::fixTDM(MachineInstr *MI) {
4548 auto IsTDM = [&](const MachineInstr &MI) -> bool {
4550 MI.getOpcode() != AMDGPU::S_WAIT_TENSORCNT;
4551 };
4552
4553 if (!IsTDM(*MI))
4554 return false;
4555
4556 auto IsExpiredFn = [](const MachineInstr &MI, int) {
4557 if (MI.getOpcode() != AMDGPU::S_WAIT_TENSORCNT)
4558 return false;
4559 return MI.getOperand(0).getImm() <= 10;
4560 };
4561
4562 if (::getWaitStatesSince(IsTDM, MI, IsExpiredFn) ==
4563 std::numeric_limits<int>::max())
4564 return false;
4565
4566 BuildMI(*MI->getParent(), MI, MI->getDebugLoc(),
4567 TII.get(AMDGPU::S_WAIT_TENSORCNT))
4568 .addImm(10);
4569 return true;
4570}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t Imm
The AMDGPU TargetMachine interface definition for hw codegen targets.
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static bool isEqual(const Function &Caller, const Function &Callee)
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static cl::opt< unsigned, false, MFMAPaddingRatioParser > MFMAPaddingRatio("amdgpu-mfma-padding-ratio", cl::init(0), cl::Hidden, cl::desc("Fill a percentage of the latency between " "neighboring MFMA with s_nops."))
static bool shouldRunLdsBranchVmemWARHazardFixup(const MachineFunction &MF, const GCNSubtarget &ST)
static cl::opt< bool > EnableWMMAVnopHoisting("amdgpu-wmma-vnop-hoisting", cl::init(true), cl::Hidden, cl::desc("Hoist WMMA hazard V_NOPs from loops to preheaders"))
static bool consumesDstSelForwardingOperand(const MachineInstr *VALU, const MachineOperand *Dst, const SIRegisterInfo *TRI)
Checks whether the provided MI "consumes" the operand with a Dest sel fowarding issue Dst .
static bool isSGetReg(unsigned Opcode)
static bool breaksSMEMSoftClause(MachineInstr *MI)
static bool isLdsDma(const MachineInstr &MI)
static int GFX940_XDL_N_PassWritesVGPROverlappedSrcABWaitStates(int NumPasses, bool IsGFX950)
static unsigned getWMMAHazardInstInCategory(const MachineInstr &MI, const SIInstrInfo *TII, const TargetSchedModel &SchedModel, const GCNSubtarget &ST)
static bool isRFE(unsigned Opcode)
static bool isRWLane(unsigned Opcode)
static bool isSMovRel(unsigned Opcode)
static unsigned getMFMANonMacAGPRFormOp(unsigned Opc)
One MFMA can be written with up to four opcodes that differ only in how vdst and src2 are encoded: bo...
#define DEBUG_TYPE_VERBOSE
static const MachineOperand * getDstSelForwardingOperand(const MachineInstr &MI, const GCNSubtarget &ST)
Dest sel forwarding issue occurs if additional logic is needed to swizzle / pack the computed value i...
static int GFX940_XDL_N_PassWritesVGPROverlappedSGEMMDGEMMSrcCWaitStates(int NumPasses, bool IsGFX950)
static void updateGetPCBundle(MachineInstr *NewMI)
static int GFX940_XDL_N_PassWriteVgprVALUMemExpReadWaitStates(int NumPasses, bool IsGFX950)
static bool isStoreCountWaitZero(const MachineInstr &I)
static bool breaksVMEMSoftClause(MachineInstr *MI)
static bool isVCmpXWritesExec(const SIInstrInfo &TII, const SIRegisterInfo &TRI, const MachineInstr &MI)
static bool isSSetReg(unsigned Opcode)
static void addRegUnits(const SIRegisterInfo &TRI, BitVector &BV, MCRegister Reg)
static unsigned getHWReg(const SIInstrInfo *TII, const MachineInstr &RegInstr)
static bool isSameMFMA(unsigned Opc0, unsigned Opc1)
static bool isDivFMas(unsigned Opcode)
static unsigned getCountedWaitStates(const MachineInstr &MI, GCNHazardRecognizer::GetNumWaitStatesFn GetNumWaitStates)
static bool hasHazard(StateT InitialState, function_ref< HazardFnResult(StateT &, const MachineInstr &)> IsHazard, function_ref< void(StateT &, const MachineInstr &)> UpdateState, const MachineBasicBlock *InitialMBB, MachineBasicBlock::const_reverse_instr_iterator InitialI)
static int getWaitStatesSince(GCNHazardRecognizer::IsHazardFn IsHazard, const MachineBasicBlock *MBB, MachineBasicBlock::const_reverse_instr_iterator I, int WaitStates, GCNHazardRecognizer::IsExpiredFn IsExpired, DenseSet< const MachineBasicBlock * > &Visited, GCNHazardRecognizer::GetNumWaitStatesFn GetNumWaitStates=SIInstrInfo::getNumWaitStates)
static int GFX940_SMFMA_N_PassWritesVGPROverlappedSrcABWaitStates(int NumPasses)
static int GFX940_XDL_N_PassWriteVgprVALUWawWaitStates(int NumPasses, bool IsGFX950)
static int GFX940_SMFMA_N_PassWriteVgprVALUMemExpReadWaitStates(int NumPasses)
static MachineBasicBlock::iterator skipMetaInstructionsForward(MachineBasicBlock::iterator I, MachineBasicBlock::iterator End)
static int GFX940_SMFMA_N_PassWritesVGPROverlappedSMFMASrcCWaitStates(int NumPasses)
static bool isCoexecutableVALUInst(const MachineInstr &MI)
static bool ensureEntrySetPrio(MachineFunction *MF, int Priority, const SIInstrInfo &TII)
static void addRegsToSet(const SIRegisterInfo &TRI, iterator_range< MachineInstr::const_mop_iterator > Ops, BitVector &DefSet, BitVector &UseSet)
static void insertNoopsInBundle(MachineInstr *MI, const SIInstrInfo &TII, unsigned Quantity)
static bool isSendMsgTraceDataOrGDS(const SIInstrInfo &TII, const MachineInstr &MI)
static unsigned countUnexecutedTerminators(const MachineBasicBlock &Pred, const MachineBasicBlock *MBB)
static cl::opt< unsigned > NopPadding("amdgpu-snop-padding", cl::init(0), cl::Hidden, cl::desc("Insert a s_nop x before every instruction"))
static bool isPermlane(const MachineInstr &MI)
static int GFX940_SMFMA_N_PassWriteVgprVALUWawWaitStates(int NumPasses)
static int GFX940_XDL_N_PassWritesVGPROverlappedXDLOrSMFMASrcCWaitStates(int NumPasses, bool IsGFX950)
AMD GCN specific subclass of TargetSubtarget.
static Register UseReg(const MachineOperand &MO)
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
static llvm::Error parse(GsymDataExtractor &Data, uint64_t BaseAddr, LineEntryCallback const &Callback)
Definition LineTable.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
#define H(x, y, z)
Definition MD5.cpp:56
Register Reg
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
static MCRegister getReg(const MCDisassembler *D, unsigned RC, unsigned RegNo)
#define P(N)
RISCVCC::CondCode CC
MachineRegisterInfo & MRI
Func MI getDebugLoc()))
static bool contains(SmallPtrSetImpl< ConstantExpr * > &Cache, ConstantExpr *Expr, Constant *C)
Definition Value.cpp:480
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
Definition Statistic.h:171
#define LLVM_DEBUG(...)
Definition Debug.h:119
#define DEBUG_WITH_TYPE(TYPE,...)
DEBUG_WITH_TYPE macro - This macro should be used by passes to emit debug information.
Definition Debug.h:72
Value * RHS
Value * LHS
static const uint32_t IV[8]
Definition blake3_impl.h:83
unsigned get(InstCounterType T) const
BitVector & set()
Set all bits in the bitvector.
Definition BitVector.h:366
A debug info location.
Definition DebugLoc.h:126
std::pair< iterator, bool > insert_as(std::pair< KeyT, ValueT > &&KV, const LookupKeyT &Val)
Alternate version of insert() which allows a different, and possibly less expensive,...
Definition DenseMap.h:874
Implements a dense probed hash-table based set.
Definition DenseSet.h:281
CallingConv::ID getCallingConv() const
getCallingConv()/setCallingConv(CC) - These method get and set the calling convention of this functio...
Definition Function.h:273
unsigned getHazardWaitStates(MachineInstr *MI) const
Returns the number of wait states until all hazards for MI are resolved.
unsigned PreEmitNoopsCommon(MachineInstr *) const
OperatingMode
Operating mode for the hazard recognizer.
void EmitNoop() override
EmitNoop - This callback is invoked when a noop was added to the instruction stream.
void Reset() override
Reset - This callback is invoked when a new block of instructions is about to be schedule.
unsigned PreEmitNoops(MachineInstr *) override
This overload will be used when the hazard recognizer is being used by a non-scheduling pass,...
void EmitInstruction(SUnit *SU) override
EmitInstruction - This callback is invoked when an instruction is emitted, to advance the hazard stat...
function_ref< bool(const MachineInstr &)> IsHazardFn
void AdvanceCycle() override
AdvanceCycle - This callback is invoked whenever the next top-down instruction to be scheduled cannot...
function_ref< unsigned int(const MachineInstr &)> GetNumWaitStatesFn
bool ShouldPreferAnother(SUnit *SU) const override
ShouldPreferAnother - This callback may be invoked if getHazardType returns NoHazard.
bool hasPhysRegs() const
Returns true if instruction operands are physical registers, so that hazards defined by register depe...
function_ref< bool(const MachineInstr &, int WaitStates)> IsExpiredFn
bool isSchedulerMode() const
Returns true if running as a scheduler (pre-RA or post-RA).
GCNHazardRecognizer(const MachineFunction &MF, OperatingMode Mode, MachineLoopInfo *MLI=nullptr)
Construct with explicit operating mode.
static AMDGPU::CoExecMaskT getCoExecMaskForMI(const MachineInstr &MI, const SIInstrInfo &TII)
Get the CoExecMask for a given instruction.
HazardType getHazardType(SUnit *SU, int Stalls) override
getHazardType - Return the hazard type of emitting this node.
void RecedeCycle() override
RecedeCycle - This callback is invoked whenever the next bottom-up instruction to be scheduled cannot...
bool isHazardRecognizerMode() const
Returns true if running as the standalone hazard recognizer pass.
bool isPreRA() const
Returns true if running in pre-RA scheduling mode.
BlockT * getLoopPreheader() const
If there is a preheader for this loop, return it.
LoopT * getParentLoop() const
Return the parent loop if it exists or nullptr for top level loops.
Wrapper class representing physical registers. Should be passed by value.
Definition MCRegister.h:41
Instructions::const_reverse_iterator const_reverse_instr_iterator
LLVM_ABI iterator getFirstTerminator()
Returns an iterator to the first terminator instruction of this basic block.
Instructions::iterator instr_iterator
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
iterator_range< pred_iterator > predecessors()
MachineInstrBundleIterator< MachineInstr > iterator
Function & getFunction()
Return the LLVM function that this machine code represents.
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.
const MachineInstrBuilder & add(const MachineOperand &MO) const
const MachineInstrBuilder & addDef(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a virtual register definition operand.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
const MachineBasicBlock * getParent() const
bool readsRegister(Register Reg, const TargetRegisterInfo *TRI) const
Return true if the MachineInstr reads the specified register.
bool isBundled() const
Return true if this instruction part of a bundle.
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.
LLVM_ABI void setReg(Register Reg)
Change the register this operand corresponds to.
void setIsKill(bool Val=true)
void setIsUndef(bool Val=true)
Register getReg() const
getReg - Returns the register number.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI bool isConstantPhysReg(MCRegister PhysReg) const
Returns true if PhysReg is unallocatable and constant throughout the function.
LLVM_ABI bool isPhysRegUsed(MCRegister PhysReg, bool SkipRegMaskTest=false) const
Return true if the specified register is modified or read in this function.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
static bool isDS(const MachineInstr &MI)
static bool isVMEM(const MachineInstr &MI)
static bool isSMRD(const MachineInstr &MI)
static bool isMTBUF(const MachineInstr &MI)
static bool isDGEMM(unsigned Opcode)
static bool isEXP(const MachineInstr &MI)
static bool isSALU(const MachineInstr &MI)
static bool isSDWA(const MachineInstr &MI)
static bool isDOT(const MachineInstr &MI)
static bool usesTENSOR_CNT(const MachineInstr &MI)
static bool isSWMMAC(const MachineInstr &MI)
static bool isLDSDIR(const MachineInstr &MI)
static bool isVPermPk16(unsigned Opcode)
static bool isVALU(const MachineInstr &MI, bool AllowLDSDMA)
static bool isTRANS(const MachineInstr &MI)
static bool isMUBUF(const MachineInstr &MI)
static bool isWaitcnt(unsigned Opcode)
static bool isVOP3(const MCInstrDesc &Desc)
static bool isDPP(const MachineInstr &MI)
static bool isMFMA(const MachineInstr &MI)
static bool isMAI(const MCInstrDesc &Desc)
static bool isFPAtomic(const MachineInstr &MI)
static bool isMIMG(const MachineInstr &MI)
static unsigned getNumWaitStates(const MachineInstr &MI)
Return the number of wait states that result from executing this instruction.
static bool isVOP3P(const MachineInstr &MI)
static bool isWMMA(const MachineInstr &MI)
static bool isLDSDMA(const MachineInstr &MI)
Scheduling unit. This is a node in the scheduling DAG.
bool isInstr() const
Returns true if this SUnit refers to a machine instruction as opposed to an SDNode.
MachineInstr * getInstr() const
Returns the representative MachineInstr for this SUnit.
unsigned MaxLookAhead
MaxLookAhead - Indicate the number of cycles in the scoreboard state.
virtual void EmitNoops(unsigned Quantity)
EmitNoops - This callback is invoked when noops were added to the instruction stream.
size_type size() const
Determine the number of elements in the SetVector.
Definition SetVector.h:103
bool insert(const value_type &X)
Insert a new element into the SetVector.
Definition SetVector.h:157
A SetVector that performs no allocations if smaller than a certain size.
Definition SetVector.h:345
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition SmallSet.h:184
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
bool getAsInteger(unsigned Radix, T &Result) const
Parse the current string as an integer of the specified radix.
Definition StringRef.h:490
Provide an instruction scheduling machine model to CodeGen passes.
std::pair< iterator, bool > insert(const ValueT &V)
Definition DenseSet.h:209
An efficient, type-erasing, non-owning reference to a callable.
self_iterator getIterator()
Definition ilist_node.h:123
A range adaptor for a pair of iterators.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned encodeFieldVaVcc(unsigned Encoded, unsigned VaVcc)
unsigned encodeFieldVaVdst(unsigned Encoded, unsigned VaVdst)
unsigned decodeFieldSaSdst(unsigned Encoded)
unsigned decodeFieldVaSdst(unsigned Encoded)
unsigned encodeFieldVmVsrc(unsigned Encoded, unsigned VmVsrc)
unsigned encodeFieldSaSdst(unsigned Encoded, unsigned SaSdst)
unsigned decodeFieldVaVdst(unsigned Encoded)
unsigned decodeFieldVmVsrc(unsigned Encoded)
unsigned encodeFieldVaSdst(unsigned Encoded, unsigned VaSdst)
const char * getCoExecMaskName(CoExecMaskT Mask)
Return a human-readable name for a mask holding a single instruction class, as produced by getCoExecM...
LLVM_READONLY const MIMGInfo * getMIMGInfo(unsigned Opc)
StringRef getSchedStrategy(const Function &F)
constexpr unsigned MaxCoExecStages
Max stages: INT8 16x16x64 = 17 cycles, round up for safety.
FPType getFPDstSelType(unsigned Opc)
bool isGFX12Plus(const MCSubtargetInfo &STI)
const char * getStageTypeName(CoExecStageType T)
LLVM_ABI IsaVersion getIsaVersion(StringRef GPU)
unsigned getRegBitWidth(unsigned RCID)
Get the size in bits of a register from the register class RC.
LLVM_READONLY int32_t getMFMAEarlyClobberOp(uint32_t Opcode)
CoExecMask CoExecMaskT
Waitcnt decodeWaitcnt(const IsaVersion &Version, unsigned Encoded)
CoExecInfo getCoExecInfo(const MachineInstr &MI, const SIInstrInfo &TII)
Get co-execution info for a WMMA instruction, selecting the per-cycle slot pattern from the opcode (a...
LLVM_READONLY bool hasNamedOperand(uint32_t Opcode, OpName NamedIdx)
LLVM_READONLY int32_t getAGPRFormOp(uint32_t Opcode)
InstructionFlavor classifyFlavor(const MachineInstr &MI, const SIInstrInfo &SII)
Classify MI into the execution flavor that drives both the scheduler's slot preferences and the hazar...
CoExecMaskT getCoExecMask(InstructionFlavor F)
Map a flavor to the co-execution class it occupies in a window slot.
bool isGFX1250(const MCSubtargetInfo &STI)
@ Entry
Definition COFF.h:862
@ AMDGPU_CS
Used for Mesa/AMDPAL compute shaders.
@ AMDGPU_KERNEL
Used for AMDGPU code object kernels.
@ AMDGPU_Gfx
Used for AMD graphics targets.
@ AMDGPU_CS_ChainPreserve
Used on AMDGPUs to give the middle-end more control over argument placement.
@ AMDGPU_CS_Chain
Used on AMDGPUs to give the middle-end more control over argument placement.
This namespace contains all of the command line option processing machinery.
initializer< Ty > init(const Ty &Val)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
constexpr double e
NodeAddr< DefNode * > Def
Definition RDFGraph.h:384
NodeAddr< UseNode * > Use
Definition RDFGraph.h:385
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:316
@ Offset
Definition DWP.cpp:577
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
@ Kill
The last use of a register.
@ Undef
Value of the register doesn't matter.
@ Define
Register definition.
@ Wait
Definition Threading.h:60
constexpr RegState getDeadRegState(bool B)
Op::Description Desc
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
Definition InstrProf.h:145
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Next
Definition InstrProf.h:147
hash_code hash_combine(const Ts &...args)
Combine values into a single hash_code.
Definition Hashing.h:307
@ End
Run the machine verifier once, at the end of the pipeline.
Definition CodeGen.h:320
LLVM_ABI Printable printMBBReference(const MachineBasicBlock &MBB)
Prints a machine basic block reference.
hash_code hash_combine_range(InputIteratorT first, InputIteratorT last)
Compute a hash_code for a sequence of values.
Definition Hashing.h:287
#define N
Co-execution characteristics for a multi-cycle instruction.
static std::tuple< typename Fields::ValueType... > decode(uint64_t Encoded)
An information struct used to provide DenseMap with the various necessary components for a given valu...