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