1//===-- AMDGPULowerBufferFatPointers.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 operations on buffer fat pointers (addrspace 7) to
10// operations on buffer resources (addrspace 8) and is needed for correct
11// codegen.
12//
13// # Background
14//
15// Address space 7 (the buffer fat pointer) is a 160-bit pointer that consists
16// of a 128-bit buffer descriptor and a 32-bit offset into that descriptor.
17// The buffer resource part needs to be it needs to be a "raw" buffer resource
18// (it must have a stride of 0 and bounds checks must be in raw buffer mode
19// or disabled).
20//
21// When these requirements are met, a buffer resource can be treated as a
22// typical (though quite wide) pointer that follows typical LLVM pointer
23// semantics. This allows the frontend to reason about such buffers (which are
24// often encountered in the context of SPIR-V kernels).
25//
26// However, because of their non-power-of-2 size, these fat pointers cannot be
27// present during translation to MIR (though this restriction may be lifted
28// during the transition to GlobalISel). Therefore, this pass is needed in order
29// to correctly implement these fat pointers.
30//
31// The resource intrinsics take the resource part (the address space 8 pointer)
32// and the offset part (the 32-bit integer) as separate arguments. In addition,
33// many users of these buffers manipulate the offset while leaving the resource
34// part alone. For these reasons, we want to typically separate the resource
35// and offset parts into separate variables, but combine them together when
36// encountering cases where this is required, such as by inserting these values
37// into aggretates or moving them to memory.
38//
39// Therefore, at a high level, `ptr addrspace(7) %x` becomes `ptr addrspace(8)
40// %x.rsrc` and `i32 %x.off`, which will be combined into `{ptr addrspace(8),
41// i32} %x = {%x.rsrc, %x.off}` if needed. Similarly, `vector<Nxp7>` becomes
42// `{vector<Nxp8>, vector<Nxi32 >}` and its component parts.
43//
44// # Implementation
45//
46// This pass proceeds in three main phases:
47//
48// ## Rewriting loads and stores of p7 and memcpy()-like handling
49//
50// The first phase is to rewrite away all loads and stors of `ptr addrspace(7)`,
51// including aggregates containing such pointers, to ones that use `i160`. This
52// is handled by `StoreFatPtrsAsIntsAndExpandMemcpyVisitor` , which visits
53// loads, stores, and allocas and, if the loaded or stored type contains `ptr
54// addrspace(7)`, rewrites that type to one where the p7s are replaced by i160s,
55// copying other parts of aggregates as needed. In the case of a store, each
56// pointer is `ptrtoint`d to i160 before storing, and load integers are
57// `inttoptr`d back. This same transformation is applied to vectors of pointers.
58//
59// Such a transformation allows the later phases of the pass to not need
60// to handle buffer fat pointers moving to and from memory, where we load
61// have to handle the incompatibility between a `{Nxp8, Nxi32}` representation
62// and `Nxi60` directly. Instead, that transposing action (where the vectors
63// of resources and vectors of offsets are concatentated before being stored to
64// memory) are handled through implementing `inttoptr` and `ptrtoint` only.
65//
66// Atomics operations on `ptr addrspace(7)` values are not suppported, as the
67// hardware does not include a 160-bit atomic.
68//
69// In order to save on O(N) work and to ensure that the contents type
70// legalizer correctly splits up wide loads, also unconditionally lower
71// memcpy-like intrinsics into loops here.
72//
73// ## Buffer contents type legalization
74//
75// The underlying buffer intrinsics only support types up to 128 bits long,
76// and don't support complex types. If buffer operations were
77// standard pointer operations that could be represented as MIR-level loads,
78// this would be handled by the various legalization schemes in instruction
79// selection. However, because we have to do the conversion from `load` and
80// `store` to intrinsics at LLVM IR level, we must perform that legalization
81// ourselves.
82//
83// This involves a combination of
84// - Converting arrays to vectors where possible
85// - Otherwise, splitting loads and stores of aggregates into loads/stores of
86// each component.
87// - Zero-extending things to fill a whole number of bytes
88// - Casting values of types that don't neatly correspond to supported machine
89// value
90// (for example, an i96 or i256) into ones that would work (
91// like <3 x i32> and <8 x i32>, respectively)
92// - Splitting values that are too long (such as aforementioned <8 x i32>) into
93// multiple operations.
94//
95// ## Type remapping
96//
97// We use a `ValueMapper` to mangle uses of [vectors of] buffer fat pointers
98// to the corresponding struct type, which has a resource part and an offset
99// part.
100//
101// This uses a `BufferFatPtrToStructTypeMap` and a `FatPtrConstMaterializer`
102// to, usually by way of `setType`ing values. Constants are handled here
103// because there isn't a good way to fix them up later.
104//
105// This has the downside of leaving the IR in an invalid state (for example,
106// the instruction `getelementptr {ptr addrspace(8), i32} %p, ...` will exist),
107// but all such invalid states will be resolved by the third phase.
108//
109// Functions that don't take buffer fat pointers are modified in place. Those
110// that do take such pointers have their basic blocks moved to a new function
111// with arguments that are {ptr addrspace(8), i32} arguments and return values.
112// This phase also records intrinsics so that they can be remangled or deleted
113// later.
114//
115// ## Splitting pointer structs
116//
117// The meat of this pass consists of defining semantics for operations that
118// produce or consume [vectors of] buffer fat pointers in terms of their
119// resource and offset parts. This is accomplished throgh the `SplitPtrStructs`
120// visitor.
121//
122// In the first pass through each function that is being lowered, the splitter
123// inserts new instructions to implement the split-structures behavior, which is
124// needed for correctness and performance. It records a list of "split users",
125// instructions that are being replaced by operations on the resource and offset
126// parts.
127//
128// Split users do not necessarily need to produce parts themselves (
129// a `load float, ptr addrspace(7)` does not, for example), but, if they do not
130// generate fat buffer pointers, they must RAUW in their replacement
131// instructions during the initial visit.
132//
133// When these new instructions are created, they use the split parts recorded
134// for their initial arguments in order to generate their replacements, creating
135// a parallel set of instructions that does not refer to the original fat
136// pointer values but instead to their resource and offset components.
137//
138// Instructions, such as `extractvalue`, that produce buffer fat pointers from
139// sources that do not have split parts, have such parts generated using
140// `extractvalue`. This is also the initial handling of PHI nodes, which
141// are then cleaned up.
142//
143// ### Conditionals
144//
145// PHI nodes are initially given resource parts via `extractvalue`. However,
146// this is not an efficient rewrite of such nodes, as, in most cases, the
147// resource part in a conditional or loop remains constant throughout the loop
148// and only the offset varies. Failing to optimize away these constant resources
149// would cause additional registers to be sent around loops and might lead to
150// waterfall loops being generated for buffer operations due to the
151// "non-uniform" resource argument.
152//
153// Therefore, after all instructions have been visited, the pointer splitter
154// post-processes all encountered conditionals. Given a PHI node or select,
155// getPossibleRsrcRoots() collects all values that the resource parts of that
156// conditional's input could come from as well as collecting all conditional
157// instructions encountered during the search. If, after filtering out the
158// initial node itself, the set of encountered conditionals is a subset of the
159// potential roots and there is a single potential resource that isn't in the
160// conditional set, that value is the only possible value the resource argument
161// could have throughout the control flow.
162//
163// If that condition is met, then a PHI node can have its resource part changed
164// to the singleton value and then be replaced by a PHI on the offsets.
165// Otherwise, each PHI node is split into two, one for the resource part and one
166// for the offset part, which replace the temporary `extractvalue` instructions
167// that were added during the first pass.
168//
169// Similar logic applies to `select`, where
170// `%z = select i1 %cond, %cond, ptr addrspace(7) %x, ptr addrspace(7) %y`
171// can be split into `%z.rsrc = %x.rsrc` and
172// `%z.off = select i1 %cond, ptr i32 %x.off, i32 %y.off`
173// if both `%x` and `%y` have the same resource part, but two `select`
174// operations will be needed if they do not.
175//
176// ### Final processing
177//
178// After conditionals have been cleaned up, the IR for each function is
179// rewritten to remove all the old instructions that have been split up.
180//
181// Any instruction that used to produce a buffer fat pointer (and therefore now
182// produces a resource-and-offset struct after type remapping) is
183// replaced as follows:
184// 1. All debug value annotations are cloned to reflect that the resource part
185// and offset parts are computed separately and constitute different
186// fragments of the underlying source language variable.
187// 2. All uses that were themselves split are replaced by a `poison` of the
188// struct type, as they will themselves be erased soon. This rule, combined
189// with debug handling, should leave the use lists of split instructions
190// empty in almost all cases.
191// 3. If a user of the original struct-valued result remains, the structure
192// needed for the new types to work is constructed out of the newly-defined
193// parts, and the original instruction is replaced by this structure
194// before being erased. Instructions requiring this construction include
195// `ret` and `insertvalue`.
196//
197// # Consequences
198//
199// This pass does not alter the CFG.
200//
201// Alias analysis information will become coarser, as the LLVM alias analyzer
202// cannot handle the buffer intrinsics. Specifically, while we can determine
203// that the following two loads do not alias:
204// ```
205// %y = getelementptr i32, ptr addrspace(7) %x, i32 1
206// %a = load i32, ptr addrspace(7) %x
207// %b = load i32, ptr addrspace(7) %y
208// ```
209// we cannot (except through some code that runs during scheduling) determine
210// that the rewritten loads below do not alias.
211// ```
212// %y.off = add i32 %x.off, 1
213// %a = call @llvm.amdgcn.raw.ptr.buffer.load(ptr addrspace(8) %x.rsrc, i32
214// %x.off, ...)
215// %b = call @llvm.amdgcn.raw.ptr.buffer.load(ptr addrspace(8)
216// %x.rsrc, i32 %y.off, ...)
217// ```
218// However, existing alias information is preserved.
219//===----------------------------------------------------------------------===//
220
221#include "AMDGPU.h"
222#include "AMDGPUTargetMachine.h"
223#include "GCNSubtarget.h"
224#include "SIDefines.h"
225#include "llvm/ADT/SetOperations.h"
226#include "llvm/ADT/SmallVector.h"
227#include "llvm/Analysis/InstSimplifyFolder.h"
228#include "llvm/Analysis/ScalarEvolution.h"
229#include "llvm/Analysis/ScalarEvolutionExpressions.h"
230#include "llvm/Analysis/TargetTransformInfo.h"
231#include "llvm/Analysis/Utils/Local.h"
232#include "llvm/CodeGen/TargetPassConfig.h"
233#include "llvm/IR/AttributeMask.h"
234#include "llvm/IR/Constants.h"
235#include "llvm/IR/DebugInfo.h"
236#include "llvm/IR/DerivedTypes.h"
237#include "llvm/IR/IRBuilder.h"
238#include "llvm/IR/InstIterator.h"
239#include "llvm/IR/InstVisitor.h"
240#include "llvm/IR/Instructions.h"
241#include "llvm/IR/IntrinsicInst.h"
242#include "llvm/IR/Intrinsics.h"
243#include "llvm/IR/IntrinsicsAMDGPU.h"
244#include "llvm/IR/Metadata.h"
245#include "llvm/IR/Operator.h"
246#include "llvm/IR/PassManager.h"
247#include "llvm/IR/PatternMatch.h"
248#include "llvm/IR/ReplaceConstant.h"
249#include "llvm/IR/ValueHandle.h"
250#include "llvm/InitializePasses.h"
251#include "llvm/Pass.h"
252#include "llvm/Support/AMDGPUAddrSpace.h"
253#include "llvm/Support/Alignment.h"
254#include "llvm/Support/AtomicOrdering.h"
255#include "llvm/Support/Debug.h"
256#include "llvm/Support/ErrorHandling.h"
257#include "llvm/Support/MathExtras.h"
258#include "llvm/Transforms/Utils/Cloning.h"
259#include "llvm/Transforms/Utils/Local.h"
260#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
261#include "llvm/Transforms/Utils/ValueMapper.h"
262
263#define DEBUG_TYPE "amdgpu-lower-buffer-fat-pointers"
264
265using namespace llvm;
266
267using GetTTIFn = function_ref<const TargetTransformInfo *(Function &)>;
268using GetSEFn = function_ref<ScalarEvolution *(Function &)>;
269
270static constexpr unsigned BufferOffsetWidth = 32;
271
272namespace {
273/// Recursively replace instances of ptr addrspace(7) and vector<Nxptr
274/// addrspace(7)> with some other type as defined by the relevant subclass.
275class BufferFatPtrTypeLoweringBase : public ValueMapTypeRemapper {
276 DenseMap<Type *, Type *> Map;
277
278 Type *remapTypeImpl(Type *Ty);
279
280protected:
281 virtual Type *remapScalar(PointerType *PT) = 0;
282 virtual Type *remapVector(VectorType *VT) = 0;
283
284 const DataLayout &DL;
285
286public:
287 BufferFatPtrTypeLoweringBase(const DataLayout &DL) : DL(DL) {}
288 Type *remapType(Type *SrcTy) override;
289 void clear() { Map.clear(); }
290};
291
292/// Remap ptr addrspace(7) to i160 and vector<Nxptr addrspace(7)> to
293/// vector<Nxi60> in order to correctly handling loading/storing these values
294/// from memory.
295class BufferFatPtrToIntTypeMap : public BufferFatPtrTypeLoweringBase {
296 using BufferFatPtrTypeLoweringBase::BufferFatPtrTypeLoweringBase;
297
298protected:
299 Type *remapScalar(PointerType *PT) override { return DL.getIntPtrType(PT); }
300 Type *remapVector(VectorType *VT) override { return DL.getIntPtrType(VT); }
301};
302
303/// Remap ptr addrspace(7) to {ptr addrspace(8), i32} (the resource and offset
304/// parts of the pointer) so that we can easily rewrite operations on these
305/// values that aren't loading them from or storing them to memory.
306class BufferFatPtrToStructTypeMap : public BufferFatPtrTypeLoweringBase {
307 using BufferFatPtrTypeLoweringBase::BufferFatPtrTypeLoweringBase;
308
309protected:
310 Type *remapScalar(PointerType *PT) override;
311 Type *remapVector(VectorType *VT) override;
312};
313} // namespace
314
315// This code is adapted from the type remapper in lib/Linker/IRMover.cpp
316Type *BufferFatPtrTypeLoweringBase::remapTypeImpl(Type *Ty) {
317 Type **Entry = &Map[Ty];
318 if (*Entry)
319 return *Entry;
320 if (auto *PT = dyn_cast<PointerType>(Val: Ty)) {
321 if (PT->getAddressSpace() == AMDGPUAS::BUFFER_FAT_POINTER) {
322 return *Entry = remapScalar(PT);
323 }
324 }
325 if (auto *VT = dyn_cast<VectorType>(Val: Ty)) {
326 auto *PT = dyn_cast<PointerType>(Val: VT->getElementType());
327 if (PT && PT->getAddressSpace() == AMDGPUAS::BUFFER_FAT_POINTER) {
328 return *Entry = remapVector(VT);
329 }
330 return *Entry = Ty;
331 }
332 // Whether the type is one that is structurally uniqued - that is, if it is
333 // not a named struct (the only kind of type where multiple structurally
334 // identical types that have a distinct `Type*`)
335 StructType *TyAsStruct = dyn_cast<StructType>(Val: Ty);
336 bool IsUniqued = !TyAsStruct || TyAsStruct->isLiteral();
337 // Base case for ints, floats, opaque pointers, and so on, which don't
338 // require recursion.
339 if (Ty->getNumContainedTypes() == 0 && IsUniqued)
340 return *Entry = Ty;
341 bool Changed = false;
342 SmallVector<Type *> ElementTypes(Ty->getNumContainedTypes(), nullptr);
343 for (unsigned int I = 0, E = Ty->getNumContainedTypes(); I < E; ++I) {
344 Type *OldElem = Ty->getContainedType(i: I);
345 Type *NewElem = remapTypeImpl(Ty: OldElem);
346 ElementTypes[I] = NewElem;
347 Changed |= (OldElem != NewElem);
348 }
349 // Recursive calls to remapTypeImpl() may have invalidated pointer.
350 Entry = &Map[Ty];
351 if (!Changed) {
352 return *Entry = Ty;
353 }
354 if (auto *ArrTy = dyn_cast<ArrayType>(Val: Ty))
355 return *Entry = ArrayType::get(ElementType: ElementTypes[0], NumElements: ArrTy->getNumElements());
356 if (auto *FnTy = dyn_cast<FunctionType>(Val: Ty))
357 return *Entry = FunctionType::get(Result: ElementTypes[0],
358 Params: ArrayRef(ElementTypes).slice(N: 1),
359 isVarArg: FnTy->isVarArg());
360 if (auto *STy = dyn_cast<StructType>(Val: Ty)) {
361 // Genuine opaque types don't have a remapping.
362 if (STy->isOpaque())
363 return *Entry = Ty;
364 bool IsPacked = STy->isPacked();
365 if (IsUniqued)
366 return *Entry = StructType::get(Context&: Ty->getContext(), Elements: ElementTypes, isPacked: IsPacked);
367 SmallString<16> Name(STy->getName());
368 STy->setName("");
369 return *Entry = StructType::create(Context&: Ty->getContext(), Elements: ElementTypes, Name,
370 isPacked: IsPacked);
371 }
372 llvm_unreachable("Unknown type of type that contains elements");
373}
374
375Type *BufferFatPtrTypeLoweringBase::remapType(Type *SrcTy) {
376 return remapTypeImpl(Ty: SrcTy);
377}
378
379Type *BufferFatPtrToStructTypeMap::remapScalar(PointerType *PT) {
380 LLVMContext &Ctx = PT->getContext();
381 return StructType::get(elt1: PointerType::get(C&: Ctx, AddressSpace: AMDGPUAS::BUFFER_RESOURCE),
382 elts: IntegerType::get(C&: Ctx, NumBits: BufferOffsetWidth));
383}
384
385Type *BufferFatPtrToStructTypeMap::remapVector(VectorType *VT) {
386 ElementCount EC = VT->getElementCount();
387 LLVMContext &Ctx = VT->getContext();
388 Type *RsrcVec =
389 VectorType::get(ElementType: PointerType::get(C&: Ctx, AddressSpace: AMDGPUAS::BUFFER_RESOURCE), EC);
390 Type *OffVec = VectorType::get(ElementType: IntegerType::get(C&: Ctx, NumBits: BufferOffsetWidth), EC);
391 return StructType::get(elt1: RsrcVec, elts: OffVec);
392}
393
394static bool isBufferFatPtrOrVector(Type *Ty) {
395 if (auto *PT = dyn_cast<PointerType>(Val: Ty->getScalarType()))
396 return PT->getAddressSpace() == AMDGPUAS::BUFFER_FAT_POINTER;
397 return false;
398}
399
400// True if the type is {ptr addrspace(8), i32} or a struct containing vectors of
401// those types. Used to quickly skip instructions we don't need to process.
402static bool isSplitFatPtr(Type *Ty) {
403 auto *ST = dyn_cast<StructType>(Val: Ty);
404 if (!ST)
405 return false;
406 if (!ST->isLiteral() || ST->getNumElements() != 2)
407 return false;
408 auto *MaybeRsrc =
409 dyn_cast<PointerType>(Val: ST->getElementType(N: 0)->getScalarType());
410 auto *MaybeOff =
411 dyn_cast<IntegerType>(Val: ST->getElementType(N: 1)->getScalarType());
412 return MaybeRsrc && MaybeOff &&
413 MaybeRsrc->getAddressSpace() == AMDGPUAS::BUFFER_RESOURCE &&
414 MaybeOff->getBitWidth() == BufferOffsetWidth;
415}
416
417// True if the result type or any argument types are buffer fat pointers.
418static bool isBufferFatPtrConst(Constant *C) {
419 Type *T = C->getType();
420 return isBufferFatPtrOrVector(Ty: T) || any_of(Range: C->operands(), P: [](const Use &U) {
421 return isBufferFatPtrOrVector(Ty: U.get()->getType());
422 });
423}
424
425namespace {
426/// Convert [vectors of] buffer fat pointers to integers when they are read from
427/// or stored to memory. This ensures that these pointers will have the same
428/// memory layout as before they are lowered, even though they will no longer
429/// have their previous layout in registers/in the program (they'll be broken
430/// down into resource and offset parts). This has the downside of imposing
431/// marshalling costs when reading or storing these values, but since placing
432/// such pointers into memory is an uncommon operation at best, we feel that
433/// this cost is acceptable for better performance in the common case.
434class StoreFatPtrsAsIntsAndExpandMemcpyVisitor
435 : public InstVisitor<StoreFatPtrsAsIntsAndExpandMemcpyVisitor, bool> {
436 BufferFatPtrToIntTypeMap *TypeMap;
437
438 ValueToValueMapTy ConvertedForStore;
439
440 IRBuilder<InstSimplifyFolder> IRB;
441
442 // Used for memcpy() lowering.
443 const TargetTransformInfo *TTI;
444 ScalarEvolution *SE;
445
446 // Convert all the buffer fat pointers within the input value to inttegers
447 // so that it can be stored in memory.
448 Value *fatPtrsToInts(Value *V, Type *From, Type *To, const Twine &Name);
449 // Convert all the i160s that need to be buffer fat pointers (as specified)
450 // by the To type) into those pointers to preserve the semantics of the rest
451 // of the program.
452 Value *intsToFatPtrs(Value *V, Type *From, Type *To, const Twine &Name);
453
454public:
455 StoreFatPtrsAsIntsAndExpandMemcpyVisitor(BufferFatPtrToIntTypeMap *TypeMap,
456 const DataLayout &DL,
457 LLVMContext &Ctx)
458 : TypeMap(TypeMap), IRB(Ctx, InstSimplifyFolder(DL)) {}
459 bool processFunction(Function &F, const TargetTransformInfo *TTI,
460 ScalarEvolution *SE);
461
462 bool visitInstruction(Instruction &I) { return false; }
463 bool visitAllocaInst(AllocaInst &I);
464 bool visitLoadInst(LoadInst &LI);
465 bool visitStoreInst(StoreInst &SI);
466 bool visitGetElementPtrInst(GetElementPtrInst &I);
467
468 bool visitMemCpyInst(MemCpyInst &MCI);
469 bool visitMemMoveInst(MemMoveInst &MMI);
470 bool visitMemSetInst(MemSetInst &MSI);
471 bool visitMemSetPatternInst(MemSetPatternInst &MSPI);
472};
473} // namespace
474
475Value *StoreFatPtrsAsIntsAndExpandMemcpyVisitor::fatPtrsToInts(
476 Value *V, Type *From, Type *To, const Twine &Name) {
477 if (From == To)
478 return V;
479 ValueToValueMapTy::iterator Find = ConvertedForStore.find(Val: V);
480 if (Find != ConvertedForStore.end())
481 return Find->second;
482 if (isBufferFatPtrOrVector(Ty: From)) {
483 Value *Cast = IRB.CreatePtrToInt(V, DestTy: To, Name: Name + ".int");
484 ConvertedForStore[V] = Cast;
485 return Cast;
486 }
487 if (From->getNumContainedTypes() == 0)
488 return V;
489 // Structs, arrays, and other compound types.
490 Value *Ret = PoisonValue::get(T: To);
491 if (auto *AT = dyn_cast<ArrayType>(Val: From)) {
492 Type *FromPart = AT->getArrayElementType();
493 Type *ToPart = cast<ArrayType>(Val: To)->getElementType();
494 for (uint64_t I = 0, E = AT->getArrayNumElements(); I < E; ++I) {
495 Value *Field = IRB.CreateExtractValue(Agg: V, Idxs: I);
496 Value *NewField =
497 fatPtrsToInts(V: Field, From: FromPart, To: ToPart, Name: Name + "." + Twine(I));
498 Ret = IRB.CreateInsertValue(Agg: Ret, Val: NewField, Idxs: I);
499 }
500 } else {
501 for (auto [Idx, FromPart, ToPart] :
502 enumerate(First: From->subtypes(), Rest: To->subtypes())) {
503 Value *Field = IRB.CreateExtractValue(Agg: V, Idxs: Idx);
504 Value *NewField =
505 fatPtrsToInts(V: Field, From: FromPart, To: ToPart, Name: Name + "." + Twine(Idx));
506 Ret = IRB.CreateInsertValue(Agg: Ret, Val: NewField, Idxs: Idx);
507 }
508 }
509 ConvertedForStore[V] = Ret;
510 return Ret;
511}
512
513Value *StoreFatPtrsAsIntsAndExpandMemcpyVisitor::intsToFatPtrs(
514 Value *V, Type *From, Type *To, const Twine &Name) {
515 if (From == To)
516 return V;
517 if (isBufferFatPtrOrVector(Ty: To)) {
518 Value *Cast = IRB.CreateIntToPtr(V, DestTy: To, Name: Name + ".ptr");
519 return Cast;
520 }
521 if (From->getNumContainedTypes() == 0)
522 return V;
523 // Structs, arrays, and other compound types.
524 Value *Ret = PoisonValue::get(T: To);
525 if (auto *AT = dyn_cast<ArrayType>(Val: From)) {
526 Type *FromPart = AT->getArrayElementType();
527 Type *ToPart = cast<ArrayType>(Val: To)->getElementType();
528 for (uint64_t I = 0, E = AT->getArrayNumElements(); I < E; ++I) {
529 Value *Field = IRB.CreateExtractValue(Agg: V, Idxs: I);
530 Value *NewField =
531 intsToFatPtrs(V: Field, From: FromPart, To: ToPart, Name: Name + "." + Twine(I));
532 Ret = IRB.CreateInsertValue(Agg: Ret, Val: NewField, Idxs: I);
533 }
534 } else {
535 for (auto [Idx, FromPart, ToPart] :
536 enumerate(First: From->subtypes(), Rest: To->subtypes())) {
537 Value *Field = IRB.CreateExtractValue(Agg: V, Idxs: Idx);
538 Value *NewField =
539 intsToFatPtrs(V: Field, From: FromPart, To: ToPart, Name: Name + "." + Twine(Idx));
540 Ret = IRB.CreateInsertValue(Agg: Ret, Val: NewField, Idxs: Idx);
541 }
542 }
543 return Ret;
544}
545
546bool StoreFatPtrsAsIntsAndExpandMemcpyVisitor::processFunction(
547 Function &F, const TargetTransformInfo *TTI, ScalarEvolution *SE) {
548 this->TTI = TTI;
549 this->SE = SE;
550 bool Changed = false;
551 // Process memcpy-like instructions after the main iteration because they can
552 // invalidate iterators.
553 SmallVector<WeakTrackingVH> CanBecomeLoops;
554 for (Instruction &I : make_early_inc_range(Range: instructions(F))) {
555 if (isa<MemTransferInst, MemSetInst, MemSetPatternInst>(Val: I))
556 CanBecomeLoops.push_back(Elt: &I);
557 else
558 Changed |= visit(I);
559 }
560 for (WeakTrackingVH VH : make_early_inc_range(Range&: CanBecomeLoops)) {
561 Changed |= visit(I: cast<Instruction>(Val&: VH));
562 }
563 ConvertedForStore.clear();
564 this->TTI = nullptr;
565 this->SE = nullptr;
566 return Changed;
567}
568
569bool StoreFatPtrsAsIntsAndExpandMemcpyVisitor::visitAllocaInst(AllocaInst &I) {
570 Type *Ty = I.getAllocatedType();
571 Type *NewTy = TypeMap->remapType(SrcTy: Ty);
572 if (Ty == NewTy)
573 return false;
574 I.setAllocatedType(NewTy);
575 return true;
576}
577
578bool StoreFatPtrsAsIntsAndExpandMemcpyVisitor::visitGetElementPtrInst(
579 GetElementPtrInst &I) {
580 Type *Ty = I.getSourceElementType();
581 Type *NewTy = TypeMap->remapType(SrcTy: Ty);
582 if (Ty == NewTy)
583 return false;
584 // We'll be rewriting the type `ptr addrspace(7)` out of existence soon, so
585 // make sure GEPs don't have different semantics with the new type.
586 I.setSourceElementType(NewTy);
587 I.setResultElementType(TypeMap->remapType(SrcTy: I.getResultElementType()));
588 return true;
589}
590
591bool StoreFatPtrsAsIntsAndExpandMemcpyVisitor::visitLoadInst(LoadInst &LI) {
592 Type *Ty = LI.getType();
593 Type *IntTy = TypeMap->remapType(SrcTy: Ty);
594 if (Ty == IntTy)
595 return false;
596
597 IRB.SetInsertPoint(&LI);
598 auto *NLI = cast<LoadInst>(Val: LI.clone());
599 NLI->mutateType(Ty: IntTy);
600 NLI = IRB.Insert(I: NLI);
601 NLI->takeName(V: &LI);
602
603 Value *CastBack = intsToFatPtrs(V: NLI, From: IntTy, To: Ty, Name: NLI->getName());
604 LI.replaceAllUsesWith(V: CastBack);
605 LI.eraseFromParent();
606 return true;
607}
608
609bool StoreFatPtrsAsIntsAndExpandMemcpyVisitor::visitStoreInst(StoreInst &SI) {
610 Value *V = SI.getValueOperand();
611 Type *Ty = V->getType();
612 Type *IntTy = TypeMap->remapType(SrcTy: Ty);
613 if (Ty == IntTy)
614 return false;
615
616 IRB.SetInsertPoint(&SI);
617 Value *IntV = fatPtrsToInts(V, From: Ty, To: IntTy, Name: V->getName());
618 for (auto *Dbg : at::getDVRAssignmentMarkers(Inst: &SI))
619 Dbg->setRawLocation(ValueAsMetadata::get(V: IntV));
620
621 SI.setOperand(i_nocapture: 0, Val_nocapture: IntV);
622 return true;
623}
624
625bool StoreFatPtrsAsIntsAndExpandMemcpyVisitor::visitMemCpyInst(
626 MemCpyInst &MCI) {
627 // TODO: Allow memcpy.p7.p3 as a synonym for the direct-to-LDS copy, which'll
628 // need loop expansion here.
629 if (MCI.getSourceAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER &&
630 MCI.getDestAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
631 return false;
632 llvm::expandMemCpyAsLoop(MemCpy: &MCI, TTI: *TTI, SE);
633 MCI.eraseFromParent();
634 return true;
635}
636
637bool StoreFatPtrsAsIntsAndExpandMemcpyVisitor::visitMemMoveInst(
638 MemMoveInst &MMI) {
639 if (MMI.getSourceAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER &&
640 MMI.getDestAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
641 return false;
642 reportFatalUsageError(
643 reason: "memmove() on buffer descriptors is not implemented because pointer "
644 "comparison on buffer descriptors isn't implemented\n");
645}
646
647bool StoreFatPtrsAsIntsAndExpandMemcpyVisitor::visitMemSetInst(
648 MemSetInst &MSI) {
649 if (MSI.getDestAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
650 return false;
651 llvm::expandMemSetAsLoop(MemSet: &MSI, TTI);
652 MSI.eraseFromParent();
653 return true;
654}
655
656bool StoreFatPtrsAsIntsAndExpandMemcpyVisitor::visitMemSetPatternInst(
657 MemSetPatternInst &MSPI) {
658 if (MSPI.getDestAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
659 return false;
660 llvm::expandMemSetPatternAsLoop(MemSet: &MSPI, TTI: *TTI);
661 MSPI.eraseFromParent();
662 return true;
663}
664
665namespace {
666/// Convert loads/stores of types that the buffer intrinsics can't handle into
667/// one ore more such loads/stores that consist of legal types.
668///
669/// Do this by
670/// 1. Recursing into structs (and arrays that don't share a memory layout with
671/// vectors) since the intrinsics can't handle complex types.
672/// 2. Converting arrays of non-aggregate, byte-sized types into their
673/// corresponding vectors
674/// 3. Bitcasting unsupported types, namely overly-long scalars and byte
675/// vectors, into vectors of supported types.
676/// 4. Splitting up excessively long reads/writes into multiple operations.
677///
678/// Note that this doesn't handle complex data strucures, but, in the future,
679/// the aggregate load splitter from SROA could be refactored to allow for that
680/// case.
681///
682/// Note that, if we can prove that the initial value of the pointer offset is 0
683/// and that the load/store won't wrap from the left or won't have bounds checks
684/// that straddle a word boundary, we can emit some of the strict bounds
685/// checking pessimizations even in strict OOB mode, and we attempt to do so.
686class LegalizeBufferContentTypesVisitor
687 : public InstVisitor<LegalizeBufferContentTypesVisitor, bool> {
688 friend class InstVisitor<LegalizeBufferContentTypesVisitor, bool>;
689
690 IRBuilder<InstSimplifyFolder> IRB;
691
692 const DataLayout &DL;
693
694 ScalarEvolution *SE = nullptr;
695
696 // Map base (non-GEP'd) pointers to the number of records they have, if known.
697 // If a pointer is known to have a starting offset of 0 but it wasn't known to
698 // have a number of records (ex. it was `addrspacecast` from a buffer
699 // resource), it will be present in this map, but the key will be null.
700 // Otherwise, there will be no map entry.
701 ValueToValueMapTy ZeroBasePointerToNumRecords;
702
703 // Subtarget info, needed for determining what cache control bits to set.
704 const TargetMachine *TM;
705 const GCNSubtarget *ST = nullptr;
706
707 /// If T is [N x U], where U is a scalar type, return the vector type
708 /// <N x U>, otherwise, return T.
709 Type *scalarArrayTypeAsVector(Type *MaybeArrayType);
710 Value *arrayToVector(Value *V, Type *TargetType, const Twine &Name);
711 Value *vectorToArray(Value *V, Type *OrigType, const Twine &Name);
712
713 /// Analyze how a given buffer access could be out of bounds. Used to optimize
714 /// the strict splitting used in strict bounds checking mode.
715 struct OobProperties {
716 // Offset is far enough from all-1s that we won't get wrapping around to 0.
717 bool NoWrapFromMax = false;
718 // Offset is either entirely in-bounds or entirely out of bounds.
719 bool NoPartialOOB = false;
720
721 OobProperties() = delete;
722 // Needed for some Clangs.
723 OobProperties(bool NoWrapFromMax, bool NoPartialOOB)
724 : NoWrapFromMax(NoWrapFromMax), NoPartialOOB(NoPartialOOB) {}
725 };
726 OobProperties analyzeOobProperties(Value *Ptr, Type *Ty, uint64_t ByteOffset);
727
728 /// Break up the loads of a struct into the loads of its components
729
730 /// Return the maximum allowed load/store width for the given type and
731 /// alignment combination based on subtarget flags.
732 /// 1. If unaligned accesses are not enabled, then any load/store that is less
733 /// than word-aligned has to be handled one byte or ushort at a time.
734 /// 2. If relaxed OOB mode is not set, we must ensure that the in-bounds
735 /// part of a partially out of bounds read/write is performed correctly. This
736 /// means that any load that isn't naturally aligned has to be split into
737 /// parts that are naturally aligned, so that, after bitcasting, we don't have
738 /// unaligned loads that could discard valid data.
739 ///
740 /// For example, if we're loading a <8 x i8>, that's actually a load of a <2 x
741 /// i32>, and if we load from an align(2) address, that address might be 2
742 /// bytes from the end of the buffer. The hardware will, when performing the
743 /// <2 x i32> load, mask off the entire first word, causing the two in-bounds
744 /// bytes to be masked off. However,if we know the offset can't be too close
745 /// to the number of records in the buffer (if known), we can skip this
746 /// expansion.
747 ///
748 /// Unlike the complete disablement of unaligned accesses from point 1,
749 /// this does not apply to unaligned scalars, but will apply to cases like
750 /// `load <2 x i32>, align 4` since the left elemenvt might be out of bounds.
751 /// Note that if the we know that the base offset is known to be
752 /// less than `uint32_max - byte_size(Ty)`, we can skip these alignment
753 /// checks.
754 uint64_t maxIntrinsicWidth(Type *Ty, Align A, OobProperties OobProps);
755
756 /// Convert a vector or scalar type that can't be operated on by buffer
757 /// intrinsics to one that would be legal through bitcasts and/or truncation.
758 /// Uses the wider of i32, i16, or i8 where possible, clamping to the maximum
759 /// allowed width under the alignment rules and subtarget flags.
760 Type *legalNonAggregateForMemOp(Type *T, uint64_t MaxWidth);
761 Value *makeLegalNonAggregate(Value *V, Type *TargetType, const Twine &Name);
762 Value *makeIllegalNonAggregate(Value *V, Type *OrigType, const Twine &Name);
763
764 struct VecSlice {
765 uint64_t Index = 0;
766 uint64_t Length = 0;
767 VecSlice() = delete;
768 // Needed for some Clangs
769 VecSlice(uint64_t Index, uint64_t Length) : Index(Index), Length(Length) {}
770 };
771 /// Return the [index, length] pairs into which `T` needs to be cut to form
772 /// legal buffer load or store operations. Clears `Slices`. Creates an empty
773 /// `Slices` for non-vector inputs and creates one slice if no slicing will be
774 /// needed. No slice may be larger than `MaxWidth`.
775 void getVecSlices(Type *T, uint64_t MaxWidth,
776 SmallVectorImpl<VecSlice> &Slices);
777
778 Value *extractSlice(Value *Vec, VecSlice S, const Twine &Name);
779 Value *insertSlice(Value *Whole, Value *Part, VecSlice S, const Twine &Name);
780
781 /// In most cases, return `LegalType`. However, when given an input that would
782 /// normally be a legal type for the buffer intrinsics to return but that
783 /// isn't hooked up through SelectionDAG, return a type of the same width that
784 /// can be used with the relevant intrinsics. Specifically, handle the cases:
785 /// - <1 x T> => T for all T
786 /// - <N x i8> <=> i16, i32, 2xi32, 4xi32 (as needed)
787 /// - <N x T> where T is under 32 bits and the total size is 96 bits <=> <3 x
788 /// i32>
789 Type *intrinsicTypeFor(Type *LegalType);
790
791 bool visitLoadImpl(LoadInst &OrigLI, Type *PartType,
792 SmallVectorImpl<uint32_t> &AggIdxs, uint64_t AggByteOffset,
793 Value *&Result, const Twine &Name);
794 /// Return value is (Changed, ModifiedInPlace)
795 std::pair<bool, bool> visitStoreImpl(StoreInst &OrigSI, Type *PartType,
796 SmallVectorImpl<uint32_t> &AggIdxs,
797 uint64_t AggByteOffset,
798 const Twine &Name);
799
800 bool visitInstruction(Instruction &I) { return false; }
801 bool visitLoadInst(LoadInst &LI);
802 bool visitStoreInst(StoreInst &SI);
803
804 // Record base pointer data and num_records (if known).
805 bool visitIntrinsicInst(IntrinsicInst &II);
806 bool visitAddrSpaceCastInst(AddrSpaceCastInst &ASCI);
807
808public:
809 LegalizeBufferContentTypesVisitor(const DataLayout &DL, LLVMContext &Ctx,
810 const TargetMachine *TM)
811 : IRB(Ctx, InstSimplifyFolder(DL)), DL(DL), TM(TM) {}
812 bool processFunction(Function &F, ScalarEvolution *SE);
813};
814} // namespace
815
816Type *LegalizeBufferContentTypesVisitor::scalarArrayTypeAsVector(Type *T) {
817 ArrayType *AT = dyn_cast<ArrayType>(Val: T);
818 if (!AT)
819 return T;
820 Type *ET = AT->getElementType();
821 if (!ET->isSingleValueType() || isa<VectorType>(Val: ET))
822 reportFatalUsageError(reason: "loading non-scalar arrays from buffer fat pointers "
823 "should have recursed");
824 if (!DL.typeSizeEqualsStoreSize(Ty: AT))
825 reportFatalUsageError(
826 reason: "loading padded arrays from buffer fat pinters should have recursed");
827 return FixedVectorType::get(ElementType: ET, NumElts: AT->getNumElements());
828}
829
830Value *LegalizeBufferContentTypesVisitor::arrayToVector(Value *V,
831 Type *TargetType,
832 const Twine &Name) {
833 Value *VectorRes = PoisonValue::get(T: TargetType);
834 auto *VT = cast<FixedVectorType>(Val: TargetType);
835 unsigned EC = VT->getNumElements();
836 for (auto I : iota_range<unsigned>(0, EC, /*Inclusive=*/false)) {
837 Value *Elem = IRB.CreateExtractValue(Agg: V, Idxs: I, Name: Name + ".elem." + Twine(I));
838 VectorRes = IRB.CreateInsertElement(Vec: VectorRes, NewElt: Elem, Idx: I,
839 Name: Name + ".as.vec." + Twine(I));
840 }
841 return VectorRes;
842}
843
844Value *LegalizeBufferContentTypesVisitor::vectorToArray(Value *V,
845 Type *OrigType,
846 const Twine &Name) {
847 Value *ArrayRes = PoisonValue::get(T: OrigType);
848 ArrayType *AT = cast<ArrayType>(Val: OrigType);
849 unsigned EC = AT->getNumElements();
850 for (auto I : iota_range<unsigned>(0, EC, /*Inclusive=*/false)) {
851 Value *Elem = IRB.CreateExtractElement(Vec: V, Idx: I, Name: Name + ".elem." + Twine(I));
852 ArrayRes = IRB.CreateInsertValue(Agg: ArrayRes, Val: Elem, Idxs: I,
853 Name: Name + ".as.array." + Twine(I));
854 }
855 return ArrayRes;
856}
857
858LegalizeBufferContentTypesVisitor::OobProperties
859LegalizeBufferContentTypesVisitor::analyzeOobProperties(Value *Ptr, Type *Ty,
860 uint64_t ByteOffset) {
861 OobProperties Result(false, false);
862
863 if (ST->hasRelaxedBufferOOBMode())
864 return OobProperties(true, true);
865
866 if (!SE)
867 return Result;
868 if (!SE->isSCEVable(Ty: Ptr->getType()))
869 return Result;
870 const SCEV *PtrOp = SE->getSCEV(V: Ptr);
871 if (ByteOffset > 0)
872 PtrOp = SE->getAddExpr(LHS: PtrOp, RHS: SE->getConstant(V: IRB.getInt32(C: ByteOffset)));
873 const auto *PtrBase = dyn_cast<SCEVUnknown>(Val: SE->getPointerBase(V: PtrOp));
874 if (!PtrBase)
875 return Result;
876 Value *PtrBaseVal = PtrBase->getValue();
877 // We don't know if the offset field started at 0, so there's no safe analysis
878 // we can do. If it weren't for the fact that nuw / inbounds / ... are
879 // properties of the pointer, we might be able to use hem, but loads where the
880 // address computation for sub-parts of the loaded type wraps the address
881 // space are explicitly in scope here so there's not much we can do inside
882 // functions that can't "see" the fat pointer creation.
883 auto NumRecordsIfKnown = ZeroBasePointerToNumRecords.find(Val: PtrBaseVal);
884 if (NumRecordsIfKnown == ZeroBasePointerToNumRecords.end())
885 return Result;
886
887 unsigned TypeSize = DL.getTypeStoreSize(Ty).getKnownMinValue();
888 const SCEV *PtrDiff = SE->getMinusSCEV(LHS: PtrOp, RHS: PtrBase);
889 APInt MaxNoWrapOffset = APInt::getAllOnes(numBits: BufferOffsetWidth) - TypeSize;
890 if (SE->isKnownNonNegative(S: PtrDiff) ||
891 SE->getUnsignedRangeMax(S: PtrDiff).ule(RHS: MaxNoWrapOffset))
892 Result.NoWrapFromMax = true;
893
894 // If we know that the pointer is zero-based but not what its upper bound is,
895 // we'll need to split up underaligned loads of small types.
896 if (!NumRecordsIfKnown->second)
897 return Result;
898 const SCEV *NumRecords = SE->getSCEV(V: NumRecordsIfKnown->second);
899 // All-1s is (per ISA or as a consequence of the bonud)check rules, depending
900 // on arcihtecture) no bounds check.
901 if (NumRecords->isAllOnesValue())
902 Result.NoPartialOOB = true;
903
904 const SCEV *BoundsDiff;
905 if (ST->has45BitNumRecordsBufferResource()) {
906 const SCEV *PtrDiffExt =
907 SE->getNoopOrZeroExtend(V: PtrDiff, Ty: NumRecords->getType());
908 BoundsDiff = SE->getMinusSCEV(LHS: NumRecords, RHS: PtrDiffExt);
909 } else {
910 const SCEV *NumRecordsI32 =
911 SE->getTruncateOrNoop(V: NumRecords, Ty: IRB.getInt32Ty());
912 BoundsDiff = SE->getMinusSCEV(LHS: NumRecordsI32, RHS: PtrDiff);
913 }
914
915 if (SE->getSignedRangeMin(S: BoundsDiff).sge(RHS: TypeSize) ||
916 SE->isKnownNonPositive(S: BoundsDiff))
917 Result.NoPartialOOB = true;
918 return Result;
919}
920
921uint64_t
922LegalizeBufferContentTypesVisitor::maxIntrinsicWidth(Type *T, Align A,
923 OobProperties OobProps) {
924 Align Result(16);
925 if (!ST->hasUnalignedBufferAccessEnabled() && A < Align(4))
926 Result = A;
927 auto *VT = dyn_cast<VectorType>(Val: T);
928 if (!ST->hasRelaxedBufferOOBMode() && VT) {
929 TypeSize ElemBits = DL.getTypeSizeInBits(Ty: VT->getElementType());
930 if (ElemBits.isKnownMultipleOf(RHS: 32)) {
931 // Word-sized operations are bounds-checked per word. So, the only case we
932 // have to worry about is stores that start out of bounds and then go in,
933 // and those can only become in-bounds on a multiple of their alignment.
934 // Therefore, we can use the declared alignment of the operation as the
935 // maximum width, rounding up to 4.
936 if (!OobProps.NoWrapFromMax)
937 Result = std::min(a: Result, b: std::max(a: A, b: Align(4)));
938 } else if ((ElemBits.isKnownMultipleOf(RHS: 8) ||
939 isPowerOf2_64(Value: ElemBits.getKnownMinValue()))) {
940 // To ensure correct behavior for sub-word types, we must always scalarize
941 // unaligned loads of sub-word types. For example, if you load
942 // a <4 x i8> from offset 7 in an 8-byte buffer, expecting the vector
943 // to be padded out with 0s after that last byte, you'll get all 0s
944 // instead. To prevent this behavior when not requested, de-vectorize such
945 // loads.
946 //
947 // If we knew that the value that triggers bounds checks was a multiple of
948 // 4 along with the access being word-aligned, we could avoid the
949 // scalarization here, as the bitcast wouldn't change any check behavior,
950 // but we don't currently try to analyze this.
951 //
952 // Strict OOB checking isn't supported if the size of each element is a
953 // non-power-of-2 value less than 8, since there's no feasible way to
954 // apply such a strict bounds check.
955 if (!OobProps.NoPartialOOB)
956 Result =
957 commonAlignment(A: Result, Offset: divideCeil(Numerator: ElemBits.getKnownMinValue(), Denominator: 8));
958 }
959 }
960 return Result.value() * 8;
961}
962
963Type *LegalizeBufferContentTypesVisitor::legalNonAggregateForMemOp(
964 Type *T, uint64_t MaxWidth) {
965 TypeSize Size = DL.getTypeStoreSizeInBits(Ty: T);
966 // Implicitly zero-extend to the next byte if needed.
967 if (!DL.typeSizeEqualsStoreSize(Ty: T))
968 T = IRB.getIntNTy(N: Size.getFixedValue());
969 Type *ElemTy = T->getScalarType();
970 if (isa<PointerType, ScalableVectorType>(Val: ElemTy)) {
971 // Pointers are always big enough, and we'll let scalable vectors through to
972 // fail in codegen.
973 return T;
974 }
975 unsigned ElemSize = DL.getTypeSizeInBits(Ty: ElemTy).getFixedValue();
976 if (isPowerOf2_32(Value: ElemSize) && ElemSize >= 16 && ElemSize <= MaxWidth) {
977 // [vectors of] anything that's 16/32/64/128 bits can be cast and split into
978 // legal buffer operations, except that we might need to cut them into
979 // smaller values if we're not allowed to do unaligned vector loads.
980 return T;
981 }
982 Type *BestVectorElemType = nullptr;
983 if (Size.isKnownMultipleOf(RHS: 32) && MaxWidth >= 32)
984 BestVectorElemType = IRB.getInt32Ty();
985 else if (Size.isKnownMultipleOf(RHS: 16) && MaxWidth >= 16)
986 BestVectorElemType = IRB.getInt16Ty();
987 else
988 BestVectorElemType = IRB.getInt8Ty();
989 unsigned NumCastElems =
990 Size.getFixedValue() / BestVectorElemType->getIntegerBitWidth();
991 if (NumCastElems == 1)
992 return BestVectorElemType;
993 return FixedVectorType::get(ElementType: BestVectorElemType, NumElts: NumCastElems);
994}
995
996Value *LegalizeBufferContentTypesVisitor::makeLegalNonAggregate(
997 Value *V, Type *TargetType, const Twine &Name) {
998 Type *SourceType = V->getType();
999 TypeSize SourceSize = DL.getTypeSizeInBits(Ty: SourceType);
1000 TypeSize TargetSize = DL.getTypeSizeInBits(Ty: TargetType);
1001 if (SourceSize != TargetSize) {
1002 Type *ShortScalarTy = IRB.getIntNTy(N: SourceSize.getFixedValue());
1003 Type *ByteScalarTy = IRB.getIntNTy(N: TargetSize.getFixedValue());
1004 Value *AsScalar = IRB.CreateBitCast(V, DestTy: ShortScalarTy, Name: Name + ".as.scalar");
1005 Value *Zext = IRB.CreateZExt(V: AsScalar, DestTy: ByteScalarTy, Name: Name + ".zext");
1006 V = Zext;
1007 SourceType = ByteScalarTy;
1008 }
1009 return IRB.CreateBitCast(V, DestTy: TargetType, Name: Name + ".legal");
1010}
1011
1012Value *LegalizeBufferContentTypesVisitor::makeIllegalNonAggregate(
1013 Value *V, Type *OrigType, const Twine &Name) {
1014 Type *LegalType = V->getType();
1015 TypeSize LegalSize = DL.getTypeSizeInBits(Ty: LegalType);
1016 TypeSize OrigSize = DL.getTypeSizeInBits(Ty: OrigType);
1017 if (LegalSize != OrigSize) {
1018 Type *ShortScalarTy = IRB.getIntNTy(N: OrigSize.getFixedValue());
1019 Type *ByteScalarTy = IRB.getIntNTy(N: LegalSize.getFixedValue());
1020 Value *AsScalar = IRB.CreateBitCast(V, DestTy: ByteScalarTy, Name: Name + ".bytes.cast");
1021 Value *Trunc = IRB.CreateTrunc(V: AsScalar, DestTy: ShortScalarTy, Name: Name + ".trunc");
1022 return IRB.CreateBitCast(V: Trunc, DestTy: OrigType, Name: Name + ".orig");
1023 }
1024 return IRB.CreateBitCast(V, DestTy: OrigType, Name: Name + ".real.ty");
1025}
1026
1027Type *LegalizeBufferContentTypesVisitor::intrinsicTypeFor(Type *LegalType) {
1028 auto *VT = dyn_cast<FixedVectorType>(Val: LegalType);
1029 if (!VT)
1030 return LegalType;
1031 Type *ET = VT->getElementType();
1032 // Explicitly return the element type of 1-element vectors because the
1033 // underlying intrinsics don't like <1 x T> even though it's a synonym for T.
1034 if (VT->getNumElements() == 1)
1035 return ET;
1036 if (DL.getTypeSizeInBits(Ty: LegalType) == 96 && DL.getTypeSizeInBits(Ty: ET) < 32)
1037 return FixedVectorType::get(ElementType: IRB.getInt32Ty(), NumElts: 3);
1038 if (ET->isIntegerTy(BitWidth: 8)) {
1039 switch (VT->getNumElements()) {
1040 default:
1041 return LegalType; // Let it crash later
1042 case 1:
1043 return IRB.getInt8Ty();
1044 case 2:
1045 return IRB.getInt16Ty();
1046 case 4:
1047 return IRB.getInt32Ty();
1048 case 8:
1049 return FixedVectorType::get(ElementType: IRB.getInt32Ty(), NumElts: 2);
1050 case 16:
1051 return FixedVectorType::get(ElementType: IRB.getInt32Ty(), NumElts: 4);
1052 }
1053 }
1054 return LegalType;
1055}
1056
1057void LegalizeBufferContentTypesVisitor::getVecSlices(
1058 Type *T, uint64_t MaxWidth, SmallVectorImpl<VecSlice> &Slices) {
1059 Slices.clear();
1060 auto *VT = dyn_cast<FixedVectorType>(Val: T);
1061 if (!VT)
1062 return;
1063
1064 uint64_t ElemBitWidth =
1065 DL.getTypeSizeInBits(Ty: VT->getElementType()).getFixedValue();
1066
1067 uint64_t ElemsPer4Words = 128 / ElemBitWidth;
1068 uint64_t ElemsPer2Words = ElemsPer4Words / 2;
1069 uint64_t ElemsPerWord = ElemsPer2Words / 2;
1070 uint64_t ElemsPerShort = ElemsPerWord / 2;
1071 uint64_t ElemsPerByte = ElemsPerShort / 2;
1072 // If the elements evenly pack into 32-bit words, we can use 3-word stores,
1073 // such as for <6 x bfloat> or <3 x i32>, but we can't dot his for, for
1074 // example, <3 x i64>, since that's not slicing.
1075 uint64_t ElemsPer3Words = ElemsPerWord * 3;
1076
1077 uint64_t TotalElems = VT->getNumElements();
1078 uint64_t Index = 0;
1079 auto TrySlice = [&](unsigned MaybeLen, unsigned Width) {
1080 if (MaybeLen > 0 && Width <= MaxWidth && Index + MaybeLen <= TotalElems) {
1081 VecSlice Slice{/*Index=*/Index, /*Length=*/MaybeLen};
1082 Slices.push_back(Elt: Slice);
1083 Index += MaybeLen;
1084 return true;
1085 }
1086 return false;
1087 };
1088 while (Index < TotalElems) {
1089 TrySlice(ElemsPer4Words, 128) || TrySlice(ElemsPer3Words, 96) ||
1090 TrySlice(ElemsPer2Words, 64) || TrySlice(ElemsPerWord, 32) ||
1091 TrySlice(ElemsPerShort, 16) || TrySlice(ElemsPerByte, 8);
1092 }
1093}
1094
1095Value *LegalizeBufferContentTypesVisitor::extractSlice(Value *Vec, VecSlice S,
1096 const Twine &Name) {
1097 auto *VecVT = dyn_cast<FixedVectorType>(Val: Vec->getType());
1098 if (!VecVT)
1099 return Vec;
1100 if (S.Length == VecVT->getNumElements() && S.Index == 0)
1101 return Vec;
1102 if (S.Length == 1)
1103 return IRB.CreateExtractElement(Vec, Idx: S.Index,
1104 Name: Name + ".slice." + Twine(S.Index));
1105 SmallVector<int> Mask = llvm::to_vector(
1106 Range: llvm::iota_range<int>(S.Index, S.Index + S.Length, /*Inclusive=*/false));
1107 return IRB.CreateShuffleVector(V: Vec, Mask, Name: Name + ".slice." + Twine(S.Index));
1108}
1109
1110Value *LegalizeBufferContentTypesVisitor::insertSlice(Value *Whole, Value *Part,
1111 VecSlice S,
1112 const Twine &Name) {
1113 auto *WholeVT = dyn_cast<FixedVectorType>(Val: Whole->getType());
1114 if (!WholeVT)
1115 return Part;
1116 if (S.Length == WholeVT->getNumElements() && S.Index == 0)
1117 return Part;
1118 if (S.Length == 1) {
1119 return IRB.CreateInsertElement(Vec: Whole, NewElt: Part, Idx: S.Index,
1120 Name: Name + ".slice." + Twine(S.Index));
1121 }
1122 int NumElems = cast<FixedVectorType>(Val: Whole->getType())->getNumElements();
1123
1124 // Extend the slice with poisons to make the main shufflevector happy.
1125 SmallVector<int> ExtPartMask(NumElems, -1);
1126 for (auto [I, E] : llvm::enumerate(
1127 First: MutableArrayRef<int>(ExtPartMask).take_front(N: S.Length))) {
1128 E = I;
1129 }
1130 Value *ExtPart = IRB.CreateShuffleVector(V: Part, Mask: ExtPartMask,
1131 Name: Name + ".ext." + Twine(S.Index));
1132
1133 SmallVector<int> Mask =
1134 llvm::to_vector(Range: llvm::iota_range<int>(0, NumElems, /*Inclusive=*/false));
1135 for (auto [I, E] :
1136 llvm::enumerate(First: MutableArrayRef<int>(Mask).slice(N: S.Index, M: S.Length)))
1137 E = I + NumElems;
1138 return IRB.CreateShuffleVector(V1: Whole, V2: ExtPart, Mask,
1139 Name: Name + ".parts." + Twine(S.Index));
1140}
1141
1142bool LegalizeBufferContentTypesVisitor::visitLoadImpl(
1143 LoadInst &OrigLI, Type *PartType, SmallVectorImpl<uint32_t> &AggIdxs,
1144 uint64_t AggByteOff, Value *&Result, const Twine &Name) {
1145 if (auto *ST = dyn_cast<StructType>(Val: PartType)) {
1146 const StructLayout *Layout = DL.getStructLayout(Ty: ST);
1147 bool Changed = false;
1148 for (auto [I, ElemTy, Offset] :
1149 llvm::enumerate(First: ST->elements(), Rest: Layout->getMemberOffsets())) {
1150 AggIdxs.push_back(Elt: I);
1151 Changed |= visitLoadImpl(OrigLI, PartType: ElemTy, AggIdxs,
1152 AggByteOff: AggByteOff + Offset.getFixedValue(), Result,
1153 Name: Name + "." + Twine(I));
1154 AggIdxs.pop_back();
1155 }
1156 return Changed;
1157 }
1158 if (auto *AT = dyn_cast<ArrayType>(Val: PartType)) {
1159 Type *ElemTy = AT->getElementType();
1160 if (!ElemTy->isSingleValueType() || !DL.typeSizeEqualsStoreSize(Ty: ElemTy) ||
1161 ElemTy->isVectorTy()) {
1162 TypeSize ElemAllocSize = DL.getTypeAllocSize(Ty: ElemTy);
1163 bool Changed = false;
1164 for (auto I : llvm::iota_range<uint32_t>(0, AT->getNumElements(),
1165 /*Inclusive=*/false)) {
1166 AggIdxs.push_back(Elt: I);
1167 Changed |= visitLoadImpl(OrigLI, PartType: ElemTy, AggIdxs,
1168 AggByteOff: AggByteOff + I * ElemAllocSize.getFixedValue(),
1169 Result, Name: Name + Twine(I));
1170 AggIdxs.pop_back();
1171 }
1172 return Changed;
1173 }
1174 }
1175
1176 // Typical case
1177
1178 Align PartAlign = commonAlignment(A: OrigLI.getAlign(), Offset: AggByteOff);
1179 Type *ArrayAsVecType = scalarArrayTypeAsVector(T: PartType);
1180 OobProperties OobProps =
1181 analyzeOobProperties(Ptr: OrigLI.getPointerOperand(), Ty: PartType, ByteOffset: AggByteOff);
1182 uint64_t MaxWidth = maxIntrinsicWidth(T: ArrayAsVecType, A: PartAlign, OobProps);
1183 Type *LegalType = legalNonAggregateForMemOp(T: ArrayAsVecType, MaxWidth);
1184
1185 SmallVector<VecSlice> Slices;
1186 getVecSlices(T: LegalType, MaxWidth, Slices);
1187 bool HasSlices = Slices.size() > 1;
1188 bool IsAggPart = !AggIdxs.empty();
1189 Value *LoadsRes;
1190 if (!HasSlices && !IsAggPart) {
1191 Type *LoadableType = intrinsicTypeFor(LegalType);
1192 if (LoadableType == PartType)
1193 return false;
1194
1195 IRB.SetInsertPoint(&OrigLI);
1196 auto *NLI = cast<LoadInst>(Val: OrigLI.clone());
1197 NLI->mutateType(Ty: LoadableType);
1198 NLI = IRB.Insert(I: NLI);
1199 NLI->setName(Name + ".loadable");
1200
1201 LoadsRes = IRB.CreateBitCast(V: NLI, DestTy: LegalType, Name: Name + ".from.loadable");
1202 } else {
1203 IRB.SetInsertPoint(&OrigLI);
1204 LoadsRes = PoisonValue::get(T: LegalType);
1205 Value *OrigPtr = OrigLI.getPointerOperand();
1206 // If we're needing to spill something into more than one load, its legal
1207 // type will be a vector (ex. an i256 load will have LegalType = <8 x i32>).
1208 // But if we're already a scalar (which can happen if we're splitting up a
1209 // struct), the element type will be the legal type itself.
1210 Type *ElemType = LegalType->getScalarType();
1211 unsigned ElemBytes = DL.getTypeStoreSize(Ty: ElemType);
1212 AAMDNodes AANodes = OrigLI.getAAMetadata();
1213 if (IsAggPart && Slices.empty())
1214 Slices.push_back(Elt: VecSlice{/*Index=*/0, /*Length=*/1});
1215 for (VecSlice S : Slices) {
1216 Type *SliceType =
1217 S.Length != 1 ? FixedVectorType::get(ElementType: ElemType, NumElts: S.Length) : ElemType;
1218 int64_t ByteOffset = AggByteOff + S.Index * ElemBytes;
1219 // You can't reasonably expect loads to wrap around the edge of memory.
1220 Value *NewPtr = IRB.CreateGEP(
1221 Ty: IRB.getInt8Ty(), Ptr: OrigLI.getPointerOperand(), IdxList: IRB.getInt32(C: ByteOffset),
1222 Name: OrigPtr->getName() + ".off.ptr." + Twine(ByteOffset),
1223 NW: ST->hasRelaxedBufferOOBMode() ? GEPNoWrapFlags::noUnsignedWrap()
1224 : GEPNoWrapFlags::none());
1225 Type *LoadableType = intrinsicTypeFor(LegalType: SliceType);
1226 LoadInst *NewLI = IRB.CreateAlignedLoad(
1227 Ty: LoadableType, Ptr: NewPtr, Align: commonAlignment(A: OrigLI.getAlign(), Offset: ByteOffset),
1228 Name: Name + ".off." + Twine(ByteOffset));
1229 copyMetadataForLoad(Dest&: *NewLI, Source: OrigLI);
1230 NewLI->setAAMetadata(
1231 AANodes.adjustForAccess(Offset: ByteOffset, AccessTy: LoadableType, DL));
1232 NewLI->setAtomic(Ordering: OrigLI.getOrdering(), SSID: OrigLI.getSyncScopeID());
1233 NewLI->setVolatile(OrigLI.isVolatile());
1234 Value *Loaded = IRB.CreateBitCast(V: NewLI, DestTy: SliceType,
1235 Name: NewLI->getName() + ".from.loadable");
1236 LoadsRes = insertSlice(Whole: LoadsRes, Part: Loaded, S, Name);
1237 }
1238 }
1239 if (LegalType != ArrayAsVecType)
1240 LoadsRes = makeIllegalNonAggregate(V: LoadsRes, OrigType: ArrayAsVecType, Name);
1241 if (ArrayAsVecType != PartType)
1242 LoadsRes = vectorToArray(V: LoadsRes, OrigType: PartType, Name);
1243
1244 if (IsAggPart)
1245 Result = IRB.CreateInsertValue(Agg: Result, Val: LoadsRes, Idxs: AggIdxs, Name);
1246 else
1247 Result = LoadsRes;
1248 return true;
1249}
1250
1251bool LegalizeBufferContentTypesVisitor::visitLoadInst(LoadInst &LI) {
1252 if (LI.getPointerAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
1253 return false;
1254
1255 SmallVector<uint32_t> AggIdxs;
1256 Type *OrigType = LI.getType();
1257 Value *Result = PoisonValue::get(T: OrigType);
1258 bool Changed = visitLoadImpl(OrigLI&: LI, PartType: OrigType, AggIdxs, AggByteOff: 0, Result, Name: LI.getName());
1259 if (!Changed)
1260 return false;
1261 Result->takeName(V: &LI);
1262 LI.replaceAllUsesWith(V: Result);
1263 LI.eraseFromParent();
1264 return Changed;
1265}
1266
1267std::pair<bool, bool> LegalizeBufferContentTypesVisitor::visitStoreImpl(
1268 StoreInst &OrigSI, Type *PartType, SmallVectorImpl<uint32_t> &AggIdxs,
1269 uint64_t AggByteOff, const Twine &Name) {
1270 if (auto *ST = dyn_cast<StructType>(Val: PartType)) {
1271 const StructLayout *Layout = DL.getStructLayout(Ty: ST);
1272 bool Changed = false;
1273 for (auto [I, ElemTy, Offset] :
1274 llvm::enumerate(First: ST->elements(), Rest: Layout->getMemberOffsets())) {
1275 AggIdxs.push_back(Elt: I);
1276 Changed |= std::get<0>(in: visitStoreImpl(OrigSI, PartType: ElemTy, AggIdxs,
1277 AggByteOff: AggByteOff + Offset.getFixedValue(),
1278 Name: Name + "." + Twine(I)));
1279 AggIdxs.pop_back();
1280 }
1281 return std::make_pair(x&: Changed, /*ModifiedInPlace=*/y: false);
1282 }
1283 if (auto *AT = dyn_cast<ArrayType>(Val: PartType)) {
1284 Type *ElemTy = AT->getElementType();
1285 if (!ElemTy->isSingleValueType() || !DL.typeSizeEqualsStoreSize(Ty: ElemTy) ||
1286 ElemTy->isVectorTy()) {
1287 TypeSize ElemAllocSize = DL.getTypeAllocSize(Ty: ElemTy);
1288 bool Changed = false;
1289 for (auto I : llvm::iota_range<uint32_t>(0, AT->getNumElements(),
1290 /*Inclusive=*/false)) {
1291 AggIdxs.push_back(Elt: I);
1292 Changed |= std::get<0>(in: visitStoreImpl(
1293 OrigSI, PartType: ElemTy, AggIdxs,
1294 AggByteOff: AggByteOff + I * ElemAllocSize.getFixedValue(), Name: Name + Twine(I)));
1295 AggIdxs.pop_back();
1296 }
1297 return std::make_pair(x&: Changed, /*ModifiedInPlace=*/y: false);
1298 }
1299 }
1300
1301 Value *OrigData = OrigSI.getValueOperand();
1302 Value *NewData = OrigData;
1303
1304 bool IsAggPart = !AggIdxs.empty();
1305 if (IsAggPart)
1306 NewData = IRB.CreateExtractValue(Agg: NewData, Idxs: AggIdxs, Name);
1307
1308 Type *ArrayAsVecType = scalarArrayTypeAsVector(T: PartType);
1309 if (ArrayAsVecType != PartType) {
1310 NewData = arrayToVector(V: NewData, TargetType: ArrayAsVecType, Name);
1311 }
1312
1313 Align PartAlign = commonAlignment(A: OrigSI.getAlign(), Offset: AggByteOff);
1314 OobProperties OobProps =
1315 analyzeOobProperties(Ptr: OrigSI.getPointerOperand(), Ty: PartType, ByteOffset: AggByteOff);
1316 uint64_t MaxWidth = maxIntrinsicWidth(T: ArrayAsVecType, A: PartAlign, OobProps);
1317 Type *LegalType = legalNonAggregateForMemOp(T: ArrayAsVecType, MaxWidth);
1318 if (LegalType != ArrayAsVecType) {
1319 NewData = makeLegalNonAggregate(V: NewData, TargetType: LegalType, Name);
1320 }
1321
1322 SmallVector<VecSlice> Slices;
1323 getVecSlices(T: LegalType, MaxWidth, Slices);
1324 bool NeedToSplit = Slices.size() > 1 || IsAggPart;
1325 if (!NeedToSplit) {
1326 Type *StorableType = intrinsicTypeFor(LegalType);
1327 if (StorableType == PartType)
1328 return std::make_pair(/*Changed=*/x: false, /*ModifiedInPlace=*/y: false);
1329 NewData = IRB.CreateBitCast(V: NewData, DestTy: StorableType, Name: Name + ".storable");
1330 OrigSI.setOperand(i_nocapture: 0, Val_nocapture: NewData);
1331 return std::make_pair(/*Changed=*/x: true, /*ModifiedInPlace=*/y: true);
1332 }
1333
1334 Value *OrigPtr = OrigSI.getPointerOperand();
1335 Type *ElemType = LegalType->getScalarType();
1336 if (IsAggPart && Slices.empty())
1337 Slices.push_back(Elt: VecSlice{/*Index=*/0, /*Length=*/1});
1338 unsigned ElemBytes = DL.getTypeStoreSize(Ty: ElemType);
1339 AAMDNodes AANodes = OrigSI.getAAMetadata();
1340 for (VecSlice S : Slices) {
1341 Type *SliceType =
1342 S.Length != 1 ? FixedVectorType::get(ElementType: ElemType, NumElts: S.Length) : ElemType;
1343 int64_t ByteOffset = AggByteOff + S.Index * ElemBytes;
1344 Value *NewPtr = IRB.CreateGEP(
1345 Ty: IRB.getInt8Ty(), Ptr: OrigPtr, IdxList: IRB.getInt32(C: ByteOffset),
1346 Name: OrigPtr->getName() + ".part." + Twine(S.Index),
1347 NW: ST->hasRelaxedBufferOOBMode() ? GEPNoWrapFlags::noUnsignedWrap()
1348 : GEPNoWrapFlags::none());
1349 Value *DataSlice = extractSlice(Vec: NewData, S, Name);
1350 Type *StorableType = intrinsicTypeFor(LegalType: SliceType);
1351 DataSlice = IRB.CreateBitCast(V: DataSlice, DestTy: StorableType,
1352 Name: DataSlice->getName() + ".storable");
1353 auto *NewSI = cast<StoreInst>(Val: OrigSI.clone());
1354 NewSI->setAlignment(commonAlignment(A: OrigSI.getAlign(), Offset: ByteOffset));
1355 IRB.Insert(I: NewSI);
1356 NewSI->setOperand(i_nocapture: 0, Val_nocapture: DataSlice);
1357 NewSI->setOperand(i_nocapture: 1, Val_nocapture: NewPtr);
1358 NewSI->setAAMetadata(AANodes.adjustForAccess(Offset: ByteOffset, AccessTy: StorableType, DL));
1359 }
1360 return std::make_pair(/*Changed=*/x: true, /*ModifiedInPlace=*/y: false);
1361}
1362
1363bool LegalizeBufferContentTypesVisitor::visitStoreInst(StoreInst &SI) {
1364 if (SI.getPointerAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
1365 return false;
1366 IRB.SetInsertPoint(&SI);
1367 SmallVector<uint32_t> AggIdxs;
1368 Value *OrigData = SI.getValueOperand();
1369 auto [Changed, ModifiedInPlace] =
1370 visitStoreImpl(OrigSI&: SI, PartType: OrigData->getType(), AggIdxs, AggByteOff: 0, Name: OrigData->getName());
1371 if (Changed && !ModifiedInPlace)
1372 SI.eraseFromParent();
1373 return Changed;
1374}
1375
1376bool LegalizeBufferContentTypesVisitor::visitAddrSpaceCastInst(
1377 AddrSpaceCastInst &AI) {
1378 if (AI.getSrcAddressSpace() != AMDGPUAS::BUFFER_RESOURCE ||
1379 AI.getDestAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
1380 return false;
1381 Value *Src = AI.getPointerOperand();
1382 auto Record = ZeroBasePointerToNumRecords.find(Val: Src);
1383 if (Record != ZeroBasePointerToNumRecords.end())
1384 ZeroBasePointerToNumRecords.insert(KV: {&AI, Record->second});
1385 else
1386 ZeroBasePointerToNumRecords.insert(KV: {&AI, nullptr});
1387 return false;
1388}
1389
1390bool LegalizeBufferContentTypesVisitor::visitIntrinsicInst(IntrinsicInst &II) {
1391 if (II.getIntrinsicID() != Intrinsic::amdgcn_make_buffer_rsrc)
1392 return false;
1393 ZeroBasePointerToNumRecords.insert(KV: {&II, II.getOperand(i_nocapture: 2)});
1394 return false;
1395}
1396
1397bool LegalizeBufferContentTypesVisitor::processFunction(Function &F,
1398 ScalarEvolution *SE) {
1399 this->SE = SE;
1400 ST = &TM->getSubtarget<GCNSubtarget>(F);
1401 bool Changed = false;
1402 for (Instruction &I : make_early_inc_range(Range: instructions(F))) {
1403 Changed |= visit(I);
1404 }
1405 ZeroBasePointerToNumRecords.clear();
1406 this->SE = nullptr;
1407 return Changed;
1408}
1409
1410/// Return the ptr addrspace(8) and i32 (resource and offset parts) in a lowered
1411/// buffer fat pointer constant.
1412static std::pair<Constant *, Constant *>
1413splitLoweredFatBufferConst(Constant *C) {
1414 assert(isSplitFatPtr(C->getType()) && "Not a split fat buffer pointer");
1415 return std::make_pair(x: C->getAggregateElement(Elt: 0u), y: C->getAggregateElement(Elt: 1u));
1416}
1417
1418namespace {
1419/// Handle the remapping of ptr addrspace(7) constants.
1420class FatPtrConstMaterializer final : public ValueMaterializer {
1421 BufferFatPtrToStructTypeMap *TypeMap;
1422 // An internal mapper that is used to recurse into the arguments of constants.
1423 // While the documentation for `ValueMapper` specifies not to use it
1424 // recursively, examination of the logic in mapValue() shows that it can
1425 // safely be used recursively when handling constants, like it does in its own
1426 // logic.
1427 ValueMapper InternalMapper;
1428
1429 Constant *materializeBufferFatPtrConst(Constant *C);
1430
1431public:
1432 // UnderlyingMap is the value map this materializer will be filling.
1433 FatPtrConstMaterializer(BufferFatPtrToStructTypeMap *TypeMap,
1434 ValueToValueMapTy &UnderlyingMap)
1435 : TypeMap(TypeMap),
1436 InternalMapper(UnderlyingMap, RF_None, TypeMap, this) {}
1437 ~FatPtrConstMaterializer() = default;
1438
1439 Value *materialize(Value *V) override;
1440};
1441} // namespace
1442
1443Constant *FatPtrConstMaterializer::materializeBufferFatPtrConst(Constant *C) {
1444 Type *SrcTy = C->getType();
1445 auto *NewTy = dyn_cast<StructType>(Val: TypeMap->remapType(SrcTy));
1446 if (C->isNullValue())
1447 return ConstantAggregateZero::getNullValue(Ty: NewTy);
1448 if (isa<PoisonValue>(Val: C)) {
1449 return ConstantStruct::get(T: NewTy,
1450 V: {PoisonValue::get(T: NewTy->getElementType(N: 0)),
1451 PoisonValue::get(T: NewTy->getElementType(N: 1))});
1452 }
1453 if (isa<UndefValue>(Val: C)) {
1454 return ConstantStruct::get(T: NewTy,
1455 V: {UndefValue::get(T: NewTy->getElementType(N: 0)),
1456 UndefValue::get(T: NewTy->getElementType(N: 1))});
1457 }
1458
1459 if (auto *VC = dyn_cast<ConstantVector>(Val: C)) {
1460 if (Constant *S = VC->getSplatValue()) {
1461 Constant *NewS = InternalMapper.mapConstant(C: *S);
1462 if (!NewS)
1463 return nullptr;
1464 auto [Rsrc, Off] = splitLoweredFatBufferConst(C: NewS);
1465 auto EC = VC->getType()->getElementCount();
1466 return ConstantStruct::get(T: NewTy, V: {ConstantVector::getSplat(EC, Elt: Rsrc),
1467 ConstantVector::getSplat(EC, Elt: Off)});
1468 }
1469 SmallVector<Constant *> Rsrcs;
1470 SmallVector<Constant *> Offs;
1471 for (Value *Op : VC->operand_values()) {
1472 auto *NewOp = dyn_cast_or_null<Constant>(Val: InternalMapper.mapValue(V: *Op));
1473 if (!NewOp)
1474 return nullptr;
1475 auto [Rsrc, Off] = splitLoweredFatBufferConst(C: NewOp);
1476 Rsrcs.push_back(Elt: Rsrc);
1477 Offs.push_back(Elt: Off);
1478 }
1479 Constant *RsrcVec = ConstantVector::get(V: Rsrcs);
1480 Constant *OffVec = ConstantVector::get(V: Offs);
1481 return ConstantStruct::get(T: NewTy, V: {RsrcVec, OffVec});
1482 }
1483
1484 if (isa<GlobalValue>(Val: C))
1485 reportFatalUsageError(reason: "global values containing ptr addrspace(7) (buffer "
1486 "fat pointer) values are not supported");
1487
1488 if (isa<ConstantExpr>(Val: C))
1489 reportFatalUsageError(
1490 reason: "constant exprs containing ptr addrspace(7) (buffer "
1491 "fat pointer) values should have been expanded earlier");
1492
1493 return nullptr;
1494}
1495
1496Value *FatPtrConstMaterializer::materialize(Value *V) {
1497 Constant *C = dyn_cast<Constant>(Val: V);
1498 if (!C)
1499 return nullptr;
1500 // Structs and other types that happen to contain fat pointers get remapped
1501 // by the mapValue() logic.
1502 if (!isBufferFatPtrConst(C))
1503 return nullptr;
1504 return materializeBufferFatPtrConst(C);
1505}
1506
1507using PtrParts = std::pair<Value *, Value *>;
1508namespace {
1509// The visitor returns the resource and offset parts for an instruction if they
1510// can be computed, or (nullptr, nullptr) for cases that don't have a meaningful
1511// value mapping.
1512class SplitPtrStructs : public InstVisitor<SplitPtrStructs, PtrParts> {
1513 ValueToValueMapTy RsrcParts;
1514 ValueToValueMapTy OffParts;
1515
1516 // Track instructions that have been rewritten into a user of the component
1517 // parts of their ptr addrspace(7) input. Instructions that produced
1518 // ptr addrspace(7) parts should **not** be RAUW'd before being added to this
1519 // set, as that replacement will be handled in a post-visit step. However,
1520 // instructions that yield values that aren't fat pointers (ex. ptrtoint)
1521 // should RAUW themselves with new instructions that use the split parts
1522 // of their arguments during processing.
1523 DenseSet<Instruction *> SplitUsers;
1524
1525 // Nodes that need a second look once we've computed the parts for all other
1526 // instructions to see if, for example, we really need to phi on the resource
1527 // part.
1528 SmallVector<Instruction *> Conditionals;
1529 // Temporary instructions produced while lowering conditionals that should be
1530 // killed.
1531 SmallVector<Instruction *> ConditionalTemps;
1532
1533 // Subtarget info, needed for determining what cache control bits to set.
1534 const TargetMachine *TM;
1535 const GCNSubtarget *ST = nullptr;
1536
1537 IRBuilder<InstSimplifyFolder> IRB;
1538
1539 // Copy metadata between instructions if applicable.
1540 void copyMetadata(Value *Dest, Value *Src);
1541
1542 // Get the resource and offset parts of the value V, inserting appropriate
1543 // extractvalue calls if needed.
1544 PtrParts getPtrParts(Value *V);
1545
1546 // Given an instruction that could produce multiple resource parts (a PHI or
1547 // select), collect the set of possible instructions that could have provided
1548 // its resource parts that it could have (the `Roots`) and the set of
1549 // conditional instructions visited during the search (`Seen`). If, after
1550 // removing the root of the search from `Seen` and `Roots`, `Seen` is a subset
1551 // of `Roots` and `Roots - Seen` contains one element, the resource part of
1552 // that element can replace the resource part of all other elements in `Seen`.
1553 void getPossibleRsrcRoots(Instruction *I, SmallPtrSetImpl<Value *> &Roots,
1554 SmallPtrSetImpl<Value *> &Seen);
1555 void processConditionals();
1556
1557 // If an instruction hav been split into resource and offset parts,
1558 // delete that instruction. If any of its uses have not themselves been split
1559 // into parts (for example, an insertvalue), construct the structure
1560 // that the type rewrites declared should be produced by the dying instruction
1561 // and use that.
1562 // Also, kill the temporary extractvalue operations produced by the two-stage
1563 // lowering of PHIs and conditionals.
1564 void killAndReplaceSplitInstructions(SmallVectorImpl<Instruction *> &Origs);
1565
1566 void setAlign(CallInst *Intr, Align A, unsigned RsrcArgIdx);
1567 void insertPreMemOpFence(AtomicOrdering Order, SyncScope::ID SSID);
1568 void insertPostMemOpFence(AtomicOrdering Order, SyncScope::ID SSID);
1569 Value *handleMemoryInst(Instruction *I, Value *Arg, Value *Ptr, Type *Ty,
1570 Align Alignment, AtomicOrdering Order,
1571 bool IsVolatile, SyncScope::ID SSID);
1572
1573public:
1574 SplitPtrStructs(const DataLayout &DL, LLVMContext &Ctx,
1575 const TargetMachine *TM)
1576 : TM(TM), IRB(Ctx, InstSimplifyFolder(DL)) {}
1577
1578 void processFunction(Function &F);
1579
1580 PtrParts visitInstruction(Instruction &I);
1581 PtrParts visitLoadInst(LoadInst &LI);
1582 PtrParts visitStoreInst(StoreInst &SI);
1583 PtrParts visitAtomicRMWInst(AtomicRMWInst &AI);
1584 PtrParts visitAtomicCmpXchgInst(AtomicCmpXchgInst &AI);
1585 PtrParts visitGetElementPtrInst(GetElementPtrInst &GEP);
1586
1587 PtrParts visitPtrToAddrInst(PtrToAddrInst &PA);
1588 PtrParts visitPtrToIntInst(PtrToIntInst &PI);
1589 PtrParts visitIntToPtrInst(IntToPtrInst &IP);
1590 PtrParts visitAddrSpaceCastInst(AddrSpaceCastInst &I);
1591 PtrParts visitICmpInst(ICmpInst &Cmp);
1592 PtrParts visitFreezeInst(FreezeInst &I);
1593
1594 PtrParts visitExtractElementInst(ExtractElementInst &I);
1595 PtrParts visitInsertElementInst(InsertElementInst &I);
1596 PtrParts visitShuffleVectorInst(ShuffleVectorInst &I);
1597
1598 PtrParts visitPHINode(PHINode &PHI);
1599 PtrParts visitSelectInst(SelectInst &SI);
1600
1601 PtrParts visitIntrinsicInst(IntrinsicInst &II);
1602};
1603} // namespace
1604
1605void SplitPtrStructs::copyMetadata(Value *Dest, Value *Src) {
1606 auto *DestI = dyn_cast<Instruction>(Val: Dest);
1607 auto *SrcI = dyn_cast<Instruction>(Val: Src);
1608
1609 if (!DestI || !SrcI)
1610 return;
1611
1612 DestI->copyMetadata(SrcInst: *SrcI);
1613}
1614
1615PtrParts SplitPtrStructs::getPtrParts(Value *V) {
1616 assert(isSplitFatPtr(V->getType()) && "it's not meaningful to get the parts "
1617 "of something that wasn't rewritten");
1618 auto *RsrcEntry = &RsrcParts[V];
1619 auto *OffEntry = &OffParts[V];
1620 if (*RsrcEntry && *OffEntry)
1621 return {*RsrcEntry, *OffEntry};
1622
1623 if (auto *C = dyn_cast<Constant>(Val: V)) {
1624 auto [Rsrc, Off] = splitLoweredFatBufferConst(C);
1625 return {*RsrcEntry = Rsrc, *OffEntry = Off};
1626 }
1627
1628 IRBuilder<InstSimplifyFolder>::InsertPointGuard Guard(IRB);
1629 if (auto *I = dyn_cast<Instruction>(Val: V)) {
1630 LLVM_DEBUG(dbgs() << "Recursing to split parts of " << *I << "\n");
1631 auto [Rsrc, Off] = visit(I&: *I);
1632 if (Rsrc && Off)
1633 return {*RsrcEntry = Rsrc, *OffEntry = Off};
1634 // We'll be creating the new values after the relevant instruction.
1635 // This instruction generates a value and so isn't a terminator.
1636 IRB.SetInsertPoint(*I->getInsertionPointAfterDef());
1637 IRB.SetCurrentDebugLocation(I->getDebugLoc());
1638 } else if (auto *A = dyn_cast<Argument>(Val: V)) {
1639 IRB.SetInsertPointPastAllocas(A->getParent());
1640 IRB.SetCurrentDebugLocation(DebugLoc());
1641 }
1642 Value *Rsrc = IRB.CreateExtractValue(Agg: V, Idxs: 0, Name: V->getName() + ".rsrc");
1643 Value *Off = IRB.CreateExtractValue(Agg: V, Idxs: 1, Name: V->getName() + ".off");
1644 return {*RsrcEntry = Rsrc, *OffEntry = Off};
1645}
1646
1647/// Returns the instruction that defines the resource part of the value V.
1648/// Note that this is not getUnderlyingObject(), since that looks through
1649/// operations like ptrmask which might modify the resource part.
1650///
1651/// We can limit ourselves to just looking through GEPs followed by looking
1652/// through addrspacecasts because only those two operations preserve the
1653/// resource part, and because operations on an `addrspace(8)` (which is the
1654/// legal input to this addrspacecast) would produce a different resource part.
1655static Value *rsrcPartRoot(Value *V) {
1656 while (auto *GEP = dyn_cast<GEPOperator>(Val: V))
1657 V = GEP->getPointerOperand();
1658 while (auto *ASC = dyn_cast<AddrSpaceCastOperator>(Val: V))
1659 V = ASC->getPointerOperand();
1660 return V;
1661}
1662
1663void SplitPtrStructs::getPossibleRsrcRoots(Instruction *I,
1664 SmallPtrSetImpl<Value *> &Roots,
1665 SmallPtrSetImpl<Value *> &Seen) {
1666 if (auto *PHI = dyn_cast<PHINode>(Val: I)) {
1667 if (!Seen.insert(Ptr: I).second)
1668 return;
1669 for (Value *In : PHI->incoming_values()) {
1670 In = rsrcPartRoot(V: In);
1671 Roots.insert(Ptr: In);
1672 if (isa<PHINode, SelectInst>(Val: In))
1673 getPossibleRsrcRoots(I: cast<Instruction>(Val: In), Roots, Seen);
1674 }
1675 } else if (auto *SI = dyn_cast<SelectInst>(Val: I)) {
1676 if (!Seen.insert(Ptr: SI).second)
1677 return;
1678 Value *TrueVal = rsrcPartRoot(V: SI->getTrueValue());
1679 Value *FalseVal = rsrcPartRoot(V: SI->getFalseValue());
1680 Roots.insert(Ptr: TrueVal);
1681 Roots.insert(Ptr: FalseVal);
1682 if (isa<PHINode, SelectInst>(Val: TrueVal))
1683 getPossibleRsrcRoots(I: cast<Instruction>(Val: TrueVal), Roots, Seen);
1684 if (isa<PHINode, SelectInst>(Val: FalseVal))
1685 getPossibleRsrcRoots(I: cast<Instruction>(Val: FalseVal), Roots, Seen);
1686 } else {
1687 llvm_unreachable("getPossibleRsrcParts() only works on phi and select");
1688 }
1689}
1690
1691void SplitPtrStructs::processConditionals() {
1692 SmallDenseMap<Value *, Value *> FoundRsrcs;
1693 SmallPtrSet<Value *, 4> Roots;
1694 SmallPtrSet<Value *, 4> Seen;
1695 for (Instruction *I : Conditionals) {
1696 // These have to exist by now because we've visited these nodes.
1697 Value *Rsrc = RsrcParts[I];
1698 Value *Off = OffParts[I];
1699 assert(Rsrc && Off && "must have visited conditionals by now");
1700
1701 std::optional<Value *> MaybeRsrc;
1702 auto MaybeFoundRsrc = FoundRsrcs.find(Val: I);
1703 if (MaybeFoundRsrc != FoundRsrcs.end()) {
1704 MaybeRsrc = MaybeFoundRsrc->second;
1705 } else {
1706 IRBuilder<InstSimplifyFolder>::InsertPointGuard Guard(IRB);
1707 Roots.clear();
1708 Seen.clear();
1709 getPossibleRsrcRoots(I, Roots, Seen);
1710 LLVM_DEBUG(dbgs() << "Processing conditional: " << *I << "\n");
1711#ifndef NDEBUG
1712 for (Value *V : Roots)
1713 LLVM_DEBUG(dbgs() << "Root: " << *V << "\n");
1714 for (Value *V : Seen)
1715 LLVM_DEBUG(dbgs() << "Seen: " << *V << "\n");
1716#endif
1717 // If we are our own possible root, then we shouldn't block our
1718 // replacement with a valid incoming value.
1719 Roots.erase(Ptr: I);
1720 // We don't want to block the optimization for conditionals that don't
1721 // refer to themselves but did see themselves during the traversal.
1722 Seen.erase(Ptr: I);
1723
1724 if (set_is_subset(S1: Seen, S2: Roots)) {
1725 auto Diff = set_difference(S1: Roots, S2: Seen);
1726 if (Diff.size() == 1) {
1727 Value *RootVal = *Diff.begin();
1728 // Handle the case where previous loops already looked through
1729 // an addrspacecast.
1730 if (isSplitFatPtr(Ty: RootVal->getType()))
1731 MaybeRsrc = std::get<0>(in: getPtrParts(V: RootVal));
1732 else
1733 MaybeRsrc = RootVal;
1734 }
1735 }
1736 }
1737
1738 if (auto *PHI = dyn_cast<PHINode>(Val: I)) {
1739 Value *NewRsrc;
1740 StructType *PHITy = cast<StructType>(Val: PHI->getType());
1741 IRB.SetInsertPoint(*PHI->getInsertionPointAfterDef());
1742 IRB.SetCurrentDebugLocation(PHI->getDebugLoc());
1743 if (MaybeRsrc) {
1744 NewRsrc = *MaybeRsrc;
1745 } else {
1746 Type *RsrcTy = PHITy->getElementType(N: 0);
1747 auto *RsrcPHI = IRB.CreatePHI(Ty: RsrcTy, NumReservedValues: PHI->getNumIncomingValues());
1748 RsrcPHI->takeName(V: Rsrc);
1749 for (auto [V, BB] : llvm::zip(t: PHI->incoming_values(), u: PHI->blocks())) {
1750 Value *VRsrc = std::get<0>(in: getPtrParts(V));
1751 RsrcPHI->addIncoming(V: VRsrc, BB);
1752 }
1753 copyMetadata(Dest: RsrcPHI, Src: PHI);
1754 NewRsrc = RsrcPHI;
1755 }
1756
1757 Type *OffTy = PHITy->getElementType(N: 1);
1758 auto *NewOff = IRB.CreatePHI(Ty: OffTy, NumReservedValues: PHI->getNumIncomingValues());
1759 NewOff->takeName(V: Off);
1760 for (auto [V, BB] : llvm::zip(t: PHI->incoming_values(), u: PHI->blocks())) {
1761 assert(OffParts.count(V) && "An offset part had to be created by now");
1762 Value *VOff = std::get<1>(in: getPtrParts(V));
1763 NewOff->addIncoming(V: VOff, BB);
1764 }
1765 copyMetadata(Dest: NewOff, Src: PHI);
1766
1767 // Note: We don't eraseFromParent() the temporaries because we don't want
1768 // to put the corrections maps in an inconstent state. That'll be handed
1769 // during the rest of the killing. Also, `ValueToValueMapTy` guarantees
1770 // that references in that map will be updated as well.
1771 // Note that if the temporary instruction got `InstSimplify`'d away, it
1772 // might be something like a block argument.
1773 if (auto *RsrcInst = dyn_cast<Instruction>(Val: Rsrc)) {
1774 ConditionalTemps.push_back(Elt: RsrcInst);
1775 RsrcInst->replaceAllUsesWith(V: NewRsrc);
1776 }
1777 if (auto *OffInst = dyn_cast<Instruction>(Val: Off)) {
1778 ConditionalTemps.push_back(Elt: OffInst);
1779 OffInst->replaceAllUsesWith(V: NewOff);
1780 }
1781
1782 // Save on recomputing the cycle traversals in known-root cases.
1783 if (MaybeRsrc)
1784 for (Value *V : Seen)
1785 FoundRsrcs[V] = NewRsrc;
1786 } else if (isa<SelectInst>(Val: I)) {
1787 if (MaybeRsrc) {
1788 if (auto *RsrcInst = dyn_cast<Instruction>(Val: Rsrc)) {
1789 // Guard against conditionals that were already folded away.
1790 if (RsrcInst != *MaybeRsrc) {
1791 ConditionalTemps.push_back(Elt: RsrcInst);
1792 RsrcInst->replaceAllUsesWith(V: *MaybeRsrc);
1793 }
1794 }
1795 for (Value *V : Seen)
1796 FoundRsrcs[V] = *MaybeRsrc;
1797 }
1798 } else {
1799 llvm_unreachable("Only PHIs and selects go in the conditionals list");
1800 }
1801 }
1802}
1803
1804void SplitPtrStructs::killAndReplaceSplitInstructions(
1805 SmallVectorImpl<Instruction *> &Origs) {
1806 for (Instruction *I : ConditionalTemps)
1807 I->eraseFromParent();
1808
1809 for (Instruction *I : Origs) {
1810 if (!SplitUsers.contains(V: I))
1811 continue;
1812
1813 SmallVector<DbgVariableRecord *> Dbgs;
1814 findDbgValues(V: I, DbgVariableRecords&: Dbgs);
1815 for (DbgVariableRecord *Dbg : Dbgs) {
1816 auto &DL = I->getDataLayout();
1817 assert(isSplitFatPtr(I->getType()) &&
1818 "We should've RAUW'd away loads, stores, etc. at this point");
1819 DbgVariableRecord *OffDbg = Dbg->clone();
1820 auto [Rsrc, Off] = getPtrParts(V: I);
1821
1822 int64_t RsrcSz = DL.getTypeSizeInBits(Ty: Rsrc->getType());
1823 int64_t OffSz = DL.getTypeSizeInBits(Ty: Off->getType());
1824
1825 std::optional<DIExpression *> RsrcExpr =
1826 DIExpression::createFragmentExpression(Expr: Dbg->getExpression(), OffsetInBits: 0,
1827 SizeInBits: RsrcSz);
1828 std::optional<DIExpression *> OffExpr =
1829 DIExpression::createFragmentExpression(Expr: Dbg->getExpression(), OffsetInBits: RsrcSz,
1830 SizeInBits: OffSz);
1831 if (OffExpr) {
1832 OffDbg->setExpression(*OffExpr);
1833 OffDbg->replaceVariableLocationOp(OldValue: I, NewValue: Off);
1834 OffDbg->insertBefore(InsertBefore: Dbg);
1835 } else {
1836 OffDbg->eraseFromParent();
1837 }
1838 if (RsrcExpr) {
1839 Dbg->setExpression(*RsrcExpr);
1840 Dbg->replaceVariableLocationOp(OldValue: I, NewValue: Rsrc);
1841 } else {
1842 Dbg->replaceVariableLocationOp(OldValue: I, NewValue: PoisonValue::get(T: I->getType()));
1843 }
1844 }
1845
1846 Value *Poison = PoisonValue::get(T: I->getType());
1847 I->replaceUsesWithIf(New: Poison, ShouldReplace: [&](const Use &U) -> bool {
1848 if (const auto *UI = dyn_cast<Instruction>(Val: U.getUser()))
1849 return SplitUsers.contains(V: UI);
1850 return false;
1851 });
1852
1853 if (I->use_empty()) {
1854 I->eraseFromParent();
1855 continue;
1856 }
1857 IRB.SetInsertPoint(*I->getInsertionPointAfterDef());
1858 IRB.SetCurrentDebugLocation(I->getDebugLoc());
1859 auto [Rsrc, Off] = getPtrParts(V: I);
1860 Value *Struct = PoisonValue::get(T: I->getType());
1861 Struct = IRB.CreateInsertValue(Agg: Struct, Val: Rsrc, Idxs: 0);
1862 Struct = IRB.CreateInsertValue(Agg: Struct, Val: Off, Idxs: 1);
1863 copyMetadata(Dest: Struct, Src: I);
1864 Struct->takeName(V: I);
1865 I->replaceAllUsesWith(V: Struct);
1866 I->eraseFromParent();
1867 }
1868}
1869
1870void SplitPtrStructs::setAlign(CallInst *Intr, Align A, unsigned RsrcArgIdx) {
1871 LLVMContext &Ctx = Intr->getContext();
1872 Intr->addParamAttr(ArgNo: RsrcArgIdx, Attr: Attribute::getWithAlignment(Context&: Ctx, Alignment: A));
1873}
1874
1875void SplitPtrStructs::insertPreMemOpFence(AtomicOrdering Order,
1876 SyncScope::ID SSID) {
1877 switch (Order) {
1878 case AtomicOrdering::Release:
1879 case AtomicOrdering::AcquireRelease:
1880 case AtomicOrdering::SequentiallyConsistent:
1881 IRB.CreateFence(Ordering: AtomicOrdering::Release, SSID);
1882 break;
1883 default:
1884 break;
1885 }
1886}
1887
1888void SplitPtrStructs::insertPostMemOpFence(AtomicOrdering Order,
1889 SyncScope::ID SSID) {
1890 switch (Order) {
1891 case AtomicOrdering::Acquire:
1892 case AtomicOrdering::AcquireRelease:
1893 case AtomicOrdering::SequentiallyConsistent:
1894 IRB.CreateFence(Ordering: AtomicOrdering::Acquire, SSID);
1895 break;
1896 default:
1897 break;
1898 }
1899}
1900
1901Value *SplitPtrStructs::handleMemoryInst(Instruction *I, Value *Arg, Value *Ptr,
1902 Type *Ty, Align Alignment,
1903 AtomicOrdering Order, bool IsVolatile,
1904 SyncScope::ID SSID) {
1905 IRB.SetInsertPoint(I);
1906
1907 auto [Rsrc, Off] = getPtrParts(V: Ptr);
1908 SmallVector<Value *, 5> Args;
1909 if (Arg)
1910 Args.push_back(Elt: Arg);
1911 Args.push_back(Elt: Rsrc);
1912 Args.push_back(Elt: Off);
1913 insertPreMemOpFence(Order, SSID);
1914 // soffset is always 0 for these cases, where we always want any offset to be
1915 // part of bounds checking and we don't know which parts of the GEPs is
1916 // uniform.
1917 Args.push_back(Elt: IRB.getInt32(C: 0));
1918
1919 uint32_t Aux = 0;
1920 if (IsVolatile)
1921 Aux |= AMDGPU::CPol::VOLATILE;
1922 Args.push_back(Elt: IRB.getInt32(C: Aux));
1923
1924 Intrinsic::ID IID = Intrinsic::not_intrinsic;
1925 if (isa<LoadInst>(Val: I))
1926 IID = Order == AtomicOrdering::NotAtomic
1927 ? Intrinsic::amdgcn_raw_ptr_buffer_load
1928 : Intrinsic::amdgcn_raw_ptr_atomic_buffer_load;
1929 else if (isa<StoreInst>(Val: I))
1930 IID = Intrinsic::amdgcn_raw_ptr_buffer_store;
1931 else if (auto *RMW = dyn_cast<AtomicRMWInst>(Val: I)) {
1932 switch (RMW->getOperation()) {
1933 case AtomicRMWInst::Xchg:
1934 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_swap;
1935 break;
1936 case AtomicRMWInst::Add:
1937 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_add;
1938 break;
1939 case AtomicRMWInst::Sub:
1940 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_sub;
1941 break;
1942 case AtomicRMWInst::And:
1943 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_and;
1944 break;
1945 case AtomicRMWInst::Or:
1946 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_or;
1947 break;
1948 case AtomicRMWInst::Xor:
1949 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_xor;
1950 break;
1951 case AtomicRMWInst::Max:
1952 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_smax;
1953 break;
1954 case AtomicRMWInst::Min:
1955 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_smin;
1956 break;
1957 case AtomicRMWInst::UMax:
1958 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_umax;
1959 break;
1960 case AtomicRMWInst::UMin:
1961 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_umin;
1962 break;
1963 case AtomicRMWInst::FAdd:
1964 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_fadd;
1965 break;
1966 case AtomicRMWInst::FMax:
1967 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_fmax;
1968 break;
1969 case AtomicRMWInst::FMin:
1970 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_fmin;
1971 break;
1972 case AtomicRMWInst::USubCond:
1973 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_cond_sub_u32;
1974 break;
1975 case AtomicRMWInst::USubSat:
1976 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_sub_clamp_u32;
1977 break;
1978 case AtomicRMWInst::FSub: {
1979 reportFatalUsageError(
1980 reason: "atomic floating point subtraction not supported for "
1981 "buffer resources and should've been expanded away");
1982 break;
1983 }
1984 case AtomicRMWInst::FMaximum: {
1985 reportFatalUsageError(
1986 reason: "atomic floating point fmaximum not supported for "
1987 "buffer resources and should've been expanded away");
1988 break;
1989 }
1990 case AtomicRMWInst::FMinimum: {
1991 reportFatalUsageError(
1992 reason: "atomic floating point fminimum not supported for "
1993 "buffer resources and should've been expanded away");
1994 break;
1995 }
1996 case AtomicRMWInst::FMaximumNum: {
1997 reportFatalUsageError(
1998 reason: "atomic floating point fmaximumnum not supported for "
1999 "buffer resources and should've been expanded away");
2000 break;
2001 }
2002 case AtomicRMWInst::FMinimumNum: {
2003 reportFatalUsageError(
2004 reason: "atomic floating point fminimumnum not supported for "
2005 "buffer resources and should've been expanded away");
2006 break;
2007 }
2008 case AtomicRMWInst::Nand:
2009 reportFatalUsageError(
2010 reason: "atomic nand not supported for buffer resources and "
2011 "should've been expanded away");
2012 break;
2013 case AtomicRMWInst::UIncWrap:
2014 case AtomicRMWInst::UDecWrap:
2015 reportFatalUsageError(
2016 reason: "wrapping increment/decrement not supported for "
2017 "buffer resources and should've been expanded away");
2018 break;
2019 case AtomicRMWInst::BAD_BINOP:
2020 llvm_unreachable("Not sure how we got a bad binop");
2021 }
2022 }
2023
2024 CallInst *Call = IRB.CreateIntrinsicWithoutFolding(ID: IID, OverloadTypes: Ty, Args);
2025 copyMetadata(Dest: Call, Src: I);
2026 setAlign(Intr: Call, A: Alignment, RsrcArgIdx: Arg ? 1 : 0);
2027 Call->takeName(V: I);
2028
2029 insertPostMemOpFence(Order, SSID);
2030 // The "no moving p7 directly" rewrites ensure that this load or store won't
2031 // itself need to be split into parts.
2032 SplitUsers.insert(V: I);
2033 I->replaceAllUsesWith(V: Call);
2034 return Call;
2035}
2036
2037PtrParts SplitPtrStructs::visitInstruction(Instruction &I) {
2038 return {nullptr, nullptr};
2039}
2040
2041PtrParts SplitPtrStructs::visitLoadInst(LoadInst &LI) {
2042 if (!isSplitFatPtr(Ty: LI.getPointerOperandType()))
2043 return {nullptr, nullptr};
2044 handleMemoryInst(I: &LI, Arg: nullptr, Ptr: LI.getPointerOperand(), Ty: LI.getType(),
2045 Alignment: LI.getAlign(), Order: LI.getOrdering(), IsVolatile: LI.isVolatile(),
2046 SSID: LI.getSyncScopeID());
2047 return {nullptr, nullptr};
2048}
2049
2050PtrParts SplitPtrStructs::visitStoreInst(StoreInst &SI) {
2051 if (!isSplitFatPtr(Ty: SI.getPointerOperandType()))
2052 return {nullptr, nullptr};
2053 Value *Arg = SI.getValueOperand();
2054 handleMemoryInst(I: &SI, Arg, Ptr: SI.getPointerOperand(), Ty: Arg->getType(),
2055 Alignment: SI.getAlign(), Order: SI.getOrdering(), IsVolatile: SI.isVolatile(),
2056 SSID: SI.getSyncScopeID());
2057 return {nullptr, nullptr};
2058}
2059
2060PtrParts SplitPtrStructs::visitAtomicRMWInst(AtomicRMWInst &AI) {
2061 if (!isSplitFatPtr(Ty: AI.getPointerOperand()->getType()))
2062 return {nullptr, nullptr};
2063 Value *Arg = AI.getValOperand();
2064 handleMemoryInst(I: &AI, Arg, Ptr: AI.getPointerOperand(), Ty: Arg->getType(),
2065 Alignment: AI.getAlign(), Order: AI.getOrdering(), IsVolatile: AI.isVolatile(),
2066 SSID: AI.getSyncScopeID());
2067 return {nullptr, nullptr};
2068}
2069
2070// Unlike load, store, and RMW, cmpxchg needs special handling to account
2071// for the boolean argument.
2072PtrParts SplitPtrStructs::visitAtomicCmpXchgInst(AtomicCmpXchgInst &AI) {
2073 Value *Ptr = AI.getPointerOperand();
2074 if (!isSplitFatPtr(Ty: Ptr->getType()))
2075 return {nullptr, nullptr};
2076 IRB.SetInsertPoint(&AI);
2077
2078 Type *Ty = AI.getNewValOperand()->getType();
2079 AtomicOrdering Order = AI.getMergedOrdering();
2080 SyncScope::ID SSID = AI.getSyncScopeID();
2081 bool IsNonTemporal = AI.getMetadata(KindID: LLVMContext::MD_nontemporal);
2082
2083 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2084 insertPreMemOpFence(Order, SSID);
2085
2086 uint32_t Aux = 0;
2087 if (IsNonTemporal)
2088 Aux |= AMDGPU::CPol::SLC;
2089 if (AI.isVolatile())
2090 Aux |= AMDGPU::CPol::VOLATILE;
2091 CallInst *Call = IRB.CreateIntrinsicWithoutFolding(
2092 ID: Intrinsic::amdgcn_raw_ptr_buffer_atomic_cmpswap, OverloadTypes: Ty,
2093 Args: {AI.getNewValOperand(), AI.getCompareOperand(), Rsrc, Off,
2094 IRB.getInt32(C: 0), IRB.getInt32(C: Aux)});
2095 copyMetadata(Dest: Call, Src: &AI);
2096 setAlign(Intr: Call, A: AI.getAlign(), RsrcArgIdx: 2);
2097 Call->takeName(V: &AI);
2098 insertPostMemOpFence(Order, SSID);
2099
2100 Value *Res = PoisonValue::get(T: AI.getType());
2101 Res = IRB.CreateInsertValue(Agg: Res, Val: Call, Idxs: 0);
2102 Value *Succeeded = IRB.CreateICmpEQ(LHS: Call, RHS: AI.getCompareOperand());
2103 Res = IRB.CreateInsertValue(Agg: Res, Val: Succeeded, Idxs: 1);
2104 SplitUsers.insert(V: &AI);
2105 AI.replaceAllUsesWith(V: Res);
2106 return {nullptr, nullptr};
2107}
2108
2109PtrParts SplitPtrStructs::visitGetElementPtrInst(GetElementPtrInst &GEP) {
2110 using namespace llvm::PatternMatch;
2111 Value *Ptr = GEP.getPointerOperand();
2112 if (!isSplitFatPtr(Ty: Ptr->getType()))
2113 return {nullptr, nullptr};
2114 IRB.SetInsertPoint(&GEP);
2115
2116 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2117 const DataLayout &DL = GEP.getDataLayout();
2118 bool IsNUW = GEP.hasNoUnsignedWrap();
2119 bool IsNUSW = GEP.hasNoUnsignedSignedWrap();
2120
2121 StructType *ResTy = cast<StructType>(Val: GEP.getType());
2122 Type *ResRsrcTy = ResTy->getElementType(N: 0);
2123 VectorType *ResRsrcVecTy = dyn_cast<VectorType>(Val: ResRsrcTy);
2124 bool BroadcastsPtr = ResRsrcVecTy && !isa<VectorType>(Val: Off->getType());
2125
2126 // In order to call emitGEPOffset() and thus not have to reimplement it,
2127 // we need the GEP result to have ptr addrspace(7) type.
2128 Type *FatPtrTy =
2129 ResRsrcTy->getWithNewType(EltTy: IRB.getPtrTy(AddrSpace: AMDGPUAS::BUFFER_FAT_POINTER));
2130 GEP.mutateType(Ty: FatPtrTy);
2131 Value *OffAccum = emitGEPOffset(Builder: &IRB, DL, GEP: &GEP);
2132 GEP.mutateType(Ty: ResTy);
2133
2134 if (BroadcastsPtr) {
2135 Rsrc = IRB.CreateVectorSplat(EC: ResRsrcVecTy->getElementCount(), V: Rsrc,
2136 Name: Rsrc->getName());
2137 Off = IRB.CreateVectorSplat(EC: ResRsrcVecTy->getElementCount(), V: Off,
2138 Name: Off->getName());
2139 }
2140 if (match(V: OffAccum, P: m_Zero())) { // Constant-zero offset
2141 SplitUsers.insert(V: &GEP);
2142 return {Rsrc, Off};
2143 }
2144
2145 bool HasNonNegativeOff = false;
2146 if (auto *CI = dyn_cast<ConstantInt>(Val: OffAccum)) {
2147 HasNonNegativeOff = !CI->isNegative();
2148 }
2149 Value *NewOff;
2150 if (match(V: Off, P: m_Zero())) {
2151 NewOff = OffAccum;
2152 } else {
2153 NewOff = IRB.CreateAdd(LHS: Off, RHS: OffAccum, Name: "",
2154 /*hasNUW=*/HasNUW: IsNUW || (IsNUSW && HasNonNegativeOff),
2155 /*hasNSW=*/HasNSW: false);
2156 }
2157 copyMetadata(Dest: NewOff, Src: &GEP);
2158 NewOff->takeName(V: &GEP);
2159 SplitUsers.insert(V: &GEP);
2160 return {Rsrc, NewOff};
2161}
2162
2163PtrParts SplitPtrStructs::visitPtrToIntInst(PtrToIntInst &PI) {
2164 Value *Ptr = PI.getPointerOperand();
2165 if (!isSplitFatPtr(Ty: Ptr->getType()))
2166 return {nullptr, nullptr};
2167 IRB.SetInsertPoint(&PI);
2168
2169 Type *ResTy = PI.getType();
2170 unsigned Width = ResTy->getScalarSizeInBits();
2171
2172 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2173 const DataLayout &DL = PI.getDataLayout();
2174 unsigned FatPtrWidth = DL.getPointerSizeInBits(AS: AMDGPUAS::BUFFER_FAT_POINTER);
2175
2176 Value *Res;
2177 if (Width <= BufferOffsetWidth) {
2178 Res = IRB.CreateIntCast(V: Off, DestTy: ResTy, /*isSigned=*/false,
2179 Name: PI.getName() + ".off");
2180 } else {
2181 Value *RsrcInt = IRB.CreatePtrToInt(V: Rsrc, DestTy: ResTy, Name: PI.getName() + ".rsrc");
2182 Value *Shl = IRB.CreateShl(
2183 LHS: RsrcInt,
2184 RHS: ConstantExpr::getIntegerValue(Ty: ResTy, V: APInt(Width, BufferOffsetWidth)),
2185 Name: "", HasNUW: Width >= FatPtrWidth, HasNSW: Width > FatPtrWidth);
2186 Value *OffCast = IRB.CreateIntCast(V: Off, DestTy: ResTy, /*isSigned=*/false,
2187 Name: PI.getName() + ".off");
2188 Res = IRB.CreateOr(LHS: Shl, RHS: OffCast);
2189 }
2190
2191 copyMetadata(Dest: Res, Src: &PI);
2192 Res->takeName(V: &PI);
2193 SplitUsers.insert(V: &PI);
2194 PI.replaceAllUsesWith(V: Res);
2195 return {nullptr, nullptr};
2196}
2197
2198PtrParts SplitPtrStructs::visitPtrToAddrInst(PtrToAddrInst &PA) {
2199 Value *Ptr = PA.getPointerOperand();
2200 if (!isSplitFatPtr(Ty: Ptr->getType()))
2201 return {nullptr, nullptr};
2202 IRB.SetInsertPoint(&PA);
2203
2204 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2205 Value *Res = IRB.CreateIntCast(V: Off, DestTy: PA.getType(), /*isSigned=*/false);
2206 copyMetadata(Dest: Res, Src: &PA);
2207 Res->takeName(V: &PA);
2208 SplitUsers.insert(V: &PA);
2209 PA.replaceAllUsesWith(V: Res);
2210 return {nullptr, nullptr};
2211}
2212
2213PtrParts SplitPtrStructs::visitIntToPtrInst(IntToPtrInst &IP) {
2214 if (!isSplitFatPtr(Ty: IP.getType()))
2215 return {nullptr, nullptr};
2216 IRB.SetInsertPoint(&IP);
2217 const DataLayout &DL = IP.getDataLayout();
2218 unsigned RsrcPtrWidth = DL.getPointerSizeInBits(AS: AMDGPUAS::BUFFER_RESOURCE);
2219 Value *Int = IP.getOperand(i_nocapture: 0);
2220 Type *IntTy = Int->getType();
2221 Type *RsrcIntTy = IntTy->getWithNewBitWidth(NewBitWidth: RsrcPtrWidth);
2222 unsigned Width = IntTy->getScalarSizeInBits();
2223
2224 auto *RetTy = cast<StructType>(Val: IP.getType());
2225 Type *RsrcTy = RetTy->getElementType(N: 0);
2226 Type *OffTy = RetTy->getElementType(N: 1);
2227 Value *RsrcPart = IRB.CreateLShr(
2228 LHS: Int,
2229 RHS: ConstantExpr::getIntegerValue(Ty: IntTy, V: APInt(Width, BufferOffsetWidth)));
2230 Value *RsrcInt = IRB.CreateIntCast(V: RsrcPart, DestTy: RsrcIntTy, /*isSigned=*/false);
2231 Value *Rsrc = IRB.CreateIntToPtr(V: RsrcInt, DestTy: RsrcTy, Name: IP.getName() + ".rsrc");
2232 Value *Off =
2233 IRB.CreateIntCast(V: Int, DestTy: OffTy, /*IsSigned=*/isSigned: false, Name: IP.getName() + ".off");
2234
2235 copyMetadata(Dest: Rsrc, Src: &IP);
2236 SplitUsers.insert(V: &IP);
2237 return {Rsrc, Off};
2238}
2239
2240PtrParts SplitPtrStructs::visitAddrSpaceCastInst(AddrSpaceCastInst &I) {
2241 // TODO(krzysz00): handle casts from ptr addrspace(7) to global pointers
2242 // by computing the effective address.
2243 if (!isSplitFatPtr(Ty: I.getType()))
2244 return {nullptr, nullptr};
2245 IRB.SetInsertPoint(&I);
2246 Value *In = I.getPointerOperand();
2247 // No-op casts preserve parts
2248 if (In->getType() == I.getType()) {
2249 auto [Rsrc, Off] = getPtrParts(V: In);
2250 SplitUsers.insert(V: &I);
2251 return {Rsrc, Off};
2252 }
2253
2254 auto *ResTy = cast<StructType>(Val: I.getType());
2255 Type *RsrcTy = ResTy->getElementType(N: 0);
2256 Type *OffTy = ResTy->getElementType(N: 1);
2257 Value *ZeroOff = Constant::getNullValue(Ty: OffTy);
2258
2259 // Special case for null pointers, undef, and poison, which can be created by
2260 // address space propagation.
2261 auto *InConst = dyn_cast<Constant>(Val: In);
2262 if (InConst && InConst->isNullValue()) {
2263 Value *NullRsrc = Constant::getNullValue(Ty: RsrcTy);
2264 SplitUsers.insert(V: &I);
2265 return {NullRsrc, ZeroOff};
2266 }
2267 if (isa<PoisonValue>(Val: In)) {
2268 Value *PoisonRsrc = PoisonValue::get(T: RsrcTy);
2269 Value *PoisonOff = PoisonValue::get(T: OffTy);
2270 SplitUsers.insert(V: &I);
2271 return {PoisonRsrc, PoisonOff};
2272 }
2273 if (isa<UndefValue>(Val: In)) {
2274 Value *UndefRsrc = UndefValue::get(T: RsrcTy);
2275 Value *UndefOff = UndefValue::get(T: OffTy);
2276 SplitUsers.insert(V: &I);
2277 return {UndefRsrc, UndefOff};
2278 }
2279
2280 if (I.getSrcAddressSpace() != AMDGPUAS::BUFFER_RESOURCE)
2281 reportFatalUsageError(
2282 reason: "only buffer resources (addrspace 8) and null/poison pointers can be "
2283 "cast to buffer fat pointers (addrspace 7)");
2284 SplitUsers.insert(V: &I);
2285 return {In, ZeroOff};
2286}
2287
2288PtrParts SplitPtrStructs::visitICmpInst(ICmpInst &Cmp) {
2289 Value *Lhs = Cmp.getOperand(i_nocapture: 0);
2290 if (!isSplitFatPtr(Ty: Lhs->getType()))
2291 return {nullptr, nullptr};
2292 Value *Rhs = Cmp.getOperand(i_nocapture: 1);
2293 IRB.SetInsertPoint(&Cmp);
2294 ICmpInst::Predicate Pred = Cmp.getPredicate();
2295
2296 assert((Pred == ICmpInst::ICMP_EQ || Pred == ICmpInst::ICMP_NE) &&
2297 "Pointer comparison is only equal or unequal");
2298 auto [LhsRsrc, LhsOff] = getPtrParts(V: Lhs);
2299 auto [RhsRsrc, RhsOff] = getPtrParts(V: Rhs);
2300 Value *Res = IRB.CreateICmp(P: Pred, LHS: LhsOff, RHS: RhsOff);
2301 copyMetadata(Dest: Res, Src: &Cmp);
2302 Res->takeName(V: &Cmp);
2303 SplitUsers.insert(V: &Cmp);
2304 Cmp.replaceAllUsesWith(V: Res);
2305 return {nullptr, nullptr};
2306}
2307
2308PtrParts SplitPtrStructs::visitFreezeInst(FreezeInst &I) {
2309 if (!isSplitFatPtr(Ty: I.getType()))
2310 return {nullptr, nullptr};
2311 IRB.SetInsertPoint(&I);
2312 auto [Rsrc, Off] = getPtrParts(V: I.getOperand(i_nocapture: 0));
2313
2314 Value *RsrcRes = IRB.CreateFreeze(V: Rsrc, Name: I.getName() + ".rsrc");
2315 copyMetadata(Dest: RsrcRes, Src: &I);
2316 Value *OffRes = IRB.CreateFreeze(V: Off, Name: I.getName() + ".off");
2317 copyMetadata(Dest: OffRes, Src: &I);
2318 SplitUsers.insert(V: &I);
2319 return {RsrcRes, OffRes};
2320}
2321
2322PtrParts SplitPtrStructs::visitExtractElementInst(ExtractElementInst &I) {
2323 if (!isSplitFatPtr(Ty: I.getType()))
2324 return {nullptr, nullptr};
2325 IRB.SetInsertPoint(&I);
2326 Value *Vec = I.getVectorOperand();
2327 Value *Idx = I.getIndexOperand();
2328 auto [Rsrc, Off] = getPtrParts(V: Vec);
2329
2330 Value *RsrcRes = IRB.CreateExtractElement(Vec: Rsrc, Idx, Name: I.getName() + ".rsrc");
2331 copyMetadata(Dest: RsrcRes, Src: &I);
2332 Value *OffRes = IRB.CreateExtractElement(Vec: Off, Idx, Name: I.getName() + ".off");
2333 copyMetadata(Dest: OffRes, Src: &I);
2334 SplitUsers.insert(V: &I);
2335 return {RsrcRes, OffRes};
2336}
2337
2338PtrParts SplitPtrStructs::visitInsertElementInst(InsertElementInst &I) {
2339 // The mutated instructions temporarily don't return vectors, and so
2340 // we need the generic getType() here to avoid crashes.
2341 if (!isSplitFatPtr(Ty: cast<Instruction>(Val&: I).getType()))
2342 return {nullptr, nullptr};
2343 IRB.SetInsertPoint(&I);
2344 Value *Vec = I.getOperand(i_nocapture: 0);
2345 Value *Elem = I.getOperand(i_nocapture: 1);
2346 Value *Idx = I.getOperand(i_nocapture: 2);
2347 auto [VecRsrc, VecOff] = getPtrParts(V: Vec);
2348 auto [ElemRsrc, ElemOff] = getPtrParts(V: Elem);
2349
2350 Value *RsrcRes =
2351 IRB.CreateInsertElement(Vec: VecRsrc, NewElt: ElemRsrc, Idx, Name: I.getName() + ".rsrc");
2352 copyMetadata(Dest: RsrcRes, Src: &I);
2353 Value *OffRes =
2354 IRB.CreateInsertElement(Vec: VecOff, NewElt: ElemOff, Idx, Name: I.getName() + ".off");
2355 copyMetadata(Dest: OffRes, Src: &I);
2356 SplitUsers.insert(V: &I);
2357 return {RsrcRes, OffRes};
2358}
2359
2360PtrParts SplitPtrStructs::visitShuffleVectorInst(ShuffleVectorInst &I) {
2361 // Cast is needed for the same reason as insertelement's.
2362 if (!isSplitFatPtr(Ty: cast<Instruction>(Val&: I).getType()))
2363 return {nullptr, nullptr};
2364 IRB.SetInsertPoint(&I);
2365
2366 Value *V1 = I.getOperand(i_nocapture: 0);
2367 Value *V2 = I.getOperand(i_nocapture: 1);
2368 ArrayRef<int> Mask = I.getShuffleMask();
2369 auto [V1Rsrc, V1Off] = getPtrParts(V: V1);
2370 auto [V2Rsrc, V2Off] = getPtrParts(V: V2);
2371
2372 Value *RsrcRes =
2373 IRB.CreateShuffleVector(V1: V1Rsrc, V2: V2Rsrc, Mask, Name: I.getName() + ".rsrc");
2374 copyMetadata(Dest: RsrcRes, Src: &I);
2375 Value *OffRes =
2376 IRB.CreateShuffleVector(V1: V1Off, V2: V2Off, Mask, Name: I.getName() + ".off");
2377 copyMetadata(Dest: OffRes, Src: &I);
2378 SplitUsers.insert(V: &I);
2379 return {RsrcRes, OffRes};
2380}
2381
2382PtrParts SplitPtrStructs::visitPHINode(PHINode &PHI) {
2383 if (!isSplitFatPtr(Ty: PHI.getType()))
2384 return {nullptr, nullptr};
2385 IRB.SetInsertPoint(*PHI.getInsertionPointAfterDef());
2386 // Phi nodes will be handled in post-processing after we've visited every
2387 // instruction. However, instead of just returning {nullptr, nullptr},
2388 // we explicitly create the temporary extractvalue operations that are our
2389 // temporary results so that they end up at the beginning of the block with
2390 // the PHIs.
2391 Value *TmpRsrc = IRB.CreateExtractValue(Agg: &PHI, Idxs: 0, Name: PHI.getName() + ".rsrc");
2392 Value *TmpOff = IRB.CreateExtractValue(Agg: &PHI, Idxs: 1, Name: PHI.getName() + ".off");
2393 Conditionals.push_back(Elt: &PHI);
2394 SplitUsers.insert(V: &PHI);
2395 return {TmpRsrc, TmpOff};
2396}
2397
2398PtrParts SplitPtrStructs::visitSelectInst(SelectInst &SI) {
2399 if (!isSplitFatPtr(Ty: SI.getType()))
2400 return {nullptr, nullptr};
2401 IRB.SetInsertPoint(&SI);
2402
2403 Value *Cond = SI.getCondition();
2404 Value *True = SI.getTrueValue();
2405 Value *False = SI.getFalseValue();
2406 auto [TrueRsrc, TrueOff] = getPtrParts(V: True);
2407 auto [FalseRsrc, FalseOff] = getPtrParts(V: False);
2408
2409 Value *RsrcRes =
2410 IRB.CreateSelect(C: Cond, True: TrueRsrc, False: FalseRsrc, Name: SI.getName() + ".rsrc", MDFrom: &SI);
2411 copyMetadata(Dest: RsrcRes, Src: &SI);
2412 Conditionals.push_back(Elt: &SI);
2413 Value *OffRes =
2414 IRB.CreateSelect(C: Cond, True: TrueOff, False: FalseOff, Name: SI.getName() + ".off", MDFrom: &SI);
2415 copyMetadata(Dest: OffRes, Src: &SI);
2416 SplitUsers.insert(V: &SI);
2417 return {RsrcRes, OffRes};
2418}
2419
2420/// Returns true if this intrinsic needs to be removed when it is
2421/// applied to `ptr addrspace(7)` values. Calls to these intrinsics are
2422/// rewritten into calls to versions of that intrinsic on the resource
2423/// descriptor.
2424static bool isRemovablePointerIntrinsic(Intrinsic::ID IID) {
2425 switch (IID) {
2426 default:
2427 return false;
2428 case Intrinsic::amdgcn_make_buffer_rsrc:
2429 case Intrinsic::ptrmask:
2430 case Intrinsic::invariant_start:
2431 case Intrinsic::invariant_end:
2432 case Intrinsic::launder_invariant_group:
2433 case Intrinsic::strip_invariant_group:
2434 case Intrinsic::memcpy:
2435 case Intrinsic::memcpy_inline:
2436 case Intrinsic::memmove:
2437 case Intrinsic::memset:
2438 case Intrinsic::memset_inline:
2439 case Intrinsic::experimental_memset_pattern:
2440 case Intrinsic::amdgcn_load_to_lds:
2441 case Intrinsic::amdgcn_load_async_to_lds:
2442 return true;
2443 }
2444}
2445
2446PtrParts SplitPtrStructs::visitIntrinsicInst(IntrinsicInst &I) {
2447 Intrinsic::ID IID = I.getIntrinsicID();
2448 switch (IID) {
2449 default:
2450 break;
2451 case Intrinsic::amdgcn_make_buffer_rsrc: {
2452 if (!isSplitFatPtr(Ty: I.getType()))
2453 return {nullptr, nullptr};
2454 Value *Base = I.getArgOperand(i: 0);
2455 Value *Stride = I.getArgOperand(i: 1);
2456 Value *NumRecords = I.getArgOperand(i: 2);
2457 Value *Flags = I.getArgOperand(i: 3);
2458 auto *SplitType = cast<StructType>(Val: I.getType());
2459 Type *RsrcType = SplitType->getElementType(N: 0);
2460 Type *OffType = SplitType->getElementType(N: 1);
2461 IRB.SetInsertPoint(&I);
2462 Value *Rsrc = IRB.CreateIntrinsic(ID: IID, OverloadTypes: {RsrcType, Base->getType()},
2463 Args: {Base, Stride, NumRecords, Flags});
2464 copyMetadata(Dest: Rsrc, Src: &I);
2465 Rsrc->takeName(V: &I);
2466 Value *Zero = Constant::getNullValue(Ty: OffType);
2467 SplitUsers.insert(V: &I);
2468 return {Rsrc, Zero};
2469 }
2470 case Intrinsic::ptrmask: {
2471 Value *Ptr = I.getArgOperand(i: 0);
2472 if (!isSplitFatPtr(Ty: Ptr->getType()))
2473 return {nullptr, nullptr};
2474 Value *Mask = I.getArgOperand(i: 1);
2475 IRB.SetInsertPoint(&I);
2476 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2477 if (Mask->getType() != Off->getType())
2478 reportFatalUsageError(reason: "offset width is not equal to index width of fat "
2479 "pointer (data layout not set up correctly?)");
2480 Value *OffRes = IRB.CreateAnd(LHS: Off, RHS: Mask, Name: I.getName() + ".off");
2481 copyMetadata(Dest: OffRes, Src: &I);
2482 SplitUsers.insert(V: &I);
2483 return {Rsrc, OffRes};
2484 }
2485 // Pointer annotation intrinsics that, given their object-wide nature
2486 // operate on the resource part.
2487 case Intrinsic::invariant_start: {
2488 Value *Ptr = I.getArgOperand(i: 1);
2489 if (!isSplitFatPtr(Ty: Ptr->getType()))
2490 return {nullptr, nullptr};
2491 IRB.SetInsertPoint(&I);
2492 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2493 Type *NewTy = PointerType::get(C&: I.getContext(), AddressSpace: AMDGPUAS::BUFFER_RESOURCE);
2494 auto *NewRsrc = IRB.CreateIntrinsic(ID: IID, OverloadTypes: {NewTy}, Args: {I.getOperand(i_nocapture: 0), Rsrc});
2495 copyMetadata(Dest: NewRsrc, Src: &I);
2496 NewRsrc->takeName(V: &I);
2497 SplitUsers.insert(V: &I);
2498 I.replaceAllUsesWith(V: NewRsrc);
2499 return {nullptr, nullptr};
2500 }
2501 case Intrinsic::invariant_end: {
2502 Value *RealPtr = I.getArgOperand(i: 2);
2503 if (!isSplitFatPtr(Ty: RealPtr->getType()))
2504 return {nullptr, nullptr};
2505 IRB.SetInsertPoint(&I);
2506 Value *RealRsrc = getPtrParts(V: RealPtr).first;
2507 Value *InvPtr = I.getArgOperand(i: 0);
2508 Value *Size = I.getArgOperand(i: 1);
2509 Value *NewRsrc = IRB.CreateIntrinsic(ID: IID, OverloadTypes: {RealRsrc->getType()},
2510 Args: {InvPtr, Size, RealRsrc});
2511 copyMetadata(Dest: NewRsrc, Src: &I);
2512 NewRsrc->takeName(V: &I);
2513 SplitUsers.insert(V: &I);
2514 I.replaceAllUsesWith(V: NewRsrc);
2515 return {nullptr, nullptr};
2516 }
2517 case Intrinsic::launder_invariant_group:
2518 case Intrinsic::strip_invariant_group: {
2519 Value *Ptr = I.getArgOperand(i: 0);
2520 if (!isSplitFatPtr(Ty: Ptr->getType()))
2521 return {nullptr, nullptr};
2522 IRB.SetInsertPoint(&I);
2523 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2524 Value *NewRsrc = IRB.CreateIntrinsic(ID: IID, OverloadTypes: {Rsrc->getType()}, Args: {Rsrc});
2525 copyMetadata(Dest: NewRsrc, Src: &I);
2526 NewRsrc->takeName(V: &I);
2527 SplitUsers.insert(V: &I);
2528 return {NewRsrc, Off};
2529 }
2530 case Intrinsic::amdgcn_load_to_lds:
2531 case Intrinsic::amdgcn_load_async_to_lds: {
2532 Value *Ptr = I.getArgOperand(i: 0);
2533 if (!isSplitFatPtr(Ty: Ptr->getType()))
2534 return {nullptr, nullptr};
2535 IRB.SetInsertPoint(&I);
2536 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2537 Value *LDSPtr = I.getArgOperand(i: 1);
2538 Value *LoadSize = I.getArgOperand(i: 2);
2539 Value *ImmOff = I.getArgOperand(i: 3);
2540 Value *Aux = I.getArgOperand(i: 4);
2541 Value *SOffset = IRB.getInt32(C: 0);
2542 Intrinsic::ID NewIntr =
2543 IID == Intrinsic::amdgcn_load_to_lds
2544 ? Intrinsic::amdgcn_raw_ptr_buffer_load_lds
2545 : Intrinsic::amdgcn_raw_ptr_buffer_load_async_lds;
2546 Instruction *NewLoad = IRB.CreateIntrinsicWithoutFolding(
2547 ID: NewIntr, OverloadTypes: {}, Args: {Rsrc, LDSPtr, LoadSize, Off, SOffset, ImmOff, Aux});
2548 copyMetadata(Dest: NewLoad, Src: &I);
2549 SplitUsers.insert(V: &I);
2550 I.replaceAllUsesWith(V: NewLoad);
2551 return {nullptr, nullptr};
2552 }
2553 }
2554 return {nullptr, nullptr};
2555}
2556
2557void SplitPtrStructs::processFunction(Function &F) {
2558 ST = &TM->getSubtarget<GCNSubtarget>(F);
2559 SmallVector<Instruction *, 0> Originals(
2560 llvm::make_pointer_range(Range: instructions(F)));
2561 LLVM_DEBUG(dbgs() << "Splitting pointer structs in function: " << F.getName()
2562 << "\n");
2563 for (Instruction *I : Originals) {
2564 // In some cases, instruction order doesn't reflect program order,
2565 // so the visit() call will have already visited coertain instructions
2566 // by the time this loop gets to them. Avoid re-visiting these so as to,
2567 // for example, avoid processing the same conditional twice.
2568 if (SplitUsers.contains(V: I))
2569 continue;
2570 auto [Rsrc, Off] = visit(I);
2571 assert(((Rsrc && Off) || (!Rsrc && !Off)) &&
2572 "Can't have a resource but no offset");
2573 if (Rsrc)
2574 RsrcParts[I] = Rsrc;
2575 if (Off)
2576 OffParts[I] = Off;
2577 }
2578 processConditionals();
2579 killAndReplaceSplitInstructions(Origs&: Originals);
2580
2581 // Clean up after ourselves to save on memory.
2582 RsrcParts.clear();
2583 OffParts.clear();
2584 SplitUsers.clear();
2585 Conditionals.clear();
2586 ConditionalTemps.clear();
2587}
2588
2589namespace {
2590class AMDGPULowerBufferFatPointers : public ModulePass {
2591public:
2592 static char ID;
2593
2594 AMDGPULowerBufferFatPointers() : ModulePass(ID) {}
2595
2596 bool run(Module &M, const TargetMachine &TM, GetTTIFn GetTTI, GetSEFn GetSE);
2597 bool runOnModule(Module &M) override;
2598
2599 void getAnalysisUsage(AnalysisUsage &AU) const override;
2600};
2601} // namespace
2602
2603/// Returns true if there are values that have a buffer fat pointer in them,
2604/// which means we'll need to perform rewrites on this function. As a side
2605/// effect, this will populate the type remapping cache.
2606static bool containsBufferFatPointers(const Function &F,
2607 BufferFatPtrToStructTypeMap *TypeMap) {
2608 bool HasFatPointers = false;
2609 for (const BasicBlock &BB : F)
2610 for (const Instruction &I : BB) {
2611 HasFatPointers |= (I.getType() != TypeMap->remapType(SrcTy: I.getType()));
2612 // Catch null pointer constants in loads, stores, etc.
2613 for (const Value *V : I.operand_values())
2614 HasFatPointers |= (V->getType() != TypeMap->remapType(SrcTy: V->getType()));
2615 }
2616 return HasFatPointers;
2617}
2618
2619static bool hasFatPointerInterface(const Function &F,
2620 BufferFatPtrToStructTypeMap *TypeMap) {
2621 Type *Ty = F.getFunctionType();
2622 return Ty != TypeMap->remapType(SrcTy: Ty);
2623}
2624
2625/// Move the body of `OldF` into a new function, returning it.
2626static Function *moveFunctionAdaptingType(Function *OldF, FunctionType *NewTy,
2627 ValueToValueMapTy &CloneMap) {
2628 bool IsIntrinsic = OldF->isIntrinsic();
2629 Function *NewF =
2630 Function::Create(Ty: NewTy, Linkage: OldF->getLinkage(), AddrSpace: OldF->getAddressSpace());
2631 NewF->copyAttributesFrom(Src: OldF);
2632 NewF->copyMetadata(Src: OldF, Offset: 0);
2633 NewF->takeName(V: OldF);
2634 NewF->updateAfterNameChange();
2635 NewF->setDLLStorageClass(OldF->getDLLStorageClass());
2636 OldF->getParent()->getFunctionList().insertAfter(where: OldF->getIterator(), New: NewF);
2637
2638 while (!OldF->empty()) {
2639 BasicBlock *BB = &OldF->front();
2640 BB->removeFromParent();
2641 BB->insertInto(Parent: NewF);
2642 CloneMap[BB] = BB;
2643 for (Instruction &I : *BB) {
2644 CloneMap[&I] = &I;
2645 }
2646 }
2647
2648 SmallVector<AttributeSet> ArgAttrs;
2649 AttributeList OldAttrs = OldF->getAttributes();
2650
2651 for (auto [I, OldArg, NewArg] : enumerate(First: OldF->args(), Rest: NewF->args())) {
2652 CloneMap[&NewArg] = &OldArg;
2653 NewArg.takeName(V: &OldArg);
2654 Type *OldArgTy = OldArg.getType(), *NewArgTy = NewArg.getType();
2655 // Temporarily mutate type of `NewArg` to allow RAUW to work.
2656 NewArg.mutateType(Ty: OldArgTy);
2657 OldArg.replaceAllUsesWith(V: &NewArg);
2658 NewArg.mutateType(Ty: NewArgTy);
2659
2660 AttributeSet ArgAttr = OldAttrs.getParamAttrs(ArgNo: I);
2661 // Intrinsics get their attributes fixed later.
2662 if (OldArgTy != NewArgTy && !IsIntrinsic)
2663 ArgAttr = ArgAttr.removeAttributes(
2664 C&: NewF->getContext(),
2665 AttrsToRemove: AttributeFuncs::typeIncompatible(Ty: NewArgTy, AS: ArgAttr));
2666 ArgAttrs.push_back(Elt: ArgAttr);
2667 }
2668 AttributeSet RetAttrs = OldAttrs.getRetAttrs();
2669 if (OldF->getReturnType() != NewF->getReturnType() && !IsIntrinsic)
2670 RetAttrs = RetAttrs.removeAttributes(
2671 C&: NewF->getContext(),
2672 AttrsToRemove: AttributeFuncs::typeIncompatible(Ty: NewF->getReturnType(), AS: RetAttrs));
2673 NewF->setAttributes(AttributeList::get(
2674 C&: NewF->getContext(), FnAttrs: OldAttrs.getFnAttrs(), RetAttrs, ArgAttrs));
2675 return NewF;
2676}
2677
2678static void makeCloneInPraceMap(Function *F, ValueToValueMapTy &CloneMap) {
2679 for (Argument &A : F->args())
2680 CloneMap[&A] = &A;
2681 for (BasicBlock &BB : *F) {
2682 CloneMap[&BB] = &BB;
2683 for (Instruction &I : BB)
2684 CloneMap[&I] = &I;
2685 }
2686}
2687
2688bool AMDGPULowerBufferFatPointers::run(Module &M, const TargetMachine &TM,
2689 GetTTIFn GetTTI, GetSEFn GetSE) {
2690 bool Changed = false;
2691 const DataLayout &DL = M.getDataLayout();
2692 // Record the functions which need to be remapped.
2693 // The second element of the pair indicates whether the function has to have
2694 // its arguments or return types adjusted.
2695 SmallVector<std::pair<Function *, bool>> NeedsRemap;
2696
2697 LLVMContext &Ctx = M.getContext();
2698
2699 BufferFatPtrToStructTypeMap StructTM(DL);
2700 BufferFatPtrToIntTypeMap IntTM(DL);
2701 for (GlobalVariable &GV : make_early_inc_range(Range: M.globals())) {
2702 if (GV.getAddressSpace() == AMDGPUAS::BUFFER_FAT_POINTER) {
2703 // FIXME: Use DiagnosticInfo unsupported but it requires a Function
2704 Ctx.emitError(ErrorStr: "global variables with a buffer fat pointer address "
2705 "space (7) are not supported");
2706 GV.replaceAllUsesWith(V: PoisonValue::get(T: GV.getType()));
2707 GV.eraseFromParent();
2708 Changed = true;
2709 continue;
2710 }
2711
2712 Type *VT = GV.getValueType();
2713 if (VT != StructTM.remapType(SrcTy: VT)) {
2714 // FIXME: Use DiagnosticInfo unsupported but it requires a Function
2715 Ctx.emitError(ErrorStr: "global variables that contain buffer fat pointers "
2716 "(address space 7 pointers) are unsupported. Use "
2717 "buffer resource pointers (address space 8) instead");
2718 GV.replaceAllUsesWith(V: PoisonValue::get(T: GV.getType()));
2719 GV.eraseFromParent();
2720 Changed = true;
2721 continue;
2722 }
2723 }
2724
2725 {
2726 // Collect all constant exprs and aggregates referenced by any function.
2727 SmallVector<Constant *, 8> Worklist;
2728 for (Function &F : M.functions())
2729 for (Instruction &I : instructions(F))
2730 for (Value *Op : I.operands())
2731 if (isa<ConstantExpr, ConstantAggregate>(Val: Op))
2732 Worklist.push_back(Elt: cast<Constant>(Val: Op));
2733
2734 // Recursively look for any referenced buffer pointer constants.
2735 SmallPtrSet<Constant *, 8> Visited;
2736 SetVector<Constant *> BufferFatPtrConsts;
2737 while (!Worklist.empty()) {
2738 Constant *C = Worklist.pop_back_val();
2739 if (!Visited.insert(Ptr: C).second)
2740 continue;
2741 if (isBufferFatPtrOrVector(Ty: C->getType()))
2742 BufferFatPtrConsts.insert(X: C);
2743 for (Value *Op : C->operands())
2744 if (isa<ConstantExpr, ConstantAggregate>(Val: Op))
2745 Worklist.push_back(Elt: cast<Constant>(Val: Op));
2746 }
2747
2748 // Expand all constant expressions using fat buffer pointers to
2749 // instructions.
2750 Changed |= convertUsersOfConstantsToInstructions(
2751 Consts: BufferFatPtrConsts.getArrayRef(), /*RestrictToFunc=*/nullptr,
2752 /*RemoveDeadConstants=*/false, /*IncludeSelf=*/true);
2753 }
2754
2755 StoreFatPtrsAsIntsAndExpandMemcpyVisitor MemOpsRewrite(&IntTM, DL,
2756 M.getContext());
2757 LegalizeBufferContentTypesVisitor BufferContentsTypeRewrite(
2758 DL, M.getContext(), &TM);
2759 for (Function &F : M.functions()) {
2760 bool InterfaceChange = hasFatPointerInterface(F, TypeMap: &StructTM);
2761 bool BodyChanges = containsBufferFatPointers(F, TypeMap: &StructTM);
2762 const TargetTransformInfo *TTI = GetTTI(F);
2763 ScalarEvolution *SE = GetSE(F);
2764 Changed |= MemOpsRewrite.processFunction(F, TTI, SE);
2765 if (InterfaceChange || BodyChanges) {
2766 NeedsRemap.push_back(Elt: std::make_pair(x: &F, y&: InterfaceChange));
2767 Changed |= BufferContentsTypeRewrite.processFunction(F, SE);
2768 }
2769 }
2770 if (NeedsRemap.empty())
2771 return Changed;
2772
2773 SmallVector<Function *> NeedsPostProcess;
2774 SmallVector<Function *> Intrinsics;
2775 // Keep one big map so as to memoize constants across functions.
2776 ValueToValueMapTy CloneMap;
2777 FatPtrConstMaterializer Materializer(&StructTM, CloneMap);
2778
2779 ValueMapper LowerInFuncs(CloneMap, RF_None, &StructTM, &Materializer);
2780 for (auto [F, InterfaceChange] : NeedsRemap) {
2781 Function *NewF = F;
2782 if (InterfaceChange)
2783 NewF = moveFunctionAdaptingType(
2784 OldF: F, NewTy: cast<FunctionType>(Val: StructTM.remapType(SrcTy: F->getFunctionType())),
2785 CloneMap);
2786 else
2787 makeCloneInPraceMap(F, CloneMap);
2788 LowerInFuncs.remapFunction(F&: *NewF);
2789 if (NewF->isIntrinsic())
2790 Intrinsics.push_back(Elt: NewF);
2791 else
2792 NeedsPostProcess.push_back(Elt: NewF);
2793 if (InterfaceChange) {
2794 F->replaceAllUsesWith(V: NewF);
2795 F->eraseFromParent();
2796 }
2797 Changed = true;
2798 }
2799 StructTM.clear();
2800 IntTM.clear();
2801 CloneMap.clear();
2802
2803 SplitPtrStructs Splitter(DL, M.getContext(), &TM);
2804 for (Function *F : NeedsPostProcess)
2805 Splitter.processFunction(F&: *F);
2806 for (Function *F : Intrinsics) {
2807 // use_empty() can also occur with cases like masked load, which will
2808 // have been rewritten out of the module by now but not erased.
2809 if (F->use_empty() || isRemovablePointerIntrinsic(IID: F->getIntrinsicID())) {
2810 F->eraseFromParent();
2811 } else {
2812 std::optional<Function *> NewF = Intrinsic::remangleIntrinsicFunction(F);
2813 if (NewF)
2814 F->replaceAllUsesWith(V: *NewF);
2815 }
2816 }
2817 return Changed;
2818}
2819
2820bool AMDGPULowerBufferFatPointers::runOnModule(Module &M) {
2821 TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
2822 const TargetMachine &TM = TPC.getTM<TargetMachine>();
2823 auto GetTTI = [&](Function &F) -> const TargetTransformInfo * {
2824 if (F.isDeclaration())
2825 return nullptr;
2826 return &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
2827 };
2828 auto GetSE = [&](Function &F) -> ScalarEvolution * {
2829 if (F.isDeclaration())
2830 return nullptr;
2831 return &getAnalysis<ScalarEvolutionWrapperPass>(F).getSE();
2832 };
2833 return run(M, TM, GetTTI, GetSE);
2834}
2835
2836char AMDGPULowerBufferFatPointers::ID = 0;
2837
2838char &llvm::AMDGPULowerBufferFatPointersID = AMDGPULowerBufferFatPointers::ID;
2839
2840void AMDGPULowerBufferFatPointers::getAnalysisUsage(AnalysisUsage &AU) const {
2841 AU.addRequired<TargetPassConfig>();
2842 AU.addRequired<TargetTransformInfoWrapperPass>();
2843 AU.addRequired<ScalarEvolutionWrapperPass>();
2844}
2845
2846#define PASS_DESC "Lower buffer fat pointer operations to buffer resources"
2847INITIALIZE_PASS_BEGIN(AMDGPULowerBufferFatPointers, DEBUG_TYPE, PASS_DESC,
2848 false, false)
2849INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
2850INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
2851INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass)
2852INITIALIZE_PASS_END(AMDGPULowerBufferFatPointers, DEBUG_TYPE, PASS_DESC, false,
2853 false)
2854#undef PASS_DESC
2855
2856ModulePass *llvm::createAMDGPULowerBufferFatPointersPass() {
2857 return new AMDGPULowerBufferFatPointers();
2858}
2859
2860PreservedAnalyses
2861AMDGPULowerBufferFatPointersPass::run(Module &M, ModuleAnalysisManager &MA) {
2862 auto &FA = MA.getResult<FunctionAnalysisManagerModuleProxy>(IR&: M).getManager();
2863 auto GetTTI = [&](Function &F) -> const TargetTransformInfo * {
2864 if (F.isDeclaration())
2865 return nullptr;
2866 return &FA.getResult<TargetIRAnalysis>(IR&: F);
2867 };
2868 auto GetSE = [&](Function &F) -> ScalarEvolution * {
2869 if (F.isDeclaration())
2870 return nullptr;
2871 return &FA.getResult<ScalarEvolutionAnalysis>(IR&: F);
2872 };
2873 return AMDGPULowerBufferFatPointers().run(M, TM, GetTTI, GetSE)
2874 ? PreservedAnalyses::none()
2875 : PreservedAnalyses::all();
2876}
2877