[llvm-branch-commits] [llvm] [AMDGPU][SIInsertWaitCnts] Remove VMemTypes (PR #206440)
Pierre van Houtryve via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Jun 29 02:29:59 PDT 2026
https://github.com/Pierre-vh updated https://github.com/llvm/llvm-project/pull/206440
>From eb97b27a71c9b39044c9e7b846bdeaaf0f564b99 Mon Sep 17 00:00:00 2001
From: pvanhout <pierre.vanhoutryve at amd.com>
Date: Mon, 29 Jun 2026 11:09:49 +0200
Subject: [PATCH 1/2] [RFC][SIInsertWaitCnts] Remove VMemTypes
This can be considered a RFC. I'd personally like to get rid of VMEMTypes
but I don't know if anyone feels strongly that they should be kept.
My motivation for removing VMemTypes is simple: They are just a repeat
of VMEM events, just under a different name, and messier (defined as a
basic enum but actually stored as a bitmask later). It's just confusing.
This patch eliminates the need for them by:
- Adding a new entrypoint in AMDGPUHWEvents to get the basic set of
VMEM events issued by a VMEM Instruction.
- Set BVH/SAMPLER events irrespective of whether the HW can track them.
These events exist anyway, it should be up to InsertWaitCnt to deal with them
properly (which is easy, only `counterOutOfOrder` needed work).
- Tracking an additional set of per-VGPR "PendingEvents" which is
set using the "basic set of VMEM events" and cleared as needed.
I believe this is worth it because it streamlines the implementation
of `SIInsertWaitCnts` and eliminates a bit of code duplication
along the way.
---
llvm/lib/Target/AMDGPU/AMDGPUHWEvents.cpp | 13 +--
llvm/lib/Target/AMDGPU/AMDGPUHWEvents.h | 11 +++
llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp | 87 ++++++++-------------
3 files changed, 51 insertions(+), 60 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUHWEvents.cpp b/llvm/lib/Target/AMDGPU/AMDGPUHWEvents.cpp
index 1087b08ac9d91..acfc54f9db04e 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUHWEvents.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUHWEvents.cpp
@@ -56,8 +56,8 @@ static HWEvents getExpertSchedulingEventType(const MachineInstr &Inst,
return HWEvents::NONE;
}
-static HWEvents getVmemHWEvent(const MachineInstr &Inst, const GCNSubtarget &ST,
- const SIInstrInfo &TII) {
+HWEvents getSimplifiedVMEMEventsFor(const MachineInstr &Inst,
+ const SIInstrInfo &TII) {
switch (Inst.getOpcode()) {
// FIXME: GLOBAL_INV needs to be tracked with xcnt too.
case AMDGPU::GLOBAL_INV:
@@ -82,7 +82,8 @@ static HWEvents getVmemHWEvent(const MachineInstr &Inst, const GCNSubtarget &ST,
return HWEvents::SCRATCH_WRITE_ACCESS;
return HWEvents::VMEM_WRITE_ACCESS;
}
- if (!ST.hasExtendedWaitCounts() || SIInstrInfo::isFLAT(Inst))
+
+ if (SIInstrInfo::isFLAT(Inst))
return HWEvents::VMEM_READ_ACCESS;
if (SIInstrInfo::isImage(Inst)) {
@@ -116,14 +117,14 @@ static HWEvents getEventsForImpl(const MachineInstr &Inst,
if (TII.isFLAT(Inst)) {
if (SIInstrInfo::isGFX12CacheInvOrWBInst(Inst.getOpcode()))
- return getVmemHWEvent(Inst, ST, TII);
+ return getSimplifiedVMEMEventsFor(Inst, TII);
assert(Inst.mayLoadOrStore());
HWEvents E = HWEvents::NONE;
if (TII.mayAccessVMEMThroughFlat(Inst)) {
if (ST.hasWaitXcnt())
E |= HWEvents::VMEM_GROUP;
- E |= getVmemHWEvent(Inst, ST, TII);
+ E |= getSimplifiedVMEMEventsFor(Inst, TII);
}
if (TII.mayAccessLDSThroughFlat(Inst))
@@ -144,7 +145,7 @@ static HWEvents getEventsForImpl(const MachineInstr &Inst,
// BUFFER_WBL2 is included here because unlike invalidates, has to be
// followed "S_WAITCNT vmcnt(0)" is needed after to ensure the writeback has
// completed.
- HWEvents E = getVmemHWEvent(Inst, ST, TII);
+ HWEvents E = getSimplifiedVMEMEventsFor(Inst, TII);
if (ST.hasWaitXcnt())
E |= HWEvents::VMEM_GROUP;
if (ST.vmemWriteNeedsExpWaitcnt() &&
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUHWEvents.h b/llvm/lib/Target/AMDGPU/AMDGPUHWEvents.h
index 5901ee40177b3..bc5e10fae1c42 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUHWEvents.h
+++ b/llvm/lib/Target/AMDGPU/AMDGPUHWEvents.h
@@ -21,6 +21,7 @@ namespace llvm {
class GCNSubtarget;
class MachineInstr;
class raw_ostream;
+class SIInstrInfo;
namespace AMDGPU {
@@ -164,6 +165,16 @@ class HWEvents {
value_type Data = NONE;
};
+/// \param Inst A VMEM instruction (as per `SIInstrInfo::isVMEM`).
+/// \returns the simplified set of events triggered by the VMEM instruction \p
+/// Inst. The returned mask is not exhaustive, but is guaranteed to be a subset
+/// of the mask that'd be returned by \ref getEventsFor.
+///
+/// Useful to quickly categorize VMEM instructions without having to fetch all
+/// events.
+HWEvents getSimplifiedVMEMEventsFor(const MachineInstr &Inst,
+ const SIInstrInfo &TII);
+
/// \returns A bitmask of HWEvent triggered by \p Inst
HWEvents getEventsFor(const MachineInstr &Inst, const GCNSubtarget &ST,
bool IsExpertMode);
diff --git a/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp b/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp
index c261ae9c4b6c6..09a10bcccfcc4 100644
--- a/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp
+++ b/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp
@@ -114,21 +114,6 @@ static constexpr VMEMID toVMEMID(MCRegUnit RU) {
namespace {
-// Enumerate different types of result-returning VMEM operations. Although
-// s_waitcnt orders them all with a single vmcnt counter, in the absence of
-// s_waitcnt only instructions of the same VmemType are guaranteed to write
-// their results in order -- so there is no need to insert an s_waitcnt between
-// two instructions of the same type that write the same vgpr.
-enum VmemType {
- // BUF instructions and MIMG instructions without a sampler.
- VMEM_NOSAMPLER,
- // MIMG instructions with a sampler.
- VMEM_SAMPLER,
- // BVH instructions
- VMEM_BVH,
- NUM_VMEM_TYPES
-};
-
// Maps values of InstCounterType to the instruction that waits on that
// counter. Only used if GCNSubtarget::hasExtendedWaitCounts()
// returns true, and does not cover VA_VDST or VM_VSRC.
@@ -163,26 +148,6 @@ static bool isNormalMode(AMDGPU::InstCounterType MaxCounter) {
}
#endif // NDEBUG
-VmemType getVmemType(const MachineInstr &Inst) {
- assert(updateVMCntOnly(Inst));
- if (!SIInstrInfo::isImage(Inst))
- return VMEM_NOSAMPLER;
- const AMDGPU::MIMGInfo *Info = AMDGPU::getMIMGInfo(Inst.getOpcode());
- const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
- AMDGPU::getMIMGBaseOpcodeInfo(Info->BaseOpcode);
-
- if (BaseInfo->BVH)
- return VMEM_BVH;
-
- // We have to make an additional check for isVSAMPLE here since some
- // instructions don't have a sampler, but are still classified as sampler
- // instructions for the purposes of e.g. waitcnt.
- if (BaseInfo->Sampler || BaseInfo->MSAA || SIInstrInfo::isVSAMPLE(Inst))
- return VMEM_SAMPLER;
-
- return VMEM_NOSAMPLER;
-}
-
class WaitcntBrackets;
// This abstracts the logic for generating and updating S_WAIT* instructions
@@ -636,20 +601,20 @@ class WaitcntBrackets {
void setPendingGDS() { LastGDS = ScoreUBs[AMDGPU::DS_CNT]; }
// Return true if there might be pending writes to the vgpr-interval by VMEM
- // instructions with types different from V.
- bool hasOtherPendingVmemTypes(MCPhysReg Reg, VmemType V) const {
+ // instructions where the HWEvents in VGPRContext are not contained in E.
+ bool hasDifferentVGPRPendingEvents(MCPhysReg Reg, HWEvents E) const {
for (MCRegUnit RU : regunits(Reg)) {
auto It = VMem.find(toVMEMID(RU));
- if (It != VMem.end() && (It->second.VMEMTypes & ~(1 << V)))
+ if (It != VMem.end() && (It->second.VGPRPendingEvents & ~E).any())
return true;
}
return false;
}
- void clearVgprVmemTypes(MCPhysReg Reg) {
+ void clearVGPRPendingEvents(MCPhysReg Reg) {
for (MCRegUnit RU : regunits(Reg)) {
if (auto It = VMem.find(toVMEMID(RU)); It != VMem.end()) {
- It->second.VMEMTypes = 0;
+ It->second.VGPRPendingEvents = HWEvents::NONE;
if (It->second.empty())
VMem.erase(It);
}
@@ -772,10 +737,10 @@ class WaitcntBrackets {
struct VMEMInfo {
// Scores for all instruction counters. Zero-initialized.
CounterValueArray Scores{};
- // Bitmask of the VmemTypes of VMEM instructions for this VGPR.
- unsigned VMEMTypes = 0;
+ // For VGPRs, we need to track an additional fine-grained set of pending events.
+ HWEvents VGPRPendingEvents;
- bool empty() const { return all_of(Scores, equal_to(0)) && !VMEMTypes; }
+ bool empty() const { return all_of(Scores, equal_to(0)) && !VGPRPendingEvents; }
};
/// Wait cnt scores for every sgpr, the DS_CNT (corresponding to LGKMcnt
@@ -884,7 +849,7 @@ bool WaitcntBrackets::hasPointSamplePendingVmemTypes(const MachineInstr &MI,
if (!hasPointSampleAccel(MI))
return false;
- return hasOtherPendingVmemTypes(Reg, VMEM_NOSAMPLER);
+ return hasDifferentVGPRPendingEvents(Reg, HWEvents::VMEM_READ_ACCESS);
}
void WaitcntBrackets::updateByEvent(HWEvents E, MachineInstr &Inst) {
@@ -1025,16 +990,16 @@ void WaitcntBrackets::updateByEvent(HWEvents E, MachineInstr &Inst) {
if (updateVMCntOnly(Inst)) {
// updateVMCntOnly should only leave us with VGPRs
// MUBUF, MTBUF, MIMG, FlatGlobal, and FlatScratch only have VGPR/AGPR
- // defs. That's required for a sane index into `VgprMemTypes` below
+ // defs.
assert(TRI.isVectorRegister(MRI, Op.getReg()));
- VmemType V = getVmemType(Inst);
- unsigned char TypesMask = 1 << V;
+ HWEvents VGPRContext =
+ AMDGPU::getSimplifiedVMEMEventsFor(Inst, Context->TII);
// If instruction can have Point Sample Accel applied, we have to flag
// this with another potential dependency
if (hasPointSampleAccel(Inst))
- TypesMask |= 1 << VMEM_NOSAMPLER;
+ VGPRContext |= HWEvents::VMEM_READ_ACCESS;
for (MCRegUnit RU : regunits(Op.getReg().asMCReg()))
- VMem[toVMEMID(RU)].VMEMTypes |= TypesMask;
+ VMem[toVMEMID(RU)].VGPRPendingEvents |= VGPRContext;
}
}
setScoreByOperand(Op, T, CurrScore);
@@ -1552,6 +1517,19 @@ bool WaitcntBrackets::counterOutOfOrder(AMDGPU::InstCounterType T) const {
HWEvents Events = PendingEvents & Context->getWaitEvents(T);
+ // If the target does not have extended counters, VMEM_BVH/SAMPLE_READ
+ // events are equivalent to VMEM_READ_ACCESS. We do not go out of order in
+ // such cases.
+ static constexpr HWEvents ExtendedImageEvents =
+ HWEvents::VMEM_SAMPLER_READ_ACCESS | HWEvents::VMEM_BVH_READ_ACCESS;
+ if (!Context->ST.hasExtendedWaitCounts() &&
+ (Events & ExtendedImageEvents).any()) {
+ Events -= ExtendedImageEvents; // TODO: Tests pass even if I only use
+ // VMEM_SAMPLER_READ_ACCESS which isn't
+ // normal; indicates weak testing coverage
+ Events |= HWEvents::VMEM_READ_ACCESS;
+ }
+
// GLOBAL_INV completes in-order with other LOAD_CNT events,
// so having GLOBAL_INV_ACCESS mixed with other LOAD_CNT
// events doesn't cause out-of-order completion.
@@ -2451,7 +2429,8 @@ bool SIInsertWaitcnts::generateWaitcntInstBefore(
// Additionally check instructions where Point Sample Acceleration
// might be applied.
if (Op.isUse() || !updateVMCntOnly(MI) ||
- ScoreBrackets.hasOtherPendingVmemTypes(Reg, getVmemType(MI)) ||
+ ScoreBrackets.hasDifferentVGPRPendingEvents(
+ Reg, AMDGPU::getSimplifiedVMEMEventsFor(MI, TII)) ||
ScoreBrackets.hasPointSamplePendingVmemTypes(MI, Reg) ||
!ST.hasVmemWriteVgprInOrder()) {
ScoreBrackets.determineWaitForPhysReg(AMDGPU::LOAD_CNT, Reg, Wait,
@@ -2460,7 +2439,7 @@ bool SIInsertWaitcnts::generateWaitcntInstBefore(
MI);
ScoreBrackets.determineWaitForPhysReg(AMDGPU::BVH_CNT, Reg, Wait,
MI);
- ScoreBrackets.clearVgprVmemTypes(Reg);
+ ScoreBrackets.clearVGPRPendingEvents(Reg);
}
if (Op.isDef() ||
@@ -2848,9 +2827,9 @@ bool WaitcntBrackets::merge(const WaitcntBrackets &Other) {
for (auto &[TID, Info] : VMem) {
if (auto It = Other.VMem.find(TID); It != Other.VMem.end()) {
- unsigned char NewVmemTypes = Info.VMEMTypes | It->second.VMEMTypes;
- StrictDom |= NewVmemTypes != Info.VMEMTypes;
- Info.VMEMTypes = NewVmemTypes;
+ HWEvents NewVGPRContext = Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
+ StrictDom |= NewVGPRContext != Info.VGPRPendingEvents;
+ Info.VGPRPendingEvents = NewVGPRContext;
}
}
>From 8a6f6bd53875398464b72d4909faaf4cc722192d Mon Sep 17 00:00:00 2001
From: pvanhout <pierre.vanhoutryve at amd.com>
Date: Mon, 29 Jun 2026 11:29:19 +0200
Subject: [PATCH 2/2] clang-format
---
llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp b/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp
index 09a10bcccfcc4..c496666601967 100644
--- a/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp
+++ b/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp
@@ -737,10 +737,13 @@ class WaitcntBrackets {
struct VMEMInfo {
// Scores for all instruction counters. Zero-initialized.
CounterValueArray Scores{};
- // For VGPRs, we need to track an additional fine-grained set of pending events.
+ // For VGPRs, we need to track an additional fine-grained set of pending
+ // events.
HWEvents VGPRPendingEvents;
- bool empty() const { return all_of(Scores, equal_to(0)) && !VGPRPendingEvents; }
+ bool empty() const {
+ return all_of(Scores, equal_to(0)) && !VGPRPendingEvents;
+ }
};
/// Wait cnt scores for every sgpr, the DS_CNT (corresponding to LGKMcnt
@@ -2827,7 +2830,8 @@ bool WaitcntBrackets::merge(const WaitcntBrackets &Other) {
for (auto &[TID, Info] : VMem) {
if (auto It = Other.VMem.find(TID); It != Other.VMem.end()) {
- HWEvents NewVGPRContext = Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
+ HWEvents NewVGPRContext =
+ Info.VGPRPendingEvents | It->second.VGPRPendingEvents;
StrictDom |= NewVGPRContext != Info.VGPRPendingEvents;
Info.VGPRPendingEvents = NewVGPRContext;
}
More information about the llvm-branch-commits
mailing list