[llvm] [AMDGPU] Add object linking support for LDS and named barrier lowering in the middle end (PR #191645)
Shilei Tian via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 13 14:20:01 PDT 2026
================
@@ -910,7 +911,170 @@ class AMDGPULowerModuleLDS {
return KernelToCreatedDynamicLDS;
}
+ // Per-TU mode for link-time LDS resolution. Instead of computing a global
+ // layout, create per-function LDS struct declarations so the linker can
+ // assign offsets across TUs.
+ bool runOnModuleLinkTime(Module &M) {
+ bool Changed = superAlignLDSGlobals(M);
+ Changed |= eliminateConstantExprUsesOfLDSFromAllInstructions(M);
+
+ CallGraph CG(M);
+ FunctionVariableMap KernelLDSUses, FunctionLDSUses;
+ getUsesOfLDSByFunction(CG, M, KernelLDSUses, FunctionLDSUses);
+
+ if (KernelLDSUses.empty() && FunctionLDSUses.empty())
+ return Changed;
+
+ FunctionVariableMap AllLDSUses;
+ for (auto &[F, Vars] : KernelLDSUses)
+ AllLDSUses[F].insert(Vars.begin(), Vars.end());
+ for (auto &[F, Vars] : FunctionLDSUses)
+ AllLDSUses[F].insert(Vars.begin(), Vars.end());
+
+ // Named barriers are handled by AMDGPULowerExecSync; filter them out.
+ for (auto &[F, Vars] : AllLDSUses) {
+ SmallVector<GlobalVariable *> Barriers;
+ for (GlobalVariable *V : Vars)
+ if (AMDGPU::isNamedBarrier(*V))
+ Barriers.push_back(V);
+ for (GlobalVariable *V : Barriers)
+ Vars.erase(V);
+ }
+
+ // Build reverse map: LDS variable -> functions that use it.
+ DenseMap<GlobalVariable *, SmallVector<Function *, 4>> VarToFuncs;
+ for (auto &[F, Vars] : AllLDSUses) {
+ for (GlobalVariable *V : Vars)
+ VarToFuncs[V].push_back(F);
+ }
+
+ // A variable is function-scope iff it has local linkage and exactly one
+ // user function. Everything else is global-scope and must remain as a
+ // standalone external declaration so the linker can assign a single shared
+ // offset.
+ DenseSet<GlobalVariable *> GlobalScopeVars;
+ DenseSet<GlobalVariable *> InternalMultiUserVars;
+ for (auto &[V, Funcs] : VarToFuncs) {
+ if (!V->hasLocalLinkage() || Funcs.size() > 1) {
+ GlobalScopeVars.insert(V);
+ if (V->hasLocalLinkage())
+ InternalMultiUserVars.insert(V);
+ }
+ }
+
+ // Wrap function-scope LDS into per-function structs (unchanged logic,
+ // but global-scope variables are excluded from the set).
+ SmallVector<std::pair<Function *, GlobalVariable *>, 4> FuncToLdsStruct;
+ DenseSet<GlobalVariable *> AllReplacedVars;
+ for (auto &KV : AllLDSUses) {
+ Function *F = KV.first;
+ DenseSet<GlobalVariable *> FuncScopeVars;
+ for (GlobalVariable *V : KV.second)
+ if (!GlobalScopeVars.count(V))
+ FuncScopeVars.insert(V);
+
+ if (FuncScopeVars.empty())
+ continue;
+
+ std::string StructName = ("__amdgpu_lds." + F->getName()).str();
+ LDSVariableReplacement Replacement =
+ createLDSVariableReplacement(M, StructName, FuncScopeVars);
+
+ GlobalVariable *SGV = Replacement.SGV;
+ SGV->setLinkage(GlobalValue::ExternalLinkage);
+ SGV->setInitializer(nullptr);
+ FuncToLdsStruct.push_back({F, SGV});
+
+ replaceLDSVariablesWithStruct(
+ M, FuncScopeVars, Replacement, [F](const Use &U) {
+ auto *I = dyn_cast<Instruction>(U.getUser());
+ return I && I->getFunction() == F;
+ });
+
+ AllReplacedVars.insert(FuncScopeVars.begin(), FuncScopeVars.end());
+ }
+
+ // Internal-linkage LDS variables used by multiple functions would collide
+ // across TUs if promoted individually to external linkage (same name in
+ // different TUs). Pack them into a single per-module struct with a
+ // module-unique name so the linker treats them as one allocation unit.
+ if (!InternalMultiUserVars.empty()) {
+ SmallString<256> ModSuffix(M.getSourceFileName());
+ std::replace_if(
+ ModSuffix.begin(), ModSuffix.end(),
+ [](char Ch) { return !isAlnum(Ch); }, '_');
----------------
shiltian wrote:
I updated it to use `getUniqueModuleId` now. WDYT?
https://github.com/llvm/llvm-project/pull/191645
More information about the llvm-commits
mailing list