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 |= visitStoreImpl(OrigSI, PartType: ElemTy, AggIdxs,
1309 AggByteOff: AggByteOff + Offset.getFixedValue(),
1310 Name: Name + "." + Twine(I))
1311 .first;
1312 AggIdxs.pop_back();
1313 }
1314 return std::make_pair(x&: Changed, /*ModifiedInPlace=*/y: false);
1315 }
1316 if (auto *AT = dyn_cast<ArrayType>(Val: PartType)) {
1317 Type *ElemTy = AT->getElementType();
1318 if (!ElemTy->isSingleValueType() || !DL.typeSizeEqualsStoreSize(Ty: ElemTy) ||
1319 ElemTy->isVectorTy()) {
1320 TypeSize ElemAllocSize = DL.getTypeAllocSize(Ty: ElemTy);
1321 bool Changed = false;
1322 for (auto I : llvm::iota_range<uint32_t>(0, AT->getNumElements(),
1323 /*Inclusive=*/false)) {
1324 AggIdxs.push_back(Elt: I);
1325 Changed |=
1326 visitStoreImpl(OrigSI, PartType: ElemTy, AggIdxs,
1327 AggByteOff: AggByteOff + I * ElemAllocSize.getFixedValue(),
1328 Name: Name + Twine(I))
1329 .first;
1330 AggIdxs.pop_back();
1331 }
1332 return std::make_pair(x&: Changed, /*ModifiedInPlace=*/y: false);
1333 }
1334 }
1335
1336 Value *OrigData = OrigSI.getValueOperand();
1337 Value *NewData = OrigData;
1338
1339 bool IsAggPart = !AggIdxs.empty();
1340 if (IsAggPart)
1341 NewData = IRB.CreateExtractValue(Agg: NewData, Idxs: AggIdxs, Name);
1342
1343 Type *ArrayAsVecType = scalarArrayTypeAsVector(T: PartType);
1344 if (ArrayAsVecType != PartType) {
1345 NewData = arrayToVector(V: NewData, TargetType: ArrayAsVecType, Name);
1346 }
1347
1348 Align PartAlign = commonAlignment(A: OrigSI.getAlign(), Offset: AggByteOff);
1349 OobProperties OobProps =
1350 analyzeOobProperties(Ptr: OrigSI.getPointerOperand(), Ty: PartType, ByteOffset: AggByteOff);
1351 uint64_t MaxWidth = maxIntrinsicWidth(T: ArrayAsVecType, A: PartAlign, OobProps);
1352 Type *LegalType = legalNonAggregateForMemOp(T: ArrayAsVecType, MaxWidth);
1353 if (LegalType != ArrayAsVecType) {
1354 NewData = makeLegalNonAggregate(V: NewData, TargetType: LegalType, Name);
1355 }
1356
1357 SmallVector<VecSlice> Slices;
1358 getVecSlices(T: LegalType, MaxWidth, Slices);
1359 bool NeedToSplit = Slices.size() > 1 || IsAggPart;
1360 if (!NeedToSplit) {
1361 Type *StorableType = intrinsicTypeFor(LegalType);
1362 if (StorableType == PartType)
1363 return std::make_pair(/*Changed=*/x: false, /*ModifiedInPlace=*/y: false);
1364 NewData = IRB.CreateBitCast(V: NewData, DestTy: StorableType, Name: Name + ".storable");
1365 OrigSI.setOperand(i_nocapture: 0, Val_nocapture: NewData);
1366 return std::make_pair(/*Changed=*/x: true, /*ModifiedInPlace=*/y: true);
1367 }
1368
1369 Value *OrigPtr = OrigSI.getPointerOperand();
1370 Type *ElemType = LegalType->getScalarType();
1371 if (IsAggPart && Slices.empty())
1372 Slices.push_back(Elt: VecSlice{/*Index=*/0, /*Length=*/1});
1373 unsigned ElemBytes = DL.getTypeStoreSize(Ty: ElemType);
1374 AAMDNodes AANodes = OrigSI.getAAMetadata();
1375 for (VecSlice S : Slices) {
1376 Type *SliceType =
1377 S.Length != 1 ? FixedVectorType::get(ElementType: ElemType, NumElts: S.Length) : ElemType;
1378 int64_t ByteOffset = AggByteOff + S.Index * ElemBytes;
1379 Value *NewPtr = IRB.CreateGEP(
1380 Ty: IRB.getInt8Ty(), Ptr: OrigPtr, IdxList: IRB.getInt32(C: ByteOffset),
1381 Name: OrigPtr->getName() + ".part." + Twine(S.Index),
1382 NW: ST->hasRelaxedBufferOOBMode() ? GEPNoWrapFlags::noUnsignedWrap()
1383 : GEPNoWrapFlags::none());
1384 Value *DataSlice = extractSlice(Vec: NewData, S, Name);
1385 Type *StorableType = intrinsicTypeFor(LegalType: SliceType);
1386 DataSlice = IRB.CreateBitCast(V: DataSlice, DestTy: StorableType,
1387 Name: DataSlice->getName() + ".storable");
1388 auto *NewSI = cast<StoreInst>(Val: OrigSI.clone());
1389 NewSI->setAlignment(commonAlignment(A: OrigSI.getAlign(), Offset: ByteOffset));
1390 IRB.Insert(I: NewSI);
1391 NewSI->setOperand(i_nocapture: 0, Val_nocapture: DataSlice);
1392 NewSI->setOperand(i_nocapture: 1, Val_nocapture: NewPtr);
1393 NewSI->setAAMetadata(AANodes.adjustForAccess(Offset: ByteOffset, AccessTy: StorableType, DL));
1394 }
1395 return std::make_pair(/*Changed=*/x: true, /*ModifiedInPlace=*/y: false);
1396}
1397
1398bool LegalizeBufferContentTypesVisitor::visitStoreInst(StoreInst &SI) {
1399 if (SI.getPointerAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
1400 return false;
1401 IRB.SetInsertPoint(&SI);
1402 SmallVector<uint32_t> AggIdxs;
1403 Value *OrigData = SI.getValueOperand();
1404 auto [Changed, ModifiedInPlace] =
1405 visitStoreImpl(OrigSI&: SI, PartType: OrigData->getType(), AggIdxs, AggByteOff: 0, Name: OrigData->getName());
1406 if (Changed && !ModifiedInPlace)
1407 SI.eraseFromParent();
1408 return Changed;
1409}
1410
1411bool LegalizeBufferContentTypesVisitor::visitAddrSpaceCastInst(
1412 AddrSpaceCastInst &AI) {
1413 if (AI.getSrcAddressSpace() != AMDGPUAS::BUFFER_RESOURCE ||
1414 AI.getDestAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
1415 return false;
1416 Value *Src = AI.getPointerOperand();
1417 auto Record = ZeroBasePointerToNumRecords.find(Val: Src);
1418 if (Record != ZeroBasePointerToNumRecords.end())
1419 ZeroBasePointerToNumRecords.insert(KV: {&AI, Record->second});
1420 else
1421 ZeroBasePointerToNumRecords.insert(KV: {&AI, nullptr});
1422 return false;
1423}
1424
1425bool LegalizeBufferContentTypesVisitor::visitIntrinsicInst(IntrinsicInst &II) {
1426 if (II.getIntrinsicID() != Intrinsic::amdgcn_make_buffer_rsrc)
1427 return false;
1428 ZeroBasePointerToNumRecords.insert(KV: {&II, II.getOperand(i_nocapture: 2)});
1429 return false;
1430}
1431
1432bool LegalizeBufferContentTypesVisitor::processFunction(Function &F,
1433 ScalarEvolution *SE) {
1434 this->SE = SE;
1435 ST = &TM->getSubtarget<GCNSubtarget>(F);
1436 bool Changed = false;
1437 for (Instruction &I : make_early_inc_range(Range: instructions(F))) {
1438 Changed |= visit(I);
1439 }
1440 ZeroBasePointerToNumRecords.clear();
1441 this->SE = nullptr;
1442 return Changed;
1443}
1444
1445/// Return the ptr addrspace(8) and i32 (resource and offset parts) in a lowered
1446/// buffer fat pointer constant.
1447static std::pair<Constant *, Constant *>
1448splitLoweredFatBufferConst(Constant *C) {
1449 assert(isSplitFatPtr(C->getType()) && "Not a split fat buffer pointer");
1450 return std::make_pair(x: C->getAggregateElement(Elt: 0u), y: C->getAggregateElement(Elt: 1u));
1451}
1452
1453namespace {
1454/// Handle the remapping of ptr addrspace(7) constants.
1455class FatPtrConstMaterializer final : public ValueMaterializer {
1456 BufferFatPtrToStructTypeMap *TypeMap;
1457 // An internal mapper that is used to recurse into the arguments of constants.
1458 // While the documentation for `ValueMapper` specifies not to use it
1459 // recursively, examination of the logic in mapValue() shows that it can
1460 // safely be used recursively when handling constants, like it does in its own
1461 // logic.
1462 ValueMapper InternalMapper;
1463
1464 Constant *materializeBufferFatPtrConst(Constant *C);
1465
1466public:
1467 // UnderlyingMap is the value map this materializer will be filling.
1468 FatPtrConstMaterializer(BufferFatPtrToStructTypeMap *TypeMap,
1469 ValueToValueMapTy &UnderlyingMap)
1470 : TypeMap(TypeMap),
1471 InternalMapper(UnderlyingMap, RF_None, TypeMap, this) {}
1472 ~FatPtrConstMaterializer() = default;
1473
1474 Value *materialize(Value *V) override;
1475};
1476} // namespace
1477
1478Constant *FatPtrConstMaterializer::materializeBufferFatPtrConst(Constant *C) {
1479 Type *SrcTy = C->getType();
1480 auto *NewTy = dyn_cast<StructType>(Val: TypeMap->remapType(SrcTy));
1481 if (C->isNullValue())
1482 return ConstantAggregateZero::getNullValue(Ty: NewTy);
1483 if (isa<PoisonValue>(Val: C)) {
1484 return ConstantStruct::get(T: NewTy,
1485 V: {PoisonValue::get(T: NewTy->getElementType(N: 0)),
1486 PoisonValue::get(T: NewTy->getElementType(N: 1))});
1487 }
1488 if (isa<UndefValue>(Val: C)) {
1489 return ConstantStruct::get(T: NewTy,
1490 V: {UndefValue::get(T: NewTy->getElementType(N: 0)),
1491 UndefValue::get(T: NewTy->getElementType(N: 1))});
1492 }
1493
1494 if (auto *VC = dyn_cast<ConstantVector>(Val: C)) {
1495 if (Constant *S = VC->getSplatValue()) {
1496 Constant *NewS = InternalMapper.mapConstant(C: *S);
1497 if (!NewS)
1498 return nullptr;
1499 auto [Rsrc, Off] = splitLoweredFatBufferConst(C: NewS);
1500 auto EC = VC->getType()->getElementCount();
1501 return ConstantStruct::get(T: NewTy, V: {ConstantVector::getSplat(EC, Elt: Rsrc),
1502 ConstantVector::getSplat(EC, Elt: Off)});
1503 }
1504 SmallVector<Constant *> Rsrcs;
1505 SmallVector<Constant *> Offs;
1506 for (Value *Op : VC->operand_values()) {
1507 auto *NewOp = dyn_cast_or_null<Constant>(Val: InternalMapper.mapValue(V: *Op));
1508 if (!NewOp)
1509 return nullptr;
1510 auto [Rsrc, Off] = splitLoweredFatBufferConst(C: NewOp);
1511 Rsrcs.push_back(Elt: Rsrc);
1512 Offs.push_back(Elt: Off);
1513 }
1514 Constant *RsrcVec = ConstantVector::get(V: Rsrcs);
1515 Constant *OffVec = ConstantVector::get(V: Offs);
1516 return ConstantStruct::get(T: NewTy, V: {RsrcVec, OffVec});
1517 }
1518
1519 if (isa<GlobalValue>(Val: C))
1520 reportFatalUsageError(reason: "global values containing ptr addrspace(7) (buffer "
1521 "fat pointer) values are not supported");
1522
1523 if (isa<ConstantExpr>(Val: C))
1524 reportFatalUsageError(
1525 reason: "constant exprs containing ptr addrspace(7) (buffer "
1526 "fat pointer) values should have been expanded earlier");
1527
1528 return nullptr;
1529}
1530
1531Value *FatPtrConstMaterializer::materialize(Value *V) {
1532 Constant *C = dyn_cast<Constant>(Val: V);
1533 if (!C)
1534 return nullptr;
1535 // Structs and other types that happen to contain fat pointers get remapped
1536 // by the mapValue() logic.
1537 if (!isBufferFatPtrConst(C))
1538 return nullptr;
1539 return materializeBufferFatPtrConst(C);
1540}
1541
1542using PtrParts = std::pair<Value *, Value *>;
1543namespace {
1544// The visitor returns the resource and offset parts for an instruction if they
1545// can be computed, or (nullptr, nullptr) for cases that don't have a meaningful
1546// value mapping.
1547class SplitPtrStructs : public InstVisitor<SplitPtrStructs, PtrParts> {
1548 ValueToValueMapTy RsrcParts;
1549 ValueToValueMapTy OffParts;
1550
1551 // Track instructions that have been rewritten into a user of the component
1552 // parts of their ptr addrspace(7) input. Instructions that produced
1553 // ptr addrspace(7) parts should **not** be RAUW'd before being added to this
1554 // set, as that replacement will be handled in a post-visit step. However,
1555 // instructions that yield values that aren't fat pointers (ex. ptrtoint)
1556 // should RAUW themselves with new instructions that use the split parts
1557 // of their arguments during processing.
1558 DenseSet<Instruction *> SplitUsers;
1559
1560 // Nodes that need a second look once we've computed the parts for all other
1561 // instructions to see if, for example, we really need to phi on the resource
1562 // part.
1563 SmallVector<Instruction *> Conditionals;
1564 // Temporary instructions produced while lowering conditionals that should be
1565 // killed.
1566 SmallVector<Instruction *> ConditionalTemps;
1567
1568 // Subtarget info, needed for determining what cache control bits to set.
1569 const TargetMachine *TM;
1570 const GCNSubtarget *ST = nullptr;
1571
1572 IRBuilder<InstSimplifyFolder> IRB;
1573
1574 // Copy metadata between instructions if applicable.
1575 void copyMetadata(Value *Dest, Value *Src);
1576
1577 // Get the resource and offset parts of the value V, inserting appropriate
1578 // extractvalue calls if needed.
1579 PtrParts getPtrParts(Value *V);
1580
1581 // Given an instruction that could produce multiple resource parts (a PHI or
1582 // select), collect the set of possible instructions that could have provided
1583 // its resource parts that it could have (the `Roots`) and the set of
1584 // conditional instructions visited during the search (`Seen`). If, after
1585 // removing the root of the search from `Seen` and `Roots`, `Seen` is a subset
1586 // of `Roots` and `Roots - Seen` contains one element, the resource part of
1587 // that element can replace the resource part of all other elements in `Seen`.
1588 void getPossibleRsrcRoots(Instruction *I, SmallPtrSetImpl<Value *> &Roots,
1589 SmallPtrSetImpl<Value *> &Seen);
1590 void processConditionals();
1591
1592 // If an instruction hav been split into resource and offset parts,
1593 // delete that instruction. If any of its uses have not themselves been split
1594 // into parts (for example, an insertvalue), construct the structure
1595 // that the type rewrites declared should be produced by the dying instruction
1596 // and use that.
1597 // Also, kill the temporary extractvalue operations produced by the two-stage
1598 // lowering of PHIs and conditionals.
1599 void killAndReplaceSplitInstructions(SmallVectorImpl<Instruction *> &Origs);
1600
1601 void setAlign(CallInst *Intr, Align A, unsigned RsrcArgIdx);
1602 void insertPreMemOpFence(AtomicOrdering Order, SyncScope::ID SSID);
1603 void insertPostMemOpFence(AtomicOrdering Order, SyncScope::ID SSID);
1604 Value *handleMemoryInst(Instruction *I, Value *Arg, Value *Ptr, Type *Ty,
1605 Align Alignment, AtomicOrdering Order,
1606 bool IsVolatile, SyncScope::ID SSID);
1607
1608public:
1609 SplitPtrStructs(Module &M, const TargetMachine *TM)
1610 : TM(TM), IRB(M, InstSimplifyFolder(M.getDataLayout())) {}
1611
1612 void processFunction(Function &F);
1613
1614 PtrParts visitInstruction(Instruction &I);
1615 PtrParts visitLoadInst(LoadInst &LI);
1616 PtrParts visitStoreInst(StoreInst &SI);
1617 PtrParts visitAtomicRMWInst(AtomicRMWInst &AI);
1618 PtrParts visitAtomicCmpXchgInst(AtomicCmpXchgInst &AI);
1619 PtrParts visitGetElementPtrInst(GetElementPtrInst &GEP);
1620
1621 PtrParts visitPtrToAddrInst(PtrToAddrInst &PA);
1622 PtrParts visitPtrToIntInst(PtrToIntInst &PI);
1623 PtrParts visitIntToPtrInst(IntToPtrInst &IP);
1624 PtrParts visitAddrSpaceCastInst(AddrSpaceCastInst &I);
1625 PtrParts visitICmpInst(ICmpInst &Cmp);
1626 PtrParts visitFreezeInst(FreezeInst &I);
1627
1628 PtrParts visitExtractElementInst(ExtractElementInst &I);
1629 PtrParts visitInsertElementInst(InsertElementInst &I);
1630 PtrParts visitShuffleVectorInst(ShuffleVectorInst &I);
1631
1632 PtrParts visitPHINode(PHINode &PHI);
1633 PtrParts visitSelectInst(SelectInst &SI);
1634
1635 PtrParts visitIntrinsicInst(IntrinsicInst &II);
1636};
1637} // namespace
1638
1639void SplitPtrStructs::copyMetadata(Value *Dest, Value *Src) {
1640 auto *DestI = dyn_cast<Instruction>(Val: Dest);
1641 auto *SrcI = dyn_cast<Instruction>(Val: Src);
1642
1643 if (!DestI || !SrcI)
1644 return;
1645
1646 DestI->copyMetadata(SrcInst: *SrcI);
1647}
1648
1649PtrParts SplitPtrStructs::getPtrParts(Value *V) {
1650 assert(isSplitFatPtr(V->getType()) && "it's not meaningful to get the parts "
1651 "of something that wasn't rewritten");
1652 auto *RsrcEntry = &RsrcParts[V];
1653 auto *OffEntry = &OffParts[V];
1654 if (*RsrcEntry && *OffEntry)
1655 return {*RsrcEntry, *OffEntry};
1656
1657 if (auto *C = dyn_cast<Constant>(Val: V)) {
1658 auto [Rsrc, Off] = splitLoweredFatBufferConst(C);
1659 return {*RsrcEntry = Rsrc, *OffEntry = Off};
1660 }
1661
1662 IRBuilder<InstSimplifyFolder>::InsertPointGuard Guard(IRB);
1663 if (auto *I = dyn_cast<Instruction>(Val: V)) {
1664 LLVM_DEBUG(dbgs() << "Recursing to split parts of " << *I << "\n");
1665 auto [Rsrc, Off] = visit(I&: *I);
1666 if (Rsrc && Off)
1667 return {*RsrcEntry = Rsrc, *OffEntry = Off};
1668 // We'll be creating the new values after the relevant instruction.
1669 // This instruction generates a value and so isn't a terminator.
1670 IRB.SetInsertPoint(*I->getInsertionPointAfterDef());
1671 IRB.SetCurrentDebugLocation(I->getDebugLoc());
1672 } else if (auto *A = dyn_cast<Argument>(Val: V)) {
1673 IRB.SetInsertPointPastAllocas(A->getParent());
1674 IRB.SetCurrentDebugLocation(DebugLoc());
1675 }
1676 Value *Rsrc = IRB.CreateExtractValue(Agg: V, Idxs: 0, Name: V->getName() + ".rsrc");
1677 Value *Off = IRB.CreateExtractValue(Agg: V, Idxs: 1, Name: V->getName() + ".off");
1678 return {*RsrcEntry = Rsrc, *OffEntry = Off};
1679}
1680
1681/// Returns the instruction that defines the resource part of the value V.
1682/// Note that this is not getUnderlyingObject(), since that looks through
1683/// operations like ptrmask which might modify the resource part.
1684///
1685/// We can limit ourselves to just looking through GEPs followed by looking
1686/// through addrspacecasts because only those two operations preserve the
1687/// resource part, and because operations on an `addrspace(8)` (which is the
1688/// legal input to this addrspacecast) would produce a different resource part.
1689static Value *rsrcPartRoot(Value *V) {
1690 while (auto *GEP = dyn_cast<GEPOperator>(Val: V))
1691 V = GEP->getPointerOperand();
1692 while (auto *ASC = dyn_cast<AddrSpaceCastOperator>(Val: V))
1693 V = ASC->getPointerOperand();
1694 return V;
1695}
1696
1697void SplitPtrStructs::getPossibleRsrcRoots(Instruction *I,
1698 SmallPtrSetImpl<Value *> &Roots,
1699 SmallPtrSetImpl<Value *> &Seen) {
1700 if (auto *PHI = dyn_cast<PHINode>(Val: I)) {
1701 if (!Seen.insert(Ptr: I).second)
1702 return;
1703 for (Value *In : PHI->incoming_values()) {
1704 In = rsrcPartRoot(V: In);
1705 Roots.insert(Ptr: In);
1706 if (isa<PHINode, SelectInst>(Val: In))
1707 getPossibleRsrcRoots(I: cast<Instruction>(Val: In), Roots, Seen);
1708 }
1709 } else if (auto *SI = dyn_cast<SelectInst>(Val: I)) {
1710 if (!Seen.insert(Ptr: SI).second)
1711 return;
1712 Value *TrueVal = rsrcPartRoot(V: SI->getTrueValue());
1713 Value *FalseVal = rsrcPartRoot(V: SI->getFalseValue());
1714 Roots.insert(Ptr: TrueVal);
1715 Roots.insert(Ptr: FalseVal);
1716 if (isa<PHINode, SelectInst>(Val: TrueVal))
1717 getPossibleRsrcRoots(I: cast<Instruction>(Val: TrueVal), Roots, Seen);
1718 if (isa<PHINode, SelectInst>(Val: FalseVal))
1719 getPossibleRsrcRoots(I: cast<Instruction>(Val: FalseVal), Roots, Seen);
1720 } else {
1721 llvm_unreachable("getPossibleRsrcParts() only works on phi and select");
1722 }
1723}
1724
1725void SplitPtrStructs::processConditionals() {
1726 SmallDenseMap<Value *, Value *> FoundRsrcs;
1727 SmallPtrSet<Value *, 4> Roots;
1728 SmallPtrSet<Value *, 4> Seen;
1729 for (Instruction *I : Conditionals) {
1730 // These have to exist by now because we've visited these nodes.
1731 Value *Rsrc = RsrcParts[I];
1732 Value *Off = OffParts[I];
1733 assert(Rsrc && Off && "must have visited conditionals by now");
1734
1735 std::optional<Value *> MaybeRsrc;
1736 auto MaybeFoundRsrc = FoundRsrcs.find(Val: I);
1737 if (MaybeFoundRsrc != FoundRsrcs.end()) {
1738 MaybeRsrc = MaybeFoundRsrc->second;
1739 } else {
1740 IRBuilder<InstSimplifyFolder>::InsertPointGuard Guard(IRB);
1741 Roots.clear();
1742 Seen.clear();
1743 getPossibleRsrcRoots(I, Roots, Seen);
1744 LLVM_DEBUG(dbgs() << "Processing conditional: " << *I << "\n");
1745#ifndef NDEBUG
1746 for (Value *V : Roots)
1747 LLVM_DEBUG(dbgs() << "Root: " << *V << "\n");
1748 for (Value *V : Seen)
1749 LLVM_DEBUG(dbgs() << "Seen: " << *V << "\n");
1750#endif
1751 // If we are our own possible root, then we shouldn't block our
1752 // replacement with a valid incoming value.
1753 Roots.erase(Ptr: I);
1754 // We don't want to block the optimization for conditionals that don't
1755 // refer to themselves but did see themselves during the traversal.
1756 Seen.erase(Ptr: I);
1757
1758 if (set_is_subset(S1: Seen, S2: Roots)) {
1759 auto Diff = set_difference(S1: Roots, S2: Seen);
1760 if (Diff.size() == 1) {
1761 Value *RootVal = *Diff.begin();
1762 // Handle the case where previous loops already looked through
1763 // an addrspacecast.
1764 if (isSplitFatPtr(Ty: RootVal->getType()))
1765 MaybeRsrc = getPtrParts(V: RootVal).first;
1766 else
1767 MaybeRsrc = RootVal;
1768 }
1769 }
1770 }
1771
1772 if (auto *PHI = dyn_cast<PHINode>(Val: I)) {
1773 Value *NewRsrc;
1774 StructType *PHITy = cast<StructType>(Val: PHI->getType());
1775 IRB.SetInsertPoint(*PHI->getInsertionPointAfterDef());
1776 IRB.SetCurrentDebugLocation(PHI->getDebugLoc());
1777 if (MaybeRsrc) {
1778 NewRsrc = *MaybeRsrc;
1779 } else {
1780 Type *RsrcTy = PHITy->getElementType(N: 0);
1781 auto *RsrcPHI = IRB.CreatePHI(Ty: RsrcTy, NumReservedValues: PHI->getNumIncomingValues());
1782 RsrcPHI->takeName(V: Rsrc);
1783 for (auto [V, BB] : llvm::zip(t: PHI->incoming_values(), u: PHI->blocks())) {
1784 Value *VRsrc = getPtrParts(V).first;
1785 RsrcPHI->addIncoming(V: VRsrc, BB);
1786 }
1787 copyMetadata(Dest: RsrcPHI, Src: PHI);
1788 NewRsrc = RsrcPHI;
1789 }
1790
1791 Type *OffTy = PHITy->getElementType(N: 1);
1792 auto *NewOff = IRB.CreatePHI(Ty: OffTy, NumReservedValues: PHI->getNumIncomingValues());
1793 NewOff->takeName(V: Off);
1794 for (auto [V, BB] : llvm::zip(t: PHI->incoming_values(), u: PHI->blocks())) {
1795 assert(OffParts.count(V) && "An offset part had to be created by now");
1796 Value *VOff = getPtrParts(V).second;
1797 NewOff->addIncoming(V: VOff, BB);
1798 }
1799 copyMetadata(Dest: NewOff, Src: PHI);
1800
1801 // Note: We don't eraseFromParent() the temporaries because we don't want
1802 // to put the corrections maps in an inconstent state. That'll be handed
1803 // during the rest of the killing. Also, `ValueToValueMapTy` guarantees
1804 // that references in that map will be updated as well.
1805 // Note that if the temporary instruction got `InstSimplify`'d away, it
1806 // might be something like a block argument.
1807 if (auto *RsrcInst = dyn_cast<Instruction>(Val: Rsrc)) {
1808 ConditionalTemps.push_back(Elt: RsrcInst);
1809 RsrcInst->replaceAllUsesWith(V: NewRsrc);
1810 }
1811 if (auto *OffInst = dyn_cast<Instruction>(Val: Off)) {
1812 ConditionalTemps.push_back(Elt: OffInst);
1813 OffInst->replaceAllUsesWith(V: NewOff);
1814 }
1815
1816 // Save on recomputing the cycle traversals in known-root cases.
1817 if (MaybeRsrc)
1818 for (Value *V : Seen)
1819 FoundRsrcs[V] = NewRsrc;
1820 } else if (isa<SelectInst>(Val: I)) {
1821 if (MaybeRsrc) {
1822 if (auto *RsrcInst = dyn_cast<Instruction>(Val: Rsrc)) {
1823 // Guard against conditionals that were already folded away.
1824 if (RsrcInst != *MaybeRsrc) {
1825 ConditionalTemps.push_back(Elt: RsrcInst);
1826 RsrcInst->replaceAllUsesWith(V: *MaybeRsrc);
1827 }
1828 }
1829 for (Value *V : Seen)
1830 FoundRsrcs[V] = *MaybeRsrc;
1831 }
1832 } else {
1833 llvm_unreachable("Only PHIs and selects go in the conditionals list");
1834 }
1835 }
1836}
1837
1838void SplitPtrStructs::killAndReplaceSplitInstructions(
1839 SmallVectorImpl<Instruction *> &Origs) {
1840 for (Instruction *I : ConditionalTemps)
1841 I->eraseFromParent();
1842
1843 for (Instruction *I : Origs) {
1844 if (!SplitUsers.contains(V: I))
1845 continue;
1846
1847 SmallVector<DbgVariableRecord *> Dbgs;
1848 findDbgValues(V: I, DbgVariableRecords&: Dbgs);
1849 for (DbgVariableRecord *Dbg : Dbgs) {
1850 auto &DL = I->getDataLayout();
1851 assert(isSplitFatPtr(I->getType()) &&
1852 "We should've RAUW'd away loads, stores, etc. at this point");
1853 DbgVariableRecord *OffDbg = Dbg->clone();
1854 auto [Rsrc, Off] = getPtrParts(V: I);
1855
1856 int64_t RsrcSz = DL.getTypeSizeInBits(Ty: Rsrc->getType());
1857 int64_t OffSz = DL.getTypeSizeInBits(Ty: Off->getType());
1858
1859 std::optional<DIExpression *> RsrcExpr =
1860 DIExpression::createFragmentExpression(Expr: Dbg->getExpression(), OffsetInBits: 0,
1861 SizeInBits: RsrcSz);
1862 std::optional<DIExpression *> OffExpr =
1863 DIExpression::createFragmentExpression(Expr: Dbg->getExpression(), OffsetInBits: RsrcSz,
1864 SizeInBits: OffSz);
1865 if (OffExpr) {
1866 OffDbg->setExpression(*OffExpr);
1867 OffDbg->replaceVariableLocationOp(OldValue: I, NewValue: Off);
1868 OffDbg->insertBefore(InsertBefore: Dbg);
1869 } else {
1870 OffDbg->eraseFromParent();
1871 }
1872 if (RsrcExpr) {
1873 Dbg->setExpression(*RsrcExpr);
1874 Dbg->replaceVariableLocationOp(OldValue: I, NewValue: Rsrc);
1875 } else {
1876 Dbg->replaceVariableLocationOp(OldValue: I, NewValue: PoisonValue::get(T: I->getType()));
1877 }
1878 }
1879
1880 Value *Poison = PoisonValue::get(T: I->getType());
1881 I->replaceUsesWithIf(New: Poison, ShouldReplace: [&](const Use &U) -> bool {
1882 if (const auto *UI = dyn_cast<Instruction>(Val: U.getUser()))
1883 return SplitUsers.contains(V: UI);
1884 return false;
1885 });
1886
1887 if (I->use_empty()) {
1888 I->eraseFromParent();
1889 continue;
1890 }
1891 IRB.SetInsertPoint(*I->getInsertionPointAfterDef());
1892 IRB.SetCurrentDebugLocation(I->getDebugLoc());
1893 auto [Rsrc, Off] = getPtrParts(V: I);
1894 Value *Struct = PoisonValue::get(T: I->getType());
1895 Struct = IRB.CreateInsertValue(Agg: Struct, Val: Rsrc, Idxs: 0);
1896 Struct = IRB.CreateInsertValue(Agg: Struct, Val: Off, Idxs: 1);
1897 copyMetadata(Dest: Struct, Src: I);
1898 Struct->takeName(V: I);
1899 I->replaceAllUsesWith(V: Struct);
1900 I->eraseFromParent();
1901 }
1902}
1903
1904void SplitPtrStructs::setAlign(CallInst *Intr, Align A, unsigned RsrcArgIdx) {
1905 LLVMContext &Ctx = Intr->getContext();
1906 Intr->addParamAttr(ArgNo: RsrcArgIdx, Attr: Attribute::getWithAlignment(Context&: Ctx, Alignment: A));
1907}
1908
1909void SplitPtrStructs::insertPreMemOpFence(AtomicOrdering Order,
1910 SyncScope::ID SSID) {
1911 switch (Order) {
1912 case AtomicOrdering::Release:
1913 case AtomicOrdering::AcquireRelease:
1914 case AtomicOrdering::SequentiallyConsistent:
1915 IRB.CreateFence(Ordering: AtomicOrdering::Release, SSID);
1916 break;
1917 default:
1918 break;
1919 }
1920}
1921
1922void SplitPtrStructs::insertPostMemOpFence(AtomicOrdering Order,
1923 SyncScope::ID SSID) {
1924 switch (Order) {
1925 case AtomicOrdering::Acquire:
1926 case AtomicOrdering::AcquireRelease:
1927 case AtomicOrdering::SequentiallyConsistent:
1928 IRB.CreateFence(Ordering: AtomicOrdering::Acquire, SSID);
1929 break;
1930 default:
1931 break;
1932 }
1933}
1934
1935Value *SplitPtrStructs::handleMemoryInst(Instruction *I, Value *Arg, Value *Ptr,
1936 Type *Ty, Align Alignment,
1937 AtomicOrdering Order, bool IsVolatile,
1938 SyncScope::ID SSID) {
1939 IRB.SetInsertPoint(I);
1940
1941 auto [Rsrc, Off] = getPtrParts(V: Ptr);
1942 SmallVector<Value *, 5> Args;
1943 if (Arg)
1944 Args.push_back(Elt: Arg);
1945 Args.push_back(Elt: Rsrc);
1946 Args.push_back(Elt: Off);
1947 insertPreMemOpFence(Order, SSID);
1948 // soffset is always 0 for these cases, where we always want any offset to be
1949 // part of bounds checking and we don't know which parts of the GEPs is
1950 // uniform.
1951 Args.push_back(Elt: IRB.getInt32(C: 0));
1952
1953 uint32_t Aux = 0;
1954 if (IsVolatile)
1955 Aux |= AMDGPU::CPol::VOLATILE;
1956 Args.push_back(Elt: IRB.getInt32(C: Aux));
1957
1958 Intrinsic::ID IID = Intrinsic::not_intrinsic;
1959 if (isa<LoadInst>(Val: I))
1960 IID = Order == AtomicOrdering::NotAtomic
1961 ? Intrinsic::amdgcn_raw_ptr_buffer_load
1962 : Intrinsic::amdgcn_raw_ptr_atomic_buffer_load;
1963 else if (isa<StoreInst>(Val: I))
1964 IID = Intrinsic::amdgcn_raw_ptr_buffer_store;
1965 else if (auto *RMW = dyn_cast<AtomicRMWInst>(Val: I)) {
1966 switch (RMW->getOperation()) {
1967 case AtomicRMWInst::Xchg:
1968 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_swap;
1969 break;
1970 case AtomicRMWInst::Add:
1971 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_add;
1972 break;
1973 case AtomicRMWInst::Sub:
1974 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_sub;
1975 break;
1976 case AtomicRMWInst::And:
1977 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_and;
1978 break;
1979 case AtomicRMWInst::Or:
1980 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_or;
1981 break;
1982 case AtomicRMWInst::Xor:
1983 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_xor;
1984 break;
1985 case AtomicRMWInst::Max:
1986 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_smax;
1987 break;
1988 case AtomicRMWInst::Min:
1989 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_smin;
1990 break;
1991 case AtomicRMWInst::UMax:
1992 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_umax;
1993 break;
1994 case AtomicRMWInst::UMin:
1995 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_umin;
1996 break;
1997 case AtomicRMWInst::FAdd:
1998 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_fadd;
1999 break;
2000 case AtomicRMWInst::FMax:
2001 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_fmax;
2002 break;
2003 case AtomicRMWInst::FMin:
2004 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_fmin;
2005 break;
2006 case AtomicRMWInst::USubCond:
2007 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_cond_sub_u32;
2008 break;
2009 case AtomicRMWInst::USubSat:
2010 IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_sub_clamp_u32;
2011 break;
2012 case AtomicRMWInst::FSub: {
2013 reportFatalUsageError(
2014 reason: "atomic floating point subtraction not supported for "
2015 "buffer resources and should've been expanded away");
2016 break;
2017 }
2018 case AtomicRMWInst::FMaximum: {
2019 reportFatalUsageError(
2020 reason: "atomic floating point fmaximum not supported for "
2021 "buffer resources and should've been expanded away");
2022 break;
2023 }
2024 case AtomicRMWInst::FMinimum: {
2025 reportFatalUsageError(
2026 reason: "atomic floating point fminimum not supported for "
2027 "buffer resources and should've been expanded away");
2028 break;
2029 }
2030 case AtomicRMWInst::FMaximumNum: {
2031 reportFatalUsageError(
2032 reason: "atomic floating point fmaximumnum not supported for "
2033 "buffer resources and should've been expanded away");
2034 break;
2035 }
2036 case AtomicRMWInst::FMinimumNum: {
2037 reportFatalUsageError(
2038 reason: "atomic floating point fminimumnum not supported for "
2039 "buffer resources and should've been expanded away");
2040 break;
2041 }
2042 case AtomicRMWInst::Nand:
2043 reportFatalUsageError(
2044 reason: "atomic nand not supported for buffer resources and "
2045 "should've been expanded away");
2046 break;
2047 case AtomicRMWInst::UIncWrap:
2048 case AtomicRMWInst::UDecWrap:
2049 reportFatalUsageError(
2050 reason: "wrapping increment/decrement not supported for "
2051 "buffer resources and should've been expanded away");
2052 break;
2053 case AtomicRMWInst::BAD_BINOP:
2054 llvm_unreachable("Not sure how we got a bad binop");
2055 }
2056 }
2057
2058 CallInst *Call = IRB.CreateIntrinsicWithoutFolding(ID: IID, OverloadTypes: Ty, Args);
2059 copyMetadata(Dest: Call, Src: I);
2060 setAlign(Intr: Call, A: Alignment, RsrcArgIdx: Arg ? 1 : 0);
2061 Call->takeName(V: I);
2062
2063 insertPostMemOpFence(Order, SSID);
2064 // The "no moving p7 directly" rewrites ensure that this load or store won't
2065 // itself need to be split into parts.
2066 SplitUsers.insert(V: I);
2067 I->replaceAllUsesWith(V: Call);
2068 return Call;
2069}
2070
2071PtrParts SplitPtrStructs::visitInstruction(Instruction &I) {
2072 return {nullptr, nullptr};
2073}
2074
2075PtrParts SplitPtrStructs::visitLoadInst(LoadInst &LI) {
2076 if (!isSplitFatPtr(Ty: LI.getPointerOperandType()))
2077 return {nullptr, nullptr};
2078 handleMemoryInst(I: &LI, Arg: nullptr, Ptr: LI.getPointerOperand(), Ty: LI.getType(),
2079 Alignment: LI.getAlign(), Order: LI.getOrdering(), IsVolatile: LI.isVolatile(),
2080 SSID: LI.getSyncScopeID());
2081 return {nullptr, nullptr};
2082}
2083
2084PtrParts SplitPtrStructs::visitStoreInst(StoreInst &SI) {
2085 if (!isSplitFatPtr(Ty: SI.getPointerOperandType()))
2086 return {nullptr, nullptr};
2087 Value *Arg = SI.getValueOperand();
2088 handleMemoryInst(I: &SI, Arg, Ptr: SI.getPointerOperand(), Ty: Arg->getType(),
2089 Alignment: SI.getAlign(), Order: SI.getOrdering(), IsVolatile: SI.isVolatile(),
2090 SSID: SI.getSyncScopeID());
2091 return {nullptr, nullptr};
2092}
2093
2094PtrParts SplitPtrStructs::visitAtomicRMWInst(AtomicRMWInst &AI) {
2095 if (!isSplitFatPtr(Ty: AI.getPointerOperand()->getType()))
2096 return {nullptr, nullptr};
2097 Value *Arg = AI.getValOperand();
2098 handleMemoryInst(I: &AI, Arg, Ptr: AI.getPointerOperand(), Ty: Arg->getType(),
2099 Alignment: AI.getAlign(), Order: AI.getOrdering(), IsVolatile: AI.isVolatile(),
2100 SSID: AI.getSyncScopeID());
2101 return {nullptr, nullptr};
2102}
2103
2104// Unlike load, store, and RMW, cmpxchg needs special handling to account
2105// for the boolean argument.
2106PtrParts SplitPtrStructs::visitAtomicCmpXchgInst(AtomicCmpXchgInst &AI) {
2107 Value *Ptr = AI.getPointerOperand();
2108 if (!isSplitFatPtr(Ty: Ptr->getType()))
2109 return {nullptr, nullptr};
2110 IRB.SetInsertPoint(&AI);
2111
2112 Type *Ty = AI.getNewValOperand()->getType();
2113 AtomicOrdering Order = AI.getMergedOrdering();
2114 SyncScope::ID SSID = AI.getSyncScopeID();
2115 bool IsNonTemporal = AI.getMetadata(KindID: LLVMContext::MD_nontemporal);
2116
2117 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2118 insertPreMemOpFence(Order, SSID);
2119
2120 uint32_t Aux = 0;
2121 if (IsNonTemporal)
2122 Aux |= AMDGPU::CPol::SLC;
2123 if (AI.isVolatile())
2124 Aux |= AMDGPU::CPol::VOLATILE;
2125 CallInst *Call = IRB.CreateIntrinsicWithoutFolding(
2126 ID: Intrinsic::amdgcn_raw_ptr_buffer_atomic_cmpswap, OverloadTypes: Ty,
2127 Args: {AI.getNewValOperand(), AI.getCompareOperand(), Rsrc, Off,
2128 IRB.getInt32(C: 0), IRB.getInt32(C: Aux)});
2129 copyMetadata(Dest: Call, Src: &AI);
2130 setAlign(Intr: Call, A: AI.getAlign(), RsrcArgIdx: 2);
2131 Call->takeName(V: &AI);
2132 insertPostMemOpFence(Order, SSID);
2133
2134 Value *Res = PoisonValue::get(T: AI.getType());
2135 Res = IRB.CreateInsertValue(Agg: Res, Val: Call, Idxs: 0);
2136 Value *Succeeded = IRB.CreateICmpEQ(LHS: Call, RHS: AI.getCompareOperand());
2137 Res = IRB.CreateInsertValue(Agg: Res, Val: Succeeded, Idxs: 1);
2138 SplitUsers.insert(V: &AI);
2139 AI.replaceAllUsesWith(V: Res);
2140 return {nullptr, nullptr};
2141}
2142
2143PtrParts SplitPtrStructs::visitGetElementPtrInst(GetElementPtrInst &GEP) {
2144 using namespace llvm::PatternMatch;
2145 Value *Ptr = GEP.getPointerOperand();
2146 if (!isSplitFatPtr(Ty: Ptr->getType()))
2147 return {nullptr, nullptr};
2148 IRB.SetInsertPoint(&GEP);
2149
2150 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2151 const DataLayout &DL = GEP.getDataLayout();
2152 bool IsNUW = GEP.hasNoUnsignedWrap();
2153 bool IsNUSW = GEP.hasNoUnsignedSignedWrap();
2154
2155 StructType *ResTy = cast<StructType>(Val: GEP.getType());
2156 Type *ResRsrcTy = ResTy->getElementType(N: 0);
2157 VectorType *ResRsrcVecTy = dyn_cast<VectorType>(Val: ResRsrcTy);
2158 bool BroadcastsPtr = ResRsrcVecTy && !isa<VectorType>(Val: Off->getType());
2159
2160 // In order to call emitGEPOffset() and thus not have to reimplement it,
2161 // we need the GEP result to have ptr addrspace(7) type.
2162 Type *FatPtrTy =
2163 ResRsrcTy->getWithNewType(EltTy: IRB.getPtrTy(AddrSpace: AMDGPUAS::BUFFER_FAT_POINTER));
2164 GEP.mutateType(Ty: FatPtrTy);
2165 Value *OffAccum = emitGEPOffset(Builder: &IRB, DL, GEP: &GEP);
2166 GEP.mutateType(Ty: ResTy);
2167
2168 if (BroadcastsPtr) {
2169 Rsrc = IRB.CreateVectorSplat(EC: ResRsrcVecTy->getElementCount(), V: Rsrc,
2170 Name: Rsrc->getName());
2171 Off = IRB.CreateVectorSplat(EC: ResRsrcVecTy->getElementCount(), V: Off,
2172 Name: Off->getName());
2173 }
2174 if (match(V: OffAccum, P: m_Zero())) { // Constant-zero offset
2175 SplitUsers.insert(V: &GEP);
2176 return {Rsrc, Off};
2177 }
2178
2179 bool HasNonNegativeOff = false;
2180 if (auto *CI = dyn_cast<ConstantInt>(Val: OffAccum)) {
2181 HasNonNegativeOff = !CI->isNegative();
2182 }
2183 Value *NewOff;
2184 if (match(V: Off, P: m_Zero())) {
2185 NewOff = OffAccum;
2186 } else {
2187 NewOff = IRB.CreateAdd(LHS: Off, RHS: OffAccum, Name: "",
2188 /*hasNUW=*/HasNUW: IsNUW || (IsNUSW && HasNonNegativeOff),
2189 /*hasNSW=*/HasNSW: false);
2190 }
2191 copyMetadata(Dest: NewOff, Src: &GEP);
2192 NewOff->takeName(V: &GEP);
2193 SplitUsers.insert(V: &GEP);
2194 return {Rsrc, NewOff};
2195}
2196
2197PtrParts SplitPtrStructs::visitPtrToIntInst(PtrToIntInst &PI) {
2198 Value *Ptr = PI.getPointerOperand();
2199 if (!isSplitFatPtr(Ty: Ptr->getType()))
2200 return {nullptr, nullptr};
2201 IRB.SetInsertPoint(&PI);
2202
2203 Type *ResTy = PI.getType();
2204 unsigned Width = ResTy->getScalarSizeInBits();
2205
2206 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2207 const DataLayout &DL = PI.getDataLayout();
2208 unsigned FatPtrWidth = DL.getPointerSizeInBits(AS: AMDGPUAS::BUFFER_FAT_POINTER);
2209
2210 Value *Res;
2211 if (Width <= BufferOffsetWidth) {
2212 Res = IRB.CreateIntCast(V: Off, DestTy: ResTy, /*isSigned=*/false,
2213 Name: PI.getName() + ".off");
2214 } else {
2215 Value *RsrcInt = IRB.CreatePtrToInt(V: Rsrc, DestTy: ResTy, Name: PI.getName() + ".rsrc");
2216 Value *Shl = IRB.CreateShl(
2217 LHS: RsrcInt,
2218 RHS: ConstantExpr::getIntegerValue(Ty: ResTy, V: APInt(Width, BufferOffsetWidth)),
2219 Name: "", HasNUW: Width >= FatPtrWidth, HasNSW: Width > FatPtrWidth);
2220 Value *OffCast = IRB.CreateIntCast(V: Off, DestTy: ResTy, /*isSigned=*/false,
2221 Name: PI.getName() + ".off");
2222 Res = IRB.CreateOr(LHS: Shl, RHS: OffCast);
2223 }
2224
2225 copyMetadata(Dest: Res, Src: &PI);
2226 Res->takeName(V: &PI);
2227 SplitUsers.insert(V: &PI);
2228 PI.replaceAllUsesWith(V: Res);
2229 return {nullptr, nullptr};
2230}
2231
2232PtrParts SplitPtrStructs::visitPtrToAddrInst(PtrToAddrInst &PA) {
2233 Value *Ptr = PA.getPointerOperand();
2234 if (!isSplitFatPtr(Ty: Ptr->getType()))
2235 return {nullptr, nullptr};
2236 IRB.SetInsertPoint(&PA);
2237
2238 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2239 Value *Res = IRB.CreateIntCast(V: Off, DestTy: PA.getType(), /*isSigned=*/false);
2240 copyMetadata(Dest: Res, Src: &PA);
2241 Res->takeName(V: &PA);
2242 SplitUsers.insert(V: &PA);
2243 PA.replaceAllUsesWith(V: Res);
2244 return {nullptr, nullptr};
2245}
2246
2247PtrParts SplitPtrStructs::visitIntToPtrInst(IntToPtrInst &IP) {
2248 if (!isSplitFatPtr(Ty: IP.getType()))
2249 return {nullptr, nullptr};
2250 IRB.SetInsertPoint(&IP);
2251 const DataLayout &DL = IP.getDataLayout();
2252 unsigned RsrcPtrWidth = DL.getPointerSizeInBits(AS: AMDGPUAS::BUFFER_RESOURCE);
2253 Value *Int = IP.getOperand(i_nocapture: 0);
2254 Type *IntTy = Int->getType();
2255 Type *RsrcIntTy = IntTy->getWithNewBitWidth(NewBitWidth: RsrcPtrWidth);
2256 unsigned Width = IntTy->getScalarSizeInBits();
2257
2258 auto *RetTy = cast<StructType>(Val: IP.getType());
2259 Type *RsrcTy = RetTy->getElementType(N: 0);
2260 Type *OffTy = RetTy->getElementType(N: 1);
2261 // inttoptr zero-extends, so narrow inputs contribute nothing to the resource
2262 // part.
2263 Value *RsrcInt;
2264 if (Width <= BufferOffsetWidth) {
2265 RsrcInt = Constant::getNullValue(Ty: RsrcIntTy);
2266 } else {
2267 Value *RsrcPart =
2268 IRB.CreateLShr(LHS: Int, RHS: ConstantInt::get(Ty: IntTy, V: BufferOffsetWidth));
2269 RsrcInt = IRB.CreateIntCast(V: RsrcPart, DestTy: RsrcIntTy, /*isSigned=*/false);
2270 }
2271 Value *Rsrc = IRB.CreateIntToPtr(V: RsrcInt, DestTy: RsrcTy, Name: IP.getName() + ".rsrc");
2272 Value *Off =
2273 IRB.CreateIntCast(V: Int, DestTy: OffTy, /*IsSigned=*/isSigned: false, Name: IP.getName() + ".off");
2274
2275 copyMetadata(Dest: Rsrc, Src: &IP);
2276 SplitUsers.insert(V: &IP);
2277 return {Rsrc, Off};
2278}
2279
2280PtrParts SplitPtrStructs::visitAddrSpaceCastInst(AddrSpaceCastInst &I) {
2281 // TODO(krzysz00): handle casts from ptr addrspace(7) to global pointers
2282 // by computing the effective address.
2283 if (!isSplitFatPtr(Ty: I.getType()))
2284 return {nullptr, nullptr};
2285 IRB.SetInsertPoint(&I);
2286 Value *In = I.getPointerOperand();
2287 // No-op casts preserve parts
2288 if (In->getType() == I.getType()) {
2289 auto [Rsrc, Off] = getPtrParts(V: In);
2290 SplitUsers.insert(V: &I);
2291 return {Rsrc, Off};
2292 }
2293
2294 auto *ResTy = cast<StructType>(Val: I.getType());
2295 Type *RsrcTy = ResTy->getElementType(N: 0);
2296 Type *OffTy = ResTy->getElementType(N: 1);
2297 Value *ZeroOff = Constant::getNullValue(Ty: OffTy);
2298
2299 // Special case for null pointers, undef, and poison, which can be created by
2300 // address space propagation.
2301 auto *InConst = dyn_cast<Constant>(Val: In);
2302 if (InConst && InConst->isNullValue()) {
2303 Value *NullRsrc = Constant::getNullValue(Ty: RsrcTy);
2304 SplitUsers.insert(V: &I);
2305 return {NullRsrc, ZeroOff};
2306 }
2307 if (isa<PoisonValue>(Val: In)) {
2308 Value *PoisonRsrc = PoisonValue::get(T: RsrcTy);
2309 Value *PoisonOff = PoisonValue::get(T: OffTy);
2310 SplitUsers.insert(V: &I);
2311 return {PoisonRsrc, PoisonOff};
2312 }
2313 if (isa<UndefValue>(Val: In)) {
2314 Value *UndefRsrc = UndefValue::get(T: RsrcTy);
2315 Value *UndefOff = UndefValue::get(T: OffTy);
2316 SplitUsers.insert(V: &I);
2317 return {UndefRsrc, UndefOff};
2318 }
2319
2320 if (I.getSrcAddressSpace() != AMDGPUAS::BUFFER_RESOURCE)
2321 reportFatalUsageError(
2322 reason: "only buffer resources (addrspace 8) and null/poison pointers can be "
2323 "cast to buffer fat pointers (addrspace 7)");
2324 SplitUsers.insert(V: &I);
2325 return {In, ZeroOff};
2326}
2327
2328PtrParts SplitPtrStructs::visitICmpInst(ICmpInst &Cmp) {
2329 Value *Lhs = Cmp.getOperand(i_nocapture: 0);
2330 if (!isSplitFatPtr(Ty: Lhs->getType()))
2331 return {nullptr, nullptr};
2332 Value *Rhs = Cmp.getOperand(i_nocapture: 1);
2333 IRB.SetInsertPoint(&Cmp);
2334 ICmpInst::Predicate Pred = Cmp.getPredicate();
2335
2336 assert((Pred == ICmpInst::ICMP_EQ || Pred == ICmpInst::ICMP_NE) &&
2337 "Pointer comparison is only equal or unequal");
2338 auto [LhsRsrc, LhsOff] = getPtrParts(V: Lhs);
2339 auto [RhsRsrc, RhsOff] = getPtrParts(V: Rhs);
2340 Value *Res = IRB.CreateICmp(P: Pred, LHS: LhsOff, RHS: RhsOff);
2341 copyMetadata(Dest: Res, Src: &Cmp);
2342 Res->takeName(V: &Cmp);
2343 SplitUsers.insert(V: &Cmp);
2344 Cmp.replaceAllUsesWith(V: Res);
2345 return {nullptr, nullptr};
2346}
2347
2348PtrParts SplitPtrStructs::visitFreezeInst(FreezeInst &I) {
2349 if (!isSplitFatPtr(Ty: I.getType()))
2350 return {nullptr, nullptr};
2351 IRB.SetInsertPoint(&I);
2352 auto [Rsrc, Off] = getPtrParts(V: I.getOperand(i_nocapture: 0));
2353
2354 Value *RsrcRes = IRB.CreateFreeze(V: Rsrc, Name: I.getName() + ".rsrc");
2355 copyMetadata(Dest: RsrcRes, Src: &I);
2356 Value *OffRes = IRB.CreateFreeze(V: Off, Name: I.getName() + ".off");
2357 copyMetadata(Dest: OffRes, Src: &I);
2358 SplitUsers.insert(V: &I);
2359 return {RsrcRes, OffRes};
2360}
2361
2362PtrParts SplitPtrStructs::visitExtractElementInst(ExtractElementInst &I) {
2363 if (!isSplitFatPtr(Ty: I.getType()))
2364 return {nullptr, nullptr};
2365 IRB.SetInsertPoint(&I);
2366 Value *Vec = I.getVectorOperand();
2367 Value *Idx = I.getIndexOperand();
2368 auto [Rsrc, Off] = getPtrParts(V: Vec);
2369
2370 Value *RsrcRes = IRB.CreateExtractElement(Vec: Rsrc, Idx, Name: I.getName() + ".rsrc");
2371 copyMetadata(Dest: RsrcRes, Src: &I);
2372 Value *OffRes = IRB.CreateExtractElement(Vec: Off, Idx, Name: I.getName() + ".off");
2373 copyMetadata(Dest: OffRes, Src: &I);
2374 SplitUsers.insert(V: &I);
2375 return {RsrcRes, OffRes};
2376}
2377
2378PtrParts SplitPtrStructs::visitInsertElementInst(InsertElementInst &I) {
2379 // The mutated instructions temporarily don't return vectors, and so
2380 // we need the generic getType() here to avoid crashes.
2381 if (!isSplitFatPtr(Ty: cast<Instruction>(Val&: I).getType()))
2382 return {nullptr, nullptr};
2383 IRB.SetInsertPoint(&I);
2384 Value *Vec = I.getOperand(i_nocapture: 0);
2385 Value *Elem = I.getOperand(i_nocapture: 1);
2386 Value *Idx = I.getOperand(i_nocapture: 2);
2387 auto [VecRsrc, VecOff] = getPtrParts(V: Vec);
2388 auto [ElemRsrc, ElemOff] = getPtrParts(V: Elem);
2389
2390 Value *RsrcRes =
2391 IRB.CreateInsertElement(Vec: VecRsrc, NewElt: ElemRsrc, Idx, Name: I.getName() + ".rsrc");
2392 copyMetadata(Dest: RsrcRes, Src: &I);
2393 Value *OffRes =
2394 IRB.CreateInsertElement(Vec: VecOff, NewElt: ElemOff, Idx, Name: I.getName() + ".off");
2395 copyMetadata(Dest: OffRes, Src: &I);
2396 SplitUsers.insert(V: &I);
2397 return {RsrcRes, OffRes};
2398}
2399
2400PtrParts SplitPtrStructs::visitShuffleVectorInst(ShuffleVectorInst &I) {
2401 // Cast is needed for the same reason as insertelement's.
2402 if (!isSplitFatPtr(Ty: cast<Instruction>(Val&: I).getType()))
2403 return {nullptr, nullptr};
2404 IRB.SetInsertPoint(&I);
2405
2406 Value *V1 = I.getOperand(i_nocapture: 0);
2407 Value *V2 = I.getOperand(i_nocapture: 1);
2408 ArrayRef<int> Mask = I.getShuffleMask();
2409 auto [V1Rsrc, V1Off] = getPtrParts(V: V1);
2410 auto [V2Rsrc, V2Off] = getPtrParts(V: V2);
2411
2412 Value *RsrcRes =
2413 IRB.CreateShuffleVector(V1: V1Rsrc, V2: V2Rsrc, Mask, Name: I.getName() + ".rsrc");
2414 copyMetadata(Dest: RsrcRes, Src: &I);
2415 Value *OffRes =
2416 IRB.CreateShuffleVector(V1: V1Off, V2: V2Off, Mask, Name: I.getName() + ".off");
2417 copyMetadata(Dest: OffRes, Src: &I);
2418 SplitUsers.insert(V: &I);
2419 return {RsrcRes, OffRes};
2420}
2421
2422PtrParts SplitPtrStructs::visitPHINode(PHINode &PHI) {
2423 if (!isSplitFatPtr(Ty: PHI.getType()))
2424 return {nullptr, nullptr};
2425 IRB.SetInsertPoint(*PHI.getInsertionPointAfterDef());
2426 // Phi nodes will be handled in post-processing after we've visited every
2427 // instruction. However, instead of just returning {nullptr, nullptr},
2428 // we explicitly create the temporary extractvalue operations that are our
2429 // temporary results so that they end up at the beginning of the block with
2430 // the PHIs.
2431 Value *TmpRsrc = IRB.CreateExtractValue(Agg: &PHI, Idxs: 0, Name: PHI.getName() + ".rsrc");
2432 Value *TmpOff = IRB.CreateExtractValue(Agg: &PHI, Idxs: 1, Name: PHI.getName() + ".off");
2433 Conditionals.push_back(Elt: &PHI);
2434 SplitUsers.insert(V: &PHI);
2435 return {TmpRsrc, TmpOff};
2436}
2437
2438PtrParts SplitPtrStructs::visitSelectInst(SelectInst &SI) {
2439 if (!isSplitFatPtr(Ty: SI.getType()))
2440 return {nullptr, nullptr};
2441 IRB.SetInsertPoint(&SI);
2442
2443 Value *Cond = SI.getCondition();
2444 Value *True = SI.getTrueValue();
2445 Value *False = SI.getFalseValue();
2446 auto [TrueRsrc, TrueOff] = getPtrParts(V: True);
2447 auto [FalseRsrc, FalseOff] = getPtrParts(V: False);
2448
2449 Value *RsrcRes =
2450 IRB.CreateSelect(C: Cond, True: TrueRsrc, False: FalseRsrc, Name: SI.getName() + ".rsrc", MDFrom: &SI);
2451 copyMetadata(Dest: RsrcRes, Src: &SI);
2452 Conditionals.push_back(Elt: &SI);
2453 Value *OffRes =
2454 IRB.CreateSelect(C: Cond, True: TrueOff, False: FalseOff, Name: SI.getName() + ".off", MDFrom: &SI);
2455 copyMetadata(Dest: OffRes, Src: &SI);
2456 SplitUsers.insert(V: &SI);
2457 return {RsrcRes, OffRes};
2458}
2459
2460/// Returns true if this intrinsic needs to be removed when it is
2461/// applied to `ptr addrspace(7)` values. Calls to these intrinsics are
2462/// rewritten into calls to versions of that intrinsic on the resource
2463/// descriptor.
2464static bool isRemovablePointerIntrinsic(Intrinsic::ID IID) {
2465 switch (IID) {
2466 default:
2467 return false;
2468 case Intrinsic::amdgcn_make_buffer_rsrc:
2469 case Intrinsic::ptrmask:
2470 case Intrinsic::invariant_start:
2471 case Intrinsic::invariant_end:
2472 case Intrinsic::launder_invariant_group:
2473 case Intrinsic::memcpy:
2474 case Intrinsic::memcpy_inline:
2475 case Intrinsic::memmove:
2476 case Intrinsic::memset:
2477 case Intrinsic::memset_inline:
2478 case Intrinsic::experimental_memset_pattern:
2479 case Intrinsic::amdgcn_load_to_lds:
2480 case Intrinsic::amdgcn_load_async_to_lds:
2481 return true;
2482 }
2483}
2484
2485PtrParts SplitPtrStructs::visitIntrinsicInst(IntrinsicInst &I) {
2486 Intrinsic::ID IID = I.getIntrinsicID();
2487 switch (IID) {
2488 default:
2489 break;
2490 case Intrinsic::amdgcn_make_buffer_rsrc: {
2491 if (!isSplitFatPtr(Ty: I.getType()))
2492 return {nullptr, nullptr};
2493 Value *Base = I.getArgOperand(i: 0);
2494 Value *Stride = I.getArgOperand(i: 1);
2495 Value *NumRecords = I.getArgOperand(i: 2);
2496 Value *Flags = I.getArgOperand(i: 3);
2497 auto *SplitType = cast<StructType>(Val: I.getType());
2498 Type *RsrcType = SplitType->getElementType(N: 0);
2499 Type *OffType = SplitType->getElementType(N: 1);
2500 IRB.SetInsertPoint(&I);
2501 Value *Rsrc = IRB.CreateIntrinsic(
2502 ID: IID, OverloadTypes: {RsrcType, Base->getType(), NumRecords->getType()},
2503 Args: {Base, Stride, NumRecords, Flags});
2504 copyMetadata(Dest: Rsrc, Src: &I);
2505 Rsrc->takeName(V: &I);
2506 Value *Zero = Constant::getNullValue(Ty: OffType);
2507 SplitUsers.insert(V: &I);
2508 return {Rsrc, Zero};
2509 }
2510 case Intrinsic::ptrmask: {
2511 Value *Ptr = I.getArgOperand(i: 0);
2512 if (!isSplitFatPtr(Ty: Ptr->getType()))
2513 return {nullptr, nullptr};
2514 Value *Mask = I.getArgOperand(i: 1);
2515 IRB.SetInsertPoint(&I);
2516 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2517 if (Mask->getType() != Off->getType())
2518 reportFatalUsageError(reason: "offset width is not equal to index width of fat "
2519 "pointer (data layout not set up correctly?)");
2520 Value *OffRes = IRB.CreateAnd(LHS: Off, RHS: Mask, Name: I.getName() + ".off");
2521 copyMetadata(Dest: OffRes, Src: &I);
2522 SplitUsers.insert(V: &I);
2523 return {Rsrc, OffRes};
2524 }
2525 // Pointer annotation intrinsics that, given their object-wide nature
2526 // operate on the resource part.
2527 case Intrinsic::invariant_start: {
2528 Value *Ptr = I.getArgOperand(i: 1);
2529 if (!isSplitFatPtr(Ty: Ptr->getType()))
2530 return {nullptr, nullptr};
2531 IRB.SetInsertPoint(&I);
2532 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2533 Type *NewTy = PointerType::get(C&: I.getContext(), AddressSpace: AMDGPUAS::BUFFER_RESOURCE);
2534 auto *NewRsrc = IRB.CreateIntrinsic(ID: IID, OverloadTypes: {NewTy}, Args: {I.getOperand(i_nocapture: 0), Rsrc});
2535 copyMetadata(Dest: NewRsrc, Src: &I);
2536 NewRsrc->takeName(V: &I);
2537 SplitUsers.insert(V: &I);
2538 I.replaceAllUsesWith(V: NewRsrc);
2539 return {nullptr, nullptr};
2540 }
2541 case Intrinsic::invariant_end: {
2542 Value *RealPtr = I.getArgOperand(i: 2);
2543 if (!isSplitFatPtr(Ty: RealPtr->getType()))
2544 return {nullptr, nullptr};
2545 IRB.SetInsertPoint(&I);
2546 Value *RealRsrc = getPtrParts(V: RealPtr).first;
2547 Value *InvPtr = I.getArgOperand(i: 0);
2548 Value *Size = I.getArgOperand(i: 1);
2549 Value *NewRsrc = IRB.CreateIntrinsic(ID: IID, OverloadTypes: {RealRsrc->getType()},
2550 Args: {InvPtr, Size, RealRsrc});
2551 copyMetadata(Dest: NewRsrc, Src: &I);
2552 NewRsrc->takeName(V: &I);
2553 SplitUsers.insert(V: &I);
2554 I.replaceAllUsesWith(V: NewRsrc);
2555 return {nullptr, nullptr};
2556 }
2557 case Intrinsic::launder_invariant_group: {
2558 Value *Ptr = I.getArgOperand(i: 0);
2559 if (!isSplitFatPtr(Ty: Ptr->getType()))
2560 return {nullptr, nullptr};
2561 IRB.SetInsertPoint(&I);
2562 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2563 Value *NewRsrc = IRB.CreateIntrinsic(ID: IID, OverloadTypes: {Rsrc->getType()}, Args: {Rsrc});
2564 copyMetadata(Dest: NewRsrc, Src: &I);
2565 NewRsrc->takeName(V: &I);
2566 SplitUsers.insert(V: &I);
2567 return {NewRsrc, Off};
2568 }
2569 case Intrinsic::amdgcn_load_to_lds:
2570 case Intrinsic::amdgcn_load_async_to_lds: {
2571 Value *Ptr = I.getArgOperand(i: 0);
2572 if (!isSplitFatPtr(Ty: Ptr->getType()))
2573 return {nullptr, nullptr};
2574 IRB.SetInsertPoint(&I);
2575 auto [Rsrc, Off] = getPtrParts(V: Ptr);
2576 Value *LDSPtr = I.getArgOperand(i: 1);
2577 Value *LoadSize = I.getArgOperand(i: 2);
2578 Value *ImmOff = I.getArgOperand(i: 3);
2579 Value *Aux = I.getArgOperand(i: 4);
2580 Value *SOffset = IRB.getInt32(C: 0);
2581 Intrinsic::ID NewIntr =
2582 IID == Intrinsic::amdgcn_load_to_lds
2583 ? Intrinsic::amdgcn_raw_ptr_buffer_load_lds
2584 : Intrinsic::amdgcn_raw_ptr_buffer_load_async_lds;
2585 Instruction *NewLoad = IRB.CreateIntrinsicWithoutFolding(
2586 ID: NewIntr, OverloadTypes: {}, Args: {Rsrc, LDSPtr, LoadSize, Off, SOffset, ImmOff, Aux});
2587 copyMetadata(Dest: NewLoad, Src: &I);
2588 SplitUsers.insert(V: &I);
2589 I.replaceAllUsesWith(V: NewLoad);
2590 return {nullptr, nullptr};
2591 }
2592 }
2593 return {nullptr, nullptr};
2594}
2595
2596void SplitPtrStructs::processFunction(Function &F) {
2597 ST = &TM->getSubtarget<GCNSubtarget>(F);
2598 SmallVector<Instruction *, 0> Originals(
2599 llvm::make_pointer_range(Range: instructions(F)));
2600 LLVM_DEBUG(dbgs() << "Splitting pointer structs in function: " << F.getName()
2601 << "\n");
2602 for (Instruction *I : Originals) {
2603 // In some cases, instruction order doesn't reflect program order,
2604 // so the visit() call will have already visited coertain instructions
2605 // by the time this loop gets to them. Avoid re-visiting these so as to,
2606 // for example, avoid processing the same conditional twice.
2607 if (SplitUsers.contains(V: I))
2608 continue;
2609 auto [Rsrc, Off] = visit(I);
2610 assert(((Rsrc && Off) || (!Rsrc && !Off)) &&
2611 "Can't have a resource but no offset");
2612 if (Rsrc)
2613 RsrcParts[I] = Rsrc;
2614 if (Off)
2615 OffParts[I] = Off;
2616 }
2617 processConditionals();
2618 killAndReplaceSplitInstructions(Origs&: Originals);
2619
2620 // Clean up after ourselves to save on memory.
2621 RsrcParts.clear();
2622 OffParts.clear();
2623 SplitUsers.clear();
2624 Conditionals.clear();
2625 ConditionalTemps.clear();
2626}
2627
2628namespace {
2629class AMDGPULowerBufferFatPointers : public ModulePass {
2630public:
2631 static char ID;
2632
2633 AMDGPULowerBufferFatPointers() : ModulePass(ID) {}
2634
2635 bool run(Module &M, const TargetMachine &TM, GetTTIFn GetTTI, GetSEFn GetSE);
2636 bool runOnModule(Module &M) override;
2637
2638 void getAnalysisUsage(AnalysisUsage &AU) const override;
2639};
2640} // namespace
2641
2642/// Returns true if there are values that have a buffer fat pointer in them,
2643/// which means we'll need to perform rewrites on this function. As a side
2644/// effect, this will populate the type remapping cache.
2645static bool containsBufferFatPointers(const Function &F,
2646 BufferFatPtrToStructTypeMap *TypeMap) {
2647 bool HasFatPointers = false;
2648 for (const BasicBlock &BB : F)
2649 for (const Instruction &I : BB) {
2650 HasFatPointers |= (I.getType() != TypeMap->remapType(SrcTy: I.getType()));
2651 // Catch null pointer constants in loads, stores, etc.
2652 for (const Value *V : I.operand_values())
2653 HasFatPointers |= (V->getType() != TypeMap->remapType(SrcTy: V->getType()));
2654 }
2655 return HasFatPointers;
2656}
2657
2658static bool hasFatPointerInterface(const Function &F,
2659 BufferFatPtrToStructTypeMap *TypeMap) {
2660 Type *Ty = F.getFunctionType();
2661 return Ty != TypeMap->remapType(SrcTy: Ty);
2662}
2663
2664/// Move the body of `OldF` into a new function, returning it.
2665static Function *moveFunctionAdaptingType(Function *OldF, FunctionType *NewTy,
2666 ValueToValueMapTy &CloneMap) {
2667 bool IsIntrinsic = OldF->isIntrinsic();
2668 Function *NewF =
2669 Function::Create(Ty: NewTy, Linkage: OldF->getLinkage(), AddrSpace: OldF->getAddressSpace());
2670 NewF->copyAttributesFrom(Src: OldF);
2671 NewF->copyMetadata(Src: OldF, Offset: 0);
2672 NewF->takeName(V: OldF);
2673 NewF->updateAfterNameChange();
2674 NewF->setDLLStorageClass(OldF->getDLLStorageClass());
2675 OldF->getParent()->getFunctionList().insertAfter(where: OldF->getIterator(), New: NewF);
2676
2677 while (!OldF->empty()) {
2678 BasicBlock *BB = &OldF->front();
2679 BB->removeFromParent();
2680 BB->insertInto(Parent: NewF);
2681 CloneMap[BB] = BB;
2682 for (Instruction &I : *BB) {
2683 CloneMap[&I] = &I;
2684 }
2685 }
2686
2687 SmallVector<AttributeSet> ArgAttrs;
2688 AttributeList OldAttrs = OldF->getAttributes();
2689
2690 for (auto [I, OldArg, NewArg] : enumerate(First: OldF->args(), Rest: NewF->args())) {
2691 CloneMap[&NewArg] = &OldArg;
2692 NewArg.takeName(V: &OldArg);
2693 Type *OldArgTy = OldArg.getType(), *NewArgTy = NewArg.getType();
2694 // Temporarily mutate type of `NewArg` to allow RAUW to work.
2695 NewArg.mutateType(Ty: OldArgTy);
2696 OldArg.replaceAllUsesWith(V: &NewArg);
2697 NewArg.mutateType(Ty: NewArgTy);
2698
2699 AttributeSet ArgAttr = OldAttrs.getParamAttrs(ArgNo: I);
2700 // Intrinsics get their attributes fixed later.
2701 if (OldArgTy != NewArgTy && !IsIntrinsic)
2702 ArgAttr = ArgAttr.removeAttributes(
2703 C&: NewF->getContext(),
2704 AttrsToRemove: AttributeFuncs::typeIncompatible(Ty: NewArgTy, AS: ArgAttr));
2705 ArgAttrs.push_back(Elt: ArgAttr);
2706 }
2707 AttributeSet RetAttrs = OldAttrs.getRetAttrs();
2708 if (OldF->getReturnType() != NewF->getReturnType() && !IsIntrinsic)
2709 RetAttrs = RetAttrs.removeAttributes(
2710 C&: NewF->getContext(),
2711 AttrsToRemove: AttributeFuncs::typeIncompatible(Ty: NewF->getReturnType(), AS: RetAttrs));
2712 NewF->setAttributes(AttributeList::get(
2713 C&: NewF->getContext(), FnAttrs: OldAttrs.getFnAttrs(), RetAttrs, ArgAttrs));
2714 return NewF;
2715}
2716
2717static void makeCloneInPraceMap(Function *F, ValueToValueMapTy &CloneMap) {
2718 for (Argument &A : F->args())
2719 CloneMap[&A] = &A;
2720 for (BasicBlock &BB : *F) {
2721 CloneMap[&BB] = &BB;
2722 for (Instruction &I : BB)
2723 CloneMap[&I] = &I;
2724 }
2725}
2726
2727bool AMDGPULowerBufferFatPointers::run(Module &M, const TargetMachine &TM,
2728 GetTTIFn GetTTI, GetSEFn GetSE) {
2729 bool Changed = false;
2730 const DataLayout &DL = M.getDataLayout();
2731 // Record the functions which need to be remapped.
2732 // The second element of the pair indicates whether the function has to have
2733 // its arguments or return types adjusted.
2734 SmallVector<std::pair<Function *, bool>> NeedsRemap;
2735
2736 LLVMContext &Ctx = M.getContext();
2737
2738 BufferFatPtrToStructTypeMap StructTM(DL);
2739 BufferFatPtrToIntTypeMap IntTM(DL);
2740 for (GlobalVariable &GV : make_early_inc_range(Range: M.globals())) {
2741 if (GV.getAddressSpace() == AMDGPUAS::BUFFER_FAT_POINTER) {
2742 // FIXME: Use DiagnosticInfo unsupported but it requires a Function
2743 Ctx.emitError(ErrorStr: "global variables with a buffer fat pointer address "
2744 "space (7) are not supported");
2745 GV.replaceAllUsesWith(V: PoisonValue::get(T: GV.getType()));
2746 GV.eraseFromParent();
2747 Changed = true;
2748 continue;
2749 }
2750
2751 Type *VT = GV.getValueType();
2752 if (VT != StructTM.remapType(SrcTy: VT)) {
2753 // FIXME: Use DiagnosticInfo unsupported but it requires a Function
2754 Ctx.emitError(ErrorStr: "global variables that contain buffer fat pointers "
2755 "(address space 7 pointers) are unsupported. Use "
2756 "buffer resource pointers (address space 8) instead");
2757 GV.replaceAllUsesWith(V: PoisonValue::get(T: GV.getType()));
2758 GV.eraseFromParent();
2759 Changed = true;
2760 continue;
2761 }
2762 }
2763
2764 {
2765 // Collect all constant exprs and aggregates referenced by any function.
2766 SmallVector<Constant *, 8> Worklist;
2767 for (Function &F : M.functions())
2768 for (Instruction &I : instructions(F))
2769 for (Value *Op : I.operands())
2770 if (isa<ConstantExpr, ConstantAggregate>(Val: Op))
2771 Worklist.push_back(Elt: cast<Constant>(Val: Op));
2772
2773 // Recursively look for any referenced buffer pointer constants.
2774 SmallPtrSet<Constant *, 8> Visited;
2775 SetVector<Constant *> BufferFatPtrConsts;
2776 while (!Worklist.empty()) {
2777 Constant *C = Worklist.pop_back_val();
2778 if (!Visited.insert(Ptr: C).second)
2779 continue;
2780 if (isBufferFatPtrOrVector(Ty: C->getType()))
2781 BufferFatPtrConsts.insert(X: C);
2782 for (Value *Op : C->operands())
2783 if (isa<ConstantExpr, ConstantAggregate>(Val: Op))
2784 Worklist.push_back(Elt: cast<Constant>(Val: Op));
2785 }
2786
2787 // Expand all constant expressions using fat buffer pointers to
2788 // instructions.
2789 Changed |= convertUsersOfConstantsToInstructions(
2790 Consts: BufferFatPtrConsts.getArrayRef(), /*RestrictToFunc=*/nullptr,
2791 /*RemoveDeadConstants=*/false, /*IncludeSelf=*/true);
2792 }
2793
2794 StoreFatPtrsAsIntsAndExpandMemcpyVisitor MemOpsRewrite(&IntTM, M);
2795 LegalizeBufferContentTypesVisitor BufferContentsTypeRewrite(M, &TM);
2796 for (Function &F : M.functions()) {
2797 bool InterfaceChange = hasFatPointerInterface(F, TypeMap: &StructTM);
2798 bool BodyChanges = containsBufferFatPointers(F, TypeMap: &StructTM);
2799 const TargetTransformInfo *TTI = GetTTI(F);
2800 ScalarEvolution *SE = GetSE(F);
2801 Changed |= MemOpsRewrite.processFunction(F, TTI, SE);
2802 if (InterfaceChange || BodyChanges) {
2803 NeedsRemap.push_back(Elt: std::make_pair(x: &F, y&: InterfaceChange));
2804 Changed |= BufferContentsTypeRewrite.processFunction(F, SE);
2805 }
2806 }
2807 if (NeedsRemap.empty())
2808 return Changed;
2809
2810 SmallVector<Function *> NeedsPostProcess;
2811 SmallVector<Function *> Intrinsics;
2812 // Keep one big map so as to memoize constants across functions.
2813 ValueToValueMapTy CloneMap;
2814 FatPtrConstMaterializer Materializer(&StructTM, CloneMap);
2815
2816 ValueMapper LowerInFuncs(CloneMap, RF_None, &StructTM, &Materializer);
2817 for (auto [F, InterfaceChange] : NeedsRemap) {
2818 Function *NewF = F;
2819 if (InterfaceChange)
2820 NewF = moveFunctionAdaptingType(
2821 OldF: F, NewTy: cast<FunctionType>(Val: StructTM.remapType(SrcTy: F->getFunctionType())),
2822 CloneMap);
2823 else
2824 makeCloneInPraceMap(F, CloneMap);
2825 LowerInFuncs.remapFunction(F&: *NewF);
2826 if (NewF->isIntrinsic())
2827 Intrinsics.push_back(Elt: NewF);
2828 else
2829 NeedsPostProcess.push_back(Elt: NewF);
2830 if (InterfaceChange) {
2831 F->replaceAllUsesWith(V: NewF);
2832 F->eraseFromParent();
2833 }
2834 Changed = true;
2835 }
2836 StructTM.clear();
2837 IntTM.clear();
2838 CloneMap.clear();
2839
2840 SplitPtrStructs Splitter(M, &TM);
2841 for (Function *F : NeedsPostProcess)
2842 Splitter.processFunction(F&: *F);
2843 for (Function *F : Intrinsics) {
2844 // use_empty() can also occur with cases like masked load, which will
2845 // have been rewritten out of the module by now but not erased.
2846 if (F->use_empty() || isRemovablePointerIntrinsic(IID: F->getIntrinsicID())) {
2847 F->eraseFromParent();
2848 } else {
2849 std::optional<Function *> NewF = Intrinsic::remangleIntrinsicFunction(F);
2850 if (NewF)
2851 F->replaceAllUsesWith(V: *NewF);
2852 }
2853 }
2854 return Changed;
2855}
2856
2857bool AMDGPULowerBufferFatPointers::runOnModule(Module &M) {
2858 TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
2859 const TargetMachine &TM = TPC.getTM<TargetMachine>();
2860 auto GetTTI = [&](Function &F) -> const TargetTransformInfo * {
2861 if (F.isDeclaration())
2862 return nullptr;
2863 return &getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
2864 };
2865 auto GetSE = [&](Function &F) -> ScalarEvolution * {
2866 if (F.isDeclaration())
2867 return nullptr;
2868 return &getAnalysis<ScalarEvolutionWrapperPass>(F).getSE();
2869 };
2870 return run(M, TM, GetTTI, GetSE);
2871}
2872
2873char AMDGPULowerBufferFatPointers::ID = 0;
2874
2875char &llvm::AMDGPULowerBufferFatPointersID = AMDGPULowerBufferFatPointers::ID;
2876
2877void AMDGPULowerBufferFatPointers::getAnalysisUsage(AnalysisUsage &AU) const {
2878 AU.addRequired<TargetPassConfig>();
2879 AU.addRequired<TargetTransformInfoWrapperPass>();
2880 AU.addRequired<ScalarEvolutionWrapperPass>();
2881}
2882
2883#define PASS_DESC "Lower buffer fat pointer operations to buffer resources"
2884INITIALIZE_PASS_BEGIN(AMDGPULowerBufferFatPointers, DEBUG_TYPE, PASS_DESC,
2885 false, false)
2886INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
2887INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
2888INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass)
2889INITIALIZE_PASS_END(AMDGPULowerBufferFatPointers, DEBUG_TYPE, PASS_DESC, false,
2890 false)
2891#undef PASS_DESC
2892
2893ModulePass *llvm::createAMDGPULowerBufferFatPointersPass() {
2894 return new AMDGPULowerBufferFatPointers();
2895}
2896
2897PreservedAnalyses
2898AMDGPULowerBufferFatPointersPass::run(Module &M, ModuleAnalysisManager &MA) {
2899 auto &FA = MA.getResult<FunctionAnalysisManagerModuleProxy>(IR&: M).getManager();
2900 auto GetTTI = [&](Function &F) -> const TargetTransformInfo * {
2901 if (F.isDeclaration())
2902 return nullptr;
2903 return &FA.getResult<TargetIRAnalysis>(IR&: F);
2904 };
2905 auto GetSE = [&](Function &F) -> ScalarEvolution * {
2906 if (F.isDeclaration())
2907 return nullptr;
2908 return &FA.getResult<ScalarEvolutionAnalysis>(IR&: F);
2909 };
2910 return AMDGPULowerBufferFatPointers().run(M, TM, GetTTI, GetSE)
2911 ? PreservedAnalyses::none()
2912 : PreservedAnalyses::all();
2913}
2914