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