[llvm] [AMDGPU] Pipeline DS_READ_B128 with F16 MFMA on gfx950 (PR #212330)
Juan Manuel Martinez CaamaƱo via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 4 06:26:46 PDT 2026
================
@@ -240,6 +288,553 @@ static bool canUsePressureDiffs(const SUnit &SU) {
return true;
}
+static bool isTargetDSReadOpcode(unsigned Opcode) {
+ switch (Opcode) {
+ case AMDGPU::DS_READ_B128:
+ case AMDGPU::DS_READ_B128_gfx9:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool isTargetMFMAOpcode(unsigned Opcode) {
+ switch (Opcode) {
+ case AMDGPU::V_MFMA_F32_32X32X16_F16_mac_vgprcd_e64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+template <typename PredicateT>
+static bool hasBundledMI(const MachineInstr *MI, PredicateT Predicate) {
+ if (!MI)
+ return false;
+
+ if (Predicate(*MI))
+ return true;
+
+ if (!MI->isBundle())
+ return false;
+
+ MachineBasicBlock::const_instr_iterator BundleI = MI->getIterator();
+ for (++BundleI;
+ BundleI != MI->getParent()->instr_end() && BundleI->isBundledWithPred();
+ ++BundleI) {
+ if (Predicate(*BundleI))
+ return true;
+ }
+
+ return false;
+}
+
+static bool isDSReadLike(const SUnit *SU) {
+ if (!SU)
+ return false;
+
+ return hasBundledMI(SU->getInstr(), [](const MachineInstr &MI) {
+ return isTargetDSReadOpcode(MI.getOpcode());
+ });
+}
+
+static bool isMFMALike(const SUnit *SU) {
+ if (!SU)
+ return false;
+
+ return hasBundledMI(SU->getInstr(), [](const MachineInstr &MI) {
+ return isTargetMFMAOpcode(MI.getOpcode());
+ });
+}
+
+static bool hasMFMAFragmentPipeline(const ScheduleDAGMI &DAG) {
+ for (const SUnit &SU : DAG.SUnits) {
+ if (!isDSReadLike(&SU))
+ continue;
+ for (const SDep &Succ : SU.Succs)
+ if (Succ.getKind() == SDep::Data && isMFMALike(Succ.getSUnit()))
+ return true;
+ }
+ return false;
+}
+
+static bool isSafeMFMAFragmentRampUpFiller(const SUnit *SU) {
+ if (!SU || isDSReadLike(SU) || isMFMALike(SU))
+ return false;
+
+ // This tune does not model buffered-load maturity windows or available
+ // in-flight memory capacity. Keep memory operations, calls, barriers, and
+ // other side effects under the normal scheduler policy.
+ return !hasBundledMI(SU->getInstr(), [](const MachineInstr &MI) {
+ return MI.mayLoadOrStore() || MI.isCall() || MI.isTerminator() ||
+ MI.isBarrier() || MI.hasUnmodeledSideEffects();
+ });
+}
+
+static unsigned getBoundaryReadyCycle(const SchedBoundary &Zone,
+ const SUnit *SU) {
+ return Zone.isTop() ? SU->TopReadyCycle : SU->BotReadyCycle;
+}
+
+static unsigned getReadyStall(const SchedBoundary &Zone, const SUnit *SU) {
+ unsigned ReadyCycle = getBoundaryReadyCycle(Zone, SU);
+ unsigned CurrCycle = Zone.getCurrCycle();
+ return ReadyCycle > CurrCycle ? ReadyCycle - CurrCycle : 0;
+}
+
+static unsigned getIssueStall(SchedBoundary &Zone, SUnit *SU) {
+ if (!SU || !SU->hasReservedResource || !Zone.SchedModel ||
+ !Zone.SchedModel->hasInstrSchedModel())
+ return 0;
+
+ const MCSchedClassDesc *SC = Zone.DAG->getSchedClass(SU);
+ unsigned Stall = 0;
+ for (const MCWriteProcResEntry &PE :
+ make_range(Zone.SchedModel->getWriteProcResBegin(SC),
+ Zone.SchedModel->getWriteProcResEnd(SC))) {
+ unsigned NextCycle;
+ unsigned InstanceIdx;
+ std::tie(NextCycle, InstanceIdx) = Zone.getNextResourceCycle(
+ SC, PE.ProcResourceIdx, PE.ReleaseAtCycle, PE.AcquireAtCycle);
+ (void)InstanceIdx;
+ if (NextCycle > Zone.getCurrCycle())
+ Stall = std::max(Stall, NextCycle - Zone.getCurrCycle());
+ }
+
+ return Stall;
+}
+
+static unsigned getEffectiveStall(SchedBoundary &Zone, SUnit *SU) {
+ return std::max(getReadyStall(Zone, SU), getIssueStall(Zone, SU));
+}
+
+static bool consumesThisDSRead(const SUnit *SU, const SUnit *Pred) {
+ if (!isMFMALike(SU) || !isDSReadLike(Pred))
+ return false;
+
+ for (const SDep &PredDep : SU->Preds) {
+ if (PredDep.getSUnit() == Pred)
+ return true;
+ }
+
+ return false;
+}
+
+static bool hasScheduledMFMASuccessor(const SUnit *SU) {
+ if (!isDSReadLike(SU))
+ return false;
+
+ for (const SDep &SuccDep : SU->Succs) {
+ const SUnit *Succ = SuccDep.getSUnit();
+ if (isMFMALike(Succ) && Succ->isScheduled)
+ return true;
+ }
+
+ return false;
+}
+
+enum class PipeKind {
+ // Not part of the DS_READ/MFMA fragment pipeline.
+ None,
+
+ // A DS_READ that may produce an operand fragment for an MFMA.
+ FragmentProducer,
+
+ // An MFMA with no remaining scheduler-model dependency stall.
+ ReadyMFMA,
+
+ // An MFMA whose scheduler-model dependency-ready cycle is still ahead of
+ // the current scheduling boundary.
+ PendingMFMA,
+};
+
+static PipeKind classifyPipeKind(SchedBoundary &Zone, SUnit *SU) {
+ if (isDSReadLike(SU))
+ return PipeKind::FragmentProducer;
+
+ if (isMFMALike(SU))
+ return getReadyStall(Zone, SU) ? PipeKind::PendingMFMA
+ : PipeKind::ReadyMFMA;
+
+ return PipeKind::None;
+}
+
+static unsigned getNumDSReadsToUnlockClosestMFMA(const SUnit *SU) {
+ // For every unscheduled MFMA successor, count its other unscheduled DS_READ
+ // predecessors. Return the minimum count, excluding SU itself, to prefer the
+ // DS_READ that can unlock any MFMA with the fewest additional reads.
+ if (!isDSReadLike(SU))
+ return std::numeric_limits<unsigned>::max();
+
+ unsigned MinRemaining = std::numeric_limits<unsigned>::max();
+ for (const SDep &SuccDep : SU->Succs) {
+ const SUnit *Succ = SuccDep.getSUnit();
+ if (!isMFMALike(Succ) || Succ->isScheduled)
+ continue;
+
+ unsigned Remaining = 0;
+ for (const SDep &PredDep : Succ->Preds) {
+ const SUnit *Pred = PredDep.getSUnit();
+ if (!isDSReadLike(Pred) || Pred == SU || Pred->isScheduled)
+ continue;
+ ++Remaining;
+ }
----------------
jmmartinez wrote:
Isn't this loop `countUnscheduledDSReadPredsToMFMASuccessorAfter` ?
https://github.com/llvm/llvm-project/pull/212330
More information about the llvm-commits
mailing list