[llvm] [AMDGPU] Make LowerModuleLDS a noop on an already lowered module (PR #217075)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 25 21:57:58 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/217075

>From 833d9248a252e28f819abfff532fe87f69704e22 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 18 Aug 2026 18:43:13 +0200
Subject: [PATCH 1/6] [AMDGPU] Make LowerModuleLDS a noop on an already lowered
 module

Full LTO reruns the pass on each codegen partition, where it matched its own lowered structs and re-expanded their constexpr uses
---
 .../AMDGPU/AMDGPULowerModuleLDSPass.cpp       | 18 +++++++--
 llvm/test/CodeGen/AMDGPU/lds-run-twice.ll     | 40 ++++++++++++++++++-
 2 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
index ef86d279d193b..4dd072c6f402d 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
@@ -1078,11 +1078,16 @@ class AMDGPULowerModuleLDS {
   }
 
   bool runOnModuleNormal(Module &M) {
-    CallGraph CG = CallGraph(M);
     bool Changed = superAlignLDSGlobals(M);
 
-    Changed |=
-        eliminateGVConstantExprUsesFromAllInstructions(M, isLDSVariableToLower);
+    // Nothing left to lower on the codegen-partition rerun.
+    if (none_of(M.globals(), isNotYetLoweredLDSVariable))
+      return Changed;
+
+    CallGraph CG = CallGraph(M);
+
+    Changed |= eliminateGVConstantExprUsesFromAllInstructions(
+        M, isNotYetLoweredLDSVariable);
 
     Changed = true; // todo: narrow this down
 
@@ -1258,7 +1263,7 @@ class AMDGPULowerModuleLDS {
     }
 
     for (auto &GV : make_early_inc_range(M.globals()))
-      if (AMDGPU::isLDSVariableToLower(GV)) {
+      if (isNotYetLoweredLDSVariable(GV)) {
         // probably want to remove from used lists
         GV.removeDeadConstantUsers();
         if (GV.use_empty())
@@ -1269,6 +1274,11 @@ class AMDGPULowerModuleLDS {
   }
 
 private:
+  // An absolute address means a previous run already placed the variable.
+  static bool isNotYetLoweredLDSVariable(const GlobalVariable &GV) {
+    return isLDSVariableToLower(GV) && !GV.isAbsoluteSymbolRef();
+  }
+
   // Increase the alignment of LDS globals if necessary to maximise the chance
   // that we can use aligned LDS instructions to access them.
   static bool superAlignLDSGlobals(Module &M) {
diff --git a/llvm/test/CodeGen/AMDGPU/lds-run-twice.ll b/llvm/test/CodeGen/AMDGPU/lds-run-twice.ll
index 2f92f7e32a239..6b244e0531523 100644
--- a/llvm/test/CodeGen/AMDGPU/lds-run-twice.ll
+++ b/llvm/test/CodeGen/AMDGPU/lds-run-twice.ll
@@ -3,14 +3,50 @@
 ; RUN: diff -ub %t.ll %t.second.ll -I ".*ModuleID.*"
 
 ; Check AMDGPULowerModuleLDS can run more than once on the same module, and that
-; the second run is a no-op.
+; the second run is a no-op, as full LTO reruns it on each codegen partition.
 
- at dynlds = external addrspace(3) global [0 x i32], align 4
+; Kernel-only static LDS. @lds2 pushes @lds to a non-zero offset, so the first
+; run leaves a constexpr GEP, which the second run used to re-expand.
 @lds = internal unnamed_addr addrspace(3) global i32 poison, align 4
+ at lds2 = internal unnamed_addr addrspace(3) global i64 poison, align 8
+
+; Called from a function shared by two kernels: module struct plus lookup table.
+ at lds.function = internal addrspace(3) global [8 x i16] poison, align 2
+
+; Dynamic LDS from a function lowers to per-kernel shadows plus an offset table.
+ at dynlds.function = external hidden addrspace(3) global [0 x float], align 8
+
+; Dynamic LDS used only from a kernel is left in place.
+ at dynlds = external addrspace(3) global [0 x i32], align 4
+
+; Never lowered, so these survive the first run without an absolute address.
+ at const.lds = internal addrspace(3) constant [4 x i32] undef, align 4
+ at initialized.lds = internal addrspace(3) global i16 0, align 2
+
+; Escapes into a global initializer, so lowering cannot place it either.
+ at escaped = internal addrspace(3) global i32 poison, align 4
+ at escape.ptr = addrspace(1) global ptr addrspace(3) @escaped, align 4
+
+define void @helper() {
+  store i16 1, ptr addrspace(3) getelementptr inbounds ([8 x i16], ptr addrspace(3) @lds.function, i32 0, i32 3), align 2
+  store float 2.0, ptr addrspace(3) @dynlds.function, align 8
+  ret void
+}
 
 define amdgpu_kernel void @test() {
 entry:
+  call void @helper()
   store i32 0, ptr addrspace(3) @dynlds
   store i32 1, ptr addrspace(3) @lds
+  store i64 2, ptr addrspace(3) @lds2
+  store i32 3, ptr addrspace(3) @escaped
+  %c = load i32, ptr addrspace(3) getelementptr inbounds ([4 x i32], ptr addrspace(3) @const.lds, i32 0, i32 2), align 4
+  store i16 9, ptr addrspace(3) @initialized.lds, align 2
+  ret void
+}
+
+define amdgpu_kernel void @test2() {
+entry:
+  call void @helper()
   ret void
 }

>From da195bc577e9d613d1d58585040f9be47966ba3d Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 18 Aug 2026 18:56:10 +0200
Subject: [PATCH 2/6] undef

---
 llvm/test/CodeGen/AMDGPU/lds-run-twice.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/AMDGPU/lds-run-twice.ll b/llvm/test/CodeGen/AMDGPU/lds-run-twice.ll
index 6b244e0531523..c022a6321323c 100644
--- a/llvm/test/CodeGen/AMDGPU/lds-run-twice.ll
+++ b/llvm/test/CodeGen/AMDGPU/lds-run-twice.ll
@@ -20,7 +20,7 @@
 @dynlds = external addrspace(3) global [0 x i32], align 4
 
 ; Never lowered, so these survive the first run without an absolute address.
- at const.lds = internal addrspace(3) constant [4 x i32] undef, align 4
+ at const.lds = internal addrspace(3) constant [4 x i32] poison, align 4
 @initialized.lds = internal addrspace(3) global i16 0, align 2
 
 ; Escapes into a global initializer, so lowering cannot place it either.

>From ba215f98c01da765a4375828754f0bbed8979cd4 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 19 Aug 2026 17:45:32 +0200
Subject: [PATCH 3/6] Address comment

---
 llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
index 4dd072c6f402d..65528ca798031 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
@@ -1093,7 +1093,8 @@ class AMDGPULowerModuleLDS {
 
     // For each kernel, what variables does it access directly or through
     // callees
-    GVUsesInfoTy LDSUsesInfo = getTransitiveUsesOfLDSForLowering(CG, M);
+    GVUsesInfoTy LDSUsesInfo =
+        getTransitiveUsesOfGV(CG, M, isNotYetLoweredLDSVariable);
 
     // For each variable accessed through callees, which kernels access it
     VariableFunctionMap LDSToKernelsThatNeedToAccessItIndirectly;

>From 74ca19bc18d90d8dd4bb5c2e9a3dd58ec5a23fff Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 19 Aug 2026 18:46:54 +0200
Subject: [PATCH 4/6] comments

---
 llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
index 65528ca798031..32bad6a08dc34 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
@@ -1078,13 +1078,13 @@ class AMDGPULowerModuleLDS {
   }
 
   bool runOnModuleNormal(Module &M) {
-    bool Changed = superAlignLDSGlobals(M);
-
     // Nothing left to lower on the codegen-partition rerun.
     if (none_of(M.globals(), isNotYetLoweredLDSVariable))
-      return Changed;
+      return false;
 
-    CallGraph CG = CallGraph(M);
+    bool Changed = superAlignLDSGlobals(M);
+
+    CallGraph CG(M);
 
     Changed |= eliminateGVConstantExprUsesFromAllInstructions(
         M, isNotYetLoweredLDSVariable);
@@ -1093,8 +1093,7 @@ class AMDGPULowerModuleLDS {
 
     // For each kernel, what variables does it access directly or through
     // callees
-    GVUsesInfoTy LDSUsesInfo =
-        getTransitiveUsesOfGV(CG, M, isNotYetLoweredLDSVariable);
+    GVUsesInfoTy LDSUsesInfo = getTransitiveUsesOfLDSForLowering(CG, M);
 
     // For each variable accessed through callees, which kernels access it
     VariableFunctionMap LDSToKernelsThatNeedToAccessItIndirectly;

>From 234ed6174efa34e3558d330c3e603a18dbe08138 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 19 Aug 2026 21:50:52 +0200
Subject: [PATCH 5/6] rm checks

---
 llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
index 32bad6a08dc34..515960182a1f5 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
@@ -1082,14 +1082,12 @@ class AMDGPULowerModuleLDS {
     if (none_of(M.globals(), isNotYetLoweredLDSVariable))
       return false;
 
-    bool Changed = superAlignLDSGlobals(M);
+    superAlignLDSGlobals(M);
 
     CallGraph CG(M);
 
-    Changed |= eliminateGVConstantExprUsesFromAllInstructions(
-        M, isNotYetLoweredLDSVariable);
-
-    Changed = true; // todo: narrow this down
+    eliminateGVConstantExprUsesFromAllInstructions(M,
+                                                   isNotYetLoweredLDSVariable);
 
     // For each kernel, what variables does it access directly or through
     // callees
@@ -1270,7 +1268,7 @@ class AMDGPULowerModuleLDS {
           GV.eraseFromParent();
       }
 
-    return Changed;
+    return true;
   }
 
 private:

>From a4aa4072b42b7d81c3b64956eed932129511a665 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 26 Aug 2026 06:57:43 +0200
Subject: [PATCH 6/6] Address comment

---
 llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
index 515960182a1f5..3371a34cda7b6 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
@@ -1078,11 +1078,9 @@ class AMDGPULowerModuleLDS {
   }
 
   bool runOnModuleNormal(Module &M) {
-    // Nothing left to lower on the codegen-partition rerun.
-    if (none_of(M.globals(), isNotYetLoweredLDSVariable))
-      return false;
+    bool Changed = superAlignLDSGlobals(M);
 
-    superAlignLDSGlobals(M);
+    Changed |= any_of(M.globals(), isNotYetLoweredLDSVariable);
 
     CallGraph CG(M);
 
@@ -1268,7 +1266,7 @@ class AMDGPULowerModuleLDS {
           GV.eraseFromParent();
       }
 
-    return true;
+    return Changed;
   }
 
 private:



More information about the llvm-commits mailing list