Skip to content

Commit 7401694

Browse files
taoyafanYaFan
andauthored
[AMDGPU] Add threshold for DPP atomic optimizer on LDS atomics (#186762)
Add amdgpu.expected.active.lane metadata which can be applied to operations by a compiler front-end. Do not apply the DPP atomic optimizer to LDS atomics where less than five active lanes are expected. This is an empirically derived threshold based on GFX11 and GFX12 testing. --------- Co-authored-by: YaFan <YaFan.Tao@amd.com>
1 parent afac946 commit 7401694

5 files changed

Lines changed: 1236 additions & 6 deletions

File tree

llvm/docs/AMDGPUUsage.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,28 @@ and
24402440

24412441
!0 = !{}
24422442

2443+
.. _amdgpu_expected_active_lanes:
2444+
2445+
'``amdgpu.expected.active.lanes``' Metadata
2446+
-------------------------------------------------
2447+
2448+
A profiling-derived hint describing how many lanes of the wavefront are
2449+
expected to be active. The metadata has a single ``i32`` constant operand
2450+
giving the expected number of active lanes as an absolute count (the same
2451+
value is used for both wave32 and wave64 targets). This structure is
2452+
enforced by the verifier.
2453+
2454+
The AMDGPU atomic optimizer uses this metadata on LDS atomics to decide
2455+
whether to skip the DPP optimization.
2456+
2457+
.. code-block:: llvm
2458+
2459+
; Few expected active lanes: the atomic optimizer may skip the DPP optimization.
2460+
%old0 = atomicrmw add ptr addrspace(3) @lds, i32 %val acq_rel, !amdgpu.expected.active.lanes !{i32 4}
2461+
2462+
; Many expected active lanes: the DPP optimization is not skipped.
2463+
%old1 = atomicrmw add ptr addrspace(3) @lds, i32 %val acq_rel, !amdgpu.expected.active.lanes !{i32 32}
2464+
24432465

24442466
LLVM IR Attributes
24452467
==================

llvm/lib/IR/Verifier.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5907,6 +5907,17 @@ void Verifier::visitInstruction(Instruction &I) {
59075907
if (MDNode *MD = I.getMetadata(LLVMContext::MD_mem_cache_hint))
59085908
visitMemCacheHintMetadata(I, MD);
59095909

5910+
if (MDNode *MD = I.getMetadata("amdgpu.expected.active.lanes")) {
5911+
Check(MD->getNumOperands() == 1,
5912+
"!amdgpu.expected.active.lanes must have exactly one operand", &I,
5913+
MD);
5914+
ConstantInt *CI =
5915+
mdconst::dyn_extract_or_null<ConstantInt>(MD->getOperand(0));
5916+
Check(CI && CI->getType()->isIntegerTy(32),
5917+
"!amdgpu.expected.active.lanes operand must be an i32 constant", &I,
5918+
MD);
5919+
}
5920+
59105921
if (MDNode *N = I.getDebugLoc().getAsMDNode()) {
59115922
CheckDI(isa<DILocation>(N), "invalid !dbg metadata attachment", &I, N);
59125923
visitMDNode(*N, AreDebugLocsAllowed::Yes);

llvm/lib/Target/AMDGPU/AMDGPUAtomicOptimizer.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ struct ReplacementInfo {
4545
AtomicRMWInst::BinOp Op;
4646
unsigned ValIdx;
4747
bool ValDivergent;
48+
bool IsLDS;
4849
};
4950

5051
class AMDGPUAtomicOptimizer : public FunctionPass {
@@ -87,7 +88,7 @@ class AMDGPUAtomicOptimizerImpl
8788
BasicBlock *ComputeLoop, BasicBlock *ComputeEnd) const;
8889

8990
void optimizeAtomic(Instruction &I, AtomicRMWInst::BinOp Op, unsigned ValIdx,
90-
bool ValDivergent) const;
91+
bool ValDivergent, bool IsLDS) const;
9192

9293
public:
9394
AMDGPUAtomicOptimizerImpl() = delete;
@@ -161,8 +162,8 @@ bool AMDGPUAtomicOptimizerImpl::run() {
161162
if (ToReplace.empty())
162163
return false;
163164

164-
for (auto &[I, Op, ValIdx, ValDivergent] : ToReplace)
165-
optimizeAtomic(*I, Op, ValIdx, ValDivergent);
165+
for (auto &[I, Op, ValIdx, ValDivergent, IsLDS] : ToReplace)
166+
optimizeAtomic(*I, Op, ValIdx, ValDivergent, IsLDS);
166167
ToReplace.clear();
167168
return true;
168169
}
@@ -241,10 +242,12 @@ void AMDGPUAtomicOptimizerImpl::visitAtomicRMWInst(AtomicRMWInst &I) {
241242
return;
242243
}
243244

245+
const bool IsLDS = I.getPointerAddressSpace() == AMDGPUAS::LOCAL_ADDRESS;
246+
244247
// If we get here, we can optimize the atomic using a single wavefront-wide
245248
// atomic operation to do the calculation for the entire wavefront, so
246249
// remember the instruction so we can come back to it.
247-
ToReplace.push_back({&I, Op, ValIdx, ValDivergent});
250+
ToReplace.push_back({&I, Op, ValIdx, ValDivergent, IsLDS});
248251
}
249252

250253
void AMDGPUAtomicOptimizerImpl::visitIntrinsicInst(IntrinsicInst &I) {
@@ -335,7 +338,8 @@ void AMDGPUAtomicOptimizerImpl::visitIntrinsicInst(IntrinsicInst &I) {
335338
// If we get here, we can optimize the atomic using a single wavefront-wide
336339
// atomic operation to do the calculation for the entire wavefront, so
337340
// remember the instruction so we can come back to it.
338-
ToReplace.push_back({&I, Op, ValIdx, ValDivergent});
341+
// Buffer atomics are never LDS.
342+
ToReplace.push_back({&I, Op, ValIdx, ValDivergent, /*IsLDS=*/false});
339343
}
340344

341345
// Use the builder to create the non-atomic counterpart of the specified
@@ -646,7 +650,23 @@ static Value *buildMul(IRBuilder<> &B, Value *LHS, Value *RHS) {
646650
void AMDGPUAtomicOptimizerImpl::optimizeAtomic(Instruction &I,
647651
AtomicRMWInst::BinOp Op,
648652
unsigned ValIdx,
649-
bool ValDivergent) const {
653+
bool ValDivergent,
654+
bool IsLDS) const {
655+
// Don't generate a DPP scan if !amdgpu.expected.active.lane hint indicates
656+
// insufficient lanes to offset fixed overhead.
657+
658+
// FIXME: The threshold was tuned empirically on gfx11 and gfx12. The DPP scan
659+
// overhead differs across subtargets, so the break-even point may differ too;
660+
// this may need to become subtarget-dependent.
661+
if (IsLDS && ValDivergent && ScanImpl == ScanOptions::DPP) {
662+
if (MDNode *MD = I.getMetadata("amdgpu.expected.active.lanes")) {
663+
auto *CI = mdconst::extract<ConstantInt>(MD->getOperand(0));
664+
constexpr unsigned ActiveLanesThreshold = 5;
665+
if (CI->getValue().ule(ActiveLanesThreshold))
666+
return;
667+
}
668+
}
669+
650670
// Start building just before the instruction.
651671
IRBuilder<> B(&I);
652672

0 commit comments

Comments
 (0)