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