LLVM 24.0.0git
AMDGPULowerExecSync.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Lower LDS global variables with target extension type "amdgpu.named.barrier"
10// that require specialized address assignment. It assigns a unique
11// barrier identifier to each named-barrier LDS variable and encodes
12// this identifier within the !absolute_symbol metadata of that global.
13// This encoding ensures that subsequent LDS lowering passes can process these
14// barriers correctly without conflicts.
15//
16//===----------------------------------------------------------------------===//
17
18#include "AMDGPU.h"
19#include "AMDGPUMemoryUtils.h"
20#include "AMDGPUTargetMachine.h"
22#include "llvm/IR/Constants.h"
26#include "llvm/Pass.h"
28
29#define DEBUG_TYPE "amdgpu-lower-exec-sync"
30
31using namespace llvm;
32using namespace AMDGPU;
33
34namespace {
35
36// Write the specified address into metadata where it can be retrieved by
37// the assembler. Format is a half open range, [Address Address+1)
38static void recordLDSAbsoluteAddress(Module *M, GlobalVariable *GV,
39 uint32_t Address) {
40 LLVMContext &Ctx = M->getContext();
41 auto *IntTy = M->getDataLayout().getIntPtrType(Ctx, AMDGPUAS::LOCAL_ADDRESS);
42 auto *MinC = ConstantAsMetadata::get(ConstantInt::get(IntTy, Address));
43 auto *MaxC = ConstantAsMetadata::get(ConstantInt::get(IntTy, Address + 1));
44 GV->setMetadata(LLVMContext::MD_absolute_symbol,
45 MDNode::get(Ctx, {MinC, MaxC}));
46}
47
48/// Get next available ID for sync object. The ID allocation is tracked in \p
49/// MaxNumGroup groups by \p NextAvailableIDTracker. Each call of the function
50/// will ask for \p IDCnt against all the \p Kernels, it will return the
51/// maximum of the available ones and update the ID tracker.
52template <typename T>
53unsigned allocateExecSyncID(T &NextAvailableIDTracker,
54 ArrayRef<Function *> Kernels, unsigned GroupID,
55 unsigned MaxNumGroup, unsigned IDCnt) {
56 constexpr unsigned InitialVal = 1;
57 unsigned NextID = InitialVal;
58 for (Function *F : Kernels) {
59 const SmallVectorImpl<unsigned> &NextAvailableID =
60 NextAvailableIDTracker.lookup(F);
61 unsigned ID = InitialVal;
62 if (!NextAvailableID.empty())
63 ID = NextAvailableID[GroupID];
64
65 if (ID > NextID)
66 NextID = ID;
67 }
68
69 // Bump the next available id for the kernels.
70 for (Function *F : Kernels) {
71 auto Inserted = NextAvailableIDTracker.try_emplace(F);
72 // Initialize on first insertion.
73 if (Inserted.second)
74 Inserted.first->second.assign(MaxNumGroup, InitialVal);
75 // Update the available ID.
76 Inserted.first->second[GroupID] = NextID + IDCnt;
77 }
78 return NextID;
79}
80
81// Main utility function for special LDS variables lowering.
82static bool lowerExecSyncGlobalVariables(Module &M, GVUsesInfoTy &GVUsesInfo) {
83 bool Changed = false;
84 const DataLayout &DL = M.getDataLayout();
85
86 constexpr unsigned NumBarScopes = 1;
89
90 for (auto &[F, GVs] : GVUsesInfo.IndirectAccess) {
91 for (auto *GV : GVs) {
92 if (!isNamedBarrier(*GV) || GV->isAbsoluteSymbolRef())
93 continue;
94 auto Iter = AllocationQ.find(GV);
95 if (Iter == AllocationQ.end())
96 AllocationQ.insert({GV, {F}});
97 else
98 Iter->second.push_back(F);
99 }
100 }
101
102 for (auto &[F, GVs] : GVUsesInfo.DirectAccess) {
103 for (auto *GV : GVs) {
104 if (!isNamedBarrier(*GV) || GV->isAbsoluteSymbolRef())
105 continue;
106 auto Iter = AllocationQ.find(GV);
107 if (Iter == AllocationQ.end())
108 AllocationQ.insert({GV, {F}});
109 else
110 Iter->second.push_back(F);
111 }
112 }
113
114 sort(AllocationQ, [](std::pair<GlobalVariable *, SmallVector<Function *>> A,
116 // First order by number of kernels that access the GlobalVariable.
117 if (A.second.size() != B.second.size())
118 return A.second.size() > B.second.size();
119
120 // Then order by their names so we always get a deterministic order.
121 return A.first->getName() < B.first->getName();
122 });
123
124 for (auto &[GV, Kernels] : AllocationQ) {
125 unsigned Offset;
126 if (TargetExtType *ExtTy = isNamedBarrier(*GV)) {
127 unsigned BarrierScope = ExtTy->getIntParameter(0);
128 unsigned BarCnt = GV->getGlobalSize(DL) / 16;
129
130 unsigned BarID = allocateExecSyncID(KernelBarrierIDs, Kernels,
131 BarrierScope, NumBarScopes, BarCnt);
132
133 LLVM_DEBUG(GV->printAsOperand(dbgs(), false);
134 dbgs() << " was assigned barrier id: " << BarID
135 << " id-count: " << BarCnt << "\n");
136 // 4 bits for alignment, 5 bits for the barrier num,
137 // 3 bits for the barrier scope
138 Offset = 0x802000u | BarrierScope << 9 | BarID << 4;
139 } else {
140 llvm_unreachable("Unhandled special variable type.");
141 }
142
143 recordLDSAbsoluteAddress(&M, GV, Offset);
144 }
145
146 // Also erase those special LDS variables from indirect_access.
147 for (auto &K : GVUsesInfo.IndirectAccess) {
148 assert(isKernel(*K.first));
149 K.second.remove_if([](GlobalVariable *GV) { return isNamedBarrier(*GV); });
150 }
151 return Changed;
152}
153
154static bool hasBarrierToLower(const GVUsesInfoTy &GVUsesInfo) {
155 for (auto &Map : {GVUsesInfo.DirectAccess, GVUsesInfo.IndirectAccess}) {
156 for (auto &[Fn, GVs] : Map) {
157 for (auto &GV : GVs) {
158 if (AMDGPU::isNamedBarrier(*GV))
159 return true;
160 }
161 }
162 }
163 return false;
164}
165
166// With object linking, barrier ID assignment is deferred to the linker.
167// Externalize named barrier globals and emit self-contained metadata so the
168// AsmPrinter can generate the callgraph entries the linker needs.
169static bool handleNamedBarriersForObjectLinking(Module &M) {
171 for (GlobalVariable &GV : M.globals()) {
172 if (!isNamedBarrier(GV) || GV.use_empty())
173 continue;
174 for (User *U : GV.users()) {
175 if (auto *I = dyn_cast<Instruction>(U))
176 BarrierToFuncs[&GV].insert(I->getFunction());
177 }
178 }
179 if (BarrierToFuncs.empty())
180 return false;
181
182 LLVMContext &Ctx = M.getContext();
183 NamedMDNode *BarMD = M.getOrInsertNamedMetadata("amdgpu.named_barrier.uses");
184
185 std::string ModuleId;
186 ModuleId = getUniqueModuleId(&M);
187 assert(!ModuleId.empty() &&
188 "modules with named barriers should have a unique ID");
189 for (auto &[V, Funcs] : BarrierToFuncs) {
190 if (V->hasLocalLinkage())
191 V->setName("__amdgpu_named_barrier." + V->getName() + ModuleId);
192 else if (!V->getName().starts_with("__amdgpu_named_barrier"))
193 V->setName("__amdgpu_named_barrier." + V->getName());
194 V->setInitializer(nullptr);
195 V->setLinkage(GlobalValue::ExternalLinkage);
196
198 Ops.push_back(ValueAsMetadata::get(V));
199 for (Function *F : Funcs)
200 Ops.push_back(ValueAsMetadata::get(F));
201 BarMD->addOperand(MDNode::get(Ctx, Ops));
202 }
203 return true;
204}
205
206static bool runLowerExecSyncGlobals(Module &M) {
208 return handleNamedBarriersForObjectLinking(M);
209
210 CallGraph CG = CallGraph(M);
211 bool Changed = false;
212 Changed |=
214
215 // For each kernel, what variables does it access directly or through
216 // callees
218
219 if (hasBarrierToLower(LDSUsesInfo)) {
220 // Special LDS variables need special address assignment
221 Changed |= lowerExecSyncGlobalVariables(M, LDSUsesInfo);
222 }
223
224 return Changed;
225}
226
227class AMDGPULowerExecSyncLegacy : public ModulePass {
228public:
229 static char ID;
230 AMDGPULowerExecSyncLegacy() : ModulePass(ID) {}
231 bool runOnModule(Module &M) override;
232};
233
234} // namespace
235
236char AMDGPULowerExecSyncLegacy::ID = 0;
237char &llvm::AMDGPULowerExecSyncLegacyPassID = AMDGPULowerExecSyncLegacy::ID;
238
239INITIALIZE_PASS_BEGIN(AMDGPULowerExecSyncLegacy, DEBUG_TYPE,
240 "AMDGPU lowering of execution synchronization", false,
241 false)
243INITIALIZE_PASS_END(AMDGPULowerExecSyncLegacy, DEBUG_TYPE,
244 "AMDGPU lowering of execution synchronization", false,
245 false)
246
247bool AMDGPULowerExecSyncLegacy::runOnModule(Module &M) {
248 return runLowerExecSyncGlobals(M);
249}
250
252 return new AMDGPULowerExecSyncLegacy();
253}
254
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
The AMDGPU TargetMachine interface definition for hw codegen targets.
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
This file provides interfaces used to build and manipulate a call graph, which is a very useful tool ...
This file contains the declarations for the subclasses of Constant, which represent the different fla...
#define DEBUG_TYPE
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
#define T
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
#define LLVM_DEBUG(...)
Definition Debug.h:119
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
The basic data container for the call graph of a Module of IR.
Definition CallGraph.h:72
static ConstantAsMetadata * get(Constant *C)
Definition Metadata.h:537
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
bool empty() const
Definition DenseMap.h:171
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition DenseMap.h:284
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set a particular kind of metadata attachment.
LLVM_ABI bool isAbsoluteSymbolRef() const
Returns whether this is a reference to an absolute symbol.
Definition Globals.cpp:526
@ ExternalLinkage
Externally visible function.
Definition GlobalValue.h:53
LLVM_ABI uint64_t getGlobalSize(const DataLayout &DL) const
Get the size of this global variable in bytes.
Definition Globals.cpp:640
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1567
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
iterator find(const KeyT &Key)
Definition MapVector.h:156
iterator end()
Definition MapVector.h:69
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition MapVector.h:126
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
A tuple of MDNodes.
Definition Metadata.h:1755
LLVM_ABI void addOperand(MDNode *M)
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Class to represent target extensions types, which are generally unintrospectable from target-independ...
Target-Independent Code Generator Pass Configuration Options.
static LLVM_ABI ValueAsMetadata * get(Value *V)
Definition Metadata.cpp:510
iterator_range< user_iterator > users()
Definition Value.h:426
LLVM_ABI void printAsOperand(raw_ostream &O, bool PrintType=true, const Module *M=nullptr) const
Print the name of this Value out to the specified raw_ostream.
bool use_empty() const
Definition Value.h:346
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ LOCAL_ADDRESS
Address space for local memory.
GVUsesInfoTy getTransitiveUsesOfLDSForLowering(const CallGraph &CG, Module &M)
Collects all uses of LDS Global Variables in M using getUsesOfGVByFunction, with isLDSVariableToLower...
bool eliminateGVConstantExprUsesFromAllInstructions(Module &M, function_ref< bool(const GlobalVariable &)> Filter)
Iterates over all GlobalVariables in M, and whenever Filter returns true, replace all constant users ...
LLVM_READNONE constexpr bool isKernel(CallingConv::ID CC)
TargetExtType * isNamedBarrier(const GlobalVariable &GV)
bool isLDSVariableToLower(const GlobalVariable &GV)
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:577
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
char & AMDGPULowerExecSyncLegacyPassID
LLVM_ABI std::string getUniqueModuleId(Module *M)
Produce a unique identifier for this module by taking the MD5 sum of the names of the module's strong...
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1636
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
ModulePass * createAMDGPULowerExecSyncLegacyPass()
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
FunctionVariableMap DirectAccess
FunctionVariableMap IndirectAccess