[llvm] [AMDGPU] Add `getLocalMemorySize` and `getAddressableLocalMemorySize` to TargetParser (PR #220718)
Chinmay Deshpande via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 12 14:12:56 PDT 2026
https://github.com/chinmaydd updated https://github.com/llvm/llvm-project/pull/220718
>From 8a3d12cec2539eb980ad3f28a7325e38e28c7dab Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <chdeshpa at amd.com>
Date: Wed, 2 Sep 2026 16:02:17 -0400
Subject: [PATCH 1/2] [AMDGPU] Add getLocalMemorySize to TargetParser
Add getLocalMemorySize and getAddressableLocalMemorySize, both taking a
GPUKind or a Triple::SubArchType, so the LDS a work-group gets can be
queried from a GPU name alone without an MCSubtargetInfo. The first
returns the physical block available in the current mode, the second
caps it at what one work-group can address, mirroring the IsaInfo pair.
The number of SIMDs a work-group runs on is a per-kernel mode rather
than a property of the GPU, so it stays a parameter. GCNSubtarget
initializes its cached sizes from the new entry points. There is no
functional change.
Change-Id: Ib71428b66032a231ed491d6294122b359abfe7e2
Co-Authored-By: Claude Opus 5 (1M context) <noreply at anthropic.com>
---
.../llvm/TargetParser/AMDGPUTargetParser.h | 42 +++++++++++++-
llvm/lib/Target/AMDGPU/GCNSubtarget.cpp | 7 ++-
llvm/lib/TargetParser/AMDGPUTargetParser.cpp | 30 ++++++++++
.../TargetParser/TargetParserTest.cpp | 56 +++++++++++++++++++
4 files changed, 129 insertions(+), 6 deletions(-)
diff --git a/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h b/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h
index b46a23ad1ec3a..67ee03f9e9762 100644
--- a/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h
@@ -192,13 +192,49 @@ LLVM_ABI unsigned getAddressableNumVGPRs(GPUKind AK, bool IsWave32);
LLVM_ABI unsigned getAddressableNumVGPRs(Triple::SubArchType SubArch,
bool IsWave32);
-/// \returns Maximum LDS in bytes a single work-group can address. This is a
-/// fixed hardware cap and does not depend on how many SIMDs a work-group runs
-/// on.
+/// LDS size queries.
+///
+/// \c getMaxHWAddressableLocalMemorySize returns the architectural limit that
+/// one work-group can address. It is independent of execution mode.
+///
+/// \c getLocalMemorySize returns the LDS available to all work-groups sharing a
+/// WGP or CU, which is the LDS capacity used to compute occupancy. In full-SIMD
+/// mode, a work-group runs on four SIMDs and the query returns the full
+/// physical block. In CU mode, it runs on two SIMDs and the query returns half
+/// the block.
+///
+/// \c getAddressableLocalMemorySize returns the amount one work-group can
+/// allocate:
+///
+/// min(getMaxHWAddressableLocalMemorySize(), getLocalMemorySize())
+///
+/// The physical LDS block belongs to a WGP on gfx10/11/12 and to a CU
+/// otherwise. On gfx10/11/12, the block is twice the address limit, so a
+/// work-group cannot address the entire block in WGP mode.
+///
+/// The mode columns below show local/addressable LDS, in KiB:
+///
+/// GPU address limit full-SIMD CU mode
+/// gfx900 64 64/64 n/a (no CU mode)
+/// gfx1030 64 128/64 64/64
+/// gfx1250 320 320/320 n/a (always full-SIMD)
+
+/// \returns Maximum LDS in bytes a single work-group can address.
LLVM_ABI unsigned getMaxHWAddressableLocalMemorySize(GPUKind AK);
LLVM_ABI unsigned
getMaxHWAddressableLocalMemorySize(Triple::SubArchType SubArch);
+/// \returns Total LDS in bytes available on one WGP or CU. \p FullSIMDMode
+/// selects whether a work-group runs on all four SIMDs.
+LLVM_ABI unsigned getLocalMemorySize(GPUKind AK, bool FullSIMDMode);
+LLVM_ABI unsigned getLocalMemorySize(Triple::SubArchType SubArch,
+ bool FullSIMDMode);
+
+/// \returns LDS in bytes a single work-group can allocate.
+LLVM_ABI unsigned getAddressableLocalMemorySize(GPUKind AK, bool FullSIMDMode);
+LLVM_ABI unsigned getAddressableLocalMemorySize(Triple::SubArchType SubArch,
+ bool FullSIMDMode);
+
/// \returns Number of LDS banks per compute unit.
LLVM_ABI unsigned getLDSBankCount(GPUKind AK);
LLVM_ABI unsigned getLDSBankCount(Triple::SubArchType SubArch);
diff --git a/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp b/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp
index 9443420c3d424..cbf2b35ebba7b 100644
--- a/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp
+++ b/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp
@@ -181,9 +181,10 @@ GCNSubtarget &GCNSubtarget::initializeSubtargetDependencies(const Triple &TT,
if (FlatOffsetBitWidth == 0)
FlatOffsetBitWidth = 13;
- LocalMemorySize = AMDGPU::IsaInfo::getLocalMemorySize(*this);
- AddressableLocalMemorySize =
- AMDGPU::IsaInfo::getAddressableLocalMemorySize(*this);
+ LocalMemorySize = AMDGPU::getLocalMemorySize(getTargetID().getGPUKind(),
+ AMDGPU::isFullSIMDMode(*this));
+ AddressableLocalMemorySize = AMDGPU::getAddressableLocalMemorySize(
+ getTargetID().getGPUKind(), AMDGPU::isFullSIMDMode(*this));
// LDS Allocation Granularity calculated in bytes from dwords
LDSAllocationGranularity =
AMDGPU::getLdsDwGranularity(*this) * sizeof(uint32_t);
diff --git a/llvm/lib/TargetParser/AMDGPUTargetParser.cpp b/llvm/lib/TargetParser/AMDGPUTargetParser.cpp
index 48e8eb3bad41d..faab914fcb645 100644
--- a/llvm/lib/TargetParser/AMDGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/AMDGPUTargetParser.cpp
@@ -16,6 +16,7 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Triple.h"
+#include <algorithm>
#include <array>
#include <cassert>
@@ -477,6 +478,35 @@ AMDGPU::getMaxHWAddressableLocalMemorySize(Triple::SubArchType SubArch) {
return getMaxHWAddressableLocalMemorySize(getGPUKindFromSubArch(SubArch));
}
+unsigned AMDGPU::getLocalMemorySize(GPUKind AK, bool FullSIMDMode) {
+ // gfx10/11/12 address half of the physical block, e.g. 64 KiB of 128 KiB.
+ unsigned Size = getMaxHWAddressableLocalMemorySize(AK);
+ if (getFeatureBitset(AK).test(FEAT_HALF_ADDRESSABLE_PHYSICAL_LOCAL_MEMORY))
+ Size *= 2;
+
+ // In CU mode the work-group runs on two SIMDs and reaches only their half.
+ if (!FullSIMDMode)
+ Size /= 2;
+
+ return Size;
+}
+
+unsigned AMDGPU::getLocalMemorySize(Triple::SubArchType SubArch,
+ bool FullSIMDMode) {
+ return getLocalMemorySize(getGPUKindFromSubArch(SubArch), FullSIMDMode);
+}
+
+unsigned AMDGPU::getAddressableLocalMemorySize(GPUKind AK, bool FullSIMDMode) {
+ return std::min(getMaxHWAddressableLocalMemorySize(AK),
+ getLocalMemorySize(AK, FullSIMDMode));
+}
+
+unsigned AMDGPU::getAddressableLocalMemorySize(Triple::SubArchType SubArch,
+ bool FullSIMDMode) {
+ return getAddressableLocalMemorySize(getGPUKindFromSubArch(SubArch),
+ FullSIMDMode);
+}
+
unsigned AMDGPU::getLDSBankCount(GPUKind AK) {
const GPUInfo *Info = getAMDGPUInfo(AK);
return Info ? Info->LDSBankCount : 32;
diff --git a/llvm/unittests/TargetParser/TargetParserTest.cpp b/llvm/unittests/TargetParser/TargetParserTest.cpp
index 0037bc63e7b0e..ad86abae775f5 100644
--- a/llvm/unittests/TargetParser/TargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/TargetParserTest.cpp
@@ -3258,6 +3258,62 @@ TEST(TargetParserTest, testAMDGPUgetBufferResourceNumRecordsWidth) {
}
}
+TEST(TargetParserTest, testAMDGPUgetLocalMemorySize) {
+ // Without a half-addressable physical block the total matches the
+ // addressable cap, and running on two SIMDs halves it.
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_GFX600, true), 32768u);
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_GFX600, false), 16384u);
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_GFX900, true), 65536u);
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_GFX950, true), 163840u);
+
+ // gfx10/11/12 address 64 KiB of a 128 KiB block.
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_GFX1030, true), 131072u);
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_GFX1030, false), 65536u);
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_GFX1100, true), 131072u);
+
+ // gfx12.5 and gfx13 dropped the half-addressable block.
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_GFX1250, true), 327680u);
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_GFX1310, true), 196608u);
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_GFX1310, false), 98304u);
+
+ // An unknown GPU falls back to the smallest block.
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(AMDGPU::GK_NONE, true), 32768u);
+
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(Triple::AMDGPUSubArch900, true), 65536u);
+ EXPECT_EQ(AMDGPU::getLocalMemorySize(Triple::AMDGPUSubArch1030, true),
+ 131072u);
+}
+
+TEST(TargetParserTest, testAMDGPUgetAddressableLocalMemorySize) {
+ // A work-group never allocates past the hardware cap, so the doubled
+ // gfx10/11/12 block is capped back to the addressable size.
+ EXPECT_EQ(AMDGPU::getAddressableLocalMemorySize(AMDGPU::GK_GFX1030, true),
+ 65536u);
+ EXPECT_EQ(AMDGPU::getAddressableLocalMemorySize(AMDGPU::GK_GFX1030, false),
+ 65536u);
+ EXPECT_EQ(AMDGPU::getAddressableLocalMemorySize(AMDGPU::GK_GFX1100, true),
+ 65536u);
+
+ // Without a doubled block the cap is only reached in full-SIMD mode.
+ EXPECT_EQ(AMDGPU::getAddressableLocalMemorySize(AMDGPU::GK_GFX600, true),
+ 32768u);
+ EXPECT_EQ(AMDGPU::getAddressableLocalMemorySize(AMDGPU::GK_GFX600, false),
+ 16384u);
+ EXPECT_EQ(AMDGPU::getAddressableLocalMemorySize(AMDGPU::GK_GFX950, true),
+ 163840u);
+ EXPECT_EQ(AMDGPU::getAddressableLocalMemorySize(AMDGPU::GK_GFX1250, true),
+ 327680u);
+ EXPECT_EQ(AMDGPU::getAddressableLocalMemorySize(AMDGPU::GK_GFX1310, false),
+ 98304u);
+
+ EXPECT_EQ(AMDGPU::getAddressableLocalMemorySize(AMDGPU::GK_NONE, true),
+ 32768u);
+
+ EXPECT_EQ(
+ AMDGPU::getAddressableLocalMemorySize(Triple::AMDGPUSubArch1030, true),
+ 65536u);
+}
+
TEST(TargetParserTest, testAMDGPUgetNumWorkGroupSIMDs) {
EXPECT_EQ(AMDGPU::getNumWorkGroupSIMDs(true), 4u);
EXPECT_EQ(AMDGPU::getNumWorkGroupSIMDs(false), 2u);
>From 97e29895c338ffb1546d2bc4f6e1748a3c77f440 Mon Sep 17 00:00:00 2001
From: Chinmay Deshpande <chdeshpa at amd.com>
Date: Thu, 10 Sep 2026 17:30:54 -0400
Subject: [PATCH 2/2] [AMDGPU] Use SIMD-mode terminology for LDS queries
Change-Id: I6d6900868d58374d295126c52f67ffcdd8676902
---
.../llvm/TargetParser/AMDGPUTargetParser.h | 19 ++++++++++---------
llvm/lib/Target/AMDGPU/GCNSubtarget.cpp | 9 ++++-----
llvm/lib/Target/AMDGPU/GCNSubtarget.h | 5 +++++
llvm/lib/TargetParser/AMDGPUTargetParser.cpp | 2 +-
4 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h b/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h
index 67ee03f9e9762..3c2037c90be51 100644
--- a/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h
+++ b/llvm/include/llvm/TargetParser/AMDGPUTargetParser.h
@@ -198,10 +198,10 @@ LLVM_ABI unsigned getAddressableNumVGPRs(Triple::SubArchType SubArch,
/// one work-group can address. It is independent of execution mode.
///
/// \c getLocalMemorySize returns the LDS available to all work-groups sharing a
-/// WGP or CU, which is the LDS capacity used to compute occupancy. In full-SIMD
-/// mode, a work-group runs on four SIMDs and the query returns the full
-/// physical block. In CU mode, it runs on two SIMDs and the query returns half
-/// the block.
+/// physical block, which is the LDS capacity used to compute occupancy. In
+/// full-SIMD mode, a work-group runs on four SIMDs and the query returns the
+/// full physical block. In half-SIMD mode, it runs on two SIMDs and the query
+/// returns half the block.
///
/// \c getAddressableLocalMemorySize returns the amount one work-group can
/// allocate:
@@ -210,12 +210,12 @@ LLVM_ABI unsigned getAddressableNumVGPRs(Triple::SubArchType SubArch,
///
/// The physical LDS block belongs to a WGP on gfx10/11/12 and to a CU
/// otherwise. On gfx10/11/12, the block is twice the address limit, so a
-/// work-group cannot address the entire block in WGP mode.
+/// work-group cannot address the entire block in full-SIMD mode.
///
/// The mode columns below show local/addressable LDS, in KiB:
///
-/// GPU address limit full-SIMD CU mode
-/// gfx900 64 64/64 n/a (no CU mode)
+/// GPU address limit full-SIMD half-SIMD
+/// gfx900 64 64/64 n/a (always full-SIMD)
/// gfx1030 64 128/64 64/64
/// gfx1250 320 320/320 n/a (always full-SIMD)
@@ -224,8 +224,9 @@ LLVM_ABI unsigned getMaxHWAddressableLocalMemorySize(GPUKind AK);
LLVM_ABI unsigned
getMaxHWAddressableLocalMemorySize(Triple::SubArchType SubArch);
-/// \returns Total LDS in bytes available on one WGP or CU. \p FullSIMDMode
-/// selects whether a work-group runs on all four SIMDs.
+/// \returns Total LDS in bytes available to work-groups sharing a physical
+/// block. \p FullSIMDMode selects full-SIMD mode (four SIMDs) when true and
+/// half-SIMD mode (two SIMDs) otherwise.
LLVM_ABI unsigned getLocalMemorySize(GPUKind AK, bool FullSIMDMode);
LLVM_ABI unsigned getLocalMemorySize(Triple::SubArchType SubArch,
bool FullSIMDMode);
diff --git a/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp b/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp
index cbf2b35ebba7b..723c260dc60f8 100644
--- a/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp
+++ b/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp
@@ -181,10 +181,10 @@ GCNSubtarget &GCNSubtarget::initializeSubtargetDependencies(const Triple &TT,
if (FlatOffsetBitWidth == 0)
FlatOffsetBitWidth = 13;
- LocalMemorySize = AMDGPU::getLocalMemorySize(getTargetID().getGPUKind(),
- AMDGPU::isFullSIMDMode(*this));
+ LocalMemorySize =
+ AMDGPU::getLocalMemorySize(getTargetID().getGPUKind(), isFullSIMDMode());
AddressableLocalMemorySize = AMDGPU::getAddressableLocalMemorySize(
- getTargetID().getGPUKind(), AMDGPU::isFullSIMDMode(*this));
+ getTargetID().getGPUKind(), isFullSIMDMode());
// LDS Allocation Granularity calculated in bytes from dwords
LDSAllocationGranularity =
AMDGPU::getLdsDwGranularity(*this) * sizeof(uint32_t);
@@ -250,8 +250,7 @@ GCNSubtarget::GCNSubtarget(const Triple &TT, StringRef GPU, StringRef FS,
LLVM_DEBUG(dbgs() << "sramecc setting for subtarget: "
<< TargetID.getSramEccSetting() << '\n');
- NumWorkGroupSIMDs =
- AMDGPU::getNumWorkGroupSIMDs(AMDGPU::isFullSIMDMode(*this));
+ NumWorkGroupSIMDs = AMDGPU::getNumWorkGroupSIMDs(isFullSIMDMode());
TSInfo = std::make_unique<AMDGPUSelectionDAGInfo>();
diff --git a/llvm/lib/Target/AMDGPU/GCNSubtarget.h b/llvm/lib/Target/AMDGPU/GCNSubtarget.h
index 5f241024a0c2b..b0a4ab2ef28db 100644
--- a/llvm/lib/Target/AMDGPU/GCNSubtarget.h
+++ b/llvm/lib/Target/AMDGPU/GCNSubtarget.h
@@ -364,6 +364,11 @@ class GCNSubtarget final : public AMDGPUGenSubtargetInfo,
bool isCuModeEnabled() const { return EnableCuMode; }
+ /// \returns Whether a work-group runs on all of the block's SIMDs.
+ bool isFullSIMDMode() const {
+ return (HasGFX1250Insts && getGeneration() < GFX13) || !EnableCuMode;
+ }
+
bool isPreciseMemoryEnabled() const { return EnablePreciseMemory; }
bool hasFlatScrRegister() const { return hasFlatAddressSpace(); }
diff --git a/llvm/lib/TargetParser/AMDGPUTargetParser.cpp b/llvm/lib/TargetParser/AMDGPUTargetParser.cpp
index faab914fcb645..c4f0d8deba089 100644
--- a/llvm/lib/TargetParser/AMDGPUTargetParser.cpp
+++ b/llvm/lib/TargetParser/AMDGPUTargetParser.cpp
@@ -484,7 +484,7 @@ unsigned AMDGPU::getLocalMemorySize(GPUKind AK, bool FullSIMDMode) {
if (getFeatureBitset(AK).test(FEAT_HALF_ADDRESSABLE_PHYSICAL_LOCAL_MEMORY))
Size *= 2;
- // In CU mode the work-group runs on two SIMDs and reaches only their half.
+ // In half-SIMD mode the work-group reaches only half of the block.
if (!FullSIMDMode)
Size /= 2;
More information about the llvm-commits
mailing list