LLVM 24.0.0git
AMDGPUSwLowerLDS.cpp
Go to the documentation of this file.
1//===-- AMDGPUSwLowerLDS.cpp -----------------------------------------===//
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// This pass lowers the local data store, LDS, uses in kernel and non-kernel
10// functions in module to use dynamically allocated global memory.
11// Packed LDS Layout is emulated in the global memory.
12// The lowered memory instructions from LDS to global memory are then
13// instrumented for address sanitizer, to catch addressing errors.
14// This pass only work when address sanitizer has been enabled and has
15// instrumented the IR. It identifies that IR has been instrumented using
16// "nosanitize_address" module flag.
17//
18// Replacement of Kernel LDS accesses:
19// For a kernel, LDS access can be static or dynamic which are direct
20// (accessed within kernel) and indirect (accessed through non-kernels).
21// All these LDS accesses corresponding to kernel will be packed together,
22// where all static LDS accesses will be allocated first and then dynamic
23// LDS follows. The total size with alignment is calculated. A new LDS global
24// will be created for the kernel called "SW LDS" and it will have the
25// attribute "amdgpu-lds-size" attached with value of the size calculated.
26// All the LDS accesses in the module will be replaced by GEP with offset
27// into the "Sw LDS".
28// A new "llvm.amdgcn.<kernel>.dynlds" is created per kernel accessing
29// the dynamic LDS. This will be marked used by kernel and will have
30// MD_absolue_symbol metadata set to total static LDS size, Since dynamic
31// LDS allocation starts after all static LDS allocation.
32//
33// A device global memory equal to the total LDS size will be allocated.
34// At the prologue of the kernel, a single work-item from the
35// work-group, does a "malloc" and stores the pointer of the
36// allocation in "SW LDS".
37//
38// To store the offsets corresponding to all LDS accesses, another global
39// variable is created which will be called "SW LDS metadata" in this pass.
40// - SW LDS Global:
41// It is LDS global of ptr type with name
42// "llvm.amdgcn.sw.lds.<kernel-name>".
43// - Metadata Global:
44// It is of struct type, with n members. n equals the number of LDS
45// globals accessed by the kernel(direct and indirect). Each member of
46// struct is another struct of type {i32, i32, i32}. First member
47// corresponds to offset, second member corresponds to size of LDS global
48// being replaced and third represents the total aligned size. It will
49// have name "llvm.amdgcn.sw.lds.<kernel-name>.md". This global will have
50// an initializer with static LDS related offsets and sizes initialized.
51// But for dynamic LDS related entries, offsets will be initialized to
52// previous static LDS allocation end offset. Sizes for them will be zero
53// initially. These dynamic LDS offset and size values will be updated
54// within the kernel, since kernel can read the dynamic LDS size
55// allocation done at runtime with query to "hidden_dynamic_lds_size"
56// hidden kernel argument.
57//
58// At the epilogue of kernel, allocated memory would be made free by the same
59// single work-item.
60//
61// Replacement of non-kernel LDS accesses:
62// Multiple kernels can access the same non-kernel function.
63// All the kernels accessing LDS through non-kernels, or reaching
64// non-kernels with LDS instructions, are sorted and assigned a kernel-id.
65// All the LDS globals accessed by non-kernels are sorted. This
66// information is used to build two tables:
67// - Base table:
68// Base table will have single row, with elements of the row
69// placed as per kernel ID. Each element in the row corresponds
70// to ptr of "SW LDS" variable created for that kernel.
71// - Offset table:
72// Offset table will have multiple rows and columns.
73// Rows are assumed to be from 0 to (n-1). n is total number
74// of kernels accessing the LDS through non-kernels.
75// Each row will have m elements. m is the total number of
76// unique LDS globals accessed by all non-kernels.
77// Each element in the row correspond to the ptr of
78// the replacement of LDS global done by that particular kernel.
79// A LDS variable in non-kernel will be replaced based on the information
80// from base and offset tables. Based on kernel-id query, ptr of "SW
81// LDS" for that corresponding kernel is obtained from base table.
82// The Offset into the base "SW LDS" is obtained from
83// corresponding element in offset table. With this information, replacement
84// value is obtained.
85//===----------------------------------------------------------------------===//
86
87#include "AMDGPU.h"
89#include "AMDGPUMemoryUtils.h"
91#include "llvm/ADT/StringRef.h"
94#include "llvm/IR/Constants.h"
95#include "llvm/IR/DIBuilder.h"
96#include "llvm/IR/DebugInfo.h"
98#include "llvm/IR/IRBuilder.h"
100#include "llvm/IR/Instructions.h"
101#include "llvm/IR/MDBuilder.h"
103#include "llvm/Pass.h"
107
108#include <algorithm>
109
110#define DEBUG_TYPE "amdgpu-sw-lower-lds"
111#define COV5_HIDDEN_DYN_LDS_SIZE_ARG 15
112
113using namespace llvm;
114using namespace AMDGPU;
115
116namespace {
117
118using DomTreeCallback = function_ref<DominatorTree *(Function &F)>;
119
120struct LDSAccessTypeInfo {
121 SetVector<GlobalVariable *> StaticLDSGlobals;
122 SetVector<GlobalVariable *> DynamicLDSGlobals;
123};
124
125// Struct to hold all the Metadata required for a kernel
126// to replace a LDS global uses with corresponding offset
127// in to device global memory.
128struct KernelLDSParameters {
129 GlobalVariable *SwLDS = nullptr;
130 GlobalVariable *SwDynLDS = nullptr;
131 GlobalVariable *SwLDSMetadata = nullptr;
132 LDSAccessTypeInfo DirectAccess;
133 LDSAccessTypeInfo IndirectAccess;
135 LDSToReplacementIndicesMap;
136 uint32_t MallocSize = 0;
137 uint32_t LDSSize = 0;
138 SmallVector<std::pair<uint32_t, uint32_t>, 64> RedzoneOffsetAndSizeVector;
139};
140
141// Struct to store information for creation of offset table
142// for all the non-kernel LDS accesses.
143struct NonKernelLDSParameters {
144 GlobalVariable *LDSBaseTable = nullptr;
145 GlobalVariable *LDSOffsetTable = nullptr;
146 SetVector<Function *> OrderedKernels;
147 SetVector<GlobalVariable *> OrdereLDSGlobals;
148};
149
150struct AsanInstrumentInfo {
151 int Scale = 0;
152 uint32_t Offset = 0;
153 SetVector<Instruction *> Instructions;
154};
155
156struct FunctionsAndLDSAccess {
157 MapVector<Function *, KernelLDSParameters> KernelToLDSParametersMap;
158 SetVector<Function *> KernelsWithIndirectLDSAccess;
159 SetVector<Function *> NonKernelsWithLDSInstructions;
160 SetVector<GlobalVariable *> AllNonKernelLDSAccess;
161 FunctionVariableMap NonKernelToLDSAccessMap;
162};
163
164class AMDGPUSwLowerLDS {
165public:
166 AMDGPUSwLowerLDS(Module &Mod, DomTreeCallback Callback)
167 : M(Mod), IRB(M), DTCallback(Callback) {}
168 bool run();
169 void getUsesOfLDSByNonKernels();
170 void getNonKernelsWithLDSInstructions(const CallGraph &CG);
172 getOrderedIndirectLDSAccessingKernels(SetVector<Function *> &Kernels);
174 getOrderedNonKernelAllLDSGlobals(SetVector<GlobalVariable *> &Variables);
175 void buildSwLDSGlobal(Function *Func);
176 void buildSwDynLDSGlobal(Function *Func);
177 void populateSwMetadataGlobal(Function *Func);
178 void populateSwLDSAttributeAndMetadata(Function *Func);
179 void populateLDSToReplacementIndicesMap(Function *Func);
180 void getLDSMemoryInstructions(Function *Func,
181 SetVector<Instruction *> &LDSInstructions);
182 void replaceKernelLDSAccesses(Function *Func);
183 Value *getTranslatedGlobalMemoryPtrOfLDS(Value *LoadMallocPtr, Value *LDSPtr);
184 Value *getTranslatedLDSPtrOfFlat(Value *LoadMallocPtr, Value *FlatPtr,
185 Type *LDSPtrTy);
186 void translateLDSMemoryOperationsToGlobalMemory(
187 Function *Func, Value *LoadMallocPtr,
188 SetVector<Instruction *> &LDSInstructions);
189 void poisonRedzones(Function *Func, Value *MallocPtr);
190 void lowerKernelLDSAccesses(Function *Func, DomTreeUpdater &DTU);
191 void buildNonKernelLDSOffsetTable(NonKernelLDSParameters &NKLDSParams);
192 void buildNonKernelLDSBaseTable(NonKernelLDSParameters &NKLDSParams);
193 Constant *
194 getAddressesOfVariablesInKernel(Function *Func,
195 SetVector<GlobalVariable *> &Variables);
196 void lowerNonKernelLDSAccesses(Function *Func,
197 SetVector<GlobalVariable *> &LDSGlobals,
198 NonKernelLDSParameters &NKLDSParams);
199 void
200 updateMallocSizeForDynamicLDS(Function *Func, Value **CurrMallocSize,
201 Value *HiddenDynLDSSize,
202 SetVector<GlobalVariable *> &DynamicLDSGlobals);
203 void initAsanInfo();
204
205private:
206 Module &M;
207 IRBuilder<> IRB;
208 DomTreeCallback DTCallback;
209 FunctionsAndLDSAccess FuncLDSAccessInfo;
210 AsanInstrumentInfo AsanInfo;
211};
212
213template <typename T> SetVector<T> sortByName(std::vector<T> &&V) {
214 // Sort the vector of globals or Functions based on their name.
215 // Returns a SetVector of globals/Functions.
216 sort(V, [](const auto *L, const auto *R) {
217 return L->getName() < R->getName();
218 });
220}
221
222SetVector<GlobalVariable *> AMDGPUSwLowerLDS::getOrderedNonKernelAllLDSGlobals(
223 SetVector<GlobalVariable *> &Variables) {
224 // Sort all the non-kernel LDS accesses based on their name.
225 return sortByName(
226 std::vector<GlobalVariable *>(Variables.begin(), Variables.end()));
227}
228
229SetVector<Function *> AMDGPUSwLowerLDS::getOrderedIndirectLDSAccessingKernels(
230 SetVector<Function *> &Kernels) {
231 // Sort the non-kernels accessing LDS based on their name.
232 // Also assign a kernel ID metadata based on the sorted order.
233 LLVMContext &Ctx = M.getContext();
234 if (Kernels.size() > UINT32_MAX) {
235 report_fatal_error("Unimplemented SW LDS lowering for > 2**32 kernels");
236 }
237 SetVector<Function *> OrderedKernels =
238 sortByName(std::vector<Function *>(Kernels.begin(), Kernels.end()));
239 for (size_t i = 0; i < Kernels.size(); i++) {
240 Metadata *AttrMDArgs[1] = {
242 };
243 Function *Func = OrderedKernels[i];
244 Func->setMetadata("llvm.amdgcn.lds.kernel.id",
245 MDNode::get(Ctx, AttrMDArgs));
246 }
247 return OrderedKernels;
248}
249
250// LDS memory operations and casts between flat and LDS.
251static bool isLDSMemoryInstruction(const Instruction &Inst) {
252 if (auto *LI = dyn_cast<LoadInst>(&Inst))
253 return LI->getPointerAddressSpace() == AMDGPUAS::LOCAL_ADDRESS;
254 if (auto *SI = dyn_cast<StoreInst>(&Inst))
255 return SI->getPointerAddressSpace() == AMDGPUAS::LOCAL_ADDRESS;
256 if (auto *RMW = dyn_cast<AtomicRMWInst>(&Inst))
257 return RMW->getPointerAddressSpace() == AMDGPUAS::LOCAL_ADDRESS;
258 if (auto *XCHG = dyn_cast<AtomicCmpXchgInst>(&Inst))
259 return XCHG->getPointerAddressSpace() == AMDGPUAS::LOCAL_ADDRESS;
260 if (auto *ASC = dyn_cast<AddrSpaceCastInst>(&Inst)) {
261 unsigned SrcAS = ASC->getSrcAddressSpace();
262 unsigned DstAS = ASC->getDestAddressSpace();
263 return (SrcAS == AMDGPUAS::LOCAL_ADDRESS &&
264 DstAS == AMDGPUAS::FLAT_ADDRESS) ||
265 (SrcAS == AMDGPUAS::FLAT_ADDRESS &&
266 DstAS == AMDGPUAS::LOCAL_ADDRESS);
267 }
268 if (auto *MI = dyn_cast<AnyMemIntrinsic>(&Inst)) {
269 if (MI->getDestAddressSpace() == AMDGPUAS::LOCAL_ADDRESS)
270 return true;
271 auto *MTI = dyn_cast<AnyMemTransferInst>(MI);
272 return MTI && MTI->getSourceAddressSpace() == AMDGPUAS::LOCAL_ADDRESS;
273 }
274 return false;
275}
276
277void AMDGPUSwLowerLDS::getNonKernelsWithLDSInstructions(const CallGraph &CG) {
278 // Non-kernels with LDS instructions find the malloc buffer through the base
279 // table, so every kernel reaching them needs a kernel ID, even one without
280 // LDS.
281 DenseMap<Function *, bool> HasLDSInstructionsCache;
282 auto HasLDSInstructions = [&](Function *F) {
283 auto [It, Inserted] = HasLDSInstructionsCache.try_emplace(F, false);
284 if (Inserted) {
285 It->second = any_of(instructions(*F), isLDSMemoryInstruction);
286 if (It->second)
287 FuncLDSAccessInfo.NonKernelsWithLDSInstructions.insert(F);
288 }
289 return It->second;
290 };
291
292 // Indirect calls may reach any address taken non-kernel.
293 SmallVector<Function *> AddressTakenFuncs;
294 for (Function &F : M)
295 if (!F.isDeclaration() && !isKernel(F) &&
296 F.hasAddressTaken(nullptr, /*IgnoreCallbackUses=*/false,
297 /*IgnoreAssumeLikeCalls=*/false,
298 /*IgnoreLLVMUsed=*/true))
299 AddressTakenFuncs.push_back(&F);
300
301 for (Function &Kernel : M) {
302 if (Kernel.isDeclaration() || !isKernel(Kernel))
303 continue;
304
305 bool ReachesLDSInstructions = false;
306 bool SeenUnknownCall = false;
307 SmallVector<Function *> WorkList = {&Kernel};
309 auto Visit = [&](Function *Callee) {
310 if (Callee->isDeclaration() || isKernel(*Callee) ||
311 !Visited.insert(Callee).second)
312 return;
313 ReachesLDSInstructions |= HasLDSInstructions(Callee);
314 WorkList.push_back(Callee);
315 };
316
317 while (!WorkList.empty()) {
318 Function *F = WorkList.pop_back_val();
319 for (const CallGraphNode::CallRecord &CallRecord : *CG[F]) {
320 if (!CallRecord.second)
321 continue;
322 if (Function *Callee = CallRecord.second->getFunction()) {
323 Visit(Callee);
324 continue;
325 }
326 if (SeenUnknownCall)
327 continue;
328 SeenUnknownCall = true;
329 for (Function *PotentialCallee : AddressTakenFuncs)
330 Visit(PotentialCallee);
331 }
332 }
333
334 if (ReachesLDSInstructions)
335 FuncLDSAccessInfo.KernelsWithIndirectLDSAccess.insert(&Kernel);
336 }
337}
338
339void AMDGPUSwLowerLDS::getUsesOfLDSByNonKernels() {
340 for (GlobalVariable *GV : FuncLDSAccessInfo.AllNonKernelLDSAccess) {
342 continue;
343
344 for (User *V : GV->users()) {
345 if (auto *I = dyn_cast<Instruction>(V)) {
346 Function *F = I->getFunction();
347 if (!isKernel(*F) && !F->isDeclaration())
348 FuncLDSAccessInfo.NonKernelToLDSAccessMap[F].insert(GV);
349 }
350 }
351 }
352}
353
354static void recordLDSAbsoluteAddress(Module &M, GlobalVariable *GV,
355 uint32_t Address) {
356 // Write the specified address into metadata where it can be retrieved by
357 // the assembler. Format is a half open range, [Address Address+1)
358 LLVMContext &Ctx = M.getContext();
359 auto *IntTy = M.getDataLayout().getIntPtrType(Ctx, AMDGPUAS::LOCAL_ADDRESS);
360 MDBuilder MDB(Ctx);
361 MDNode *MetadataNode = MDB.createRange(ConstantInt::get(IntTy, Address),
362 ConstantInt::get(IntTy, Address + 1));
363 GV->setMetadata(LLVMContext::MD_absolute_symbol, MetadataNode);
364}
365
366static void addLDSSizeAttribute(Function *Func, uint32_t Offset,
367 bool IsDynLDS) {
368 if (Offset != 0) {
369 std::string Buffer;
370 raw_string_ostream SS{Buffer};
371 SS << Offset;
372 if (IsDynLDS)
373 SS << "," << Offset;
374 Func->addFnAttr("amdgpu-lds-size", Buffer);
375 }
376}
377
378static void markUsedByKernel(Function *Func, GlobalVariable *SGV) {
379 BasicBlock *Entry = &Func->getEntryBlock();
380 IRBuilder<> Builder(Entry->getFirstNonPHIIt());
381
382 Function *Decl = Intrinsic::getOrInsertDeclaration(Func->getParent(),
383 Intrinsic::donothing, {});
384
385 Value *UseInstance[1] = {
386 Builder.CreateConstInBoundsGEP1_32(SGV->getValueType(), SGV, 0)};
387
388 Builder.CreateCall(Decl, {},
389 {OperandBundleDefT<Value *>("ExplicitUse", UseInstance)});
390}
391
392void AMDGPUSwLowerLDS::buildSwLDSGlobal(Function *Func) {
393 // Create new LDS global required for each kernel to store
394 // device global memory pointer.
395 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
396 // Create new global pointer variable
397 LDSParams.SwLDS = new GlobalVariable(
398 M, IRB.getPtrTy(), false, GlobalValue::InternalLinkage,
399 PoisonValue::get(IRB.getPtrTy()), "llvm.amdgcn.sw.lds." + Func->getName(),
402 MD.NoAddress = true;
403 LDSParams.SwLDS->setSanitizerMetadata(MD);
404}
405
406void AMDGPUSwLowerLDS::buildSwDynLDSGlobal(Function *Func) {
407 // Create new Dyn LDS global if kernel accesses dyn LDS.
408 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
409 if (LDSParams.DirectAccess.DynamicLDSGlobals.empty() &&
410 LDSParams.IndirectAccess.DynamicLDSGlobals.empty())
411 return;
412 // Create new global pointer variable
413 auto *emptyCharArray = ArrayType::get(IRB.getInt8Ty(), 0);
414 LDSParams.SwDynLDS = new GlobalVariable(
415 M, emptyCharArray, false, GlobalValue::ExternalLinkage, nullptr,
416 "llvm.amdgcn." + Func->getName() + ".dynlds", nullptr,
418 markUsedByKernel(Func, LDSParams.SwDynLDS);
420 MD.NoAddress = true;
421 LDSParams.SwDynLDS->setSanitizerMetadata(MD);
422}
423
424void AMDGPUSwLowerLDS::populateSwLDSAttributeAndMetadata(Function *Func) {
425 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
426 bool IsDynLDSUsed = LDSParams.SwDynLDS;
427 uint32_t Offset = LDSParams.LDSSize;
428 recordLDSAbsoluteAddress(M, LDSParams.SwLDS, 0);
429 addLDSSizeAttribute(Func, Offset, IsDynLDSUsed);
430 if (LDSParams.SwDynLDS)
431 recordLDSAbsoluteAddress(M, LDSParams.SwDynLDS, Offset);
432}
433
434void AMDGPUSwLowerLDS::populateSwMetadataGlobal(Function *Func) {
435 // Create new metadata global for every kernel and initialize the
436 // start offsets and sizes corresponding to each LDS accesses.
437 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
438 auto &Ctx = M.getContext();
439 auto &DL = M.getDataLayout();
440 std::vector<Type *> Items;
441 Type *Int32Ty = IRB.getInt32Ty();
442 std::vector<Constant *> Initializers;
443 Align MaxAlignment(1);
444 auto UpdateMaxAlignment = [&MaxAlignment, &DL](GlobalVariable *GV) {
445 Align GVAlign = AMDGPU::getAlign(DL, GV);
446 MaxAlignment = std::max(MaxAlignment, GVAlign);
447 };
448
449 for (GlobalVariable *GV : LDSParams.DirectAccess.StaticLDSGlobals)
450 UpdateMaxAlignment(GV);
451
452 for (GlobalVariable *GV : LDSParams.DirectAccess.DynamicLDSGlobals)
453 UpdateMaxAlignment(GV);
454
455 for (GlobalVariable *GV : LDSParams.IndirectAccess.StaticLDSGlobals)
456 UpdateMaxAlignment(GV);
457
458 for (GlobalVariable *GV : LDSParams.IndirectAccess.DynamicLDSGlobals)
459 UpdateMaxAlignment(GV);
460
461 //{StartOffset, AlignedSizeInBytes}
462 SmallString<128> MDItemStr;
463 raw_svector_ostream MDItemOS(MDItemStr);
464 MDItemOS << "llvm.amdgcn.sw.lds." << Func->getName() << ".md.item";
465
466 StructType *LDSItemTy =
467 StructType::create(Ctx, {Int32Ty, Int32Ty, Int32Ty}, MDItemOS.str());
468 uint32_t &MallocSize = LDSParams.MallocSize;
469 SetVector<GlobalVariable *> UniqueLDSGlobals;
470 int AsanScale = AsanInfo.Scale;
471 auto buildInitializerForSwLDSMD =
472 [&](SetVector<GlobalVariable *> &LDSGlobals) {
473 for (auto &GV : LDSGlobals) {
474 if (is_contained(UniqueLDSGlobals, GV))
475 continue;
476 UniqueLDSGlobals.insert(GV);
477
478 Type *Ty = GV->getValueType();
479 const uint64_t SizeInBytes = DL.getTypeAllocSize(Ty);
480 Items.push_back(LDSItemTy);
481 Constant *ItemStartOffset = ConstantInt::get(Int32Ty, MallocSize);
482 Constant *SizeInBytesConst = ConstantInt::get(Int32Ty, SizeInBytes);
483 // Get redzone size corresponding a size.
484 const uint64_t RightRedzoneSize =
485 AMDGPU::getRedzoneSizeForGlobal(AsanScale, SizeInBytes);
486 // Update MallocSize with current size and redzone size.
487 MallocSize += SizeInBytes;
488 if (!AMDGPU::isDynamicLDS(*GV))
489 LDSParams.RedzoneOffsetAndSizeVector.emplace_back(MallocSize,
490 RightRedzoneSize);
491 MallocSize += RightRedzoneSize;
492 // Align current size plus redzone.
493 uint64_t AlignedSize =
494 alignTo(SizeInBytes + RightRedzoneSize, MaxAlignment);
495 Constant *AlignedSizeInBytesConst =
496 ConstantInt::get(Int32Ty, AlignedSize);
497 // Align MallocSize
498 MallocSize = alignTo(MallocSize, MaxAlignment);
499 Constant *InitItem =
500 ConstantStruct::get(LDSItemTy, {ItemStartOffset, SizeInBytesConst,
501 AlignedSizeInBytesConst});
502 Initializers.push_back(InitItem);
503 }
504 };
505 SetVector<GlobalVariable *> SwLDSVector;
506 SwLDSVector.insert(LDSParams.SwLDS);
507 buildInitializerForSwLDSMD(SwLDSVector);
508 buildInitializerForSwLDSMD(LDSParams.DirectAccess.StaticLDSGlobals);
509 buildInitializerForSwLDSMD(LDSParams.IndirectAccess.StaticLDSGlobals);
510 buildInitializerForSwLDSMD(LDSParams.DirectAccess.DynamicLDSGlobals);
511 buildInitializerForSwLDSMD(LDSParams.IndirectAccess.DynamicLDSGlobals);
512
513 // Update the LDS size used by the kernel.
514 Type *Ty = LDSParams.SwLDS->getValueType();
515 const uint64_t SizeInBytes = DL.getTypeAllocSize(Ty);
516 uint64_t AlignedSize = alignTo(SizeInBytes, MaxAlignment);
517 LDSParams.LDSSize = AlignedSize;
518 SmallString<128> MDTypeStr;
519 raw_svector_ostream MDTypeOS(MDTypeStr);
520 MDTypeOS << "llvm.amdgcn.sw.lds." << Func->getName() << ".md.type";
521 StructType *MetadataStructType =
522 StructType::create(Ctx, Items, MDTypeOS.str());
523 SmallString<128> MDStr;
524 raw_svector_ostream MDOS(MDStr);
525 MDOS << "llvm.amdgcn.sw.lds." << Func->getName() << ".md";
526 LDSParams.SwLDSMetadata = new GlobalVariable(
527 M, MetadataStructType, false, GlobalValue::InternalLinkage,
528 PoisonValue::get(MetadataStructType), MDOS.str(), nullptr,
530 Constant *data = ConstantStruct::get(MetadataStructType, Initializers);
531 LDSParams.SwLDSMetadata->setInitializer(data);
532 assert(LDSParams.SwLDS);
533 // Set the alignment to MaxAlignment for SwLDS.
534 LDSParams.SwLDS->setAlignment(MaxAlignment);
535 if (LDSParams.SwDynLDS)
536 LDSParams.SwDynLDS->setAlignment(MaxAlignment);
538 MD.NoAddress = true;
539 LDSParams.SwLDSMetadata->setSanitizerMetadata(MD);
540}
541
542void AMDGPUSwLowerLDS::populateLDSToReplacementIndicesMap(Function *Func) {
543 // Fill the corresponding LDS replacement indices for each LDS access
544 // related to this kernel.
545 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
546 SetVector<GlobalVariable *> UniqueLDSGlobals;
547 auto PopulateIndices = [&](SetVector<GlobalVariable *> &LDSGlobals,
548 uint32_t &Idx) {
549 for (auto &GV : LDSGlobals) {
550 if (is_contained(UniqueLDSGlobals, GV))
551 continue;
552 UniqueLDSGlobals.insert(GV);
553 LDSParams.LDSToReplacementIndicesMap[GV] = {0, Idx, 0};
554 ++Idx;
555 }
556 };
557 uint32_t Idx = 0;
558 SetVector<GlobalVariable *> SwLDSVector;
559 SwLDSVector.insert(LDSParams.SwLDS);
560 PopulateIndices(SwLDSVector, Idx);
561 PopulateIndices(LDSParams.DirectAccess.StaticLDSGlobals, Idx);
562 PopulateIndices(LDSParams.IndirectAccess.StaticLDSGlobals, Idx);
563 PopulateIndices(LDSParams.DirectAccess.DynamicLDSGlobals, Idx);
564 PopulateIndices(LDSParams.IndirectAccess.DynamicLDSGlobals, Idx);
565}
566
567static void replacesUsesOfGlobalInFunction(Function *Func, GlobalVariable *GV,
568 Value *Replacement) {
569 // Replace all uses of LDS global in this Function with a Replacement.
570 auto ReplaceUsesLambda = [Func](const Use &U) -> bool {
571 auto *V = U.getUser();
572 if (auto *Inst = dyn_cast<Instruction>(V)) {
573 auto *Func1 = Inst->getFunction();
574 if (Func == Func1)
575 return true;
576 }
577 return false;
578 };
579 GV->replaceUsesWithIf(Replacement, ReplaceUsesLambda);
580}
581
582void AMDGPUSwLowerLDS::replaceKernelLDSAccesses(Function *Func) {
583 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
584 GlobalVariable *SwLDS = LDSParams.SwLDS;
585 assert(SwLDS);
586 GlobalVariable *SwLDSMetadata = LDSParams.SwLDSMetadata;
587 assert(SwLDSMetadata);
588 StructType *SwLDSMetadataStructType =
589 cast<StructType>(SwLDSMetadata->getValueType());
590 Type *Int32Ty = IRB.getInt32Ty();
591 auto &IndirectAccess = LDSParams.IndirectAccess;
592 auto &DirectAccess = LDSParams.DirectAccess;
593 // Replace all uses of LDS global in this Function with a Replacement.
594 SetVector<GlobalVariable *> UniqueLDSGlobals;
595 auto ReplaceLDSGlobalUses = [&](SetVector<GlobalVariable *> &LDSGlobals) {
596 for (auto &GV : LDSGlobals) {
597 // Do not generate instructions if LDS access is in non-kernel
598 // i.e indirect-access.
599 if ((IndirectAccess.StaticLDSGlobals.contains(GV) ||
600 IndirectAccess.DynamicLDSGlobals.contains(GV)) &&
601 (!DirectAccess.StaticLDSGlobals.contains(GV) &&
602 !DirectAccess.DynamicLDSGlobals.contains(GV)))
603 continue;
604 if (is_contained(UniqueLDSGlobals, GV))
605 continue;
606 UniqueLDSGlobals.insert(GV);
607 auto &Indices = LDSParams.LDSToReplacementIndicesMap[GV];
608 assert(Indices.size() == 3);
609 Constant *GEPIdx[] = {ConstantInt::get(Int32Ty, Indices[0]),
610 ConstantInt::get(Int32Ty, Indices[1]),
611 ConstantInt::get(Int32Ty, Indices[2])};
613 Func->getDataLayout(), SwLDSMetadataStructType, SwLDSMetadata, GEPIdx,
615 Value *Offset = IRB.CreateLoad(Int32Ty, GEP);
616 Value *BasePlusOffset =
617 IRB.CreateInBoundsGEP(IRB.getInt8Ty(), SwLDS, {Offset});
618 LLVM_DEBUG(GV->printAsOperand(dbgs() << "Sw LDS Lowering, Replacing LDS ",
619 false));
620 replacesUsesOfGlobalInFunction(Func, GV, BasePlusOffset);
621 }
622 };
623 ReplaceLDSGlobalUses(DirectAccess.StaticLDSGlobals);
624 ReplaceLDSGlobalUses(IndirectAccess.StaticLDSGlobals);
625 ReplaceLDSGlobalUses(DirectAccess.DynamicLDSGlobals);
626 ReplaceLDSGlobalUses(IndirectAccess.DynamicLDSGlobals);
627}
628
629void AMDGPUSwLowerLDS::updateMallocSizeForDynamicLDS(
630 Function *Func, Value **CurrMallocSize, Value *HiddenDynLDSSize,
631 SetVector<GlobalVariable *> &DynamicLDSGlobals) {
632 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
633 Type *Int32Ty = IRB.getInt32Ty();
634
635 GlobalVariable *SwLDS = LDSParams.SwLDS;
636 GlobalVariable *SwLDSMetadata = LDSParams.SwLDSMetadata;
637 assert(SwLDS && SwLDSMetadata);
638 StructType *MetadataStructType =
639 cast<StructType>(SwLDSMetadata->getValueType());
640 unsigned MaxAlignment = SwLDS->getAlign().valueOrOne().value();
641 Value *MaxAlignValue = IRB.getInt32(MaxAlignment);
642 Value *MaxAlignValueMinusOne = IRB.getInt32(MaxAlignment - 1);
643
644 for (GlobalVariable *DynGV : DynamicLDSGlobals) {
645 auto &Indices = LDSParams.LDSToReplacementIndicesMap[DynGV];
646 // Update the Offset metadata.
647 Constant *Index0 = ConstantInt::get(Int32Ty, 0);
648 Constant *Index1 = ConstantInt::get(Int32Ty, Indices[1]);
649
650 Constant *Index2Offset = ConstantInt::get(Int32Ty, 0);
651 auto *GEPForOffset = IRB.CreateInBoundsGEP(
652 MetadataStructType, SwLDSMetadata, {Index0, Index1, Index2Offset});
653
654 IRB.CreateStore(*CurrMallocSize, GEPForOffset);
655 // Update the size and Aligned Size metadata.
656 Constant *Index2Size = ConstantInt::get(Int32Ty, 1);
657 auto *GEPForSize = IRB.CreateInBoundsGEP(MetadataStructType, SwLDSMetadata,
658 {Index0, Index1, Index2Size});
659
660 Value *CurrDynLDSSize = IRB.CreateLoad(Int32Ty, HiddenDynLDSSize);
661 IRB.CreateStore(CurrDynLDSSize, GEPForSize);
662 Constant *Index2AlignedSize = ConstantInt::get(Int32Ty, 2);
663 auto *GEPForAlignedSize = IRB.CreateInBoundsGEP(
664 MetadataStructType, SwLDSMetadata, {Index0, Index1, Index2AlignedSize});
665
666 Value *AlignedDynLDSSize =
667 IRB.CreateAdd(CurrDynLDSSize, MaxAlignValueMinusOne);
668 AlignedDynLDSSize = IRB.CreateUDiv(AlignedDynLDSSize, MaxAlignValue);
669 AlignedDynLDSSize = IRB.CreateMul(AlignedDynLDSSize, MaxAlignValue);
670 IRB.CreateStore(AlignedDynLDSSize, GEPForAlignedSize);
671
672 // Update the Current Malloc Size
673 *CurrMallocSize = IRB.CreateAdd(*CurrMallocSize, AlignedDynLDSSize);
674 }
675}
676
677static DebugLoc getOrCreateDebugLoc(const Instruction *InsertBefore,
678 DISubprogram *SP) {
679 assert(InsertBefore);
680 if (InsertBefore->getDebugLoc())
681 return InsertBefore->getDebugLoc();
682 if (SP)
683 return DILocation::get(SP->getContext(), SP->getLine(), 1, SP);
684 return DebugLoc();
685}
686
687void AMDGPUSwLowerLDS::getLDSMemoryInstructions(
688 Function *Func, SetVector<Instruction *> &LDSInstructions) {
689 for (Instruction &Inst : instructions(Func))
690 if (isLDSMemoryInstruction(Inst))
691 LDSInstructions.insert(&Inst);
692}
693
694Value *AMDGPUSwLowerLDS::getTranslatedGlobalMemoryPtrOfLDS(Value *LoadMallocPtr,
695 Value *LDSPtr) {
696 assert(LDSPtr && "Invalid LDS pointer operand");
697 Type *LDSPtrType = LDSPtr->getType();
698 LLVMContext &Ctx = M.getContext();
699 const DataLayout &DL = M.getDataLayout();
700 Type *IntTy = DL.getIntPtrType(Ctx, AMDGPUAS::LOCAL_ADDRESS);
701 if (auto *VecPtrTy = dyn_cast<VectorType>(LDSPtrType)) {
702 // Handle vector of pointers
703 ElementCount NumElements = VecPtrTy->getElementCount();
704 IntTy = VectorType::get(IntTy, NumElements);
705 }
706 Value *GepIndex = IRB.CreatePtrToInt(LDSPtr, IntTy);
707 return IRB.CreateInBoundsGEP(IRB.getInt8Ty(), LoadMallocPtr, {GepIndex});
708}
709
710// A flat pointer to lowered LDS holds a global address inside the malloc
711// buffer, but LDS values in this pass are offsets into that buffer.
712// Rebase it as inttoptr(trunc(ptrtoaddr(flat) - ptrtoaddr(buf))).
713Value *AMDGPUSwLowerLDS::getTranslatedLDSPtrOfFlat(Value *LoadMallocPtr,
714 Value *FlatPtr,
715 Type *LDSPtrTy) {
716 const DataLayout &DL = M.getDataLayout();
717 Value *FlatAddr = IRB.CreatePtrToAddr(FlatPtr);
718 Value *Base = IRB.CreatePtrToAddr(LoadMallocPtr);
719 if (auto *VecTy = dyn_cast<VectorType>(FlatAddr->getType()))
720 Base = IRB.CreateVectorSplat(VecTy->getElementCount(), Base);
721 Value *Diff = IRB.CreateSub(FlatAddr, Base);
722 Value *Offset = IRB.CreateTrunc(Diff, DL.getAddressType(LDSPtrTy));
723 Value *LDSPtr = IRB.CreateIntToPtr(Offset, LDSPtrTy);
724 // Flat null is 0 but LDS null is -1, which the rebased offset cannot keep.
726 Constant::getNullValue(FlatPtr->getType()), LDSPtrTy);
727 return IRB.CreateSelect(IRB.CreateIsNull(FlatPtr), LDSNull, LDSPtr);
728}
729
730void AMDGPUSwLowerLDS::translateLDSMemoryOperationsToGlobalMemory(
731 Function *Func, Value *LoadMallocPtr,
732 SetVector<Instruction *> &LDSInstructions) {
733 LLVM_DEBUG(dbgs() << "Translating LDS memory operations to global memory : "
734 << Func->getName());
735 for (Instruction *Inst : LDSInstructions) {
736 IRB.SetInsertPoint(Inst);
737 if (LoadInst *LI = dyn_cast<LoadInst>(Inst)) {
738 Value *LIOperand = LI->getPointerOperand();
739 Value *Replacement =
740 getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, LIOperand);
741 LoadInst *NewLI =
742 IRB.CreateLoad(LI->getType(), Replacement, LI->getProperties());
743 AsanInfo.Instructions.insert(NewLI);
744 LI->replaceAllUsesWith(NewLI);
745 LI->eraseFromParent();
746 } else if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) {
747 Value *SIOperand = SI->getPointerOperand();
748 Value *Replacement =
749 getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, SIOperand);
750 StoreInst *NewSI = IRB.CreateStore(SI->getValueOperand(), Replacement,
751 SI->getProperties());
752 AsanInfo.Instructions.insert(NewSI);
753 SI->replaceAllUsesWith(NewSI);
754 SI->eraseFromParent();
755 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(Inst)) {
756 Value *RMWPtrOperand = RMW->getPointerOperand();
757 Value *RMWValOperand = RMW->getValOperand();
758 Value *Replacement =
759 getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, RMWPtrOperand);
760 AtomicRMWInst *NewRMW = IRB.CreateAtomicRMW(
761 RMW->getOperation(), Replacement, RMWValOperand, RMW->getAlign(),
762 RMW->getOrdering(), RMW->getSyncScopeID());
763 NewRMW->setVolatile(RMW->isVolatile());
764 AsanInfo.Instructions.insert(NewRMW);
765 RMW->replaceAllUsesWith(NewRMW);
766 RMW->eraseFromParent();
767 } else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(Inst)) {
768 Value *XCHGPtrOperand = XCHG->getPointerOperand();
769 Value *Replacement =
770 getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, XCHGPtrOperand);
772 Replacement, XCHG->getCompareOperand(), XCHG->getNewValOperand(),
773 XCHG->getAlign(), XCHG->getSuccessOrdering(),
774 XCHG->getFailureOrdering(), XCHG->getSyncScopeID());
775 NewXCHG->setVolatile(XCHG->isVolatile());
776 AsanInfo.Instructions.insert(NewXCHG);
777 XCHG->replaceAllUsesWith(NewXCHG);
778 XCHG->eraseFromParent();
779 } else if (AnyMemIntrinsic *MI = dyn_cast<AnyMemIntrinsic>(Inst)) {
780 Value *NewDest = MI->getRawDest();
781 if (MI->getDestAddressSpace() == AMDGPUAS::LOCAL_ADDRESS)
782 NewDest = getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, NewDest);
783 CallInst *NewMI = nullptr;
785 if (MI->isAtomic()) {
787 NewDest, MSI->getValue(), MSI->getLength(),
788 MSI->getDestAlign().valueOrOne(), MSI->getElementSizeInBytes());
789 } else {
790 NewMI = IRB.CreateMemSet(NewDest, MSI->getValue(), MSI->getLength(),
791 MSI->getDestAlign(),
792 cast<MemSetInst>(MI)->isVolatile());
793 }
795 Value *NewSrc = MTI->getRawSource();
796 if (MTI->getSourceAddressSpace() == AMDGPUAS::LOCAL_ADDRESS)
797 NewSrc = getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, NewSrc);
798 if (MI->isAtomic()) {
799 if (MI->getIntrinsicID() ==
800 Intrinsic::memmove_element_unordered_atomic) {
802 NewDest, MTI->getDestAlign().valueOrOne(), NewSrc,
803 MTI->getSourceAlign().valueOrOne(), MTI->getLength(),
804 MTI->getElementSizeInBytes());
805 } else {
807 NewDest, MTI->getDestAlign().valueOrOne(), NewSrc,
808 MTI->getSourceAlign().valueOrOne(), MTI->getLength(),
809 MTI->getElementSizeInBytes());
810 }
811 } else {
812 NewMI = IRB.CreateMemTransferInst(
813 MI->getIntrinsicID(), NewDest, MTI->getDestAlign(), NewSrc,
814 MTI->getSourceAlign(), MTI->getLength(),
815 cast<MemTransferInst>(MI)->isVolatile());
816 }
817 } else
818 reportFatalUsageError("Unimplemented LDS lowering memory intrinsic");
819 AsanInfo.Instructions.insert(NewMI);
820 MI->replaceAllUsesWith(NewMI);
821 MI->eraseFromParent();
822 } else if (AddrSpaceCastInst *ASC = dyn_cast<AddrSpaceCastInst>(Inst)) {
823 Value *AIOperand = ASC->getPointerOperand();
824 Value *NewAI = nullptr;
825 if (ASC->getDestAddressSpace() == AMDGPUAS::LOCAL_ADDRESS) {
826 NewAI =
827 getTranslatedLDSPtrOfFlat(LoadMallocPtr, AIOperand, ASC->getType());
828 } else {
829 Value *Replacement =
830 getTranslatedGlobalMemoryPtrOfLDS(LoadMallocPtr, AIOperand);
831 // Note: No need to add the instruction to AsanInfo instructions to be
832 // instrumented list. FLAT_ADDRESS ptr would have been already
833 // instrumented by asan pass prior to this pass.
834 NewAI = IRB.CreateAddrSpaceCast(Replacement, ASC->getType());
835 }
836 ASC->replaceAllUsesWith(NewAI);
837 ASC->eraseFromParent();
838 } else
839 report_fatal_error("Unimplemented LDS lowering instruction");
840 }
841}
842
843void AMDGPUSwLowerLDS::poisonRedzones(Function *Func, Value *MallocPtr) {
844 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
845 Type *Int64Ty = IRB.getInt64Ty();
846 Type *VoidTy = IRB.getVoidTy();
847 FunctionCallee AsanPoisonRegion = M.getOrInsertFunction(
848 "__asan_poison_region",
849 FunctionType::get(VoidTy, {Int64Ty, Int64Ty}, false));
850
851 auto RedzonesVec = LDSParams.RedzoneOffsetAndSizeVector;
852 size_t VecSize = RedzonesVec.size();
853 for (unsigned i = 0; i < VecSize; i++) {
854 auto &RedzonePair = RedzonesVec[i];
855 uint64_t RedzoneOffset = RedzonePair.first;
856 uint64_t RedzoneSize = RedzonePair.second;
857 Value *RedzoneAddrOffset = IRB.CreateInBoundsGEP(
858 IRB.getInt8Ty(), MallocPtr, {IRB.getInt64(RedzoneOffset)});
859 Value *RedzoneAddress = IRB.CreatePtrToInt(RedzoneAddrOffset, Int64Ty);
860 IRB.CreateCall(AsanPoisonRegion,
861 {RedzoneAddress, IRB.getInt64(RedzoneSize)});
862 }
863}
864
865void AMDGPUSwLowerLDS::lowerKernelLDSAccesses(Function *Func,
866 DomTreeUpdater &DTU) {
867 LLVM_DEBUG(dbgs() << "Sw Lowering Kernel LDS for : " << Func->getName());
868 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
869 auto &Ctx = M.getContext();
870 auto *PrevEntryBlock = &Func->getEntryBlock();
871 SetVector<Instruction *> LDSInstructions;
872 getLDSMemoryInstructions(Func, LDSInstructions);
873 const DataLayout &DL = M.getDataLayout();
874
875 // Create malloc block.
876 auto *MallocBlock = BasicBlock::Create(Ctx, "Malloc", Func, PrevEntryBlock);
877
878 // Create WIdBlock block which has instructions related to selection of
879 // {0,0,0} indiex work item in the work group.
880 auto *WIdBlock = BasicBlock::Create(Ctx, "WId", Func, MallocBlock);
881
882 // Move constant-size allocas from the original entry block to the new entry
883 // block (WIdBlock) so they remain static allocas. Splice the leading cluster
884 // in bulk, then move any stragglers that are interleaved with other
885 // instructions.
886 auto SplitIt = PrevEntryBlock->getFirstNonPHIOrAlloca();
887 WIdBlock->splice(WIdBlock->end(), PrevEntryBlock, PrevEntryBlock->begin(),
888 SplitIt);
889 for (Instruction &I : make_early_inc_range(*PrevEntryBlock))
890 if (auto *AI = dyn_cast<AllocaInst>(&I))
891 if (isa<ConstantInt>(AI->getArraySize()))
892 AI->moveBefore(*WIdBlock, WIdBlock->end());
893
894 IRB.SetInsertPoint(WIdBlock->end());
895 DebugLoc FirstDL =
896 getOrCreateDebugLoc(&*PrevEntryBlock->begin(), Func->getSubprogram());
897 IRB.SetCurrentDebugLocation(FirstDL);
898 Value *WIdx = IRB.CreateIntrinsic(Intrinsic::amdgcn_workitem_id_x, {});
899 Value *WIdy = IRB.CreateIntrinsic(Intrinsic::amdgcn_workitem_id_y, {});
900 Value *WIdz = IRB.CreateIntrinsic(Intrinsic::amdgcn_workitem_id_z, {});
901 Value *XYOr = IRB.CreateOr(WIdx, WIdy);
902 Value *XYZOr = IRB.CreateOr(XYOr, WIdz);
903 Value *WIdzCond = IRB.CreateICmpEQ(XYZOr, IRB.getInt32(0));
904
905 // All work items will branch to PrevEntryBlock except {0,0,0} index
906 // work item which will branch to malloc block.
907 IRB.CreateCondBr(WIdzCond, MallocBlock, PrevEntryBlock);
908
909 // Malloc block
910 IRB.SetInsertPoint(MallocBlock->begin());
911
912 // If Dynamic LDS globals are accessed by the kernel,
913 // Get the size of dyn lds from hidden dyn_lds_size kernel arg.
914 // Update the corresponding metadata global entries for this dyn lds global.
915 GlobalVariable *SwLDS = LDSParams.SwLDS;
916 GlobalVariable *SwLDSMetadata = LDSParams.SwLDSMetadata;
917 assert(SwLDS && SwLDSMetadata);
918 StructType *MetadataStructType =
919 cast<StructType>(SwLDSMetadata->getValueType());
920 Type *Int32Ty = IRB.getInt32Ty();
921 Type *Int64Ty = IRB.getInt64Ty();
922
923 SetVector<GlobalVariable *> UniqueLDSGlobals;
924 auto GetUniqueLDSGlobals = [&](SetVector<GlobalVariable *> &LDSGlobals) {
925 for (auto &GV : LDSGlobals) {
926 if (is_contained(UniqueLDSGlobals, GV))
927 continue;
928 UniqueLDSGlobals.insert(GV);
929 }
930 };
931
932 GetUniqueLDSGlobals(LDSParams.DirectAccess.StaticLDSGlobals);
933 GetUniqueLDSGlobals(LDSParams.IndirectAccess.StaticLDSGlobals);
934 // The metadata global always has an item for the SwLDS pointer itself, so
935 // there is at least one static item and the last one ends the static region.
936 unsigned LastStaticLDSIdx = UniqueLDSGlobals.size();
937 UniqueLDSGlobals.clear();
938
939 auto *GEPForEndStaticLDSOffset =
940 IRB.CreateInBoundsGEP(MetadataStructType, SwLDSMetadata,
941 {ConstantInt::get(Int32Ty, 0),
942 ConstantInt::get(Int32Ty, LastStaticLDSIdx),
943 ConstantInt::get(Int32Ty, 0)});
944
945 auto *GEPForEndStaticLDSSize =
946 IRB.CreateInBoundsGEP(MetadataStructType, SwLDSMetadata,
947 {ConstantInt::get(Int32Ty, 0),
948 ConstantInt::get(Int32Ty, LastStaticLDSIdx),
949 ConstantInt::get(Int32Ty, 2)});
950
951 Value *EndStaticLDSOffset = IRB.CreateLoad(Int32Ty, GEPForEndStaticLDSOffset);
952 Value *EndStaticLDSSize = IRB.CreateLoad(Int32Ty, GEPForEndStaticLDSSize);
953 Value *CurrMallocSize = IRB.CreateAdd(EndStaticLDSOffset, EndStaticLDSSize);
954
955 if (LDSParams.SwDynLDS) {
958 "Dynamic LDS size query is only supported for CO V5 and later.");
959 // Get size from hidden dyn_lds_size argument of kernel
961 IRB.CreateIntrinsic(Intrinsic::amdgcn_implicitarg_ptr, {});
962 Value *HiddenDynLDSSize = IRB.CreateInBoundsGEP(
963 ImplicitArg->getType(), ImplicitArg,
964 {ConstantInt::get(Int64Ty, COV5_HIDDEN_DYN_LDS_SIZE_ARG)});
965 UniqueLDSGlobals.clear();
966 GetUniqueLDSGlobals(LDSParams.DirectAccess.DynamicLDSGlobals);
967 GetUniqueLDSGlobals(LDSParams.IndirectAccess.DynamicLDSGlobals);
968 updateMallocSizeForDynamicLDS(Func, &CurrMallocSize, HiddenDynLDSSize,
969 UniqueLDSGlobals);
970 }
971
972 CurrMallocSize = IRB.CreateZExt(CurrMallocSize, Int64Ty);
973
974 // Create a call to malloc function which does device global memory allocation
975 // with size equals to all LDS global accesses size in this kernel.
976 Value *ReturnAddress = IRB.CreateIntrinsic(
977 Intrinsic::returnaddress, IRB.getPtrTy(DL.getProgramAddressSpace()),
978 {IRB.getInt32(0)});
979 FunctionCallee MallocFunc = M.getOrInsertFunction(
980 StringRef("__asan_malloc_impl"),
981 FunctionType::get(Int64Ty, {Int64Ty, Int64Ty}, false));
982 Value *RAPtrToInt = IRB.CreatePtrToInt(ReturnAddress, Int64Ty);
983 Value *MallocCall = IRB.CreateCall(MallocFunc, {CurrMallocSize, RAPtrToInt});
984
985 Value *MallocPtr =
987
988 // Create store of malloc to new global
989 IRB.CreateStore(MallocPtr, SwLDS);
990
991 // Create calls to __asan_poison_region to poison redzones.
992 poisonRedzones(Func, MallocPtr);
993
994 // Create branch to PrevEntryBlock
995 IRB.CreateBr(PrevEntryBlock);
996
997 // Create wave-group barrier at the starting of Previous entry block
998 Type *Int1Ty = IRB.getInt1Ty();
999 IRB.SetInsertPoint(PrevEntryBlock->begin());
1000 auto *XYZCondPhi = IRB.CreatePHI(Int1Ty, 2, "xyzCond");
1001 XYZCondPhi->addIncoming(IRB.getInt1(0), WIdBlock);
1002 XYZCondPhi->addIncoming(IRB.getInt1(1), MallocBlock);
1003
1004 IRB.CreateIntrinsic(Intrinsic::amdgcn_s_barrier, {});
1005
1006 // Load malloc pointer from Sw LDS.
1007 Value *LoadMallocPtr =
1009
1010 // Replace All uses of LDS globals with new LDS pointers.
1011 replaceKernelLDSAccesses(Func);
1012
1013 // Replace Memory Operations on LDS with corresponding
1014 // global memory pointers.
1015 translateLDSMemoryOperationsToGlobalMemory(Func, LoadMallocPtr,
1016 LDSInstructions);
1017
1018 auto *CondFreeBlock = BasicBlock::Create(Ctx, "CondFree", Func);
1019 auto *FreeBlock = BasicBlock::Create(Ctx, "Free", Func);
1020 auto *EndBlock = BasicBlock::Create(Ctx, "End", Func);
1021 for (BasicBlock &BB : *Func) {
1022 if (!BB.empty()) {
1023 if (ReturnInst *RI = dyn_cast<ReturnInst>(&BB.back())) {
1024 RI->eraseFromParent();
1025 IRB.SetInsertPoint(BB.end());
1026 IRB.CreateBr(CondFreeBlock);
1027 }
1028 }
1029 }
1030
1031 // Cond Free Block
1032 IRB.SetInsertPoint(CondFreeBlock->begin());
1033 IRB.CreateIntrinsic(Intrinsic::amdgcn_s_barrier, {});
1034 IRB.CreateCondBr(XYZCondPhi, FreeBlock, EndBlock);
1035
1036 // Free Block
1037 IRB.SetInsertPoint(FreeBlock->begin());
1038
1039 // Free the previously allocate device global memory.
1040 FunctionCallee AsanFreeFunc = M.getOrInsertFunction(
1041 StringRef("__asan_free_impl"),
1042 FunctionType::get(IRB.getVoidTy(), {Int64Ty, Int64Ty}, false));
1043 Value *ReturnAddr = IRB.CreateIntrinsic(
1044 Intrinsic::returnaddress, IRB.getPtrTy(DL.getProgramAddressSpace()),
1045 IRB.getInt32(0));
1046 Value *RAPToInt = IRB.CreatePtrToInt(ReturnAddr, Int64Ty);
1047 Value *MallocPtrToInt = IRB.CreatePtrToInt(LoadMallocPtr, Int64Ty);
1048 IRB.CreateCall(AsanFreeFunc, {MallocPtrToInt, RAPToInt});
1049
1050 IRB.CreateBr(EndBlock);
1051
1052 // End Block
1053 IRB.SetInsertPoint(EndBlock->begin());
1054 IRB.CreateRetVoid();
1055 // Update the DomTree with corresponding links to basic blocks.
1056 DTU.applyUpdates({{DominatorTree::Insert, WIdBlock, MallocBlock},
1057 {DominatorTree::Insert, MallocBlock, PrevEntryBlock},
1058 {DominatorTree::Insert, CondFreeBlock, FreeBlock},
1059 {DominatorTree::Insert, FreeBlock, EndBlock}});
1060}
1061
1062Constant *AMDGPUSwLowerLDS::getAddressesOfVariablesInKernel(
1063 Function *Func, SetVector<GlobalVariable *> &Variables) {
1064 Type *Int32Ty = IRB.getInt32Ty();
1065 ArrayType *KernelOffsetsType =
1067
1068 // Kernels with no direct or indirect LDS uses never read their offset row.
1069 auto KernelIt = FuncLDSAccessInfo.KernelToLDSParametersMap.find(Func);
1070 if (KernelIt == FuncLDSAccessInfo.KernelToLDSParametersMap.end() ||
1071 !KernelIt->second.SwLDSMetadata)
1072 return PoisonValue::get(KernelOffsetsType);
1073
1074 auto &LDSParams = KernelIt->second;
1075 GlobalVariable *SwLDSMetadata = LDSParams.SwLDSMetadata;
1076 auto *SwLDSMetadataStructType =
1077 cast<StructType>(SwLDSMetadata->getValueType());
1078
1079 SmallVector<Constant *> Elements;
1080 for (auto *GV : Variables) {
1081 auto It = LDSParams.LDSToReplacementIndicesMap.find(GV);
1082 if (It == LDSParams.LDSToReplacementIndicesMap.end()) {
1083 Elements.push_back(
1085 continue;
1086 }
1087 auto &Indices = It->second;
1088 Constant *GEPIdx[] = {ConstantInt::get(Int32Ty, Indices[0]),
1089 ConstantInt::get(Int32Ty, Indices[1]),
1090 ConstantInt::get(Int32Ty, Indices[2])};
1092 Func->getDataLayout(), SwLDSMetadataStructType, SwLDSMetadata, GEPIdx,
1094 Elements.push_back(GEP);
1095 }
1096 return ConstantArray::get(KernelOffsetsType, Elements);
1097}
1098
1099void AMDGPUSwLowerLDS::buildNonKernelLDSBaseTable(
1100 NonKernelLDSParameters &NKLDSParams) {
1101 // Base table will have single row, with elements of the row
1102 // placed as per kernel ID. Each element in the row corresponds
1103 // to addresss of "SW LDS" global of the kernel.
1104 auto &Kernels = NKLDSParams.OrderedKernels;
1105 if (Kernels.empty())
1106 return;
1107 const size_t NumberKernels = Kernels.size();
1109 ArrayType *AllKernelsOffsetsType = ArrayType::get(LDSPtrTy, NumberKernels);
1110 std::vector<Constant *> OverallConstantExprElts(NumberKernels);
1111 for (size_t i = 0; i < NumberKernels; i++) {
1112 // Null for kernels without LDS. Lowered functions still load this entry
1113 // on entry, but only use it on LDS paths these kernels never take.
1114 Constant *SwLDS = ConstantPointerNull::get(LDSPtrTy);
1115 auto It = FuncLDSAccessInfo.KernelToLDSParametersMap.find(Kernels[i]);
1116 if (It != FuncLDSAccessInfo.KernelToLDSParametersMap.end() &&
1117 It->second.SwLDS)
1118 SwLDS = It->second.SwLDS;
1119 OverallConstantExprElts[i] = SwLDS;
1120 }
1121 Constant *init =
1122 ConstantArray::get(AllKernelsOffsetsType, OverallConstantExprElts);
1123 NKLDSParams.LDSBaseTable = new GlobalVariable(
1124 M, AllKernelsOffsetsType, true, GlobalValue::InternalLinkage, init,
1125 "llvm.amdgcn.sw.lds.base.table", nullptr, GlobalValue::NotThreadLocal,
1128 MD.NoAddress = true;
1129 NKLDSParams.LDSBaseTable->setSanitizerMetadata(MD);
1130}
1131
1132void AMDGPUSwLowerLDS::buildNonKernelLDSOffsetTable(
1133 NonKernelLDSParameters &NKLDSParams) {
1134 // Offset table will have multiple rows and columns.
1135 // Rows are assumed to be from 0 to (n-1). n is total number
1136 // of kernels accessing the LDS through non-kernels.
1137 // Each row will have m elements. m is the total number of
1138 // unique LDS globals accessed by non-kernels.
1139 // Each element in the row correspond to the address of
1140 // the replacement of LDS global done by that particular kernel.
1141 auto &Variables = NKLDSParams.OrdereLDSGlobals;
1142 auto &Kernels = NKLDSParams.OrderedKernels;
1143 if (Variables.empty() || Kernels.empty())
1144 return;
1145 const size_t NumberVariables = Variables.size();
1146 const size_t NumberKernels = Kernels.size();
1147
1148 ArrayType *KernelOffsetsType =
1149 ArrayType::get(IRB.getPtrTy(AMDGPUAS::GLOBAL_ADDRESS), NumberVariables);
1150
1151 ArrayType *AllKernelsOffsetsType =
1152 ArrayType::get(KernelOffsetsType, NumberKernels);
1153 std::vector<Constant *> overallConstantExprElts(NumberKernels);
1154 for (size_t i = 0; i < NumberKernels; i++) {
1155 Function *Func = Kernels[i];
1156 overallConstantExprElts[i] =
1157 getAddressesOfVariablesInKernel(Func, Variables);
1158 }
1159 Constant *Init =
1160 ConstantArray::get(AllKernelsOffsetsType, overallConstantExprElts);
1161 NKLDSParams.LDSOffsetTable = new GlobalVariable(
1162 M, AllKernelsOffsetsType, true, GlobalValue::InternalLinkage, Init,
1163 "llvm.amdgcn.sw.lds.offset.table", nullptr, GlobalValue::NotThreadLocal,
1166 MD.NoAddress = true;
1167 NKLDSParams.LDSOffsetTable->setSanitizerMetadata(MD);
1168}
1169
1170void AMDGPUSwLowerLDS::lowerNonKernelLDSAccesses(
1171 Function *Func, SetVector<GlobalVariable *> &LDSGlobals,
1172 NonKernelLDSParameters &NKLDSParams) {
1173 // Replace LDS access in non-kernel with replacement queried from
1174 // Base table and offset from offset table.
1175 LLVM_DEBUG(dbgs() << "Sw LDS lowering, lower non-kernel access for : "
1176 << Func->getName());
1177 auto InsertAt = Func->getEntryBlock().getFirstNonPHIOrAlloca();
1178 IRB.SetInsertPoint(InsertAt);
1179
1180 // Get LDS memory instructions.
1181 SetVector<Instruction *> LDSInstructions;
1182 getLDSMemoryInstructions(Func, LDSInstructions);
1183
1184 auto *KernelId = IRB.CreateIntrinsic(Intrinsic::amdgcn_lds_kernel_id, {});
1185 GlobalVariable *LDSBaseTable = NKLDSParams.LDSBaseTable;
1186 GlobalVariable *LDSOffsetTable = NKLDSParams.LDSOffsetTable;
1187 auto &OrdereLDSGlobals = NKLDSParams.OrdereLDSGlobals;
1188 Value *BaseGEP = IRB.CreateInBoundsGEP(
1189 LDSBaseTable->getValueType(), LDSBaseTable, {IRB.getInt32(0), KernelId});
1190 Value *BaseLoad =
1191 IRB.CreateLoad(IRB.getPtrTy(AMDGPUAS::LOCAL_ADDRESS), BaseGEP);
1192 Value *LoadMallocPtr =
1193 IRB.CreateLoad(IRB.getPtrTy(AMDGPUAS::GLOBAL_ADDRESS), BaseLoad);
1194
1195 for (GlobalVariable *GV : LDSGlobals) {
1196 const auto *GVIt = llvm::find(OrdereLDSGlobals, GV);
1197 assert(GVIt != OrdereLDSGlobals.end());
1198 uint32_t GVOffset = std::distance(OrdereLDSGlobals.begin(), GVIt);
1199
1200 Value *OffsetGEP = IRB.CreateInBoundsGEP(
1201 LDSOffsetTable->getValueType(), LDSOffsetTable,
1202 {IRB.getInt32(0), KernelId, IRB.getInt32(GVOffset)});
1203 Value *OffsetLoad =
1204 IRB.CreateLoad(IRB.getPtrTy(AMDGPUAS::GLOBAL_ADDRESS), OffsetGEP);
1205 Value *Offset = IRB.CreateLoad(IRB.getInt32Ty(), OffsetLoad);
1206 Value *BasePlusOffset =
1207 IRB.CreateInBoundsGEP(IRB.getInt8Ty(), BaseLoad, {Offset});
1208 LLVM_DEBUG(dbgs() << "Sw LDS Lowering, Replace non-kernel LDS for "
1209 << GV->getName());
1210 replacesUsesOfGlobalInFunction(Func, GV, BasePlusOffset);
1211 }
1212 translateLDSMemoryOperationsToGlobalMemory(Func, LoadMallocPtr,
1213 LDSInstructions);
1214}
1215
1216static void reorderStaticDynamicIndirectLDSSet(KernelLDSParameters &LDSParams) {
1217 // Sort Static, dynamic LDS globals which are either
1218 // direct or indirect access on basis of name.
1219 auto &DirectAccess = LDSParams.DirectAccess;
1220 auto &IndirectAccess = LDSParams.IndirectAccess;
1221 LDSParams.DirectAccess.StaticLDSGlobals = sortByName(
1222 std::vector<GlobalVariable *>(DirectAccess.StaticLDSGlobals.begin(),
1223 DirectAccess.StaticLDSGlobals.end()));
1224 LDSParams.DirectAccess.DynamicLDSGlobals = sortByName(
1225 std::vector<GlobalVariable *>(DirectAccess.DynamicLDSGlobals.begin(),
1226 DirectAccess.DynamicLDSGlobals.end()));
1227 LDSParams.IndirectAccess.StaticLDSGlobals = sortByName(
1228 std::vector<GlobalVariable *>(IndirectAccess.StaticLDSGlobals.begin(),
1229 IndirectAccess.StaticLDSGlobals.end()));
1230 LDSParams.IndirectAccess.DynamicLDSGlobals = sortByName(
1231 std::vector<GlobalVariable *>(IndirectAccess.DynamicLDSGlobals.begin(),
1232 IndirectAccess.DynamicLDSGlobals.end()));
1233}
1234
1235void AMDGPUSwLowerLDS::initAsanInfo() {
1236 // Get Shadow mapping scale and offset.
1237 unsigned LongSize =
1238 M.getDataLayout().getPointerSizeInBits(AMDGPUAS::GLOBAL_ADDRESS);
1240 int Scale;
1241 bool OrShadowOffset;
1242 llvm::getAddressSanitizerParams(M.getContext(), M.getTargetTriple(), LongSize,
1243 false, &Offset, &Scale, &OrShadowOffset);
1244 AsanInfo.Scale = Scale;
1245 AsanInfo.Offset = Offset;
1246}
1247
1248static bool hasFnWithSanitizeAddressAttr(FunctionVariableMap &LDSAccesses) {
1249 for (auto &K : LDSAccesses) {
1250 Function *F = K.first;
1251 if (!F)
1252 continue;
1253 if (F->hasFnAttribute(Attribute::SanitizeAddress))
1254 return true;
1255 }
1256 return false;
1257}
1258
1259bool AMDGPUSwLowerLDS::run() {
1260 bool Changed = false;
1261
1262 CallGraph CG = CallGraph(M);
1263
1264 Changed |=
1266
1267 // Get all the direct and indirect access of LDS for all the kernels.
1269
1270 // Flag to decide whether to lower all the LDS accesses
1271 // based on sanitize_address attribute.
1272 bool LowerAllLDS = hasFnWithSanitizeAddressAttr(LDSUsesInfo.DirectAccess) ||
1273 hasFnWithSanitizeAddressAttr(LDSUsesInfo.IndirectAccess);
1274
1275 if (!LowerAllLDS)
1276 return Changed;
1277
1278 // Utility to group LDS access into direct, indirect, static and dynamic.
1279 auto PopulateKernelStaticDynamicLDS = [&](FunctionVariableMap &LDSAccesses,
1280 bool DirectAccess) {
1281 for (auto &K : LDSAccesses) {
1282 Function *F = K.first;
1283 if (!F || K.second.empty())
1284 continue;
1285
1286 assert(isKernel(*F));
1287
1288 // Only inserts if key isn't already in the map.
1289 FuncLDSAccessInfo.KernelToLDSParametersMap.insert(
1290 {F, KernelLDSParameters()});
1291
1292 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[F];
1293 if (!DirectAccess)
1294 FuncLDSAccessInfo.KernelsWithIndirectLDSAccess.insert(F);
1295 for (GlobalVariable *GV : K.second) {
1296 if (!DirectAccess) {
1297 if (AMDGPU::isDynamicLDS(*GV))
1298 LDSParams.IndirectAccess.DynamicLDSGlobals.insert(GV);
1299 else
1300 LDSParams.IndirectAccess.StaticLDSGlobals.insert(GV);
1301 FuncLDSAccessInfo.AllNonKernelLDSAccess.insert(GV);
1302 } else {
1303 if (AMDGPU::isDynamicLDS(*GV))
1304 LDSParams.DirectAccess.DynamicLDSGlobals.insert(GV);
1305 else
1306 LDSParams.DirectAccess.StaticLDSGlobals.insert(GV);
1307 }
1308 }
1309 }
1310 };
1311
1312 PopulateKernelStaticDynamicLDS(LDSUsesInfo.DirectAccess, true);
1313 PopulateKernelStaticDynamicLDS(LDSUsesInfo.IndirectAccess, false);
1314
1315 // Get address sanitizer scale.
1316 initAsanInfo();
1317
1318 for (auto &K : FuncLDSAccessInfo.KernelToLDSParametersMap) {
1319 Function *Func = K.first;
1320 auto &LDSParams = FuncLDSAccessInfo.KernelToLDSParametersMap[Func];
1321 if (LDSParams.DirectAccess.StaticLDSGlobals.empty() &&
1322 LDSParams.DirectAccess.DynamicLDSGlobals.empty() &&
1323 LDSParams.IndirectAccess.StaticLDSGlobals.empty() &&
1324 LDSParams.IndirectAccess.DynamicLDSGlobals.empty())
1325 continue;
1326
1328 CG, Func,
1329 {"amdgpu-no-workitem-id-x", "amdgpu-no-workitem-id-y",
1330 "amdgpu-no-workitem-id-z", "amdgpu-no-heap-ptr"});
1331 reorderStaticDynamicIndirectLDSSet(LDSParams);
1332 buildSwLDSGlobal(Func);
1333 buildSwDynLDSGlobal(Func);
1334 populateSwMetadataGlobal(Func);
1335 populateSwLDSAttributeAndMetadata(Func);
1336 populateLDSToReplacementIndicesMap(Func);
1337 DomTreeUpdater DTU(DTCallback(*Func), DomTreeUpdater::UpdateStrategy::Lazy);
1338 lowerKernelLDSAccesses(Func, DTU);
1339 Changed = true;
1340 }
1341
1342 // Get the Uses of LDS from non-kernels.
1343 getUsesOfLDSByNonKernels();
1344
1345 // Get non-kernels with LDS instructions and the kernels reaching them.
1346 getNonKernelsWithLDSInstructions(CG);
1347
1348 // TODO: removeFnAttrFromReachable does not visit callees of indirect call
1349 // targets.
1350 for (Function *Func : FuncLDSAccessInfo.KernelsWithIndirectLDSAccess)
1351 removeFnAttrFromReachable(CG, Func, {"amdgpu-no-lds-kernel-id"});
1352
1353 // Lower LDS accesses in non-kernels.
1354 if (!FuncLDSAccessInfo.NonKernelToLDSAccessMap.empty() ||
1355 !FuncLDSAccessInfo.NonKernelsWithLDSInstructions.empty()) {
1356 NonKernelLDSParameters NKLDSParams;
1357 NKLDSParams.OrderedKernels = getOrderedIndirectLDSAccessingKernels(
1358 FuncLDSAccessInfo.KernelsWithIndirectLDSAccess);
1359 NKLDSParams.OrdereLDSGlobals = getOrderedNonKernelAllLDSGlobals(
1360 FuncLDSAccessInfo.AllNonKernelLDSAccess);
1361 buildNonKernelLDSBaseTable(NKLDSParams);
1362 buildNonKernelLDSOffsetTable(NKLDSParams);
1363 for (auto &K : FuncLDSAccessInfo.NonKernelToLDSAccessMap) {
1364 Function *Func = K.first;
1365 DenseSet<GlobalVariable *> &LDSGlobals = K.second;
1366 SetVector<GlobalVariable *> OrderedLDSGlobals = sortByName(
1367 std::vector<GlobalVariable *>(LDSGlobals.begin(), LDSGlobals.end()));
1368 lowerNonKernelLDSAccesses(Func, OrderedLDSGlobals, NKLDSParams);
1369 }
1370 for (Function *Func : FuncLDSAccessInfo.NonKernelsWithLDSInstructions) {
1371 if (FuncLDSAccessInfo.NonKernelToLDSAccessMap.contains(Func))
1372 continue;
1373 SetVector<GlobalVariable *> NoLDSGlobals;
1374 lowerNonKernelLDSAccesses(Func, NoLDSGlobals, NKLDSParams);
1375 }
1376 Changed = true;
1377 }
1378
1379 if (!Changed)
1380 return Changed;
1381
1382 for (auto &GV : make_early_inc_range(M.globals())) {
1384 // probably want to remove from used lists
1386 if (GV.use_empty())
1387 GV.eraseFromParent();
1388 }
1389 }
1390
1391 if (M.getContext()
1392 .getLibraryOptions()
1393 .get<AMDGPUOptions>()
1394 .asan_instrument_lds) {
1395 SmallVector<InterestingMemoryOperand, 16> OperandsToInstrument;
1396 for (Instruction *Inst : AsanInfo.Instructions) {
1397 SmallVector<InterestingMemoryOperand, 1> InterestingOperands;
1398 getInterestingMemoryOperands(M, Inst, InterestingOperands);
1399 llvm::append_range(OperandsToInstrument, InterestingOperands);
1400 }
1401 for (auto &Operand : OperandsToInstrument) {
1402 Value *Addr = Operand.getPtr();
1403 instrumentAddress(M, IRB, Operand.getInsn(), Operand.getInsn(), Addr,
1404 Operand.Alignment.valueOrOne(), Operand.TypeStoreSize,
1405 Operand.IsWrite, nullptr, false, false, AsanInfo.Scale,
1406 AsanInfo.Offset);
1407 Changed = true;
1408 }
1409 }
1410
1411 return Changed;
1412}
1413
1414class AMDGPUSwLowerLDSLegacy : public ModulePass {
1415public:
1416 static char ID;
1417 AMDGPUSwLowerLDSLegacy() : ModulePass(ID) {}
1418 bool runOnModule(Module &M) override;
1419 void getAnalysisUsage(AnalysisUsage &AU) const override {
1421 }
1422};
1423} // namespace
1424
1425char AMDGPUSwLowerLDSLegacy::ID = 0;
1426char &llvm::AMDGPUSwLowerLDSLegacyPassID = AMDGPUSwLowerLDSLegacy::ID;
1427
1428INITIALIZE_PASS_BEGIN(AMDGPUSwLowerLDSLegacy, "amdgpu-sw-lower-lds",
1429 "AMDGPU Software lowering of LDS", false, false)
1431INITIALIZE_PASS_END(AMDGPUSwLowerLDSLegacy, "amdgpu-sw-lower-lds",
1432 "AMDGPU Software lowering of LDS", false, false)
1433
1434bool AMDGPUSwLowerLDSLegacy::runOnModule(Module &M) {
1435 // AddressSanitizer pass adds "nosanitize_address" module flag if it has
1436 // instrumented the IR. Return early if the flag is not present.
1437 if (!M.getModuleFlag("nosanitize_address"))
1438 return false;
1439 DominatorTreeWrapperPass *const DTW =
1440 getAnalysisIfAvailable<DominatorTreeWrapperPass>();
1441 auto DTCallback = [&DTW](Function &F) -> DominatorTree * {
1442 return DTW ? &DTW->getDomTree() : nullptr;
1443 };
1444
1445 AMDGPUSwLowerLDS SwLowerLDSImpl(M, DTCallback);
1446 bool IsChanged = SwLowerLDSImpl.run();
1447 return IsChanged;
1448}
1449
1451 return new AMDGPUSwLowerLDSLegacy();
1452}
1453
1456 // AddressSanitizer pass adds "nosanitize_address" module flag if it has
1457 // instrumented the IR. Return early if the flag is not present.
1458 if (!M.getModuleFlag("nosanitize_address"))
1459 return PreservedAnalyses::all();
1460 auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
1461 auto DTCallback = [&FAM](Function &F) -> DominatorTree * {
1462 return &FAM.getResult<DominatorTreeAnalysis>(F);
1463 };
1464 AMDGPUSwLowerLDS SwLowerLDSImpl(M, DTCallback);
1465 bool IsChanged = SwLowerLDSImpl.run();
1466 if (!IsChanged)
1467 return PreservedAnalyses::all();
1468
1471 return PA;
1472}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Expand Atomic instructions
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...
Hexagon Common GEP
IRTranslator LLVM IR MI
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
if(auto Err=PB.parsePassPipeline(MPM, Passes)) return wrap(std MPM run * Mod
FunctionAnalysisManager FAM
#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
static Split data
This file contains some functions that are useful when dealing with strings.
#define LLVM_DEBUG(...)
Definition Debug.h:119
static DebugLoc getOrCreateDebugLoc(const Instruction *InsertBefore, DISubprogram *SP)
This class represents a conversion between pointers from one address space to another.
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Represent the analysis usage information of a pass.
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
This class represents any memset intrinsic.
static LLVM_ABI ArrayType * get(Type *ElementType, uint64_t NumElements)
This static method is the primary way to construct an ArrayType.
An instruction that atomically checks whether a specified value is in a memory location,...
void setVolatile(bool V)
Specify whether this is a volatile cmpxchg.
an instruction that atomically reads a memory location, combines it with another value,...
void setVolatile(bool V)
Specify whether this is a volatile RMW or not.
LLVM Basic Block Representation.
Definition BasicBlock.h:62
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
std::pair< std::optional< WeakTrackingVH >, CallGraphNode * > CallRecord
A pair of the calling instruction (a call or invoke) and the call graph node being called.
Definition CallGraph.h:174
The basic data container for the call graph of a Module of IR.
Definition CallGraph.h:72
This class represents a function call, abstracting a target machine's calling convention.
static LLVM_ABI Constant * get(ArrayType *T, ArrayRef< Constant * > V)
static ConstantAsMetadata * get(Constant *C)
Definition Metadata.h:548
static LLVM_SUPPRESS_DEPRECATED_DECLARATIONS_PUSH Constant * getGetElementPtr(Type *Ty, Constant *C, ArrayRef< Constant * > IdxList, GEPNoWrapFlags NW=GEPNoWrapFlags::none(), std::optional< ConstantRange > InRange=std::nullopt, Type *OnlyIfReducedTy=nullptr)
Getelementptr form.
Definition Constants.h:1477
static LLVM_ABI Constant * getAddrSpaceCast(Constant *C, Type *Ty, bool OnlyIfReduced=false)
static LLVM_ABI ConstantPointerNull * get(PointerType *T)
Static factory methods - Return objects of the specified value.
static LLVM_ABI Constant * get(StructType *T, ArrayRef< Constant * > V)
This is an important base class in LLVM.
Definition Constant.h:44
LLVM_ABI void removeDeadConstantUsers() const
If there are any dead constant users dangling off of this constant, remove them.
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
Subprogram description. Uses SubclassData1.
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
A debug info location.
Definition DebugLoc.h:126
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
Definition DenseMap.h:856
Implements a dense probed hash-table based set.
Definition DenseSet.h:283
Analysis pass which computes a DominatorTree.
Definition Dominators.h:241
Legacy analysis pass which computes a DominatorTree.
Definition Dominators.h:277
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition Dominators.h:122
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
static GEPNoWrapFlags inBounds()
void applyUpdates(ArrayRef< UpdateT > Updates)
Submit updates to all available trees.
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set a particular kind of metadata attachment.
LLVM_ABI bool isDeclaration() const
Return true if the primary definition of this global value is outside of the current translation unit...
Definition Globals.cpp:408
LLVM_ABI void setSanitizerMetadata(SanitizerMetadata Meta)
Definition Globals.cpp:324
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
@ ExternalLinkage
Externally visible function.
Definition GlobalValue.h:53
Type * getValueType() const
MaybeAlign getAlign() const
Returns the alignment of the given variable.
LLVM_ABI void eraseFromParent()
eraseFromParent - This method unlinks 'this' from the containing module and deletes it.
Definition Globals.cpp:609
ConstantInt * getInt1(bool V)
Get a constant value representing either true or false.
Definition IRBuilder.h:446
Value * CreateAddrSpaceCast(Value *V, Type *DestTy, const Twine &Name="", bool IsNonNull=false)
Definition IRBuilder.h:2324
AtomicCmpXchgInst * CreateAtomicCmpXchg(Value *Ptr, Value *Cmp, Value *New, MaybeAlign Align, AtomicOrdering SuccessOrdering, AtomicOrdering FailureOrdering, SyncScope::ID SSID=SyncScope::System)
Definition IRBuilder.h:1974
IntegerType * getInt1Ty()
Fetch the type representing a single bit.
Definition IRBuilder.h:513
CondBrInst * CreateCondBr(Value *Cond, BasicBlock *True, BasicBlock *False, MDNode *BranchWeights=nullptr, MDNode *Unpredictable=nullptr)
Create a conditional 'br Cond, TrueDest, FalseDest' instruction.
Definition IRBuilder.h:1218
Value * CreatePtrToAddr(Value *V, const Twine &Name="")
Definition IRBuilder.h:2305
LLVM_ABI Value * CreateVectorSplat(unsigned NumElts, Value *V, const Twine &Name="")
Return a vector value that contains.
LLVM_ABI Value * CreateSelect(Value *C, Value *True, Value *False, const Twine &Name="", Instruction *MDFrom=nullptr)
Value * CreateIntToPtr(Value *V, Type *DestTy, const Twine &Name="")
Definition IRBuilder.h:2314
void SetCurrentDebugLocation(const DebugLoc &L)
Set location information used by debugging information.
Definition IRBuilder.h:229
IntegerType * getInt32Ty()
Fetch the type representing a 32-bit integer.
Definition IRBuilder.h:528
IntegerType * getInt64Ty()
Fetch the type representing a 64-bit integer.
Definition IRBuilder.h:533
Value * CreateInBoundsGEP(Type *Ty, Value *Ptr, ArrayRef< Value * > IdxList, const Twine &Name="")
Definition IRBuilder.h:2021
LLVM_ABI CallInst * CreateElementUnorderedAtomicMemMove(Value *Dst, Align DstAlign, Value *Src, Align SrcAlign, Value *Size, uint32_t ElementSize, const AAMDNodes &AAInfo=AAMDNodes())
Create and insert an element unordered-atomic memmove between the specified pointers.
Value * CreateUDiv(Value *LHS, Value *RHS, const Twine &Name="", bool isExact=false)
Definition IRBuilder.h:1475
ConstantInt * getInt64(uint64_t C)
Get a constant 64-bit value.
Definition IRBuilder.h:476
UncondBrInst * CreateBr(BasicBlock *Dest)
Create an unconditional 'br label X' instruction.
Definition IRBuilder.h:1212
ConstantInt * getInt32(uint32_t C)
Get a constant 32-bit value.
Definition IRBuilder.h:471
PHINode * CreatePHI(Type *Ty, unsigned NumReservedValues, const Twine &Name="")
Definition IRBuilder.h:2623
Value * CreateICmpEQ(Value *LHS, Value *RHS, const Twine &Name="")
Definition IRBuilder.h:2458
Value * CreateSub(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition IRBuilder.h:1441
LoadInst * CreateLoad(Type *Ty, Value *Ptr, const char *Name)
Provided to resolve 'CreateLoad(Ty, Ptr, "...")' correctly, instead of converting the string to 'bool...
Definition IRBuilder.h:1913
CallInst * CreateElementUnorderedAtomicMemSet(Value *Ptr, Value *Val, uint64_t Size, Align Alignment, uint32_t ElementSize, const AAMDNodes &AAInfo=AAMDNodes())
Create and insert an element unordered-atomic memset of the region of memory starting at the given po...
Definition IRBuilder.h:623
CallInst * CreateMemSet(Value *Ptr, Value *Val, uint64_t Size, MaybeAlign Align, bool isVolatile=false, const AAMDNodes &AAInfo=AAMDNodes())
Create and insert a memset to the specified pointer and the specified value.
Definition IRBuilder.h:602
Value * CreateZExt(Value *V, Type *DestTy, const Twine &Name="", bool IsNonNeg=false)
Definition IRBuilder.h:2197
ReturnInst * CreateRetVoid()
Create a 'ret void' instruction.
Definition IRBuilder.h:1189
LLVM_ABI Value * CreateIntrinsic(Intrinsic::ID ID, ArrayRef< Type * > OverloadTypes, ArrayRef< Value * > Args, FMFSource FMFSource={}, const Twine &Name="", ArrayRef< OperandBundleDef > OpBundles={}, function_ref< void(CallInst *)> SetFn=[](CallInst *) {})
Variant to create a possibly constant-folded intrinsic.
StoreInst * CreateStore(Value *Val, Value *Ptr, bool isVolatile=false)
Definition IRBuilder.h:1931
Value * CreateAdd(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition IRBuilder.h:1424
Value * CreatePtrToInt(Value *V, Type *DestTy, const Twine &Name="")
Definition IRBuilder.h:2309
CallInst * CreateCall(FunctionType *FTy, Value *Callee, ArrayRef< Value * > Args={}, const Twine &Name="", MDNode *FPMathTag=nullptr)
Definition IRBuilder.h:2637
Value * CreateTrunc(Value *V, Type *DestTy, const Twine &Name="", bool IsNUW=false, bool IsNSW=false)
Definition IRBuilder.h:2183
PointerType * getPtrTy(unsigned AddrSpace=0)
Fetch the type representing a pointer.
Definition IRBuilder.h:571
Value * CreateIsNull(Value *Arg, const Twine &Name="")
Return a boolean value testing if Arg == 0.
Definition IRBuilder.h:2834
void SetInsertPoint(BasicBlock *TheBB)
This specifies that created instructions should be appended to the end of the specified block.
Definition IRBuilder.h:197
Type * getVoidTy()
Fetch the type representing void.
Definition IRBuilder.h:566
LLVM_ABI CallInst * CreateElementUnorderedAtomicMemCpy(Value *Dst, Align DstAlign, Value *Src, Align SrcAlign, Value *Size, uint32_t ElementSize, const AAMDNodes &AAInfo=AAMDNodes())
Create and insert an element unordered-atomic memcpy between the specified pointers.
LLVM_ABI CallInst * CreateMemTransferInst(Intrinsic::ID IntrID, Value *Dst, MaybeAlign DstAlign, Value *Src, MaybeAlign SrcAlign, Value *Size, bool isVolatile=false, const AAMDNodes &AAInfo=AAMDNodes())
Value * CreateOr(Value *LHS, Value *RHS, const Twine &Name="", bool IsDisjoint=false)
Definition IRBuilder.h:1594
IntegerType * getInt8Ty()
Fetch the type representing an 8-bit integer.
Definition IRBuilder.h:518
Value * CreateMul(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition IRBuilder.h:1458
AtomicRMWInst * CreateAtomicRMW(AtomicRMWInst::BinOp Op, Value *Ptr, Value *Val, MaybeAlign Align, AtomicOrdering Ordering, SyncScope::ID SSID=SyncScope::System, bool Elementwise=false)
Definition IRBuilder.h:1987
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2987
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI const Function * getFunction() const
Return the function this instruction belongs to.
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:76
An instruction for reading from memory.
LLVM_ABI MDNode * createRange(const APInt &Lo, const APInt &Hi)
Return metadata describing the range [Lo, Hi).
Definition MDBuilder.cpp:92
Metadata node.
Definition Metadata.h:1081
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1579
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:39
bool empty() const
Definition MapVector.h:80
bool contains(const KeyT &Key) const
Definition MapVector.h:149
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition MapVector.h:127
Root of the metadata hierarchy.
Definition Metadata.h:64
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:253
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
A container for an operand bundle being viewed as a set of values rather than a set of uses.
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserve()
Mark an analysis as preserved.
Definition Analysis.h:132
Return a value (possibly void), from a function.
A vector that has set insertion semantics.
Definition SetVector.h:57
size_type size() const
Determine the number of elements in the SetVector.
Definition SetVector.h:103
iterator end()
Get an iterator to the end of the SetVector.
Definition SetVector.h:118
void clear()
Completely clear the SetVector.
Definition SetVector.h:273
bool empty() const
Determine if the SetVector is empty or not.
Definition SetVector.h:100
iterator begin()
Get an iterator to the beginning of the SetVector.
Definition SetVector.h:112
bool insert(const value_type &X)
Insert a new element into the SetVector.
Definition SetVector.h:157
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:28
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
Class to represent struct types.
static LLVM_ABI StructType * create(LLVMContext &Context, StringRef Name)
This creates an identified struct.
Definition Type.cpp:662
Target-Independent Code Generator Pass Configuration Options.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:548
iterator_range< user_iterator > users()
Definition Value.h:428
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:348
LLVM_ABI bool replaceUsesWithIf(Value *New, llvm::function_ref< bool(Use &U)> ShouldReplace)
Go through the uses list for this definition and make each use point to "V" if the callback ShouldRep...
Definition Value.cpp:556
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:314
static LLVM_ABI VectorType * get(Type *ElementType, ElementCount EC)
This static method is the primary way to construct an VectorType.
An efficient, type-erasing, non-owning reference to a callable.
A raw_ostream that writes to an std::string.
A raw_ostream that writes to an SmallVector or SmallString.
StringRef str() const
Return a StringRef for the vector contents.
@ LOCAL_ADDRESS
Address space for local memory.
@ FLAT_ADDRESS
Address space for flat memory.
@ GLOBAL_ADDRESS
Address space for global memory (RAT0, VTX0).
GVUsesInfoTy getTransitiveUsesOfLDSForLowering(const CallGraph &CG, Module &M)
Collects all uses of LDS Global Variables in M using getUsesOfGVByFunction, with isLDSVariableToLower...
void getInterestingMemoryOperands(Module &M, Instruction *I, SmallVectorImpl< InterestingMemoryOperand > &Interesting)
Get all the memory operands from the instruction that needs to be instrumented.
bool isDynamicLDS(const GlobalVariable &GV)
unsigned getAMDHSACodeObjectVersion(const Module &M)
void removeFnAttrFromReachable(CallGraph &CG, Function *KernelRoot, ArrayRef< StringRef > FnAttrs)
Strip FnAttr attribute from any functions where we may have introduced its use.
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)
MapVector< Function *, DenseSet< GlobalVariable * > > FunctionVariableMap
bool isLDSVariableToLower(const GlobalVariable &GV)
Align getAlign(const DataLayout &DL, const GlobalVariable *GV)
void instrumentAddress(Module &M, IRBuilder<> &IRB, Instruction *OrigIns, Instruction *InsertBefore, Value *Addr, Align Alignment, TypeSize TypeStoreSize, bool IsWrite, Value *SizeArgument, bool UseCalls, bool Recover, int AsanScale, int AsanOffset)
Instrument the memory operand Addr.
uint64_t getRedzoneSizeForGlobal(int AsanScale, uint64_t SizeInBytes)
Given SizeInBytes of the Value to be instrunmented, Returns the redzone size corresponding to it.
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
This is an optimization pass for GlobalISel generic memory operations.
ModulePass * createAMDGPUSwLowerLDSLegacyPass()
@ Offset
Definition DWP.cpp:577
auto find(R &&Range, const T &Val)
Provide wrappers to std::find which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1781
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
constexpr from_range_t from_range
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
Definition STLExtras.h:2224
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:649
InnerAnalysisManagerProxy< FunctionAnalysisManager, Module > FunctionAnalysisManagerModuleProxy
Provide the FunctionAnalysisManager to Module proxy.
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1762
char & AMDGPUSwLowerLDSLegacyPassID
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1652
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
constexpr uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition Alignment.h:149
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
LLVM_ABI void getAddressSanitizerParams(const LLVMContext &Ctx, const Triple &TargetTriple, int LongSize, bool IsKasan, uint64_t *ShadowBase, int *MappingScale, bool *OrShadowOffset)
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1963
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
LLVM_ABI void reportFatalUsageError(Error Err)
Report a fatal error that does not indicate a bug in LLVM.
Definition Error.cpp:177
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
FunctionVariableMap DirectAccess
FunctionVariableMap IndirectAccess
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
constexpr uint64_t value() const
This is a hole in the type system and should not be abused.
Definition Alignment.h:82
Align valueOrOne() const
For convenience, returns a valid alignment or 1 if undefined.
Definition Alignment.h:135