xref: /llvm-project/llvm/lib/Target/AMDGPU/AMDGPULowerBufferFatPointers.cpp (revision 697c1883f15b81cc526ed2d72cf00f9eaea2502f)
16540f163SKrzysztof Drewniak //===-- AMDGPULowerBufferFatPointers.cpp ---------------------------=//
26540f163SKrzysztof Drewniak //
36540f163SKrzysztof Drewniak // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
46540f163SKrzysztof Drewniak // See https://llvm.org/LICENSE.txt for license information.
56540f163SKrzysztof Drewniak // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66540f163SKrzysztof Drewniak //
76540f163SKrzysztof Drewniak //===----------------------------------------------------------------------===//
86540f163SKrzysztof Drewniak //
96540f163SKrzysztof Drewniak // This pass lowers operations on buffer fat pointers (addrspace 7) to
106540f163SKrzysztof Drewniak // operations on buffer resources (addrspace 8) and is needed for correct
116540f163SKrzysztof Drewniak // codegen.
126540f163SKrzysztof Drewniak //
136540f163SKrzysztof Drewniak // # Background
146540f163SKrzysztof Drewniak //
156540f163SKrzysztof Drewniak // Address space 7 (the buffer fat pointer) is a 160-bit pointer that consists
166540f163SKrzysztof Drewniak // of a 128-bit buffer descriptor and a 32-bit offset into that descriptor.
176540f163SKrzysztof Drewniak // The buffer resource part needs to be it needs to be a "raw" buffer resource
186540f163SKrzysztof Drewniak // (it must have a stride of 0 and bounds checks must be in raw buffer mode
196540f163SKrzysztof Drewniak // or disabled).
206540f163SKrzysztof Drewniak //
216540f163SKrzysztof Drewniak // When these requirements are met, a buffer resource can be treated as a
226540f163SKrzysztof Drewniak // typical (though quite wide) pointer that follows typical LLVM pointer
236540f163SKrzysztof Drewniak // semantics. This allows the frontend to reason about such buffers (which are
246540f163SKrzysztof Drewniak // often encountered in the context of SPIR-V kernels).
256540f163SKrzysztof Drewniak //
266540f163SKrzysztof Drewniak // However, because of their non-power-of-2 size, these fat pointers cannot be
276540f163SKrzysztof Drewniak // present during translation to MIR (though this restriction may be lifted
286540f163SKrzysztof Drewniak // during the transition to GlobalISel). Therefore, this pass is needed in order
296540f163SKrzysztof Drewniak // to correctly implement these fat pointers.
306540f163SKrzysztof Drewniak //
316540f163SKrzysztof Drewniak // The resource intrinsics take the resource part (the address space 8 pointer)
326540f163SKrzysztof Drewniak // and the offset part (the 32-bit integer) as separate arguments. In addition,
336540f163SKrzysztof Drewniak // many users of these buffers manipulate the offset while leaving the resource
346540f163SKrzysztof Drewniak // part alone. For these reasons, we want to typically separate the resource
356540f163SKrzysztof Drewniak // and offset parts into separate variables, but combine them together when
366540f163SKrzysztof Drewniak // encountering cases where this is required, such as by inserting these values
376540f163SKrzysztof Drewniak // into aggretates or moving them to memory.
386540f163SKrzysztof Drewniak //
396540f163SKrzysztof Drewniak // Therefore, at a high level, `ptr addrspace(7) %x` becomes `ptr addrspace(8)
406540f163SKrzysztof Drewniak // %x.rsrc` and `i32 %x.off`, which will be combined into `{ptr addrspace(8),
416540f163SKrzysztof Drewniak // i32} %x = {%x.rsrc, %x.off}` if needed. Similarly, `vector<Nxp7>` becomes
426540f163SKrzysztof Drewniak // `{vector<Nxp8>, vector<Nxi32 >}` and its component parts.
436540f163SKrzysztof Drewniak //
446540f163SKrzysztof Drewniak // # Implementation
456540f163SKrzysztof Drewniak //
466540f163SKrzysztof Drewniak // This pass proceeds in three main phases:
476540f163SKrzysztof Drewniak //
486540f163SKrzysztof Drewniak // ## Rewriting loads and stores of p7
496540f163SKrzysztof Drewniak //
506540f163SKrzysztof Drewniak // The first phase is to rewrite away all loads and stors of `ptr addrspace(7)`,
516540f163SKrzysztof Drewniak // including aggregates containing such pointers, to ones that use `i160`. This
526540f163SKrzysztof Drewniak // is handled by `StoreFatPtrsAsIntsVisitor` , which visits loads, stores, and
536540f163SKrzysztof Drewniak // allocas and, if the loaded or stored type contains `ptr addrspace(7)`,
546540f163SKrzysztof Drewniak // rewrites that type to one where the p7s are replaced by i160s, copying other
556540f163SKrzysztof Drewniak // parts of aggregates as needed. In the case of a store, each pointer is
566540f163SKrzysztof Drewniak // `ptrtoint`d to i160 before storing, and load integers are `inttoptr`d back.
576540f163SKrzysztof Drewniak // This same transformation is applied to vectors of pointers.
586540f163SKrzysztof Drewniak //
596540f163SKrzysztof Drewniak // Such a transformation allows the later phases of the pass to not need
606540f163SKrzysztof Drewniak // to handle buffer fat pointers moving to and from memory, where we load
616540f163SKrzysztof Drewniak // have to handle the incompatibility between a `{Nxp8, Nxi32}` representation
626540f163SKrzysztof Drewniak // and `Nxi60` directly. Instead, that transposing action (where the vectors
636540f163SKrzysztof Drewniak // of resources and vectors of offsets are concatentated before being stored to
646540f163SKrzysztof Drewniak // memory) are handled through implementing `inttoptr` and `ptrtoint` only.
656540f163SKrzysztof Drewniak //
666540f163SKrzysztof Drewniak // Atomics operations on `ptr addrspace(7)` values are not suppported, as the
676540f163SKrzysztof Drewniak // hardware does not include a 160-bit atomic.
686540f163SKrzysztof Drewniak //
69*697c1883SKrzysztof Drewniak // ## Buffer contents type legalization
70*697c1883SKrzysztof Drewniak //
71*697c1883SKrzysztof Drewniak // The underlying buffer intrinsics only support types up to 128 bits long,
72*697c1883SKrzysztof Drewniak // and don't support complex types. If buffer operations were
73*697c1883SKrzysztof Drewniak // standard pointer operations that could be represented as MIR-level loads,
74*697c1883SKrzysztof Drewniak // this would be handled by the various legalization schemes in instruction
75*697c1883SKrzysztof Drewniak // selection. However, because we have to do the conversion from `load` and
76*697c1883SKrzysztof Drewniak // `store` to intrinsics at LLVM IR level, we must perform that legalization
77*697c1883SKrzysztof Drewniak // ourselves.
78*697c1883SKrzysztof Drewniak //
79*697c1883SKrzysztof Drewniak // This involves a combination of
80*697c1883SKrzysztof Drewniak // - Converting arrays to vectors where possible
81*697c1883SKrzysztof Drewniak // - Otherwise, splitting loads and stores of aggregates into loads/stores of
82*697c1883SKrzysztof Drewniak //   each component.
83*697c1883SKrzysztof Drewniak // - Zero-extending things to fill a whole number of bytes
84*697c1883SKrzysztof Drewniak // - Casting values of types that don't neatly correspond to supported machine
85*697c1883SKrzysztof Drewniak // value
86*697c1883SKrzysztof Drewniak //   (for example, an i96 or i256) into ones that would work (
87*697c1883SKrzysztof Drewniak //    like <3 x i32> and <8 x i32>, respectively)
88*697c1883SKrzysztof Drewniak // - Splitting values that are too long (such as aforementioned <8 x i32>) into
89*697c1883SKrzysztof Drewniak //   multiple operations.
90*697c1883SKrzysztof Drewniak //
916540f163SKrzysztof Drewniak // ## Type remapping
926540f163SKrzysztof Drewniak //
936540f163SKrzysztof Drewniak // We use a `ValueMapper` to mangle uses of [vectors of] buffer fat pointers
946540f163SKrzysztof Drewniak // to the corresponding struct type, which has a resource part and an offset
956540f163SKrzysztof Drewniak // part.
966540f163SKrzysztof Drewniak //
976540f163SKrzysztof Drewniak // This uses a `BufferFatPtrToStructTypeMap` and a `FatPtrConstMaterializer`
986540f163SKrzysztof Drewniak // to, usually by way of `setType`ing values. Constants are handled here
996540f163SKrzysztof Drewniak // because there isn't a good way to fix them up later.
1006540f163SKrzysztof Drewniak //
1016540f163SKrzysztof Drewniak // This has the downside of leaving the IR in an invalid state (for example,
1026540f163SKrzysztof Drewniak // the instruction `getelementptr {ptr addrspace(8), i32} %p, ...` will exist),
1036540f163SKrzysztof Drewniak // but all such invalid states will be resolved by the third phase.
1046540f163SKrzysztof Drewniak //
1056540f163SKrzysztof Drewniak // Functions that don't take buffer fat pointers are modified in place. Those
1066540f163SKrzysztof Drewniak // that do take such pointers have their basic blocks moved to a new function
1076540f163SKrzysztof Drewniak // with arguments that are {ptr addrspace(8), i32} arguments and return values.
1086540f163SKrzysztof Drewniak // This phase also records intrinsics so that they can be remangled or deleted
1096540f163SKrzysztof Drewniak // later.
1106540f163SKrzysztof Drewniak //
1116540f163SKrzysztof Drewniak // ## Splitting pointer structs
1126540f163SKrzysztof Drewniak //
1136540f163SKrzysztof Drewniak // The meat of this pass consists of defining semantics for operations that
1146540f163SKrzysztof Drewniak // produce or consume [vectors of] buffer fat pointers in terms of their
1156540f163SKrzysztof Drewniak // resource and offset parts. This is accomplished throgh the `SplitPtrStructs`
1166540f163SKrzysztof Drewniak // visitor.
1176540f163SKrzysztof Drewniak //
1186540f163SKrzysztof Drewniak // In the first pass through each function that is being lowered, the splitter
1196540f163SKrzysztof Drewniak // inserts new instructions to implement the split-structures behavior, which is
1206540f163SKrzysztof Drewniak // needed for correctness and performance. It records a list of "split users",
1216540f163SKrzysztof Drewniak // instructions that are being replaced by operations on the resource and offset
1226540f163SKrzysztof Drewniak // parts.
1236540f163SKrzysztof Drewniak //
1246540f163SKrzysztof Drewniak // Split users do not necessarily need to produce parts themselves (
1256540f163SKrzysztof Drewniak // a `load float, ptr addrspace(7)` does not, for example), but, if they do not
1266540f163SKrzysztof Drewniak // generate fat buffer pointers, they must RAUW in their replacement
1276540f163SKrzysztof Drewniak // instructions during the initial visit.
1286540f163SKrzysztof Drewniak //
1296540f163SKrzysztof Drewniak // When these new instructions are created, they use the split parts recorded
1306540f163SKrzysztof Drewniak // for their initial arguments in order to generate their replacements, creating
1316540f163SKrzysztof Drewniak // a parallel set of instructions that does not refer to the original fat
1326540f163SKrzysztof Drewniak // pointer values but instead to their resource and offset components.
1336540f163SKrzysztof Drewniak //
1346540f163SKrzysztof Drewniak // Instructions, such as `extractvalue`, that produce buffer fat pointers from
1356540f163SKrzysztof Drewniak // sources that do not have split parts, have such parts generated using
1366540f163SKrzysztof Drewniak // `extractvalue`. This is also the initial handling of PHI nodes, which
1376540f163SKrzysztof Drewniak // are then cleaned up.
1386540f163SKrzysztof Drewniak //
1396540f163SKrzysztof Drewniak // ### Conditionals
1406540f163SKrzysztof Drewniak //
1416540f163SKrzysztof Drewniak // PHI nodes are initially given resource parts via `extractvalue`. However,
1426540f163SKrzysztof Drewniak // this is not an efficient rewrite of such nodes, as, in most cases, the
1436540f163SKrzysztof Drewniak // resource part in a conditional or loop remains constant throughout the loop
1446540f163SKrzysztof Drewniak // and only the offset varies. Failing to optimize away these constant resources
1456540f163SKrzysztof Drewniak // would cause additional registers to be sent around loops and might lead to
1466540f163SKrzysztof Drewniak // waterfall loops being generated for buffer operations due to the
1476540f163SKrzysztof Drewniak // "non-uniform" resource argument.
1486540f163SKrzysztof Drewniak //
1496540f163SKrzysztof Drewniak // Therefore, after all instructions have been visited, the pointer splitter
1506540f163SKrzysztof Drewniak // post-processes all encountered conditionals. Given a PHI node or select,
1516540f163SKrzysztof Drewniak // getPossibleRsrcRoots() collects all values that the resource parts of that
1526540f163SKrzysztof Drewniak // conditional's input could come from as well as collecting all conditional
1536540f163SKrzysztof Drewniak // instructions encountered during the search. If, after filtering out the
1546540f163SKrzysztof Drewniak // initial node itself, the set of encountered conditionals is a subset of the
1556540f163SKrzysztof Drewniak // potential roots and there is a single potential resource that isn't in the
1566540f163SKrzysztof Drewniak // conditional set, that value is the only possible value the resource argument
1576540f163SKrzysztof Drewniak // could have throughout the control flow.
1586540f163SKrzysztof Drewniak //
1596540f163SKrzysztof Drewniak // If that condition is met, then a PHI node can have its resource part changed
1606540f163SKrzysztof Drewniak // to the singleton value and then be replaced by a PHI on the offsets.
1616540f163SKrzysztof Drewniak // Otherwise, each PHI node is split into two, one for the resource part and one
1626540f163SKrzysztof Drewniak // for the offset part, which replace the temporary `extractvalue` instructions
1636540f163SKrzysztof Drewniak // that were added during the first pass.
1646540f163SKrzysztof Drewniak //
1656540f163SKrzysztof Drewniak // Similar logic applies to `select`, where
1666540f163SKrzysztof Drewniak // `%z = select i1 %cond, %cond, ptr addrspace(7) %x, ptr addrspace(7) %y`
1676540f163SKrzysztof Drewniak // can be split into `%z.rsrc = %x.rsrc` and
1686540f163SKrzysztof Drewniak // `%z.off = select i1 %cond, ptr i32 %x.off, i32 %y.off`
1696540f163SKrzysztof Drewniak // if both `%x` and `%y` have the same resource part, but two `select`
1706540f163SKrzysztof Drewniak // operations will be needed if they do not.
1716540f163SKrzysztof Drewniak //
1726540f163SKrzysztof Drewniak // ### Final processing
1736540f163SKrzysztof Drewniak //
1746540f163SKrzysztof Drewniak // After conditionals have been cleaned up, the IR for each function is
1756540f163SKrzysztof Drewniak // rewritten to remove all the old instructions that have been split up.
1766540f163SKrzysztof Drewniak //
1776540f163SKrzysztof Drewniak // Any instruction that used to produce a buffer fat pointer (and therefore now
1786540f163SKrzysztof Drewniak // produces a resource-and-offset struct after type remapping) is
1796540f163SKrzysztof Drewniak // replaced as follows:
1806540f163SKrzysztof Drewniak // 1. All debug value annotations are cloned to reflect that the resource part
1816540f163SKrzysztof Drewniak //    and offset parts are computed separately and constitute different
1826540f163SKrzysztof Drewniak //    fragments of the underlying source language variable.
1836540f163SKrzysztof Drewniak // 2. All uses that were themselves split are replaced by a `poison` of the
1846540f163SKrzysztof Drewniak //    struct type, as they will themselves be erased soon. This rule, combined
1856540f163SKrzysztof Drewniak //    with debug handling, should leave the use lists of split instructions
1866540f163SKrzysztof Drewniak //    empty in almost all cases.
1876540f163SKrzysztof Drewniak // 3. If a user of the original struct-valued result remains, the structure
1886540f163SKrzysztof Drewniak //    needed for the new types to work is constructed out of the newly-defined
1896540f163SKrzysztof Drewniak //    parts, and the original instruction is replaced by this structure
1906540f163SKrzysztof Drewniak //    before being erased. Instructions requiring this construction include
1916540f163SKrzysztof Drewniak //    `ret` and `insertvalue`.
1926540f163SKrzysztof Drewniak //
1936540f163SKrzysztof Drewniak // # Consequences
1946540f163SKrzysztof Drewniak //
1956540f163SKrzysztof Drewniak // This pass does not alter the CFG.
1966540f163SKrzysztof Drewniak //
1976540f163SKrzysztof Drewniak // Alias analysis information will become coarser, as the LLVM alias analyzer
1986540f163SKrzysztof Drewniak // cannot handle the buffer intrinsics. Specifically, while we can determine
1996540f163SKrzysztof Drewniak // that the following two loads do not alias:
2006540f163SKrzysztof Drewniak // ```
2016540f163SKrzysztof Drewniak //   %y = getelementptr i32, ptr addrspace(7) %x, i32 1
2026540f163SKrzysztof Drewniak //   %a = load i32, ptr addrspace(7) %x
2036540f163SKrzysztof Drewniak //   %b = load i32, ptr addrspace(7) %y
2046540f163SKrzysztof Drewniak // ```
2056540f163SKrzysztof Drewniak // we cannot (except through some code that runs during scheduling) determine
2066540f163SKrzysztof Drewniak // that the rewritten loads below do not alias.
2076540f163SKrzysztof Drewniak // ```
2086540f163SKrzysztof Drewniak //   %y.off = add i32 %x.off, 1
2096540f163SKrzysztof Drewniak //   %a = call @llvm.amdgcn.raw.ptr.buffer.load(ptr addrspace(8) %x.rsrc, i32
2106540f163SKrzysztof Drewniak //     %x.off, ...)
2116540f163SKrzysztof Drewniak //   %b = call @llvm.amdgcn.raw.ptr.buffer.load(ptr addrspace(8)
2126540f163SKrzysztof Drewniak //     %x.rsrc, i32 %y.off, ...)
2136540f163SKrzysztof Drewniak // ```
2146540f163SKrzysztof Drewniak // However, existing alias information is preserved.
2156540f163SKrzysztof Drewniak //===----------------------------------------------------------------------===//
2166540f163SKrzysztof Drewniak 
2176540f163SKrzysztof Drewniak #include "AMDGPU.h"
2186540f163SKrzysztof Drewniak #include "AMDGPUTargetMachine.h"
2196540f163SKrzysztof Drewniak #include "GCNSubtarget.h"
2206540f163SKrzysztof Drewniak #include "SIDefines.h"
2216540f163SKrzysztof Drewniak #include "llvm/ADT/SetOperations.h"
2226540f163SKrzysztof Drewniak #include "llvm/ADT/SmallVector.h"
2236540f163SKrzysztof Drewniak #include "llvm/Analysis/ConstantFolding.h"
2246fc63ab7SNikita Popov #include "llvm/Analysis/Utils/Local.h"
2256540f163SKrzysztof Drewniak #include "llvm/CodeGen/TargetPassConfig.h"
2266540f163SKrzysztof Drewniak #include "llvm/IR/AttributeMask.h"
2276540f163SKrzysztof Drewniak #include "llvm/IR/Constants.h"
2286540f163SKrzysztof Drewniak #include "llvm/IR/DebugInfo.h"
2296540f163SKrzysztof Drewniak #include "llvm/IR/DerivedTypes.h"
2306540f163SKrzysztof Drewniak #include "llvm/IR/IRBuilder.h"
2316540f163SKrzysztof Drewniak #include "llvm/IR/InstIterator.h"
2326540f163SKrzysztof Drewniak #include "llvm/IR/InstVisitor.h"
2336540f163SKrzysztof Drewniak #include "llvm/IR/Instructions.h"
2346540f163SKrzysztof Drewniak #include "llvm/IR/Intrinsics.h"
2356540f163SKrzysztof Drewniak #include "llvm/IR/IntrinsicsAMDGPU.h"
2366540f163SKrzysztof Drewniak #include "llvm/IR/Metadata.h"
2376540f163SKrzysztof Drewniak #include "llvm/IR/Operator.h"
2386540f163SKrzysztof Drewniak #include "llvm/IR/PatternMatch.h"
2395ef768d2SNikita Popov #include "llvm/IR/ReplaceConstant.h"
2406540f163SKrzysztof Drewniak #include "llvm/InitializePasses.h"
2416540f163SKrzysztof Drewniak #include "llvm/Pass.h"
242*697c1883SKrzysztof Drewniak #include "llvm/Support/Alignment.h"
2436540f163SKrzysztof Drewniak #include "llvm/Support/AtomicOrdering.h"
2446540f163SKrzysztof Drewniak #include "llvm/Support/Debug.h"
2456540f163SKrzysztof Drewniak #include "llvm/Support/ErrorHandling.h"
2466540f163SKrzysztof Drewniak #include "llvm/Transforms/Utils/Cloning.h"
2476540f163SKrzysztof Drewniak #include "llvm/Transforms/Utils/Local.h"
2486540f163SKrzysztof Drewniak #include "llvm/Transforms/Utils/ValueMapper.h"
2496540f163SKrzysztof Drewniak 
2506540f163SKrzysztof Drewniak #define DEBUG_TYPE "amdgpu-lower-buffer-fat-pointers"
2516540f163SKrzysztof Drewniak 
2526540f163SKrzysztof Drewniak using namespace llvm;
2536540f163SKrzysztof Drewniak 
2546540f163SKrzysztof Drewniak static constexpr unsigned BufferOffsetWidth = 32;
2556540f163SKrzysztof Drewniak 
2566540f163SKrzysztof Drewniak namespace {
2576540f163SKrzysztof Drewniak /// Recursively replace instances of ptr addrspace(7) and vector<Nxptr
2586540f163SKrzysztof Drewniak /// addrspace(7)> with some other type as defined by the relevant subclass.
2596540f163SKrzysztof Drewniak class BufferFatPtrTypeLoweringBase : public ValueMapTypeRemapper {
2606540f163SKrzysztof Drewniak   DenseMap<Type *, Type *> Map;
2616540f163SKrzysztof Drewniak 
2626540f163SKrzysztof Drewniak   Type *remapTypeImpl(Type *Ty, SmallPtrSetImpl<StructType *> &Seen);
2636540f163SKrzysztof Drewniak 
2646540f163SKrzysztof Drewniak protected:
2656540f163SKrzysztof Drewniak   virtual Type *remapScalar(PointerType *PT) = 0;
2666540f163SKrzysztof Drewniak   virtual Type *remapVector(VectorType *VT) = 0;
2676540f163SKrzysztof Drewniak 
2686540f163SKrzysztof Drewniak   const DataLayout &DL;
2696540f163SKrzysztof Drewniak 
2706540f163SKrzysztof Drewniak public:
2716540f163SKrzysztof Drewniak   BufferFatPtrTypeLoweringBase(const DataLayout &DL) : DL(DL) {}
2726540f163SKrzysztof Drewniak   Type *remapType(Type *SrcTy) override;
2736540f163SKrzysztof Drewniak   void clear() { Map.clear(); }
2746540f163SKrzysztof Drewniak };
2756540f163SKrzysztof Drewniak 
2766540f163SKrzysztof Drewniak /// Remap ptr addrspace(7) to i160 and vector<Nxptr addrspace(7)> to
2776540f163SKrzysztof Drewniak /// vector<Nxi60> in order to correctly handling loading/storing these values
2786540f163SKrzysztof Drewniak /// from memory.
2796540f163SKrzysztof Drewniak class BufferFatPtrToIntTypeMap : public BufferFatPtrTypeLoweringBase {
2806540f163SKrzysztof Drewniak   using BufferFatPtrTypeLoweringBase::BufferFatPtrTypeLoweringBase;
2816540f163SKrzysztof Drewniak 
2826540f163SKrzysztof Drewniak protected:
2836540f163SKrzysztof Drewniak   Type *remapScalar(PointerType *PT) override { return DL.getIntPtrType(PT); }
2846540f163SKrzysztof Drewniak   Type *remapVector(VectorType *VT) override { return DL.getIntPtrType(VT); }
2856540f163SKrzysztof Drewniak };
2866540f163SKrzysztof Drewniak 
2876540f163SKrzysztof Drewniak /// Remap ptr addrspace(7) to {ptr addrspace(8), i32} (the resource and offset
2886540f163SKrzysztof Drewniak /// parts of the pointer) so that we can easily rewrite operations on these
2896540f163SKrzysztof Drewniak /// values that aren't loading them from or storing them to memory.
2906540f163SKrzysztof Drewniak class BufferFatPtrToStructTypeMap : public BufferFatPtrTypeLoweringBase {
2916540f163SKrzysztof Drewniak   using BufferFatPtrTypeLoweringBase::BufferFatPtrTypeLoweringBase;
2926540f163SKrzysztof Drewniak 
2936540f163SKrzysztof Drewniak protected:
2946540f163SKrzysztof Drewniak   Type *remapScalar(PointerType *PT) override;
2956540f163SKrzysztof Drewniak   Type *remapVector(VectorType *VT) override;
2966540f163SKrzysztof Drewniak };
2976540f163SKrzysztof Drewniak } // namespace
2986540f163SKrzysztof Drewniak 
2996540f163SKrzysztof Drewniak // This code is adapted from the type remapper in lib/Linker/IRMover.cpp
3006540f163SKrzysztof Drewniak Type *BufferFatPtrTypeLoweringBase::remapTypeImpl(
3016540f163SKrzysztof Drewniak     Type *Ty, SmallPtrSetImpl<StructType *> &Seen) {
3026540f163SKrzysztof Drewniak   Type **Entry = &Map[Ty];
3036540f163SKrzysztof Drewniak   if (*Entry)
3046540f163SKrzysztof Drewniak     return *Entry;
3056540f163SKrzysztof Drewniak   if (auto *PT = dyn_cast<PointerType>(Ty)) {
3066540f163SKrzysztof Drewniak     if (PT->getAddressSpace() == AMDGPUAS::BUFFER_FAT_POINTER) {
3076540f163SKrzysztof Drewniak       return *Entry = remapScalar(PT);
3086540f163SKrzysztof Drewniak     }
3096540f163SKrzysztof Drewniak   }
3106540f163SKrzysztof Drewniak   if (auto *VT = dyn_cast<VectorType>(Ty)) {
3116540f163SKrzysztof Drewniak     auto *PT = dyn_cast<PointerType>(VT->getElementType());
3126540f163SKrzysztof Drewniak     if (PT && PT->getAddressSpace() == AMDGPUAS::BUFFER_FAT_POINTER) {
3136540f163SKrzysztof Drewniak       return *Entry = remapVector(VT);
3146540f163SKrzysztof Drewniak     }
3156540f163SKrzysztof Drewniak     return *Entry = Ty;
3166540f163SKrzysztof Drewniak   }
3176540f163SKrzysztof Drewniak   // Whether the type is one that is structurally uniqued - that is, if it is
3186540f163SKrzysztof Drewniak   // not a named struct (the only kind of type where multiple structurally
3196540f163SKrzysztof Drewniak   // identical types that have a distinct `Type*`)
3206540f163SKrzysztof Drewniak   StructType *TyAsStruct = dyn_cast<StructType>(Ty);
3216540f163SKrzysztof Drewniak   bool IsUniqued = !TyAsStruct || TyAsStruct->isLiteral();
3226540f163SKrzysztof Drewniak   // Base case for ints, floats, opaque pointers, and so on, which don't
3236540f163SKrzysztof Drewniak   // require recursion.
3246540f163SKrzysztof Drewniak   if (Ty->getNumContainedTypes() == 0 && IsUniqued)
3256540f163SKrzysztof Drewniak     return *Entry = Ty;
3266540f163SKrzysztof Drewniak   if (!IsUniqued) {
3276540f163SKrzysztof Drewniak     // Create a dummy type for recursion purposes.
3286540f163SKrzysztof Drewniak     if (!Seen.insert(TyAsStruct).second) {
3296540f163SKrzysztof Drewniak       StructType *Placeholder = StructType::create(Ty->getContext());
3306540f163SKrzysztof Drewniak       return *Entry = Placeholder;
3316540f163SKrzysztof Drewniak     }
3326540f163SKrzysztof Drewniak   }
3336540f163SKrzysztof Drewniak   bool Changed = false;
3346540f163SKrzysztof Drewniak   SmallVector<Type *> ElementTypes(Ty->getNumContainedTypes(), nullptr);
3356540f163SKrzysztof Drewniak   for (unsigned int I = 0, E = Ty->getNumContainedTypes(); I < E; ++I) {
3366540f163SKrzysztof Drewniak     Type *OldElem = Ty->getContainedType(I);
3376540f163SKrzysztof Drewniak     Type *NewElem = remapTypeImpl(OldElem, Seen);
3386540f163SKrzysztof Drewniak     ElementTypes[I] = NewElem;
3396540f163SKrzysztof Drewniak     Changed |= (OldElem != NewElem);
3406540f163SKrzysztof Drewniak   }
3416540f163SKrzysztof Drewniak   // Recursive calls to remapTypeImpl() may have invalidated pointer.
3426540f163SKrzysztof Drewniak   Entry = &Map[Ty];
3436540f163SKrzysztof Drewniak   if (!Changed) {
3446540f163SKrzysztof Drewniak     return *Entry = Ty;
3456540f163SKrzysztof Drewniak   }
3466540f163SKrzysztof Drewniak   if (auto *ArrTy = dyn_cast<ArrayType>(Ty))
3476540f163SKrzysztof Drewniak     return *Entry = ArrayType::get(ElementTypes[0], ArrTy->getNumElements());
3486540f163SKrzysztof Drewniak   if (auto *FnTy = dyn_cast<FunctionType>(Ty))
3496540f163SKrzysztof Drewniak     return *Entry = FunctionType::get(ElementTypes[0],
3506540f163SKrzysztof Drewniak                                       ArrayRef(ElementTypes).slice(1),
3516540f163SKrzysztof Drewniak                                       FnTy->isVarArg());
3526540f163SKrzysztof Drewniak   if (auto *STy = dyn_cast<StructType>(Ty)) {
3536540f163SKrzysztof Drewniak     // Genuine opaque types don't have a remapping.
3546540f163SKrzysztof Drewniak     if (STy->isOpaque())
3556540f163SKrzysztof Drewniak       return *Entry = Ty;
3566540f163SKrzysztof Drewniak     bool IsPacked = STy->isPacked();
3576540f163SKrzysztof Drewniak     if (IsUniqued)
3586540f163SKrzysztof Drewniak       return *Entry = StructType::get(Ty->getContext(), ElementTypes, IsPacked);
3596540f163SKrzysztof Drewniak     SmallString<16> Name(STy->getName());
3606540f163SKrzysztof Drewniak     STy->setName("");
3616540f163SKrzysztof Drewniak     Type **RecursionEntry = &Map[Ty];
3626540f163SKrzysztof Drewniak     if (*RecursionEntry) {
3636540f163SKrzysztof Drewniak       auto *Placeholder = cast<StructType>(*RecursionEntry);
3646540f163SKrzysztof Drewniak       Placeholder->setBody(ElementTypes, IsPacked);
3656540f163SKrzysztof Drewniak       Placeholder->setName(Name);
3666540f163SKrzysztof Drewniak       return *Entry = Placeholder;
3676540f163SKrzysztof Drewniak     }
3686540f163SKrzysztof Drewniak     return *Entry = StructType::create(Ty->getContext(), ElementTypes, Name,
3696540f163SKrzysztof Drewniak                                        IsPacked);
3706540f163SKrzysztof Drewniak   }
3716540f163SKrzysztof Drewniak   llvm_unreachable("Unknown type of type that contains elements");
3726540f163SKrzysztof Drewniak }
3736540f163SKrzysztof Drewniak 
3746540f163SKrzysztof Drewniak Type *BufferFatPtrTypeLoweringBase::remapType(Type *SrcTy) {
3756540f163SKrzysztof Drewniak   SmallPtrSet<StructType *, 2> Visited;
3766540f163SKrzysztof Drewniak   return remapTypeImpl(SrcTy, Visited);
3776540f163SKrzysztof Drewniak }
3786540f163SKrzysztof Drewniak 
3796540f163SKrzysztof Drewniak Type *BufferFatPtrToStructTypeMap::remapScalar(PointerType *PT) {
3806540f163SKrzysztof Drewniak   LLVMContext &Ctx = PT->getContext();
3816540f163SKrzysztof Drewniak   return StructType::get(PointerType::get(Ctx, AMDGPUAS::BUFFER_RESOURCE),
3826540f163SKrzysztof Drewniak                          IntegerType::get(Ctx, BufferOffsetWidth));
3836540f163SKrzysztof Drewniak }
3846540f163SKrzysztof Drewniak 
3856540f163SKrzysztof Drewniak Type *BufferFatPtrToStructTypeMap::remapVector(VectorType *VT) {
3866540f163SKrzysztof Drewniak   ElementCount EC = VT->getElementCount();
3876540f163SKrzysztof Drewniak   LLVMContext &Ctx = VT->getContext();
3886540f163SKrzysztof Drewniak   Type *RsrcVec =
3896540f163SKrzysztof Drewniak       VectorType::get(PointerType::get(Ctx, AMDGPUAS::BUFFER_RESOURCE), EC);
3906540f163SKrzysztof Drewniak   Type *OffVec = VectorType::get(IntegerType::get(Ctx, BufferOffsetWidth), EC);
3916540f163SKrzysztof Drewniak   return StructType::get(RsrcVec, OffVec);
3926540f163SKrzysztof Drewniak }
3936540f163SKrzysztof Drewniak 
3946540f163SKrzysztof Drewniak static bool isBufferFatPtrOrVector(Type *Ty) {
3956540f163SKrzysztof Drewniak   if (auto *PT = dyn_cast<PointerType>(Ty->getScalarType()))
3966540f163SKrzysztof Drewniak     return PT->getAddressSpace() == AMDGPUAS::BUFFER_FAT_POINTER;
3976540f163SKrzysztof Drewniak   return false;
3986540f163SKrzysztof Drewniak }
3996540f163SKrzysztof Drewniak 
4006540f163SKrzysztof Drewniak // True if the type is {ptr addrspace(8), i32} or a struct containing vectors of
4016540f163SKrzysztof Drewniak // those types. Used to quickly skip instructions we don't need to process.
4026540f163SKrzysztof Drewniak static bool isSplitFatPtr(Type *Ty) {
4036540f163SKrzysztof Drewniak   auto *ST = dyn_cast<StructType>(Ty);
4046540f163SKrzysztof Drewniak   if (!ST)
4056540f163SKrzysztof Drewniak     return false;
4066540f163SKrzysztof Drewniak   if (!ST->isLiteral() || ST->getNumElements() != 2)
4076540f163SKrzysztof Drewniak     return false;
4086540f163SKrzysztof Drewniak   auto *MaybeRsrc =
4096540f163SKrzysztof Drewniak       dyn_cast<PointerType>(ST->getElementType(0)->getScalarType());
4106540f163SKrzysztof Drewniak   auto *MaybeOff =
4116540f163SKrzysztof Drewniak       dyn_cast<IntegerType>(ST->getElementType(1)->getScalarType());
4126540f163SKrzysztof Drewniak   return MaybeRsrc && MaybeOff &&
4136540f163SKrzysztof Drewniak          MaybeRsrc->getAddressSpace() == AMDGPUAS::BUFFER_RESOURCE &&
4146540f163SKrzysztof Drewniak          MaybeOff->getBitWidth() == BufferOffsetWidth;
4156540f163SKrzysztof Drewniak }
4166540f163SKrzysztof Drewniak 
4176540f163SKrzysztof Drewniak // True if the result type or any argument types are buffer fat pointers.
4186540f163SKrzysztof Drewniak static bool isBufferFatPtrConst(Constant *C) {
4196540f163SKrzysztof Drewniak   Type *T = C->getType();
4206540f163SKrzysztof Drewniak   return isBufferFatPtrOrVector(T) || any_of(C->operands(), [](const Use &U) {
4216540f163SKrzysztof Drewniak            return isBufferFatPtrOrVector(U.get()->getType());
4226540f163SKrzysztof Drewniak          });
4236540f163SKrzysztof Drewniak }
4246540f163SKrzysztof Drewniak 
4256540f163SKrzysztof Drewniak namespace {
4266540f163SKrzysztof Drewniak /// Convert [vectors of] buffer fat pointers to integers when they are read from
4276540f163SKrzysztof Drewniak /// or stored to memory. This ensures that these pointers will have the same
4286540f163SKrzysztof Drewniak /// memory layout as before they are lowered, even though they will no longer
4296540f163SKrzysztof Drewniak /// have their previous layout in registers/in the program (they'll be broken
4306540f163SKrzysztof Drewniak /// down into resource and offset parts). This has the downside of imposing
4316540f163SKrzysztof Drewniak /// marshalling costs when reading or storing these values, but since placing
4326540f163SKrzysztof Drewniak /// such pointers into memory is an uncommon operation at best, we feel that
4336540f163SKrzysztof Drewniak /// this cost is acceptable for better performance in the common case.
4346540f163SKrzysztof Drewniak class StoreFatPtrsAsIntsVisitor
4356540f163SKrzysztof Drewniak     : public InstVisitor<StoreFatPtrsAsIntsVisitor, bool> {
4366540f163SKrzysztof Drewniak   BufferFatPtrToIntTypeMap *TypeMap;
4376540f163SKrzysztof Drewniak 
4386540f163SKrzysztof Drewniak   ValueToValueMapTy ConvertedForStore;
4396540f163SKrzysztof Drewniak 
4406540f163SKrzysztof Drewniak   IRBuilder<> IRB;
4416540f163SKrzysztof Drewniak 
4426540f163SKrzysztof Drewniak   // Convert all the buffer fat pointers within the input value to inttegers
4436540f163SKrzysztof Drewniak   // so that it can be stored in memory.
4446540f163SKrzysztof Drewniak   Value *fatPtrsToInts(Value *V, Type *From, Type *To, const Twine &Name);
4456540f163SKrzysztof Drewniak   // Convert all the i160s that need to be buffer fat pointers (as specified)
4466540f163SKrzysztof Drewniak   // by the To type) into those pointers to preserve the semantics of the rest
4476540f163SKrzysztof Drewniak   // of the program.
4486540f163SKrzysztof Drewniak   Value *intsToFatPtrs(Value *V, Type *From, Type *To, const Twine &Name);
4496540f163SKrzysztof Drewniak 
4506540f163SKrzysztof Drewniak public:
4516540f163SKrzysztof Drewniak   StoreFatPtrsAsIntsVisitor(BufferFatPtrToIntTypeMap *TypeMap, LLVMContext &Ctx)
4526540f163SKrzysztof Drewniak       : TypeMap(TypeMap), IRB(Ctx) {}
4536540f163SKrzysztof Drewniak   bool processFunction(Function &F);
4546540f163SKrzysztof Drewniak 
4556540f163SKrzysztof Drewniak   bool visitInstruction(Instruction &I) { return false; }
4566540f163SKrzysztof Drewniak   bool visitAllocaInst(AllocaInst &I);
4576540f163SKrzysztof Drewniak   bool visitLoadInst(LoadInst &LI);
4586540f163SKrzysztof Drewniak   bool visitStoreInst(StoreInst &SI);
4596540f163SKrzysztof Drewniak   bool visitGetElementPtrInst(GetElementPtrInst &I);
4606540f163SKrzysztof Drewniak };
4616540f163SKrzysztof Drewniak } // namespace
4626540f163SKrzysztof Drewniak 
4636540f163SKrzysztof Drewniak Value *StoreFatPtrsAsIntsVisitor::fatPtrsToInts(Value *V, Type *From, Type *To,
4646540f163SKrzysztof Drewniak                                                 const Twine &Name) {
4656540f163SKrzysztof Drewniak   if (From == To)
4666540f163SKrzysztof Drewniak     return V;
4676540f163SKrzysztof Drewniak   ValueToValueMapTy::iterator Find = ConvertedForStore.find(V);
4686540f163SKrzysztof Drewniak   if (Find != ConvertedForStore.end())
4696540f163SKrzysztof Drewniak     return Find->second;
4706540f163SKrzysztof Drewniak   if (isBufferFatPtrOrVector(From)) {
4716540f163SKrzysztof Drewniak     Value *Cast = IRB.CreatePtrToInt(V, To, Name + ".int");
4726540f163SKrzysztof Drewniak     ConvertedForStore[V] = Cast;
4736540f163SKrzysztof Drewniak     return Cast;
4746540f163SKrzysztof Drewniak   }
4756540f163SKrzysztof Drewniak   if (From->getNumContainedTypes() == 0)
4766540f163SKrzysztof Drewniak     return V;
4776540f163SKrzysztof Drewniak   // Structs, arrays, and other compound types.
4786540f163SKrzysztof Drewniak   Value *Ret = PoisonValue::get(To);
4796540f163SKrzysztof Drewniak   if (auto *AT = dyn_cast<ArrayType>(From)) {
4806540f163SKrzysztof Drewniak     Type *FromPart = AT->getArrayElementType();
4816540f163SKrzysztof Drewniak     Type *ToPart = cast<ArrayType>(To)->getElementType();
4826540f163SKrzysztof Drewniak     for (uint64_t I = 0, E = AT->getArrayNumElements(); I < E; ++I) {
4836540f163SKrzysztof Drewniak       Value *Field = IRB.CreateExtractValue(V, I);
4846540f163SKrzysztof Drewniak       Value *NewField =
4856540f163SKrzysztof Drewniak           fatPtrsToInts(Field, FromPart, ToPart, Name + "." + Twine(I));
4866540f163SKrzysztof Drewniak       Ret = IRB.CreateInsertValue(Ret, NewField, I);
4876540f163SKrzysztof Drewniak     }
4886540f163SKrzysztof Drewniak   } else {
4896540f163SKrzysztof Drewniak     for (auto [Idx, FromPart, ToPart] :
4906540f163SKrzysztof Drewniak          enumerate(From->subtypes(), To->subtypes())) {
4916540f163SKrzysztof Drewniak       Value *Field = IRB.CreateExtractValue(V, Idx);
4926540f163SKrzysztof Drewniak       Value *NewField =
4936540f163SKrzysztof Drewniak           fatPtrsToInts(Field, FromPart, ToPart, Name + "." + Twine(Idx));
4946540f163SKrzysztof Drewniak       Ret = IRB.CreateInsertValue(Ret, NewField, Idx);
4956540f163SKrzysztof Drewniak     }
4966540f163SKrzysztof Drewniak   }
4976540f163SKrzysztof Drewniak   ConvertedForStore[V] = Ret;
4986540f163SKrzysztof Drewniak   return Ret;
4996540f163SKrzysztof Drewniak }
5006540f163SKrzysztof Drewniak 
5016540f163SKrzysztof Drewniak Value *StoreFatPtrsAsIntsVisitor::intsToFatPtrs(Value *V, Type *From, Type *To,
5026540f163SKrzysztof Drewniak                                                 const Twine &Name) {
5036540f163SKrzysztof Drewniak   if (From == To)
5046540f163SKrzysztof Drewniak     return V;
5056540f163SKrzysztof Drewniak   if (isBufferFatPtrOrVector(To)) {
5066540f163SKrzysztof Drewniak     Value *Cast = IRB.CreateIntToPtr(V, To, Name + ".ptr");
5076540f163SKrzysztof Drewniak     return Cast;
5086540f163SKrzysztof Drewniak   }
5096540f163SKrzysztof Drewniak   if (From->getNumContainedTypes() == 0)
5106540f163SKrzysztof Drewniak     return V;
5116540f163SKrzysztof Drewniak   // Structs, arrays, and other compound types.
5126540f163SKrzysztof Drewniak   Value *Ret = PoisonValue::get(To);
5136540f163SKrzysztof Drewniak   if (auto *AT = dyn_cast<ArrayType>(From)) {
5146540f163SKrzysztof Drewniak     Type *FromPart = AT->getArrayElementType();
5156540f163SKrzysztof Drewniak     Type *ToPart = cast<ArrayType>(To)->getElementType();
5166540f163SKrzysztof Drewniak     for (uint64_t I = 0, E = AT->getArrayNumElements(); I < E; ++I) {
5176540f163SKrzysztof Drewniak       Value *Field = IRB.CreateExtractValue(V, I);
5186540f163SKrzysztof Drewniak       Value *NewField =
5196540f163SKrzysztof Drewniak           intsToFatPtrs(Field, FromPart, ToPart, Name + "." + Twine(I));
5206540f163SKrzysztof Drewniak       Ret = IRB.CreateInsertValue(Ret, NewField, I);
5216540f163SKrzysztof Drewniak     }
5226540f163SKrzysztof Drewniak   } else {
5236540f163SKrzysztof Drewniak     for (auto [Idx, FromPart, ToPart] :
5246540f163SKrzysztof Drewniak          enumerate(From->subtypes(), To->subtypes())) {
5256540f163SKrzysztof Drewniak       Value *Field = IRB.CreateExtractValue(V, Idx);
5266540f163SKrzysztof Drewniak       Value *NewField =
5276540f163SKrzysztof Drewniak           intsToFatPtrs(Field, FromPart, ToPart, Name + "." + Twine(Idx));
5286540f163SKrzysztof Drewniak       Ret = IRB.CreateInsertValue(Ret, NewField, Idx);
5296540f163SKrzysztof Drewniak     }
5306540f163SKrzysztof Drewniak   }
5316540f163SKrzysztof Drewniak   return Ret;
5326540f163SKrzysztof Drewniak }
5336540f163SKrzysztof Drewniak 
5346540f163SKrzysztof Drewniak bool StoreFatPtrsAsIntsVisitor::processFunction(Function &F) {
5356540f163SKrzysztof Drewniak   bool Changed = false;
5366540f163SKrzysztof Drewniak   // The visitors will mutate GEPs and allocas, but will push loads and stores
5376540f163SKrzysztof Drewniak   // to the worklist to avoid invalidation.
5386540f163SKrzysztof Drewniak   for (Instruction &I : make_early_inc_range(instructions(F))) {
5396540f163SKrzysztof Drewniak     Changed |= visit(I);
5406540f163SKrzysztof Drewniak   }
5416540f163SKrzysztof Drewniak   ConvertedForStore.clear();
5426540f163SKrzysztof Drewniak   return Changed;
5436540f163SKrzysztof Drewniak }
5446540f163SKrzysztof Drewniak 
5456540f163SKrzysztof Drewniak bool StoreFatPtrsAsIntsVisitor::visitAllocaInst(AllocaInst &I) {
5466540f163SKrzysztof Drewniak   Type *Ty = I.getAllocatedType();
5476540f163SKrzysztof Drewniak   Type *NewTy = TypeMap->remapType(Ty);
5486540f163SKrzysztof Drewniak   if (Ty == NewTy)
5496540f163SKrzysztof Drewniak     return false;
5506540f163SKrzysztof Drewniak   I.setAllocatedType(NewTy);
5516540f163SKrzysztof Drewniak   return true;
5526540f163SKrzysztof Drewniak }
5536540f163SKrzysztof Drewniak 
5546540f163SKrzysztof Drewniak bool StoreFatPtrsAsIntsVisitor::visitGetElementPtrInst(GetElementPtrInst &I) {
5556540f163SKrzysztof Drewniak   Type *Ty = I.getSourceElementType();
5566540f163SKrzysztof Drewniak   Type *NewTy = TypeMap->remapType(Ty);
5576540f163SKrzysztof Drewniak   if (Ty == NewTy)
5586540f163SKrzysztof Drewniak     return false;
5596540f163SKrzysztof Drewniak   // We'll be rewriting the type `ptr addrspace(7)` out of existence soon, so
5606540f163SKrzysztof Drewniak   // make sure GEPs don't have different semantics with the new type.
5616540f163SKrzysztof Drewniak   I.setSourceElementType(NewTy);
5626540f163SKrzysztof Drewniak   I.setResultElementType(TypeMap->remapType(I.getResultElementType()));
5636540f163SKrzysztof Drewniak   return true;
5646540f163SKrzysztof Drewniak }
5656540f163SKrzysztof Drewniak 
5666540f163SKrzysztof Drewniak bool StoreFatPtrsAsIntsVisitor::visitLoadInst(LoadInst &LI) {
5676540f163SKrzysztof Drewniak   Type *Ty = LI.getType();
5686540f163SKrzysztof Drewniak   Type *IntTy = TypeMap->remapType(Ty);
5696540f163SKrzysztof Drewniak   if (Ty == IntTy)
5706540f163SKrzysztof Drewniak     return false;
5716540f163SKrzysztof Drewniak 
5726540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&LI);
5736540f163SKrzysztof Drewniak   auto *NLI = cast<LoadInst>(LI.clone());
5746540f163SKrzysztof Drewniak   NLI->mutateType(IntTy);
5756540f163SKrzysztof Drewniak   NLI = IRB.Insert(NLI);
5766540f163SKrzysztof Drewniak   NLI->takeName(&LI);
5776540f163SKrzysztof Drewniak 
5786540f163SKrzysztof Drewniak   Value *CastBack = intsToFatPtrs(NLI, IntTy, Ty, NLI->getName());
5796540f163SKrzysztof Drewniak   LI.replaceAllUsesWith(CastBack);
5806540f163SKrzysztof Drewniak   LI.eraseFromParent();
5816540f163SKrzysztof Drewniak   return true;
5826540f163SKrzysztof Drewniak }
5836540f163SKrzysztof Drewniak 
5846540f163SKrzysztof Drewniak bool StoreFatPtrsAsIntsVisitor::visitStoreInst(StoreInst &SI) {
5856540f163SKrzysztof Drewniak   Value *V = SI.getValueOperand();
5866540f163SKrzysztof Drewniak   Type *Ty = V->getType();
5876540f163SKrzysztof Drewniak   Type *IntTy = TypeMap->remapType(Ty);
5886540f163SKrzysztof Drewniak   if (Ty == IntTy)
5896540f163SKrzysztof Drewniak     return false;
5906540f163SKrzysztof Drewniak 
5916540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&SI);
5926540f163SKrzysztof Drewniak   Value *IntV = fatPtrsToInts(V, Ty, IntTy, V->getName());
5936540f163SKrzysztof Drewniak   for (auto *Dbg : at::getAssignmentMarkers(&SI))
5946540f163SKrzysztof Drewniak     Dbg->setValue(IntV);
5956540f163SKrzysztof Drewniak 
5966540f163SKrzysztof Drewniak   SI.setOperand(0, IntV);
5976540f163SKrzysztof Drewniak   return true;
5986540f163SKrzysztof Drewniak }
5996540f163SKrzysztof Drewniak 
600*697c1883SKrzysztof Drewniak namespace {
601*697c1883SKrzysztof Drewniak /// Convert loads/stores of types that the buffer intrinsics can't handle into
602*697c1883SKrzysztof Drewniak /// one ore more such loads/stores that consist of legal types.
603*697c1883SKrzysztof Drewniak ///
604*697c1883SKrzysztof Drewniak /// Do this by
605*697c1883SKrzysztof Drewniak /// 1. Recursing into structs (and arrays that don't share a memory layout with
606*697c1883SKrzysztof Drewniak /// vectors) since the intrinsics can't handle complex types.
607*697c1883SKrzysztof Drewniak /// 2. Converting arrays of non-aggregate, byte-sized types into their
608*697c1883SKrzysztof Drewniak /// corresponding vectors
609*697c1883SKrzysztof Drewniak /// 3. Bitcasting unsupported types, namely overly-long scalars and byte
610*697c1883SKrzysztof Drewniak /// vectors, into vectors of supported types.
611*697c1883SKrzysztof Drewniak /// 4. Splitting up excessively long reads/writes into multiple operations.
612*697c1883SKrzysztof Drewniak ///
613*697c1883SKrzysztof Drewniak /// Note that this doesn't handle complex data strucures, but, in the future,
614*697c1883SKrzysztof Drewniak /// the aggregate load splitter from SROA could be refactored to allow for that
615*697c1883SKrzysztof Drewniak /// case.
616*697c1883SKrzysztof Drewniak class LegalizeBufferContentTypesVisitor
617*697c1883SKrzysztof Drewniak     : public InstVisitor<LegalizeBufferContentTypesVisitor, bool> {
618*697c1883SKrzysztof Drewniak   friend class InstVisitor<LegalizeBufferContentTypesVisitor, bool>;
619*697c1883SKrzysztof Drewniak 
620*697c1883SKrzysztof Drewniak   IRBuilder<> IRB;
621*697c1883SKrzysztof Drewniak 
622*697c1883SKrzysztof Drewniak   const DataLayout &DL;
623*697c1883SKrzysztof Drewniak 
624*697c1883SKrzysztof Drewniak   /// If T is [N x U], where U is a scalar type, return the vector type
625*697c1883SKrzysztof Drewniak   /// <N x U>, otherwise, return T.
626*697c1883SKrzysztof Drewniak   Type *scalarArrayTypeAsVector(Type *MaybeArrayType);
627*697c1883SKrzysztof Drewniak   Value *arrayToVector(Value *V, Type *TargetType, const Twine &Name);
628*697c1883SKrzysztof Drewniak   Value *vectorToArray(Value *V, Type *OrigType, const Twine &Name);
629*697c1883SKrzysztof Drewniak 
630*697c1883SKrzysztof Drewniak   /// Break up the loads of a struct into the loads of its components
631*697c1883SKrzysztof Drewniak 
632*697c1883SKrzysztof Drewniak   /// Convert a vector or scalar type that can't be operated on by buffer
633*697c1883SKrzysztof Drewniak   /// intrinsics to one that would be legal through bitcasts and/or truncation.
634*697c1883SKrzysztof Drewniak   /// Uses the wider of i32, i16, or i8 where possible.
635*697c1883SKrzysztof Drewniak   Type *legalNonAggregateFor(Type *T);
636*697c1883SKrzysztof Drewniak   Value *makeLegalNonAggregate(Value *V, Type *TargetType, const Twine &Name);
637*697c1883SKrzysztof Drewniak   Value *makeIllegalNonAggregate(Value *V, Type *OrigType, const Twine &Name);
638*697c1883SKrzysztof Drewniak 
639*697c1883SKrzysztof Drewniak   struct VecSlice {
640*697c1883SKrzysztof Drewniak     uint64_t Index = 0;
641*697c1883SKrzysztof Drewniak     uint64_t Length = 0;
642*697c1883SKrzysztof Drewniak     VecSlice() = delete;
643*697c1883SKrzysztof Drewniak     // Needed for some Clangs
644*697c1883SKrzysztof Drewniak     VecSlice(uint64_t Index, uint64_t Length) : Index(Index), Length(Length) {}
645*697c1883SKrzysztof Drewniak   };
646*697c1883SKrzysztof Drewniak   /// Return the [index, length] pairs into which `T` needs to be cut to form
647*697c1883SKrzysztof Drewniak   /// legal buffer load or store operations. Clears `Slices`. Creates an empty
648*697c1883SKrzysztof Drewniak   /// `Slices` for non-vector inputs and creates one slice if no slicing will be
649*697c1883SKrzysztof Drewniak   /// needed.
650*697c1883SKrzysztof Drewniak   void getVecSlices(Type *T, SmallVectorImpl<VecSlice> &Slices);
651*697c1883SKrzysztof Drewniak 
652*697c1883SKrzysztof Drewniak   Value *extractSlice(Value *Vec, VecSlice S, const Twine &Name);
653*697c1883SKrzysztof Drewniak   Value *insertSlice(Value *Whole, Value *Part, VecSlice S, const Twine &Name);
654*697c1883SKrzysztof Drewniak 
655*697c1883SKrzysztof Drewniak   /// In most cases, return `LegalType`. However, when given an input that would
656*697c1883SKrzysztof Drewniak   /// normally be a legal type for the buffer intrinsics to return but that
657*697c1883SKrzysztof Drewniak   /// isn't hooked up through SelectionDAG, return a type of the same width that
658*697c1883SKrzysztof Drewniak   /// can be used with the relevant intrinsics. Specifically, handle the cases:
659*697c1883SKrzysztof Drewniak   /// - <1 x T> => T for all T
660*697c1883SKrzysztof Drewniak   /// - <N x i8> <=> i16, i32, 2xi32, 4xi32 (as needed)
661*697c1883SKrzysztof Drewniak   /// - <N x T> where T is under 32 bits and the total size is 96 bits <=> <3 x
662*697c1883SKrzysztof Drewniak   /// i32>
663*697c1883SKrzysztof Drewniak   Type *intrinsicTypeFor(Type *LegalType);
664*697c1883SKrzysztof Drewniak 
665*697c1883SKrzysztof Drewniak   bool visitLoadImpl(LoadInst &OrigLI, Type *PartType,
666*697c1883SKrzysztof Drewniak                      SmallVectorImpl<uint32_t> &AggIdxs, uint64_t AggByteOffset,
667*697c1883SKrzysztof Drewniak                      Value *&Result, const Twine &Name);
668*697c1883SKrzysztof Drewniak   /// Return value is (Changed, ModifiedInPlace)
669*697c1883SKrzysztof Drewniak   std::pair<bool, bool> visitStoreImpl(StoreInst &OrigSI, Type *PartType,
670*697c1883SKrzysztof Drewniak                                        SmallVectorImpl<uint32_t> &AggIdxs,
671*697c1883SKrzysztof Drewniak                                        uint64_t AggByteOffset,
672*697c1883SKrzysztof Drewniak                                        const Twine &Name);
673*697c1883SKrzysztof Drewniak 
674*697c1883SKrzysztof Drewniak   bool visitInstruction(Instruction &I) { return false; }
675*697c1883SKrzysztof Drewniak   bool visitLoadInst(LoadInst &LI);
676*697c1883SKrzysztof Drewniak   bool visitStoreInst(StoreInst &SI);
677*697c1883SKrzysztof Drewniak 
678*697c1883SKrzysztof Drewniak public:
679*697c1883SKrzysztof Drewniak   LegalizeBufferContentTypesVisitor(const DataLayout &DL, LLVMContext &Ctx)
680*697c1883SKrzysztof Drewniak       : IRB(Ctx), DL(DL) {}
681*697c1883SKrzysztof Drewniak   bool processFunction(Function &F);
682*697c1883SKrzysztof Drewniak };
683*697c1883SKrzysztof Drewniak } // namespace
684*697c1883SKrzysztof Drewniak 
685*697c1883SKrzysztof Drewniak Type *LegalizeBufferContentTypesVisitor::scalarArrayTypeAsVector(Type *T) {
686*697c1883SKrzysztof Drewniak   ArrayType *AT = dyn_cast<ArrayType>(T);
687*697c1883SKrzysztof Drewniak   if (!AT)
688*697c1883SKrzysztof Drewniak     return T;
689*697c1883SKrzysztof Drewniak   Type *ET = AT->getElementType();
690*697c1883SKrzysztof Drewniak   if (!ET->isSingleValueType() || isa<VectorType>(ET))
691*697c1883SKrzysztof Drewniak     report_fatal_error("loading non-scalar arrays from buffer fat pointers "
692*697c1883SKrzysztof Drewniak                        "should have recursed");
693*697c1883SKrzysztof Drewniak   if (!DL.typeSizeEqualsStoreSize(AT))
694*697c1883SKrzysztof Drewniak     report_fatal_error(
695*697c1883SKrzysztof Drewniak         "loading padded arrays from buffer fat pinters should have recursed");
696*697c1883SKrzysztof Drewniak   return FixedVectorType::get(ET, AT->getNumElements());
697*697c1883SKrzysztof Drewniak }
698*697c1883SKrzysztof Drewniak 
699*697c1883SKrzysztof Drewniak Value *LegalizeBufferContentTypesVisitor::arrayToVector(Value *V,
700*697c1883SKrzysztof Drewniak                                                         Type *TargetType,
701*697c1883SKrzysztof Drewniak                                                         const Twine &Name) {
702*697c1883SKrzysztof Drewniak   Value *VectorRes = PoisonValue::get(TargetType);
703*697c1883SKrzysztof Drewniak   auto *VT = cast<FixedVectorType>(TargetType);
704*697c1883SKrzysztof Drewniak   unsigned EC = VT->getNumElements();
705*697c1883SKrzysztof Drewniak   for (auto I : iota_range<unsigned>(0, EC, /*Inclusive=*/false)) {
706*697c1883SKrzysztof Drewniak     Value *Elem = IRB.CreateExtractValue(V, I, Name + ".elem." + Twine(I));
707*697c1883SKrzysztof Drewniak     VectorRes = IRB.CreateInsertElement(VectorRes, Elem, I,
708*697c1883SKrzysztof Drewniak                                         Name + ".as.vec." + Twine(I));
709*697c1883SKrzysztof Drewniak   }
710*697c1883SKrzysztof Drewniak   return VectorRes;
711*697c1883SKrzysztof Drewniak }
712*697c1883SKrzysztof Drewniak 
713*697c1883SKrzysztof Drewniak Value *LegalizeBufferContentTypesVisitor::vectorToArray(Value *V,
714*697c1883SKrzysztof Drewniak                                                         Type *OrigType,
715*697c1883SKrzysztof Drewniak                                                         const Twine &Name) {
716*697c1883SKrzysztof Drewniak   Value *ArrayRes = PoisonValue::get(OrigType);
717*697c1883SKrzysztof Drewniak   ArrayType *AT = cast<ArrayType>(OrigType);
718*697c1883SKrzysztof Drewniak   unsigned EC = AT->getNumElements();
719*697c1883SKrzysztof Drewniak   for (auto I : iota_range<unsigned>(0, EC, /*Inclusive=*/false)) {
720*697c1883SKrzysztof Drewniak     Value *Elem = IRB.CreateExtractElement(V, I, Name + ".elem." + Twine(I));
721*697c1883SKrzysztof Drewniak     ArrayRes = IRB.CreateInsertValue(ArrayRes, Elem, I,
722*697c1883SKrzysztof Drewniak                                      Name + ".as.array." + Twine(I));
723*697c1883SKrzysztof Drewniak   }
724*697c1883SKrzysztof Drewniak   return ArrayRes;
725*697c1883SKrzysztof Drewniak }
726*697c1883SKrzysztof Drewniak 
727*697c1883SKrzysztof Drewniak Type *LegalizeBufferContentTypesVisitor::legalNonAggregateFor(Type *T) {
728*697c1883SKrzysztof Drewniak   TypeSize Size = DL.getTypeStoreSizeInBits(T);
729*697c1883SKrzysztof Drewniak   // Implicitly zero-extend to the next byte if needed
730*697c1883SKrzysztof Drewniak   if (!DL.typeSizeEqualsStoreSize(T))
731*697c1883SKrzysztof Drewniak     T = IRB.getIntNTy(Size.getFixedValue());
732*697c1883SKrzysztof Drewniak   Type *ElemTy = T->getScalarType();
733*697c1883SKrzysztof Drewniak   if (isa<PointerType, ScalableVectorType>(ElemTy)) {
734*697c1883SKrzysztof Drewniak     // Pointers are always big enough, and we'll let scalable vectors through to
735*697c1883SKrzysztof Drewniak     // fail in codegen.
736*697c1883SKrzysztof Drewniak     return T;
737*697c1883SKrzysztof Drewniak   }
738*697c1883SKrzysztof Drewniak   unsigned ElemSize = DL.getTypeSizeInBits(ElemTy).getFixedValue();
739*697c1883SKrzysztof Drewniak   if (isPowerOf2_32(ElemSize) && ElemSize >= 16 && ElemSize <= 128) {
740*697c1883SKrzysztof Drewniak     // [vectors of] anything that's 16/32/64/128 bits can be cast and split into
741*697c1883SKrzysztof Drewniak     // legal buffer operations.
742*697c1883SKrzysztof Drewniak     return T;
743*697c1883SKrzysztof Drewniak   }
744*697c1883SKrzysztof Drewniak   Type *BestVectorElemType = nullptr;
745*697c1883SKrzysztof Drewniak   if (Size.isKnownMultipleOf(32))
746*697c1883SKrzysztof Drewniak     BestVectorElemType = IRB.getInt32Ty();
747*697c1883SKrzysztof Drewniak   else if (Size.isKnownMultipleOf(16))
748*697c1883SKrzysztof Drewniak     BestVectorElemType = IRB.getInt16Ty();
749*697c1883SKrzysztof Drewniak   else
750*697c1883SKrzysztof Drewniak     BestVectorElemType = IRB.getInt8Ty();
751*697c1883SKrzysztof Drewniak   unsigned NumCastElems =
752*697c1883SKrzysztof Drewniak       Size.getFixedValue() / BestVectorElemType->getIntegerBitWidth();
753*697c1883SKrzysztof Drewniak   if (NumCastElems == 1)
754*697c1883SKrzysztof Drewniak     return BestVectorElemType;
755*697c1883SKrzysztof Drewniak   return FixedVectorType::get(BestVectorElemType, NumCastElems);
756*697c1883SKrzysztof Drewniak }
757*697c1883SKrzysztof Drewniak 
758*697c1883SKrzysztof Drewniak Value *LegalizeBufferContentTypesVisitor::makeLegalNonAggregate(
759*697c1883SKrzysztof Drewniak     Value *V, Type *TargetType, const Twine &Name) {
760*697c1883SKrzysztof Drewniak   Type *SourceType = V->getType();
761*697c1883SKrzysztof Drewniak   TypeSize SourceSize = DL.getTypeSizeInBits(SourceType);
762*697c1883SKrzysztof Drewniak   TypeSize TargetSize = DL.getTypeSizeInBits(TargetType);
763*697c1883SKrzysztof Drewniak   if (SourceSize != TargetSize) {
764*697c1883SKrzysztof Drewniak     Type *ShortScalarTy = IRB.getIntNTy(SourceSize.getFixedValue());
765*697c1883SKrzysztof Drewniak     Type *ByteScalarTy = IRB.getIntNTy(TargetSize.getFixedValue());
766*697c1883SKrzysztof Drewniak     Value *AsScalar = IRB.CreateBitCast(V, ShortScalarTy, Name + ".as.scalar");
767*697c1883SKrzysztof Drewniak     Value *Zext = IRB.CreateZExt(AsScalar, ByteScalarTy, Name + ".zext");
768*697c1883SKrzysztof Drewniak     V = Zext;
769*697c1883SKrzysztof Drewniak     SourceType = ByteScalarTy;
770*697c1883SKrzysztof Drewniak   }
771*697c1883SKrzysztof Drewniak   return IRB.CreateBitCast(V, TargetType, Name + ".legal");
772*697c1883SKrzysztof Drewniak }
773*697c1883SKrzysztof Drewniak 
774*697c1883SKrzysztof Drewniak Value *LegalizeBufferContentTypesVisitor::makeIllegalNonAggregate(
775*697c1883SKrzysztof Drewniak     Value *V, Type *OrigType, const Twine &Name) {
776*697c1883SKrzysztof Drewniak   Type *LegalType = V->getType();
777*697c1883SKrzysztof Drewniak   TypeSize LegalSize = DL.getTypeSizeInBits(LegalType);
778*697c1883SKrzysztof Drewniak   TypeSize OrigSize = DL.getTypeSizeInBits(OrigType);
779*697c1883SKrzysztof Drewniak   if (LegalSize != OrigSize) {
780*697c1883SKrzysztof Drewniak     Type *ShortScalarTy = IRB.getIntNTy(OrigSize.getFixedValue());
781*697c1883SKrzysztof Drewniak     Type *ByteScalarTy = IRB.getIntNTy(LegalSize.getFixedValue());
782*697c1883SKrzysztof Drewniak     Value *AsScalar = IRB.CreateBitCast(V, ByteScalarTy, Name + ".bytes.cast");
783*697c1883SKrzysztof Drewniak     Value *Trunc = IRB.CreateTrunc(AsScalar, ShortScalarTy, Name + ".trunc");
784*697c1883SKrzysztof Drewniak     return IRB.CreateBitCast(Trunc, OrigType, Name + ".orig");
785*697c1883SKrzysztof Drewniak   }
786*697c1883SKrzysztof Drewniak   return IRB.CreateBitCast(V, OrigType, Name + ".real.ty");
787*697c1883SKrzysztof Drewniak }
788*697c1883SKrzysztof Drewniak 
789*697c1883SKrzysztof Drewniak Type *LegalizeBufferContentTypesVisitor::intrinsicTypeFor(Type *LegalType) {
790*697c1883SKrzysztof Drewniak   auto *VT = dyn_cast<FixedVectorType>(LegalType);
791*697c1883SKrzysztof Drewniak   if (!VT)
792*697c1883SKrzysztof Drewniak     return LegalType;
793*697c1883SKrzysztof Drewniak   Type *ET = VT->getElementType();
794*697c1883SKrzysztof Drewniak   // Explicitly return the element type of 1-element vectors because the
795*697c1883SKrzysztof Drewniak   // underlying intrinsics don't like <1 x T> even though it's a synonym for T.
796*697c1883SKrzysztof Drewniak   if (VT->getNumElements() == 1)
797*697c1883SKrzysztof Drewniak     return ET;
798*697c1883SKrzysztof Drewniak   if (DL.getTypeSizeInBits(LegalType) == 96 && DL.getTypeSizeInBits(ET) < 32)
799*697c1883SKrzysztof Drewniak     return FixedVectorType::get(IRB.getInt32Ty(), 3);
800*697c1883SKrzysztof Drewniak   if (ET->isIntegerTy(8)) {
801*697c1883SKrzysztof Drewniak     switch (VT->getNumElements()) {
802*697c1883SKrzysztof Drewniak     default:
803*697c1883SKrzysztof Drewniak       return LegalType; // Let it crash later
804*697c1883SKrzysztof Drewniak     case 1:
805*697c1883SKrzysztof Drewniak       return IRB.getInt8Ty();
806*697c1883SKrzysztof Drewniak     case 2:
807*697c1883SKrzysztof Drewniak       return IRB.getInt16Ty();
808*697c1883SKrzysztof Drewniak     case 4:
809*697c1883SKrzysztof Drewniak       return IRB.getInt32Ty();
810*697c1883SKrzysztof Drewniak     case 8:
811*697c1883SKrzysztof Drewniak       return FixedVectorType::get(IRB.getInt32Ty(), 2);
812*697c1883SKrzysztof Drewniak     case 16:
813*697c1883SKrzysztof Drewniak       return FixedVectorType::get(IRB.getInt32Ty(), 4);
814*697c1883SKrzysztof Drewniak     }
815*697c1883SKrzysztof Drewniak   }
816*697c1883SKrzysztof Drewniak   return LegalType;
817*697c1883SKrzysztof Drewniak }
818*697c1883SKrzysztof Drewniak 
819*697c1883SKrzysztof Drewniak void LegalizeBufferContentTypesVisitor::getVecSlices(
820*697c1883SKrzysztof Drewniak     Type *T, SmallVectorImpl<VecSlice> &Slices) {
821*697c1883SKrzysztof Drewniak   Slices.clear();
822*697c1883SKrzysztof Drewniak   auto *VT = dyn_cast<FixedVectorType>(T);
823*697c1883SKrzysztof Drewniak   if (!VT)
824*697c1883SKrzysztof Drewniak     return;
825*697c1883SKrzysztof Drewniak 
826*697c1883SKrzysztof Drewniak   uint64_t ElemBitWidth =
827*697c1883SKrzysztof Drewniak       DL.getTypeSizeInBits(VT->getElementType()).getFixedValue();
828*697c1883SKrzysztof Drewniak 
829*697c1883SKrzysztof Drewniak   uint64_t ElemsPer4Words = 128 / ElemBitWidth;
830*697c1883SKrzysztof Drewniak   uint64_t ElemsPer2Words = ElemsPer4Words / 2;
831*697c1883SKrzysztof Drewniak   uint64_t ElemsPerWord = ElemsPer2Words / 2;
832*697c1883SKrzysztof Drewniak   uint64_t ElemsPerShort = ElemsPerWord / 2;
833*697c1883SKrzysztof Drewniak   uint64_t ElemsPerByte = ElemsPerShort / 2;
834*697c1883SKrzysztof Drewniak   // If the elements evenly pack into 32-bit words, we can use 3-word stores,
835*697c1883SKrzysztof Drewniak   // such as for <6 x bfloat> or <3 x i32>, but we can't dot his for, for
836*697c1883SKrzysztof Drewniak   // example, <3 x i64>, since that's not slicing.
837*697c1883SKrzysztof Drewniak   uint64_t ElemsPer3Words = ElemsPerWord * 3;
838*697c1883SKrzysztof Drewniak 
839*697c1883SKrzysztof Drewniak   uint64_t TotalElems = VT->getNumElements();
840*697c1883SKrzysztof Drewniak   uint64_t Index = 0;
841*697c1883SKrzysztof Drewniak   auto TrySlice = [&](unsigned MaybeLen) {
842*697c1883SKrzysztof Drewniak     if (MaybeLen > 0 && Index + MaybeLen <= TotalElems) {
843*697c1883SKrzysztof Drewniak       VecSlice Slice{/*Index=*/Index, /*Length=*/MaybeLen};
844*697c1883SKrzysztof Drewniak       Slices.push_back(Slice);
845*697c1883SKrzysztof Drewniak       Index += MaybeLen;
846*697c1883SKrzysztof Drewniak       return true;
847*697c1883SKrzysztof Drewniak     }
848*697c1883SKrzysztof Drewniak     return false;
849*697c1883SKrzysztof Drewniak   };
850*697c1883SKrzysztof Drewniak   while (Index < TotalElems) {
851*697c1883SKrzysztof Drewniak     TrySlice(ElemsPer4Words) || TrySlice(ElemsPer3Words) ||
852*697c1883SKrzysztof Drewniak         TrySlice(ElemsPer2Words) || TrySlice(ElemsPerWord) ||
853*697c1883SKrzysztof Drewniak         TrySlice(ElemsPerShort) || TrySlice(ElemsPerByte);
854*697c1883SKrzysztof Drewniak   }
855*697c1883SKrzysztof Drewniak }
856*697c1883SKrzysztof Drewniak 
857*697c1883SKrzysztof Drewniak Value *LegalizeBufferContentTypesVisitor::extractSlice(Value *Vec, VecSlice S,
858*697c1883SKrzysztof Drewniak                                                        const Twine &Name) {
859*697c1883SKrzysztof Drewniak   auto *VecVT = dyn_cast<FixedVectorType>(Vec->getType());
860*697c1883SKrzysztof Drewniak   if (!VecVT)
861*697c1883SKrzysztof Drewniak     return Vec;
862*697c1883SKrzysztof Drewniak   if (S.Length == VecVT->getNumElements() && S.Index == 0)
863*697c1883SKrzysztof Drewniak     return Vec;
864*697c1883SKrzysztof Drewniak   if (S.Length == 1)
865*697c1883SKrzysztof Drewniak     return IRB.CreateExtractElement(Vec, S.Index,
866*697c1883SKrzysztof Drewniak                                     Name + ".slice." + Twine(S.Index));
867*697c1883SKrzysztof Drewniak   SmallVector<int> Mask = llvm::to_vector(
868*697c1883SKrzysztof Drewniak       llvm::iota_range<int>(S.Index, S.Index + S.Length, /*Inclusive=*/false));
869*697c1883SKrzysztof Drewniak   return IRB.CreateShuffleVector(Vec, Mask, Name + ".slice." + Twine(S.Index));
870*697c1883SKrzysztof Drewniak }
871*697c1883SKrzysztof Drewniak 
872*697c1883SKrzysztof Drewniak Value *LegalizeBufferContentTypesVisitor::insertSlice(Value *Whole, Value *Part,
873*697c1883SKrzysztof Drewniak                                                       VecSlice S,
874*697c1883SKrzysztof Drewniak                                                       const Twine &Name) {
875*697c1883SKrzysztof Drewniak   auto *WholeVT = dyn_cast<FixedVectorType>(Whole->getType());
876*697c1883SKrzysztof Drewniak   if (!WholeVT)
877*697c1883SKrzysztof Drewniak     return Part;
878*697c1883SKrzysztof Drewniak   if (S.Length == WholeVT->getNumElements() && S.Index == 0)
879*697c1883SKrzysztof Drewniak     return Part;
880*697c1883SKrzysztof Drewniak   if (S.Length == 1) {
881*697c1883SKrzysztof Drewniak     return IRB.CreateInsertElement(Whole, Part, S.Index,
882*697c1883SKrzysztof Drewniak                                    Name + ".slice." + Twine(S.Index));
883*697c1883SKrzysztof Drewniak   }
884*697c1883SKrzysztof Drewniak   int NumElems = cast<FixedVectorType>(Whole->getType())->getNumElements();
885*697c1883SKrzysztof Drewniak 
886*697c1883SKrzysztof Drewniak   // Extend the slice with poisons to make the main shufflevector happy.
887*697c1883SKrzysztof Drewniak   SmallVector<int> ExtPartMask(NumElems, -1);
888*697c1883SKrzysztof Drewniak   for (auto [I, E] : llvm::enumerate(
889*697c1883SKrzysztof Drewniak            MutableArrayRef<int>(ExtPartMask).take_front(S.Length))) {
890*697c1883SKrzysztof Drewniak     E = I;
891*697c1883SKrzysztof Drewniak   }
892*697c1883SKrzysztof Drewniak   Value *ExtPart = IRB.CreateShuffleVector(Part, ExtPartMask,
893*697c1883SKrzysztof Drewniak                                            Name + ".ext." + Twine(S.Index));
894*697c1883SKrzysztof Drewniak 
895*697c1883SKrzysztof Drewniak   SmallVector<int> Mask =
896*697c1883SKrzysztof Drewniak       llvm::to_vector(llvm::iota_range<int>(0, NumElems, /*Inclusive=*/false));
897*697c1883SKrzysztof Drewniak   for (auto [I, E] :
898*697c1883SKrzysztof Drewniak        llvm::enumerate(MutableArrayRef<int>(Mask).slice(S.Index, S.Length)))
899*697c1883SKrzysztof Drewniak     E = I + NumElems;
900*697c1883SKrzysztof Drewniak   return IRB.CreateShuffleVector(Whole, ExtPart, Mask,
901*697c1883SKrzysztof Drewniak                                  Name + ".parts." + Twine(S.Index));
902*697c1883SKrzysztof Drewniak }
903*697c1883SKrzysztof Drewniak 
904*697c1883SKrzysztof Drewniak bool LegalizeBufferContentTypesVisitor::visitLoadImpl(
905*697c1883SKrzysztof Drewniak     LoadInst &OrigLI, Type *PartType, SmallVectorImpl<uint32_t> &AggIdxs,
906*697c1883SKrzysztof Drewniak     uint64_t AggByteOff, Value *&Result, const Twine &Name) {
907*697c1883SKrzysztof Drewniak   if (auto *ST = dyn_cast<StructType>(PartType)) {
908*697c1883SKrzysztof Drewniak     const StructLayout *Layout = DL.getStructLayout(ST);
909*697c1883SKrzysztof Drewniak     bool Changed = false;
910*697c1883SKrzysztof Drewniak     for (auto [I, ElemTy, Offset] :
911*697c1883SKrzysztof Drewniak          llvm::enumerate(ST->elements(), Layout->getMemberOffsets())) {
912*697c1883SKrzysztof Drewniak       AggIdxs.push_back(I);
913*697c1883SKrzysztof Drewniak       Changed |= visitLoadImpl(OrigLI, ElemTy, AggIdxs,
914*697c1883SKrzysztof Drewniak                                AggByteOff + Offset.getFixedValue(), Result,
915*697c1883SKrzysztof Drewniak                                Name + "." + Twine(I));
916*697c1883SKrzysztof Drewniak       AggIdxs.pop_back();
917*697c1883SKrzysztof Drewniak     }
918*697c1883SKrzysztof Drewniak     return Changed;
919*697c1883SKrzysztof Drewniak   }
920*697c1883SKrzysztof Drewniak   if (auto *AT = dyn_cast<ArrayType>(PartType)) {
921*697c1883SKrzysztof Drewniak     Type *ElemTy = AT->getElementType();
922*697c1883SKrzysztof Drewniak     if (!ElemTy->isSingleValueType() || !DL.typeSizeEqualsStoreSize(ElemTy) ||
923*697c1883SKrzysztof Drewniak         ElemTy->isVectorTy()) {
924*697c1883SKrzysztof Drewniak       TypeSize ElemStoreSize = DL.getTypeStoreSize(ElemTy);
925*697c1883SKrzysztof Drewniak       bool Changed = false;
926*697c1883SKrzysztof Drewniak       for (auto I : llvm::iota_range<uint32_t>(0, AT->getNumElements(),
927*697c1883SKrzysztof Drewniak                                                /*Inclusive=*/false)) {
928*697c1883SKrzysztof Drewniak         AggIdxs.push_back(I);
929*697c1883SKrzysztof Drewniak         Changed |= visitLoadImpl(OrigLI, ElemTy, AggIdxs,
930*697c1883SKrzysztof Drewniak                                  AggByteOff + I * ElemStoreSize.getFixedValue(),
931*697c1883SKrzysztof Drewniak                                  Result, Name + Twine(I));
932*697c1883SKrzysztof Drewniak         AggIdxs.pop_back();
933*697c1883SKrzysztof Drewniak       }
934*697c1883SKrzysztof Drewniak       return Changed;
935*697c1883SKrzysztof Drewniak     }
936*697c1883SKrzysztof Drewniak   }
937*697c1883SKrzysztof Drewniak 
938*697c1883SKrzysztof Drewniak   // Typical case
939*697c1883SKrzysztof Drewniak 
940*697c1883SKrzysztof Drewniak   Type *ArrayAsVecType = scalarArrayTypeAsVector(PartType);
941*697c1883SKrzysztof Drewniak   Type *LegalType = legalNonAggregateFor(ArrayAsVecType);
942*697c1883SKrzysztof Drewniak 
943*697c1883SKrzysztof Drewniak   SmallVector<VecSlice> Slices;
944*697c1883SKrzysztof Drewniak   getVecSlices(LegalType, Slices);
945*697c1883SKrzysztof Drewniak   bool HasSlices = Slices.size() > 1;
946*697c1883SKrzysztof Drewniak   bool IsAggPart = !AggIdxs.empty();
947*697c1883SKrzysztof Drewniak   Value *LoadsRes;
948*697c1883SKrzysztof Drewniak   if (!HasSlices && !IsAggPart) {
949*697c1883SKrzysztof Drewniak     Type *LoadableType = intrinsicTypeFor(LegalType);
950*697c1883SKrzysztof Drewniak     if (LoadableType == PartType)
951*697c1883SKrzysztof Drewniak       return false;
952*697c1883SKrzysztof Drewniak 
953*697c1883SKrzysztof Drewniak     IRB.SetInsertPoint(&OrigLI);
954*697c1883SKrzysztof Drewniak     auto *NLI = cast<LoadInst>(OrigLI.clone());
955*697c1883SKrzysztof Drewniak     NLI->mutateType(LoadableType);
956*697c1883SKrzysztof Drewniak     NLI = IRB.Insert(NLI);
957*697c1883SKrzysztof Drewniak     NLI->setName(Name + ".loadable");
958*697c1883SKrzysztof Drewniak 
959*697c1883SKrzysztof Drewniak     LoadsRes = IRB.CreateBitCast(NLI, LegalType, Name + ".from.loadable");
960*697c1883SKrzysztof Drewniak   } else {
961*697c1883SKrzysztof Drewniak     IRB.SetInsertPoint(&OrigLI);
962*697c1883SKrzysztof Drewniak     LoadsRes = PoisonValue::get(LegalType);
963*697c1883SKrzysztof Drewniak     Value *OrigPtr = OrigLI.getPointerOperand();
964*697c1883SKrzysztof Drewniak     // If we're needing to spill something into more than one load, its legal
965*697c1883SKrzysztof Drewniak     // type will be a vector (ex. an i256 load will have LegalType = <8 x i32>).
966*697c1883SKrzysztof Drewniak     // But if we're already a scalar (which can happen if we're splitting up a
967*697c1883SKrzysztof Drewniak     // struct), the element type will be the legal type itself.
968*697c1883SKrzysztof Drewniak     Type *ElemType = LegalType->getScalarType();
969*697c1883SKrzysztof Drewniak     unsigned ElemBytes = DL.getTypeStoreSize(ElemType);
970*697c1883SKrzysztof Drewniak     AAMDNodes AANodes = OrigLI.getAAMetadata();
971*697c1883SKrzysztof Drewniak     if (IsAggPart && Slices.empty())
972*697c1883SKrzysztof Drewniak       Slices.push_back(VecSlice{/*Index=*/0, /*Length=*/1});
973*697c1883SKrzysztof Drewniak     for (VecSlice S : Slices) {
974*697c1883SKrzysztof Drewniak       Type *SliceType =
975*697c1883SKrzysztof Drewniak           S.Length != 1 ? FixedVectorType::get(ElemType, S.Length) : ElemType;
976*697c1883SKrzysztof Drewniak       int64_t ByteOffset = AggByteOff + S.Index * ElemBytes;
977*697c1883SKrzysztof Drewniak       // You can't reasonably expect loads to wrap around the edge of memory.
978*697c1883SKrzysztof Drewniak       Value *NewPtr = IRB.CreateGEP(
979*697c1883SKrzysztof Drewniak           IRB.getInt8Ty(), OrigLI.getPointerOperand(), IRB.getInt32(ByteOffset),
980*697c1883SKrzysztof Drewniak           OrigPtr->getName() + ".off.ptr." + Twine(ByteOffset),
981*697c1883SKrzysztof Drewniak           GEPNoWrapFlags::noUnsignedWrap());
982*697c1883SKrzysztof Drewniak       Type *LoadableType = intrinsicTypeFor(SliceType);
983*697c1883SKrzysztof Drewniak       LoadInst *NewLI = IRB.CreateAlignedLoad(
984*697c1883SKrzysztof Drewniak           LoadableType, NewPtr, commonAlignment(OrigLI.getAlign(), ByteOffset),
985*697c1883SKrzysztof Drewniak           Name + ".off." + Twine(ByteOffset));
986*697c1883SKrzysztof Drewniak       copyMetadataForLoad(*NewLI, OrigLI);
987*697c1883SKrzysztof Drewniak       NewLI->setAAMetadata(
988*697c1883SKrzysztof Drewniak           AANodes.adjustForAccess(ByteOffset, LoadableType, DL));
989*697c1883SKrzysztof Drewniak       NewLI->setAtomic(OrigLI.getOrdering(), OrigLI.getSyncScopeID());
990*697c1883SKrzysztof Drewniak       NewLI->setVolatile(OrigLI.isVolatile());
991*697c1883SKrzysztof Drewniak       Value *Loaded = IRB.CreateBitCast(NewLI, SliceType,
992*697c1883SKrzysztof Drewniak                                         NewLI->getName() + ".from.loadable");
993*697c1883SKrzysztof Drewniak       LoadsRes = insertSlice(LoadsRes, Loaded, S, Name);
994*697c1883SKrzysztof Drewniak     }
995*697c1883SKrzysztof Drewniak   }
996*697c1883SKrzysztof Drewniak   if (LegalType != ArrayAsVecType)
997*697c1883SKrzysztof Drewniak     LoadsRes = makeIllegalNonAggregate(LoadsRes, ArrayAsVecType, Name);
998*697c1883SKrzysztof Drewniak   if (ArrayAsVecType != PartType)
999*697c1883SKrzysztof Drewniak     LoadsRes = vectorToArray(LoadsRes, PartType, Name);
1000*697c1883SKrzysztof Drewniak 
1001*697c1883SKrzysztof Drewniak   if (IsAggPart)
1002*697c1883SKrzysztof Drewniak     Result = IRB.CreateInsertValue(Result, LoadsRes, AggIdxs, Name);
1003*697c1883SKrzysztof Drewniak   else
1004*697c1883SKrzysztof Drewniak     Result = LoadsRes;
1005*697c1883SKrzysztof Drewniak   return true;
1006*697c1883SKrzysztof Drewniak }
1007*697c1883SKrzysztof Drewniak 
1008*697c1883SKrzysztof Drewniak bool LegalizeBufferContentTypesVisitor::visitLoadInst(LoadInst &LI) {
1009*697c1883SKrzysztof Drewniak   if (LI.getPointerAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
1010*697c1883SKrzysztof Drewniak     return false;
1011*697c1883SKrzysztof Drewniak 
1012*697c1883SKrzysztof Drewniak   SmallVector<uint32_t> AggIdxs;
1013*697c1883SKrzysztof Drewniak   Type *OrigType = LI.getType();
1014*697c1883SKrzysztof Drewniak   Value *Result = PoisonValue::get(OrigType);
1015*697c1883SKrzysztof Drewniak   bool Changed = visitLoadImpl(LI, OrigType, AggIdxs, 0, Result, LI.getName());
1016*697c1883SKrzysztof Drewniak   if (!Changed)
1017*697c1883SKrzysztof Drewniak     return false;
1018*697c1883SKrzysztof Drewniak   Result->takeName(&LI);
1019*697c1883SKrzysztof Drewniak   LI.replaceAllUsesWith(Result);
1020*697c1883SKrzysztof Drewniak   LI.eraseFromParent();
1021*697c1883SKrzysztof Drewniak   return Changed;
1022*697c1883SKrzysztof Drewniak }
1023*697c1883SKrzysztof Drewniak 
1024*697c1883SKrzysztof Drewniak std::pair<bool, bool> LegalizeBufferContentTypesVisitor::visitStoreImpl(
1025*697c1883SKrzysztof Drewniak     StoreInst &OrigSI, Type *PartType, SmallVectorImpl<uint32_t> &AggIdxs,
1026*697c1883SKrzysztof Drewniak     uint64_t AggByteOff, const Twine &Name) {
1027*697c1883SKrzysztof Drewniak   if (auto *ST = dyn_cast<StructType>(PartType)) {
1028*697c1883SKrzysztof Drewniak     const StructLayout *Layout = DL.getStructLayout(ST);
1029*697c1883SKrzysztof Drewniak     bool Changed = false;
1030*697c1883SKrzysztof Drewniak     for (auto [I, ElemTy, Offset] :
1031*697c1883SKrzysztof Drewniak          llvm::enumerate(ST->elements(), Layout->getMemberOffsets())) {
1032*697c1883SKrzysztof Drewniak       AggIdxs.push_back(I);
1033*697c1883SKrzysztof Drewniak       Changed |= std::get<0>(visitStoreImpl(OrigSI, ElemTy, AggIdxs,
1034*697c1883SKrzysztof Drewniak                                             AggByteOff + Offset.getFixedValue(),
1035*697c1883SKrzysztof Drewniak                                             Name + "." + Twine(I)));
1036*697c1883SKrzysztof Drewniak       AggIdxs.pop_back();
1037*697c1883SKrzysztof Drewniak     }
1038*697c1883SKrzysztof Drewniak     return std::make_pair(Changed, /*ModifiedInPlace=*/false);
1039*697c1883SKrzysztof Drewniak   }
1040*697c1883SKrzysztof Drewniak   if (auto *AT = dyn_cast<ArrayType>(PartType)) {
1041*697c1883SKrzysztof Drewniak     Type *ElemTy = AT->getElementType();
1042*697c1883SKrzysztof Drewniak     if (!ElemTy->isSingleValueType() || !DL.typeSizeEqualsStoreSize(ElemTy) ||
1043*697c1883SKrzysztof Drewniak         ElemTy->isVectorTy()) {
1044*697c1883SKrzysztof Drewniak       TypeSize ElemStoreSize = DL.getTypeStoreSize(ElemTy);
1045*697c1883SKrzysztof Drewniak       bool Changed = false;
1046*697c1883SKrzysztof Drewniak       for (auto I : llvm::iota_range<uint32_t>(0, AT->getNumElements(),
1047*697c1883SKrzysztof Drewniak                                                /*Inclusive=*/false)) {
1048*697c1883SKrzysztof Drewniak         AggIdxs.push_back(I);
1049*697c1883SKrzysztof Drewniak         Changed |= std::get<0>(visitStoreImpl(
1050*697c1883SKrzysztof Drewniak             OrigSI, ElemTy, AggIdxs,
1051*697c1883SKrzysztof Drewniak             AggByteOff + I * ElemStoreSize.getFixedValue(), Name + Twine(I)));
1052*697c1883SKrzysztof Drewniak         AggIdxs.pop_back();
1053*697c1883SKrzysztof Drewniak       }
1054*697c1883SKrzysztof Drewniak       return std::make_pair(Changed, /*ModifiedInPlace=*/false);
1055*697c1883SKrzysztof Drewniak     }
1056*697c1883SKrzysztof Drewniak   }
1057*697c1883SKrzysztof Drewniak 
1058*697c1883SKrzysztof Drewniak   Value *OrigData = OrigSI.getValueOperand();
1059*697c1883SKrzysztof Drewniak   Value *NewData = OrigData;
1060*697c1883SKrzysztof Drewniak 
1061*697c1883SKrzysztof Drewniak   bool IsAggPart = !AggIdxs.empty();
1062*697c1883SKrzysztof Drewniak   if (IsAggPart)
1063*697c1883SKrzysztof Drewniak     NewData = IRB.CreateExtractValue(NewData, AggIdxs, Name);
1064*697c1883SKrzysztof Drewniak 
1065*697c1883SKrzysztof Drewniak   Type *ArrayAsVecType = scalarArrayTypeAsVector(PartType);
1066*697c1883SKrzysztof Drewniak   if (ArrayAsVecType != PartType) {
1067*697c1883SKrzysztof Drewniak     NewData = arrayToVector(NewData, ArrayAsVecType, Name);
1068*697c1883SKrzysztof Drewniak   }
1069*697c1883SKrzysztof Drewniak 
1070*697c1883SKrzysztof Drewniak   Type *LegalType = legalNonAggregateFor(ArrayAsVecType);
1071*697c1883SKrzysztof Drewniak   if (LegalType != ArrayAsVecType) {
1072*697c1883SKrzysztof Drewniak     NewData = makeLegalNonAggregate(NewData, LegalType, Name);
1073*697c1883SKrzysztof Drewniak   }
1074*697c1883SKrzysztof Drewniak 
1075*697c1883SKrzysztof Drewniak   SmallVector<VecSlice> Slices;
1076*697c1883SKrzysztof Drewniak   getVecSlices(LegalType, Slices);
1077*697c1883SKrzysztof Drewniak   bool NeedToSplit = Slices.size() > 1 || IsAggPart;
1078*697c1883SKrzysztof Drewniak   if (!NeedToSplit) {
1079*697c1883SKrzysztof Drewniak     Type *StorableType = intrinsicTypeFor(LegalType);
1080*697c1883SKrzysztof Drewniak     if (StorableType == PartType)
1081*697c1883SKrzysztof Drewniak       return std::make_pair(/*Changed=*/false, /*ModifiedInPlace=*/false);
1082*697c1883SKrzysztof Drewniak     NewData = IRB.CreateBitCast(NewData, StorableType, Name + ".storable");
1083*697c1883SKrzysztof Drewniak     OrigSI.setOperand(0, NewData);
1084*697c1883SKrzysztof Drewniak     return std::make_pair(/*Changed=*/true, /*ModifiedInPlace=*/true);
1085*697c1883SKrzysztof Drewniak   }
1086*697c1883SKrzysztof Drewniak 
1087*697c1883SKrzysztof Drewniak   Value *OrigPtr = OrigSI.getPointerOperand();
1088*697c1883SKrzysztof Drewniak   Type *ElemType = LegalType->getScalarType();
1089*697c1883SKrzysztof Drewniak   if (IsAggPart && Slices.empty())
1090*697c1883SKrzysztof Drewniak     Slices.push_back(VecSlice{/*Index=*/0, /*Length=*/1});
1091*697c1883SKrzysztof Drewniak   unsigned ElemBytes = DL.getTypeStoreSize(ElemType);
1092*697c1883SKrzysztof Drewniak   AAMDNodes AANodes = OrigSI.getAAMetadata();
1093*697c1883SKrzysztof Drewniak   for (VecSlice S : Slices) {
1094*697c1883SKrzysztof Drewniak     Type *SliceType =
1095*697c1883SKrzysztof Drewniak         S.Length != 1 ? FixedVectorType::get(ElemType, S.Length) : ElemType;
1096*697c1883SKrzysztof Drewniak     int64_t ByteOffset = AggByteOff + S.Index * ElemBytes;
1097*697c1883SKrzysztof Drewniak     Value *NewPtr =
1098*697c1883SKrzysztof Drewniak         IRB.CreateGEP(IRB.getInt8Ty(), OrigPtr, IRB.getInt32(ByteOffset),
1099*697c1883SKrzysztof Drewniak                       OrigPtr->getName() + ".part." + Twine(S.Index),
1100*697c1883SKrzysztof Drewniak                       GEPNoWrapFlags::noUnsignedWrap());
1101*697c1883SKrzysztof Drewniak     Value *DataSlice = extractSlice(NewData, S, Name);
1102*697c1883SKrzysztof Drewniak     Type *StorableType = intrinsicTypeFor(SliceType);
1103*697c1883SKrzysztof Drewniak     DataSlice = IRB.CreateBitCast(DataSlice, StorableType,
1104*697c1883SKrzysztof Drewniak                                   DataSlice->getName() + ".storable");
1105*697c1883SKrzysztof Drewniak     auto *NewSI = cast<StoreInst>(OrigSI.clone());
1106*697c1883SKrzysztof Drewniak     NewSI->setAlignment(commonAlignment(OrigSI.getAlign(), ByteOffset));
1107*697c1883SKrzysztof Drewniak     IRB.Insert(NewSI);
1108*697c1883SKrzysztof Drewniak     NewSI->setOperand(0, DataSlice);
1109*697c1883SKrzysztof Drewniak     NewSI->setOperand(1, NewPtr);
1110*697c1883SKrzysztof Drewniak     NewSI->setAAMetadata(AANodes.adjustForAccess(ByteOffset, StorableType, DL));
1111*697c1883SKrzysztof Drewniak   }
1112*697c1883SKrzysztof Drewniak   return std::make_pair(/*Changed=*/true, /*ModifiedInPlace=*/false);
1113*697c1883SKrzysztof Drewniak }
1114*697c1883SKrzysztof Drewniak 
1115*697c1883SKrzysztof Drewniak bool LegalizeBufferContentTypesVisitor::visitStoreInst(StoreInst &SI) {
1116*697c1883SKrzysztof Drewniak   if (SI.getPointerAddressSpace() != AMDGPUAS::BUFFER_FAT_POINTER)
1117*697c1883SKrzysztof Drewniak     return false;
1118*697c1883SKrzysztof Drewniak   IRB.SetInsertPoint(&SI);
1119*697c1883SKrzysztof Drewniak   SmallVector<uint32_t> AggIdxs;
1120*697c1883SKrzysztof Drewniak   Value *OrigData = SI.getValueOperand();
1121*697c1883SKrzysztof Drewniak   auto [Changed, ModifiedInPlace] =
1122*697c1883SKrzysztof Drewniak       visitStoreImpl(SI, OrigData->getType(), AggIdxs, 0, OrigData->getName());
1123*697c1883SKrzysztof Drewniak   if (Changed && !ModifiedInPlace)
1124*697c1883SKrzysztof Drewniak     SI.eraseFromParent();
1125*697c1883SKrzysztof Drewniak   return Changed;
1126*697c1883SKrzysztof Drewniak }
1127*697c1883SKrzysztof Drewniak 
1128*697c1883SKrzysztof Drewniak bool LegalizeBufferContentTypesVisitor::processFunction(Function &F) {
1129*697c1883SKrzysztof Drewniak   bool Changed = false;
1130*697c1883SKrzysztof Drewniak   for (Instruction &I : make_early_inc_range(instructions(F))) {
1131*697c1883SKrzysztof Drewniak     Changed |= visit(I);
1132*697c1883SKrzysztof Drewniak   }
1133*697c1883SKrzysztof Drewniak   return Changed;
1134*697c1883SKrzysztof Drewniak }
1135*697c1883SKrzysztof Drewniak 
11366540f163SKrzysztof Drewniak /// Return the ptr addrspace(8) and i32 (resource and offset parts) in a lowered
11376540f163SKrzysztof Drewniak /// buffer fat pointer constant.
11386540f163SKrzysztof Drewniak static std::pair<Constant *, Constant *>
11396540f163SKrzysztof Drewniak splitLoweredFatBufferConst(Constant *C) {
11405ef768d2SNikita Popov   assert(isSplitFatPtr(C->getType()) && "Not a split fat buffer pointer");
11415ef768d2SNikita Popov   return std::make_pair(C->getAggregateElement(0u), C->getAggregateElement(1u));
11426540f163SKrzysztof Drewniak }
11436540f163SKrzysztof Drewniak 
11446540f163SKrzysztof Drewniak namespace {
11456540f163SKrzysztof Drewniak /// Handle the remapping of ptr addrspace(7) constants.
11466540f163SKrzysztof Drewniak class FatPtrConstMaterializer final : public ValueMaterializer {
11476540f163SKrzysztof Drewniak   BufferFatPtrToStructTypeMap *TypeMap;
11486540f163SKrzysztof Drewniak   // An internal mapper that is used to recurse into the arguments of constants.
11496540f163SKrzysztof Drewniak   // While the documentation for `ValueMapper` specifies not to use it
11506540f163SKrzysztof Drewniak   // recursively, examination of the logic in mapValue() shows that it can
11516540f163SKrzysztof Drewniak   // safely be used recursively when handling constants, like it does in its own
11526540f163SKrzysztof Drewniak   // logic.
11536540f163SKrzysztof Drewniak   ValueMapper InternalMapper;
11546540f163SKrzysztof Drewniak 
11556540f163SKrzysztof Drewniak   Constant *materializeBufferFatPtrConst(Constant *C);
11566540f163SKrzysztof Drewniak 
11576540f163SKrzysztof Drewniak public:
11586540f163SKrzysztof Drewniak   // UnderlyingMap is the value map this materializer will be filling.
11596540f163SKrzysztof Drewniak   FatPtrConstMaterializer(BufferFatPtrToStructTypeMap *TypeMap,
11605ef768d2SNikita Popov                           ValueToValueMapTy &UnderlyingMap)
11615ef768d2SNikita Popov       : TypeMap(TypeMap),
11625ef768d2SNikita Popov         InternalMapper(UnderlyingMap, RF_None, TypeMap, this) {}
11636540f163SKrzysztof Drewniak   virtual ~FatPtrConstMaterializer() = default;
11646540f163SKrzysztof Drewniak 
11656540f163SKrzysztof Drewniak   Value *materialize(Value *V) override;
11666540f163SKrzysztof Drewniak };
11676540f163SKrzysztof Drewniak } // namespace
11686540f163SKrzysztof Drewniak 
11696540f163SKrzysztof Drewniak Constant *FatPtrConstMaterializer::materializeBufferFatPtrConst(Constant *C) {
11706540f163SKrzysztof Drewniak   Type *SrcTy = C->getType();
11716540f163SKrzysztof Drewniak   auto *NewTy = dyn_cast<StructType>(TypeMap->remapType(SrcTy));
11726540f163SKrzysztof Drewniak   if (C->isNullValue())
11736540f163SKrzysztof Drewniak     return ConstantAggregateZero::getNullValue(NewTy);
11746540f163SKrzysztof Drewniak   if (isa<PoisonValue>(C)) {
11756540f163SKrzysztof Drewniak     return ConstantStruct::get(NewTy,
11766540f163SKrzysztof Drewniak                                {PoisonValue::get(NewTy->getElementType(0)),
11776540f163SKrzysztof Drewniak                                 PoisonValue::get(NewTy->getElementType(1))});
11786540f163SKrzysztof Drewniak   }
11796540f163SKrzysztof Drewniak   if (isa<UndefValue>(C)) {
11806540f163SKrzysztof Drewniak     return ConstantStruct::get(NewTy,
11816540f163SKrzysztof Drewniak                                {UndefValue::get(NewTy->getElementType(0)),
11826540f163SKrzysztof Drewniak                                 UndefValue::get(NewTy->getElementType(1))});
11836540f163SKrzysztof Drewniak   }
11846540f163SKrzysztof Drewniak 
11856540f163SKrzysztof Drewniak   if (auto *VC = dyn_cast<ConstantVector>(C)) {
11866540f163SKrzysztof Drewniak     if (Constant *S = VC->getSplatValue()) {
11876540f163SKrzysztof Drewniak       Constant *NewS = InternalMapper.mapConstant(*S);
11886540f163SKrzysztof Drewniak       if (!NewS)
11896540f163SKrzysztof Drewniak         return nullptr;
11906540f163SKrzysztof Drewniak       auto [Rsrc, Off] = splitLoweredFatBufferConst(NewS);
11916540f163SKrzysztof Drewniak       auto EC = VC->getType()->getElementCount();
11926540f163SKrzysztof Drewniak       return ConstantStruct::get(NewTy, {ConstantVector::getSplat(EC, Rsrc),
11936540f163SKrzysztof Drewniak                                          ConstantVector::getSplat(EC, Off)});
11946540f163SKrzysztof Drewniak     }
11956540f163SKrzysztof Drewniak     SmallVector<Constant *> Rsrcs;
11966540f163SKrzysztof Drewniak     SmallVector<Constant *> Offs;
11976540f163SKrzysztof Drewniak     for (Value *Op : VC->operand_values()) {
11986540f163SKrzysztof Drewniak       auto *NewOp = dyn_cast_or_null<Constant>(InternalMapper.mapValue(*Op));
11996540f163SKrzysztof Drewniak       if (!NewOp)
12006540f163SKrzysztof Drewniak         return nullptr;
12016540f163SKrzysztof Drewniak       auto [Rsrc, Off] = splitLoweredFatBufferConst(NewOp);
12026540f163SKrzysztof Drewniak       Rsrcs.push_back(Rsrc);
12036540f163SKrzysztof Drewniak       Offs.push_back(Off);
12046540f163SKrzysztof Drewniak     }
12056540f163SKrzysztof Drewniak     Constant *RsrcVec = ConstantVector::get(Rsrcs);
12066540f163SKrzysztof Drewniak     Constant *OffVec = ConstantVector::get(Offs);
12076540f163SKrzysztof Drewniak     return ConstantStruct::get(NewTy, {RsrcVec, OffVec});
12086540f163SKrzysztof Drewniak   }
12096540f163SKrzysztof Drewniak 
12105ef768d2SNikita Popov   if (isa<GlobalValue>(C))
12115ef768d2SNikita Popov     report_fatal_error("Global values containing ptr addrspace(7) (buffer "
12125ef768d2SNikita Popov                        "fat pointer) values are not supported");
12136540f163SKrzysztof Drewniak 
12145ef768d2SNikita Popov   if (isa<ConstantExpr>(C))
12155ef768d2SNikita Popov     report_fatal_error("Constant exprs containing ptr addrspace(7) (buffer "
12165ef768d2SNikita Popov                        "fat pointer) values should have been expanded earlier");
12176540f163SKrzysztof Drewniak 
12186540f163SKrzysztof Drewniak   return nullptr;
12196540f163SKrzysztof Drewniak }
12206540f163SKrzysztof Drewniak 
12216540f163SKrzysztof Drewniak Value *FatPtrConstMaterializer::materialize(Value *V) {
12226540f163SKrzysztof Drewniak   Constant *C = dyn_cast<Constant>(V);
12236540f163SKrzysztof Drewniak   if (!C)
12246540f163SKrzysztof Drewniak     return nullptr;
12256540f163SKrzysztof Drewniak   // Structs and other types that happen to contain fat pointers get remapped
12266540f163SKrzysztof Drewniak   // by the mapValue() logic.
12276540f163SKrzysztof Drewniak   if (!isBufferFatPtrConst(C))
12286540f163SKrzysztof Drewniak     return nullptr;
12296540f163SKrzysztof Drewniak   return materializeBufferFatPtrConst(C);
12306540f163SKrzysztof Drewniak }
12316540f163SKrzysztof Drewniak 
12326540f163SKrzysztof Drewniak using PtrParts = std::pair<Value *, Value *>;
12336540f163SKrzysztof Drewniak namespace {
12346540f163SKrzysztof Drewniak // The visitor returns the resource and offset parts for an instruction if they
12356540f163SKrzysztof Drewniak // can be computed, or (nullptr, nullptr) for cases that don't have a meaningful
12366540f163SKrzysztof Drewniak // value mapping.
12376540f163SKrzysztof Drewniak class SplitPtrStructs : public InstVisitor<SplitPtrStructs, PtrParts> {
12386540f163SKrzysztof Drewniak   ValueToValueMapTy RsrcParts;
12396540f163SKrzysztof Drewniak   ValueToValueMapTy OffParts;
12406540f163SKrzysztof Drewniak 
12416540f163SKrzysztof Drewniak   // Track instructions that have been rewritten into a user of the component
12426540f163SKrzysztof Drewniak   // parts of their ptr addrspace(7) input. Instructions that produced
12436540f163SKrzysztof Drewniak   // ptr addrspace(7) parts should **not** be RAUW'd before being added to this
12446540f163SKrzysztof Drewniak   // set, as that replacement will be handled in a post-visit step. However,
12456540f163SKrzysztof Drewniak   // instructions that yield values that aren't fat pointers (ex. ptrtoint)
12466540f163SKrzysztof Drewniak   // should RAUW themselves with new instructions that use the split parts
12476540f163SKrzysztof Drewniak   // of their arguments during processing.
12486540f163SKrzysztof Drewniak   DenseSet<Instruction *> SplitUsers;
12496540f163SKrzysztof Drewniak 
12506540f163SKrzysztof Drewniak   // Nodes that need a second look once we've computed the parts for all other
12516540f163SKrzysztof Drewniak   // instructions to see if, for example, we really need to phi on the resource
12526540f163SKrzysztof Drewniak   // part.
12536540f163SKrzysztof Drewniak   SmallVector<Instruction *> Conditionals;
12546540f163SKrzysztof Drewniak   // Temporary instructions produced while lowering conditionals that should be
12556540f163SKrzysztof Drewniak   // killed.
12566540f163SKrzysztof Drewniak   SmallVector<Instruction *> ConditionalTemps;
12576540f163SKrzysztof Drewniak 
12586540f163SKrzysztof Drewniak   // Subtarget info, needed for determining what cache control bits to set.
12596540f163SKrzysztof Drewniak   const TargetMachine *TM;
12606bba44e8SJay Foad   const GCNSubtarget *ST = nullptr;
12616540f163SKrzysztof Drewniak 
12626540f163SKrzysztof Drewniak   IRBuilder<> IRB;
12636540f163SKrzysztof Drewniak 
12646540f163SKrzysztof Drewniak   // Copy metadata between instructions if applicable.
12656540f163SKrzysztof Drewniak   void copyMetadata(Value *Dest, Value *Src);
12666540f163SKrzysztof Drewniak 
12676540f163SKrzysztof Drewniak   // Get the resource and offset parts of the value V, inserting appropriate
12686540f163SKrzysztof Drewniak   // extractvalue calls if needed.
12696540f163SKrzysztof Drewniak   PtrParts getPtrParts(Value *V);
12706540f163SKrzysztof Drewniak 
12716540f163SKrzysztof Drewniak   // Given an instruction that could produce multiple resource parts (a PHI or
12726540f163SKrzysztof Drewniak   // select), collect the set of possible instructions that could have provided
12736540f163SKrzysztof Drewniak   // its resource parts  that it could have (the `Roots`) and the set of
12746540f163SKrzysztof Drewniak   // conditional instructions visited during the search (`Seen`). If, after
12756540f163SKrzysztof Drewniak   // removing the root of the search from `Seen` and `Roots`, `Seen` is a subset
12766540f163SKrzysztof Drewniak   // of `Roots` and `Roots - Seen` contains one element, the resource part of
12776540f163SKrzysztof Drewniak   // that element can replace the resource part of all other elements in `Seen`.
12786540f163SKrzysztof Drewniak   void getPossibleRsrcRoots(Instruction *I, SmallPtrSetImpl<Value *> &Roots,
12796540f163SKrzysztof Drewniak                             SmallPtrSetImpl<Value *> &Seen);
12806540f163SKrzysztof Drewniak   void processConditionals();
12816540f163SKrzysztof Drewniak 
12826540f163SKrzysztof Drewniak   // If an instruction hav been split into resource and offset parts,
12836540f163SKrzysztof Drewniak   // delete that instruction. If any of its uses have not themselves been split
12846540f163SKrzysztof Drewniak   // into parts (for example, an insertvalue), construct the structure
12856540f163SKrzysztof Drewniak   // that the type rewrites declared should be produced by the dying instruction
12866540f163SKrzysztof Drewniak   // and use that.
12876540f163SKrzysztof Drewniak   // Also, kill the temporary extractvalue operations produced by the two-stage
12886540f163SKrzysztof Drewniak   // lowering of PHIs and conditionals.
12896540f163SKrzysztof Drewniak   void killAndReplaceSplitInstructions(SmallVectorImpl<Instruction *> &Origs);
12906540f163SKrzysztof Drewniak 
12916540f163SKrzysztof Drewniak   void setAlign(CallInst *Intr, Align A, unsigned RsrcArgIdx);
12926540f163SKrzysztof Drewniak   void insertPreMemOpFence(AtomicOrdering Order, SyncScope::ID SSID);
12936540f163SKrzysztof Drewniak   void insertPostMemOpFence(AtomicOrdering Order, SyncScope::ID SSID);
12946540f163SKrzysztof Drewniak   Value *handleMemoryInst(Instruction *I, Value *Arg, Value *Ptr, Type *Ty,
12956540f163SKrzysztof Drewniak                           Align Alignment, AtomicOrdering Order,
12966540f163SKrzysztof Drewniak                           bool IsVolatile, SyncScope::ID SSID);
12976540f163SKrzysztof Drewniak 
12986540f163SKrzysztof Drewniak public:
12996540f163SKrzysztof Drewniak   SplitPtrStructs(LLVMContext &Ctx, const TargetMachine *TM)
13006bba44e8SJay Foad       : TM(TM), IRB(Ctx) {}
13016540f163SKrzysztof Drewniak 
13026540f163SKrzysztof Drewniak   void processFunction(Function &F);
13036540f163SKrzysztof Drewniak 
13046540f163SKrzysztof Drewniak   PtrParts visitInstruction(Instruction &I);
13056540f163SKrzysztof Drewniak   PtrParts visitLoadInst(LoadInst &LI);
13066540f163SKrzysztof Drewniak   PtrParts visitStoreInst(StoreInst &SI);
13076540f163SKrzysztof Drewniak   PtrParts visitAtomicRMWInst(AtomicRMWInst &AI);
13086540f163SKrzysztof Drewniak   PtrParts visitAtomicCmpXchgInst(AtomicCmpXchgInst &AI);
13096540f163SKrzysztof Drewniak   PtrParts visitGetElementPtrInst(GetElementPtrInst &GEP);
13106540f163SKrzysztof Drewniak 
13116540f163SKrzysztof Drewniak   PtrParts visitPtrToIntInst(PtrToIntInst &PI);
13126540f163SKrzysztof Drewniak   PtrParts visitIntToPtrInst(IntToPtrInst &IP);
13136540f163SKrzysztof Drewniak   PtrParts visitAddrSpaceCastInst(AddrSpaceCastInst &I);
13146540f163SKrzysztof Drewniak   PtrParts visitICmpInst(ICmpInst &Cmp);
13156540f163SKrzysztof Drewniak   PtrParts visitFreezeInst(FreezeInst &I);
13166540f163SKrzysztof Drewniak 
13176540f163SKrzysztof Drewniak   PtrParts visitExtractElementInst(ExtractElementInst &I);
13186540f163SKrzysztof Drewniak   PtrParts visitInsertElementInst(InsertElementInst &I);
13196540f163SKrzysztof Drewniak   PtrParts visitShuffleVectorInst(ShuffleVectorInst &I);
13206540f163SKrzysztof Drewniak 
13216540f163SKrzysztof Drewniak   PtrParts visitPHINode(PHINode &PHI);
13226540f163SKrzysztof Drewniak   PtrParts visitSelectInst(SelectInst &SI);
13236540f163SKrzysztof Drewniak 
13246540f163SKrzysztof Drewniak   PtrParts visitIntrinsicInst(IntrinsicInst &II);
13256540f163SKrzysztof Drewniak };
13266540f163SKrzysztof Drewniak } // namespace
13276540f163SKrzysztof Drewniak 
13286540f163SKrzysztof Drewniak void SplitPtrStructs::copyMetadata(Value *Dest, Value *Src) {
13296540f163SKrzysztof Drewniak   auto *DestI = dyn_cast<Instruction>(Dest);
13306540f163SKrzysztof Drewniak   auto *SrcI = dyn_cast<Instruction>(Src);
13316540f163SKrzysztof Drewniak 
13326540f163SKrzysztof Drewniak   if (!DestI || !SrcI)
13336540f163SKrzysztof Drewniak     return;
13346540f163SKrzysztof Drewniak 
13356540f163SKrzysztof Drewniak   DestI->copyMetadata(*SrcI);
13366540f163SKrzysztof Drewniak }
13376540f163SKrzysztof Drewniak 
13386540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::getPtrParts(Value *V) {
13396540f163SKrzysztof Drewniak   assert(isSplitFatPtr(V->getType()) && "it's not meaningful to get the parts "
13406540f163SKrzysztof Drewniak                                         "of something that wasn't rewritten");
13416540f163SKrzysztof Drewniak   auto *RsrcEntry = &RsrcParts[V];
13426540f163SKrzysztof Drewniak   auto *OffEntry = &OffParts[V];
13436540f163SKrzysztof Drewniak   if (*RsrcEntry && *OffEntry)
13446540f163SKrzysztof Drewniak     return {*RsrcEntry, *OffEntry};
13456540f163SKrzysztof Drewniak 
13466540f163SKrzysztof Drewniak   if (auto *C = dyn_cast<Constant>(V)) {
13476540f163SKrzysztof Drewniak     auto [Rsrc, Off] = splitLoweredFatBufferConst(C);
13486540f163SKrzysztof Drewniak     return {*RsrcEntry = Rsrc, *OffEntry = Off};
13496540f163SKrzysztof Drewniak   }
13506540f163SKrzysztof Drewniak 
13516540f163SKrzysztof Drewniak   IRBuilder<>::InsertPointGuard Guard(IRB);
13526540f163SKrzysztof Drewniak   if (auto *I = dyn_cast<Instruction>(V)) {
13536540f163SKrzysztof Drewniak     LLVM_DEBUG(dbgs() << "Recursing to split parts of " << *I << "\n");
13546540f163SKrzysztof Drewniak     auto [Rsrc, Off] = visit(*I);
13556540f163SKrzysztof Drewniak     if (Rsrc && Off)
13566540f163SKrzysztof Drewniak       return {*RsrcEntry = Rsrc, *OffEntry = Off};
13576540f163SKrzysztof Drewniak     // We'll be creating the new values after the relevant instruction.
13586540f163SKrzysztof Drewniak     // This instruction generates a value and so isn't a terminator.
13596540f163SKrzysztof Drewniak     IRB.SetInsertPoint(*I->getInsertionPointAfterDef());
13606540f163SKrzysztof Drewniak     IRB.SetCurrentDebugLocation(I->getDebugLoc());
13616540f163SKrzysztof Drewniak   } else if (auto *A = dyn_cast<Argument>(V)) {
13626540f163SKrzysztof Drewniak     IRB.SetInsertPointPastAllocas(A->getParent());
13636540f163SKrzysztof Drewniak     IRB.SetCurrentDebugLocation(DebugLoc());
13646540f163SKrzysztof Drewniak   }
13656540f163SKrzysztof Drewniak   Value *Rsrc = IRB.CreateExtractValue(V, 0, V->getName() + ".rsrc");
13666540f163SKrzysztof Drewniak   Value *Off = IRB.CreateExtractValue(V, 1, V->getName() + ".off");
13676540f163SKrzysztof Drewniak   return {*RsrcEntry = Rsrc, *OffEntry = Off};
13686540f163SKrzysztof Drewniak }
13696540f163SKrzysztof Drewniak 
13706540f163SKrzysztof Drewniak /// Returns the instruction that defines the resource part of the value V.
13716540f163SKrzysztof Drewniak /// Note that this is not getUnderlyingObject(), since that looks through
13726540f163SKrzysztof Drewniak /// operations like ptrmask which might modify the resource part.
13736540f163SKrzysztof Drewniak ///
13746540f163SKrzysztof Drewniak /// We can limit ourselves to just looking through GEPs followed by looking
13756540f163SKrzysztof Drewniak /// through addrspacecasts because only those two operations preserve the
13766540f163SKrzysztof Drewniak /// resource part, and because operations on an `addrspace(8)` (which is the
13776540f163SKrzysztof Drewniak /// legal input to this addrspacecast) would produce a different resource part.
13786540f163SKrzysztof Drewniak static Value *rsrcPartRoot(Value *V) {
13796540f163SKrzysztof Drewniak   while (auto *GEP = dyn_cast<GEPOperator>(V))
13806540f163SKrzysztof Drewniak     V = GEP->getPointerOperand();
13816540f163SKrzysztof Drewniak   while (auto *ASC = dyn_cast<AddrSpaceCastOperator>(V))
13826540f163SKrzysztof Drewniak     V = ASC->getPointerOperand();
13836540f163SKrzysztof Drewniak   return V;
13846540f163SKrzysztof Drewniak }
13856540f163SKrzysztof Drewniak 
13866540f163SKrzysztof Drewniak void SplitPtrStructs::getPossibleRsrcRoots(Instruction *I,
13876540f163SKrzysztof Drewniak                                            SmallPtrSetImpl<Value *> &Roots,
13886540f163SKrzysztof Drewniak                                            SmallPtrSetImpl<Value *> &Seen) {
13896540f163SKrzysztof Drewniak   if (auto *PHI = dyn_cast<PHINode>(I)) {
13906540f163SKrzysztof Drewniak     if (!Seen.insert(I).second)
13916540f163SKrzysztof Drewniak       return;
13926540f163SKrzysztof Drewniak     for (Value *In : PHI->incoming_values()) {
13936540f163SKrzysztof Drewniak       In = rsrcPartRoot(In);
13946540f163SKrzysztof Drewniak       Roots.insert(In);
13956540f163SKrzysztof Drewniak       if (isa<PHINode, SelectInst>(In))
13966540f163SKrzysztof Drewniak         getPossibleRsrcRoots(cast<Instruction>(In), Roots, Seen);
13976540f163SKrzysztof Drewniak     }
13986540f163SKrzysztof Drewniak   } else if (auto *SI = dyn_cast<SelectInst>(I)) {
13996540f163SKrzysztof Drewniak     if (!Seen.insert(SI).second)
14006540f163SKrzysztof Drewniak       return;
14016540f163SKrzysztof Drewniak     Value *TrueVal = rsrcPartRoot(SI->getTrueValue());
14026540f163SKrzysztof Drewniak     Value *FalseVal = rsrcPartRoot(SI->getFalseValue());
14036540f163SKrzysztof Drewniak     Roots.insert(TrueVal);
14046540f163SKrzysztof Drewniak     Roots.insert(FalseVal);
14056540f163SKrzysztof Drewniak     if (isa<PHINode, SelectInst>(TrueVal))
14066540f163SKrzysztof Drewniak       getPossibleRsrcRoots(cast<Instruction>(TrueVal), Roots, Seen);
14076540f163SKrzysztof Drewniak     if (isa<PHINode, SelectInst>(FalseVal))
14086540f163SKrzysztof Drewniak       getPossibleRsrcRoots(cast<Instruction>(FalseVal), Roots, Seen);
14096540f163SKrzysztof Drewniak   } else {
14106540f163SKrzysztof Drewniak     llvm_unreachable("getPossibleRsrcParts() only works on phi and select");
14116540f163SKrzysztof Drewniak   }
14126540f163SKrzysztof Drewniak }
14136540f163SKrzysztof Drewniak 
14146540f163SKrzysztof Drewniak void SplitPtrStructs::processConditionals() {
14156540f163SKrzysztof Drewniak   SmallDenseMap<Instruction *, Value *> FoundRsrcs;
14166540f163SKrzysztof Drewniak   SmallPtrSet<Value *, 4> Roots;
14176540f163SKrzysztof Drewniak   SmallPtrSet<Value *, 4> Seen;
14186540f163SKrzysztof Drewniak   for (Instruction *I : Conditionals) {
14196540f163SKrzysztof Drewniak     // These have to exist by now because we've visited these nodes.
14206540f163SKrzysztof Drewniak     Value *Rsrc = RsrcParts[I];
14216540f163SKrzysztof Drewniak     Value *Off = OffParts[I];
14226540f163SKrzysztof Drewniak     assert(Rsrc && Off && "must have visited conditionals by now");
14236540f163SKrzysztof Drewniak 
14246540f163SKrzysztof Drewniak     std::optional<Value *> MaybeRsrc;
14256540f163SKrzysztof Drewniak     auto MaybeFoundRsrc = FoundRsrcs.find(I);
14266540f163SKrzysztof Drewniak     if (MaybeFoundRsrc != FoundRsrcs.end()) {
14276540f163SKrzysztof Drewniak       MaybeRsrc = MaybeFoundRsrc->second;
14286540f163SKrzysztof Drewniak     } else {
14296540f163SKrzysztof Drewniak       IRBuilder<>::InsertPointGuard Guard(IRB);
14306540f163SKrzysztof Drewniak       Roots.clear();
14316540f163SKrzysztof Drewniak       Seen.clear();
14326540f163SKrzysztof Drewniak       getPossibleRsrcRoots(I, Roots, Seen);
14336540f163SKrzysztof Drewniak       LLVM_DEBUG(dbgs() << "Processing conditional: " << *I << "\n");
14346540f163SKrzysztof Drewniak #ifndef NDEBUG
14356540f163SKrzysztof Drewniak       for (Value *V : Roots)
14366540f163SKrzysztof Drewniak         LLVM_DEBUG(dbgs() << "Root: " << *V << "\n");
14376540f163SKrzysztof Drewniak       for (Value *V : Seen)
14386540f163SKrzysztof Drewniak         LLVM_DEBUG(dbgs() << "Seen: " << *V << "\n");
14396540f163SKrzysztof Drewniak #endif
14406540f163SKrzysztof Drewniak       // If we are our own possible root, then we shouldn't block our
14416540f163SKrzysztof Drewniak       // replacement with a valid incoming value.
14426540f163SKrzysztof Drewniak       Roots.erase(I);
14436540f163SKrzysztof Drewniak       // We don't want to block the optimization for conditionals that don't
14446540f163SKrzysztof Drewniak       // refer to themselves but did see themselves during the traversal.
14456540f163SKrzysztof Drewniak       Seen.erase(I);
14466540f163SKrzysztof Drewniak 
14476540f163SKrzysztof Drewniak       if (set_is_subset(Seen, Roots)) {
14486540f163SKrzysztof Drewniak         auto Diff = set_difference(Roots, Seen);
14496540f163SKrzysztof Drewniak         if (Diff.size() == 1) {
14506540f163SKrzysztof Drewniak           Value *RootVal = *Diff.begin();
14516540f163SKrzysztof Drewniak           // Handle the case where previous loops already looked through
14526540f163SKrzysztof Drewniak           // an addrspacecast.
14536540f163SKrzysztof Drewniak           if (isSplitFatPtr(RootVal->getType()))
14546540f163SKrzysztof Drewniak             MaybeRsrc = std::get<0>(getPtrParts(RootVal));
14556540f163SKrzysztof Drewniak           else
14566540f163SKrzysztof Drewniak             MaybeRsrc = RootVal;
14576540f163SKrzysztof Drewniak         }
14586540f163SKrzysztof Drewniak       }
14596540f163SKrzysztof Drewniak     }
14606540f163SKrzysztof Drewniak 
14616540f163SKrzysztof Drewniak     if (auto *PHI = dyn_cast<PHINode>(I)) {
14626540f163SKrzysztof Drewniak       Value *NewRsrc;
14636540f163SKrzysztof Drewniak       StructType *PHITy = cast<StructType>(PHI->getType());
14646540f163SKrzysztof Drewniak       IRB.SetInsertPoint(*PHI->getInsertionPointAfterDef());
14656540f163SKrzysztof Drewniak       IRB.SetCurrentDebugLocation(PHI->getDebugLoc());
14666540f163SKrzysztof Drewniak       if (MaybeRsrc) {
14676540f163SKrzysztof Drewniak         NewRsrc = *MaybeRsrc;
14686540f163SKrzysztof Drewniak       } else {
14696540f163SKrzysztof Drewniak         Type *RsrcTy = PHITy->getElementType(0);
14706540f163SKrzysztof Drewniak         auto *RsrcPHI = IRB.CreatePHI(RsrcTy, PHI->getNumIncomingValues());
14716540f163SKrzysztof Drewniak         RsrcPHI->takeName(Rsrc);
14726540f163SKrzysztof Drewniak         for (auto [V, BB] : llvm::zip(PHI->incoming_values(), PHI->blocks())) {
14736540f163SKrzysztof Drewniak           Value *VRsrc = std::get<0>(getPtrParts(V));
14746540f163SKrzysztof Drewniak           RsrcPHI->addIncoming(VRsrc, BB);
14756540f163SKrzysztof Drewniak         }
14766540f163SKrzysztof Drewniak         copyMetadata(RsrcPHI, PHI);
14776540f163SKrzysztof Drewniak         NewRsrc = RsrcPHI;
14786540f163SKrzysztof Drewniak       }
14796540f163SKrzysztof Drewniak 
14806540f163SKrzysztof Drewniak       Type *OffTy = PHITy->getElementType(1);
14816540f163SKrzysztof Drewniak       auto *NewOff = IRB.CreatePHI(OffTy, PHI->getNumIncomingValues());
14826540f163SKrzysztof Drewniak       NewOff->takeName(Off);
14836540f163SKrzysztof Drewniak       for (auto [V, BB] : llvm::zip(PHI->incoming_values(), PHI->blocks())) {
14846540f163SKrzysztof Drewniak         assert(OffParts.count(V) && "An offset part had to be created by now");
14856540f163SKrzysztof Drewniak         Value *VOff = std::get<1>(getPtrParts(V));
14866540f163SKrzysztof Drewniak         NewOff->addIncoming(VOff, BB);
14876540f163SKrzysztof Drewniak       }
14886540f163SKrzysztof Drewniak       copyMetadata(NewOff, PHI);
14896540f163SKrzysztof Drewniak 
14906540f163SKrzysztof Drewniak       // Note: We don't eraseFromParent() the temporaries because we don't want
14916540f163SKrzysztof Drewniak       // to put the corrections maps in an inconstent state. That'll be handed
14926540f163SKrzysztof Drewniak       // during the rest of the killing. Also, `ValueToValueMapTy` guarantees
14936540f163SKrzysztof Drewniak       // that references in that map will be updated as well.
14946540f163SKrzysztof Drewniak       ConditionalTemps.push_back(cast<Instruction>(Rsrc));
14956540f163SKrzysztof Drewniak       ConditionalTemps.push_back(cast<Instruction>(Off));
14966540f163SKrzysztof Drewniak       Rsrc->replaceAllUsesWith(NewRsrc);
14976540f163SKrzysztof Drewniak       Off->replaceAllUsesWith(NewOff);
14986540f163SKrzysztof Drewniak 
14996540f163SKrzysztof Drewniak       // Save on recomputing the cycle traversals in known-root cases.
15006540f163SKrzysztof Drewniak       if (MaybeRsrc)
15016540f163SKrzysztof Drewniak         for (Value *V : Seen)
15026540f163SKrzysztof Drewniak           FoundRsrcs[cast<Instruction>(V)] = NewRsrc;
15036fa2d03bSSimon Pilgrim     } else if (isa<SelectInst>(I)) {
15046540f163SKrzysztof Drewniak       if (MaybeRsrc) {
15056540f163SKrzysztof Drewniak         ConditionalTemps.push_back(cast<Instruction>(Rsrc));
15066540f163SKrzysztof Drewniak         Rsrc->replaceAllUsesWith(*MaybeRsrc);
15076540f163SKrzysztof Drewniak         for (Value *V : Seen)
15086540f163SKrzysztof Drewniak           FoundRsrcs[cast<Instruction>(V)] = *MaybeRsrc;
15096540f163SKrzysztof Drewniak       }
15106540f163SKrzysztof Drewniak     } else {
15116540f163SKrzysztof Drewniak       llvm_unreachable("Only PHIs and selects go in the conditionals list");
15126540f163SKrzysztof Drewniak     }
15136540f163SKrzysztof Drewniak   }
15146540f163SKrzysztof Drewniak }
15156540f163SKrzysztof Drewniak 
15166540f163SKrzysztof Drewniak void SplitPtrStructs::killAndReplaceSplitInstructions(
15176540f163SKrzysztof Drewniak     SmallVectorImpl<Instruction *> &Origs) {
15186540f163SKrzysztof Drewniak   for (Instruction *I : ConditionalTemps)
15196540f163SKrzysztof Drewniak     I->eraseFromParent();
15206540f163SKrzysztof Drewniak 
15216540f163SKrzysztof Drewniak   for (Instruction *I : Origs) {
15226540f163SKrzysztof Drewniak     if (!SplitUsers.contains(I))
15236540f163SKrzysztof Drewniak       continue;
15246540f163SKrzysztof Drewniak 
15256540f163SKrzysztof Drewniak     SmallVector<DbgValueInst *> Dbgs;
15266540f163SKrzysztof Drewniak     findDbgValues(Dbgs, I);
15276540f163SKrzysztof Drewniak     for (auto *Dbg : Dbgs) {
15286540f163SKrzysztof Drewniak       IRB.SetInsertPoint(Dbg);
15292d209d96SNikita Popov       auto &DL = I->getDataLayout();
15306540f163SKrzysztof Drewniak       assert(isSplitFatPtr(I->getType()) &&
15316540f163SKrzysztof Drewniak              "We should've RAUW'd away loads, stores, etc. at this point");
15326540f163SKrzysztof Drewniak       auto *OffDbg = cast<DbgValueInst>(Dbg->clone());
15336540f163SKrzysztof Drewniak       copyMetadata(OffDbg, Dbg);
15346540f163SKrzysztof Drewniak       auto [Rsrc, Off] = getPtrParts(I);
15356540f163SKrzysztof Drewniak 
15366540f163SKrzysztof Drewniak       int64_t RsrcSz = DL.getTypeSizeInBits(Rsrc->getType());
15376540f163SKrzysztof Drewniak       int64_t OffSz = DL.getTypeSizeInBits(Off->getType());
15386540f163SKrzysztof Drewniak 
15396540f163SKrzysztof Drewniak       std::optional<DIExpression *> RsrcExpr =
15406540f163SKrzysztof Drewniak           DIExpression::createFragmentExpression(Dbg->getExpression(), 0,
15416540f163SKrzysztof Drewniak                                                  RsrcSz);
15426540f163SKrzysztof Drewniak       std::optional<DIExpression *> OffExpr =
15436540f163SKrzysztof Drewniak           DIExpression::createFragmentExpression(Dbg->getExpression(), RsrcSz,
15446540f163SKrzysztof Drewniak                                                  OffSz);
15456540f163SKrzysztof Drewniak       if (OffExpr) {
15466540f163SKrzysztof Drewniak         OffDbg->setExpression(*OffExpr);
15476540f163SKrzysztof Drewniak         OffDbg->replaceVariableLocationOp(I, Off);
15486540f163SKrzysztof Drewniak         IRB.Insert(OffDbg);
15496540f163SKrzysztof Drewniak       } else {
15506540f163SKrzysztof Drewniak         OffDbg->deleteValue();
15516540f163SKrzysztof Drewniak       }
15526540f163SKrzysztof Drewniak       if (RsrcExpr) {
15536540f163SKrzysztof Drewniak         Dbg->setExpression(*RsrcExpr);
15546540f163SKrzysztof Drewniak         Dbg->replaceVariableLocationOp(I, Rsrc);
15556540f163SKrzysztof Drewniak       } else {
15566540f163SKrzysztof Drewniak         Dbg->replaceVariableLocationOp(I, UndefValue::get(I->getType()));
15576540f163SKrzysztof Drewniak       }
15586540f163SKrzysztof Drewniak     }
15596540f163SKrzysztof Drewniak 
15606540f163SKrzysztof Drewniak     Value *Poison = PoisonValue::get(I->getType());
15616540f163SKrzysztof Drewniak     I->replaceUsesWithIf(Poison, [&](const Use &U) -> bool {
15626540f163SKrzysztof Drewniak       if (const auto *UI = dyn_cast<Instruction>(U.getUser()))
15636540f163SKrzysztof Drewniak         return SplitUsers.contains(UI);
15646540f163SKrzysztof Drewniak       return false;
15656540f163SKrzysztof Drewniak     });
15666540f163SKrzysztof Drewniak 
15676540f163SKrzysztof Drewniak     if (I->use_empty()) {
15686540f163SKrzysztof Drewniak       I->eraseFromParent();
15696540f163SKrzysztof Drewniak       continue;
15706540f163SKrzysztof Drewniak     }
15716540f163SKrzysztof Drewniak     IRB.SetInsertPoint(*I->getInsertionPointAfterDef());
15726540f163SKrzysztof Drewniak     IRB.SetCurrentDebugLocation(I->getDebugLoc());
15736540f163SKrzysztof Drewniak     auto [Rsrc, Off] = getPtrParts(I);
15746540f163SKrzysztof Drewniak     Value *Struct = PoisonValue::get(I->getType());
15756540f163SKrzysztof Drewniak     Struct = IRB.CreateInsertValue(Struct, Rsrc, 0);
15766540f163SKrzysztof Drewniak     Struct = IRB.CreateInsertValue(Struct, Off, 1);
15776540f163SKrzysztof Drewniak     copyMetadata(Struct, I);
15786540f163SKrzysztof Drewniak     Struct->takeName(I);
15796540f163SKrzysztof Drewniak     I->replaceAllUsesWith(Struct);
15806540f163SKrzysztof Drewniak     I->eraseFromParent();
15816540f163SKrzysztof Drewniak   }
15826540f163SKrzysztof Drewniak }
15836540f163SKrzysztof Drewniak 
15846540f163SKrzysztof Drewniak void SplitPtrStructs::setAlign(CallInst *Intr, Align A, unsigned RsrcArgIdx) {
15856540f163SKrzysztof Drewniak   LLVMContext &Ctx = Intr->getContext();
15866540f163SKrzysztof Drewniak   Intr->addParamAttr(RsrcArgIdx, Attribute::getWithAlignment(Ctx, A));
15876540f163SKrzysztof Drewniak }
15886540f163SKrzysztof Drewniak 
15896540f163SKrzysztof Drewniak void SplitPtrStructs::insertPreMemOpFence(AtomicOrdering Order,
15906540f163SKrzysztof Drewniak                                           SyncScope::ID SSID) {
15916540f163SKrzysztof Drewniak   switch (Order) {
15926540f163SKrzysztof Drewniak   case AtomicOrdering::Release:
15936540f163SKrzysztof Drewniak   case AtomicOrdering::AcquireRelease:
15946540f163SKrzysztof Drewniak   case AtomicOrdering::SequentiallyConsistent:
15956540f163SKrzysztof Drewniak     IRB.CreateFence(AtomicOrdering::Release, SSID);
15966540f163SKrzysztof Drewniak     break;
15976540f163SKrzysztof Drewniak   default:
15986540f163SKrzysztof Drewniak     break;
15996540f163SKrzysztof Drewniak   }
16006540f163SKrzysztof Drewniak }
16016540f163SKrzysztof Drewniak 
16026540f163SKrzysztof Drewniak void SplitPtrStructs::insertPostMemOpFence(AtomicOrdering Order,
16036540f163SKrzysztof Drewniak                                            SyncScope::ID SSID) {
16046540f163SKrzysztof Drewniak   switch (Order) {
16056540f163SKrzysztof Drewniak   case AtomicOrdering::Acquire:
16066540f163SKrzysztof Drewniak   case AtomicOrdering::AcquireRelease:
16076540f163SKrzysztof Drewniak   case AtomicOrdering::SequentiallyConsistent:
16086540f163SKrzysztof Drewniak     IRB.CreateFence(AtomicOrdering::Acquire, SSID);
16096540f163SKrzysztof Drewniak     break;
16106540f163SKrzysztof Drewniak   default:
16116540f163SKrzysztof Drewniak     break;
16126540f163SKrzysztof Drewniak   }
16136540f163SKrzysztof Drewniak }
16146540f163SKrzysztof Drewniak 
16156540f163SKrzysztof Drewniak Value *SplitPtrStructs::handleMemoryInst(Instruction *I, Value *Arg, Value *Ptr,
16166540f163SKrzysztof Drewniak                                          Type *Ty, Align Alignment,
16176540f163SKrzysztof Drewniak                                          AtomicOrdering Order, bool IsVolatile,
16186540f163SKrzysztof Drewniak                                          SyncScope::ID SSID) {
16196540f163SKrzysztof Drewniak   IRB.SetInsertPoint(I);
16206540f163SKrzysztof Drewniak 
16216540f163SKrzysztof Drewniak   auto [Rsrc, Off] = getPtrParts(Ptr);
16226540f163SKrzysztof Drewniak   SmallVector<Value *, 5> Args;
16236540f163SKrzysztof Drewniak   if (Arg)
16246540f163SKrzysztof Drewniak     Args.push_back(Arg);
16256540f163SKrzysztof Drewniak   Args.push_back(Rsrc);
16266540f163SKrzysztof Drewniak   Args.push_back(Off);
16276540f163SKrzysztof Drewniak   insertPreMemOpFence(Order, SSID);
16286540f163SKrzysztof Drewniak   // soffset is always 0 for these cases, where we always want any offset to be
16296540f163SKrzysztof Drewniak   // part of bounds checking and we don't know which parts of the GEPs is
16306540f163SKrzysztof Drewniak   // uniform.
16316540f163SKrzysztof Drewniak   Args.push_back(IRB.getInt32(0));
16326540f163SKrzysztof Drewniak 
16336540f163SKrzysztof Drewniak   uint32_t Aux = 0;
16346540f163SKrzysztof Drewniak   if (IsVolatile)
16356540f163SKrzysztof Drewniak     Aux |= AMDGPU::CPol::VOLATILE;
16366540f163SKrzysztof Drewniak   Args.push_back(IRB.getInt32(Aux));
16376540f163SKrzysztof Drewniak 
16386540f163SKrzysztof Drewniak   Intrinsic::ID IID = Intrinsic::not_intrinsic;
16396540f163SKrzysztof Drewniak   if (isa<LoadInst>(I))
1640ec7f8e11SJessica Del     IID = Order == AtomicOrdering::NotAtomic
1641ec7f8e11SJessica Del               ? Intrinsic::amdgcn_raw_ptr_buffer_load
1642ec7f8e11SJessica Del               : Intrinsic::amdgcn_raw_ptr_atomic_buffer_load;
16436540f163SKrzysztof Drewniak   else if (isa<StoreInst>(I))
16446540f163SKrzysztof Drewniak     IID = Intrinsic::amdgcn_raw_ptr_buffer_store;
16456540f163SKrzysztof Drewniak   else if (auto *RMW = dyn_cast<AtomicRMWInst>(I)) {
16466540f163SKrzysztof Drewniak     switch (RMW->getOperation()) {
16476540f163SKrzysztof Drewniak     case AtomicRMWInst::Xchg:
16486540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_swap;
16496540f163SKrzysztof Drewniak       break;
16506540f163SKrzysztof Drewniak     case AtomicRMWInst::Add:
16516540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_add;
16526540f163SKrzysztof Drewniak       break;
16536540f163SKrzysztof Drewniak     case AtomicRMWInst::Sub:
16546540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_sub;
16556540f163SKrzysztof Drewniak       break;
16566540f163SKrzysztof Drewniak     case AtomicRMWInst::And:
16576540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_and;
16586540f163SKrzysztof Drewniak       break;
16596540f163SKrzysztof Drewniak     case AtomicRMWInst::Or:
16606540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_or;
16616540f163SKrzysztof Drewniak       break;
16626540f163SKrzysztof Drewniak     case AtomicRMWInst::Xor:
16636540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_xor;
16646540f163SKrzysztof Drewniak       break;
16656540f163SKrzysztof Drewniak     case AtomicRMWInst::Max:
16666540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_smax;
16676540f163SKrzysztof Drewniak       break;
16686540f163SKrzysztof Drewniak     case AtomicRMWInst::Min:
16696540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_smin;
16706540f163SKrzysztof Drewniak       break;
16716540f163SKrzysztof Drewniak     case AtomicRMWInst::UMax:
16726540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_umax;
16736540f163SKrzysztof Drewniak       break;
16746540f163SKrzysztof Drewniak     case AtomicRMWInst::UMin:
16756540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_umin;
16766540f163SKrzysztof Drewniak       break;
16776540f163SKrzysztof Drewniak     case AtomicRMWInst::FAdd:
16786540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_fadd;
16796540f163SKrzysztof Drewniak       break;
16806540f163SKrzysztof Drewniak     case AtomicRMWInst::FMax:
16816540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_fmax;
16826540f163SKrzysztof Drewniak       break;
16836540f163SKrzysztof Drewniak     case AtomicRMWInst::FMin:
16846540f163SKrzysztof Drewniak       IID = Intrinsic::amdgcn_raw_ptr_buffer_atomic_fmin;
16856540f163SKrzysztof Drewniak       break;
16866540f163SKrzysztof Drewniak     case AtomicRMWInst::FSub: {
16876540f163SKrzysztof Drewniak       report_fatal_error("atomic floating point subtraction not supported for "
16886540f163SKrzysztof Drewniak                          "buffer resources and should've been expanded away");
16896540f163SKrzysztof Drewniak       break;
16906540f163SKrzysztof Drewniak     }
16916540f163SKrzysztof Drewniak     case AtomicRMWInst::Nand:
16926540f163SKrzysztof Drewniak       report_fatal_error("atomic nand not supported for buffer resources and "
16936540f163SKrzysztof Drewniak                          "should've been expanded away");
16946540f163SKrzysztof Drewniak       break;
16956540f163SKrzysztof Drewniak     case AtomicRMWInst::UIncWrap:
16966540f163SKrzysztof Drewniak     case AtomicRMWInst::UDecWrap:
16976540f163SKrzysztof Drewniak       report_fatal_error("wrapping increment/decrement not supported for "
16986540f163SKrzysztof Drewniak                          "buffer resources and should've ben expanded away");
16996540f163SKrzysztof Drewniak       break;
17006540f163SKrzysztof Drewniak     case AtomicRMWInst::BAD_BINOP:
17016540f163SKrzysztof Drewniak       llvm_unreachable("Not sure how we got a bad binop");
1702e1fdaaafSKazu Hirata     case AtomicRMWInst::USubCond:
1703e1fdaaafSKazu Hirata     case AtomicRMWInst::USubSat:
1704e1fdaaafSKazu Hirata       break;
17056540f163SKrzysztof Drewniak     }
17066540f163SKrzysztof Drewniak   }
17076540f163SKrzysztof Drewniak 
17086540f163SKrzysztof Drewniak   auto *Call = IRB.CreateIntrinsic(IID, Ty, Args);
17096540f163SKrzysztof Drewniak   copyMetadata(Call, I);
17106540f163SKrzysztof Drewniak   setAlign(Call, Alignment, Arg ? 1 : 0);
17116540f163SKrzysztof Drewniak   Call->takeName(I);
17126540f163SKrzysztof Drewniak 
17136540f163SKrzysztof Drewniak   insertPostMemOpFence(Order, SSID);
17146540f163SKrzysztof Drewniak   // The "no moving p7 directly" rewrites ensure that this load or store won't
17156540f163SKrzysztof Drewniak   // itself need to be split into parts.
17166540f163SKrzysztof Drewniak   SplitUsers.insert(I);
17176540f163SKrzysztof Drewniak   I->replaceAllUsesWith(Call);
17186540f163SKrzysztof Drewniak   return Call;
17196540f163SKrzysztof Drewniak }
17206540f163SKrzysztof Drewniak 
17216540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitInstruction(Instruction &I) {
17226540f163SKrzysztof Drewniak   return {nullptr, nullptr};
17236540f163SKrzysztof Drewniak }
17246540f163SKrzysztof Drewniak 
17256540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitLoadInst(LoadInst &LI) {
17266540f163SKrzysztof Drewniak   if (!isSplitFatPtr(LI.getPointerOperandType()))
17276540f163SKrzysztof Drewniak     return {nullptr, nullptr};
17286540f163SKrzysztof Drewniak   handleMemoryInst(&LI, nullptr, LI.getPointerOperand(), LI.getType(),
17296540f163SKrzysztof Drewniak                    LI.getAlign(), LI.getOrdering(), LI.isVolatile(),
17306540f163SKrzysztof Drewniak                    LI.getSyncScopeID());
17316540f163SKrzysztof Drewniak   return {nullptr, nullptr};
17326540f163SKrzysztof Drewniak }
17336540f163SKrzysztof Drewniak 
17346540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitStoreInst(StoreInst &SI) {
17356540f163SKrzysztof Drewniak   if (!isSplitFatPtr(SI.getPointerOperandType()))
17366540f163SKrzysztof Drewniak     return {nullptr, nullptr};
17376540f163SKrzysztof Drewniak   Value *Arg = SI.getValueOperand();
17386540f163SKrzysztof Drewniak   handleMemoryInst(&SI, Arg, SI.getPointerOperand(), Arg->getType(),
17396540f163SKrzysztof Drewniak                    SI.getAlign(), SI.getOrdering(), SI.isVolatile(),
17406540f163SKrzysztof Drewniak                    SI.getSyncScopeID());
17416540f163SKrzysztof Drewniak   return {nullptr, nullptr};
17426540f163SKrzysztof Drewniak }
17436540f163SKrzysztof Drewniak 
17446540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitAtomicRMWInst(AtomicRMWInst &AI) {
17456540f163SKrzysztof Drewniak   if (!isSplitFatPtr(AI.getPointerOperand()->getType()))
17466540f163SKrzysztof Drewniak     return {nullptr, nullptr};
17476540f163SKrzysztof Drewniak   Value *Arg = AI.getValOperand();
17486540f163SKrzysztof Drewniak   handleMemoryInst(&AI, Arg, AI.getPointerOperand(), Arg->getType(),
17496540f163SKrzysztof Drewniak                    AI.getAlign(), AI.getOrdering(), AI.isVolatile(),
17506540f163SKrzysztof Drewniak                    AI.getSyncScopeID());
17516540f163SKrzysztof Drewniak   return {nullptr, nullptr};
17526540f163SKrzysztof Drewniak }
17536540f163SKrzysztof Drewniak 
17546540f163SKrzysztof Drewniak // Unlike load, store, and RMW, cmpxchg needs special handling to account
17556540f163SKrzysztof Drewniak // for the boolean argument.
17566540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitAtomicCmpXchgInst(AtomicCmpXchgInst &AI) {
17576540f163SKrzysztof Drewniak   Value *Ptr = AI.getPointerOperand();
17586540f163SKrzysztof Drewniak   if (!isSplitFatPtr(Ptr->getType()))
17596540f163SKrzysztof Drewniak     return {nullptr, nullptr};
17606540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&AI);
17616540f163SKrzysztof Drewniak 
17626540f163SKrzysztof Drewniak   Type *Ty = AI.getNewValOperand()->getType();
17636540f163SKrzysztof Drewniak   AtomicOrdering Order = AI.getMergedOrdering();
17646540f163SKrzysztof Drewniak   SyncScope::ID SSID = AI.getSyncScopeID();
17656540f163SKrzysztof Drewniak   bool IsNonTemporal = AI.getMetadata(LLVMContext::MD_nontemporal);
17666540f163SKrzysztof Drewniak 
17676540f163SKrzysztof Drewniak   auto [Rsrc, Off] = getPtrParts(Ptr);
17686540f163SKrzysztof Drewniak   insertPreMemOpFence(Order, SSID);
17696540f163SKrzysztof Drewniak 
17706540f163SKrzysztof Drewniak   uint32_t Aux = 0;
17716540f163SKrzysztof Drewniak   if (IsNonTemporal)
17726540f163SKrzysztof Drewniak     Aux |= AMDGPU::CPol::SLC;
17736540f163SKrzysztof Drewniak   if (AI.isVolatile())
17746540f163SKrzysztof Drewniak     Aux |= AMDGPU::CPol::VOLATILE;
17756540f163SKrzysztof Drewniak   auto *Call =
17766540f163SKrzysztof Drewniak       IRB.CreateIntrinsic(Intrinsic::amdgcn_raw_ptr_buffer_atomic_cmpswap, Ty,
17776540f163SKrzysztof Drewniak                           {AI.getNewValOperand(), AI.getCompareOperand(), Rsrc,
17786540f163SKrzysztof Drewniak                            Off, IRB.getInt32(0), IRB.getInt32(Aux)});
17796540f163SKrzysztof Drewniak   copyMetadata(Call, &AI);
17806540f163SKrzysztof Drewniak   setAlign(Call, AI.getAlign(), 2);
17816540f163SKrzysztof Drewniak   Call->takeName(&AI);
17826540f163SKrzysztof Drewniak   insertPostMemOpFence(Order, SSID);
17836540f163SKrzysztof Drewniak 
17846540f163SKrzysztof Drewniak   Value *Res = PoisonValue::get(AI.getType());
17856540f163SKrzysztof Drewniak   Res = IRB.CreateInsertValue(Res, Call, 0);
17866540f163SKrzysztof Drewniak   if (!AI.isWeak()) {
17876540f163SKrzysztof Drewniak     Value *Succeeded = IRB.CreateICmpEQ(Call, AI.getCompareOperand());
17886540f163SKrzysztof Drewniak     Res = IRB.CreateInsertValue(Res, Succeeded, 1);
17896540f163SKrzysztof Drewniak   }
17906540f163SKrzysztof Drewniak   SplitUsers.insert(&AI);
17916540f163SKrzysztof Drewniak   AI.replaceAllUsesWith(Res);
17926540f163SKrzysztof Drewniak   return {nullptr, nullptr};
17936540f163SKrzysztof Drewniak }
17946540f163SKrzysztof Drewniak 
17956540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitGetElementPtrInst(GetElementPtrInst &GEP) {
1796cb3a6bdeSNikita Popov   using namespace llvm::PatternMatch;
17976540f163SKrzysztof Drewniak   Value *Ptr = GEP.getPointerOperand();
17986540f163SKrzysztof Drewniak   if (!isSplitFatPtr(Ptr->getType()))
17996540f163SKrzysztof Drewniak     return {nullptr, nullptr};
18006540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&GEP);
18016540f163SKrzysztof Drewniak 
18026540f163SKrzysztof Drewniak   auto [Rsrc, Off] = getPtrParts(Ptr);
18032d209d96SNikita Popov   const DataLayout &DL = GEP.getDataLayout();
18043b0f506cSKrzysztof Drewniak   bool IsNUW = GEP.hasNoUnsignedWrap();
18053b0f506cSKrzysztof Drewniak   bool IsNUSW = GEP.hasNoUnsignedSignedWrap();
18066540f163SKrzysztof Drewniak 
18076fc63ab7SNikita Popov   // In order to call emitGEPOffset() and thus not have to reimplement it,
18086fc63ab7SNikita Popov   // we need the GEP result to have ptr addrspace(7) type.
18096fc63ab7SNikita Popov   Type *FatPtrTy = IRB.getPtrTy(AMDGPUAS::BUFFER_FAT_POINTER);
18106fc63ab7SNikita Popov   if (auto *VT = dyn_cast<VectorType>(Off->getType()))
18116fc63ab7SNikita Popov     FatPtrTy = VectorType::get(FatPtrTy, VT->getElementCount());
18126fc63ab7SNikita Popov   GEP.mutateType(FatPtrTy);
18136fc63ab7SNikita Popov   Value *OffAccum = emitGEPOffset(&IRB, DL, &GEP);
18146fc63ab7SNikita Popov   GEP.mutateType(Ptr->getType());
1815cb3a6bdeSNikita Popov   if (match(OffAccum, m_Zero())) { // Constant-zero offset
18166540f163SKrzysztof Drewniak     SplitUsers.insert(&GEP);
18176540f163SKrzysztof Drewniak     return {Rsrc, Off};
18186540f163SKrzysztof Drewniak   }
18196540f163SKrzysztof Drewniak 
18206540f163SKrzysztof Drewniak   bool HasNonNegativeOff = false;
18216540f163SKrzysztof Drewniak   if (auto *CI = dyn_cast<ConstantInt>(OffAccum)) {
18226540f163SKrzysztof Drewniak     HasNonNegativeOff = !CI->isNegative();
18236540f163SKrzysztof Drewniak   }
18246540f163SKrzysztof Drewniak   Value *NewOff;
1825cb3a6bdeSNikita Popov   if (match(Off, m_Zero())) {
18266540f163SKrzysztof Drewniak     NewOff = OffAccum;
18276540f163SKrzysztof Drewniak   } else {
18286540f163SKrzysztof Drewniak     NewOff = IRB.CreateAdd(Off, OffAccum, "",
18293b0f506cSKrzysztof Drewniak                            /*hasNUW=*/IsNUW || (IsNUSW && HasNonNegativeOff),
18306540f163SKrzysztof Drewniak                            /*hasNSW=*/false);
18316540f163SKrzysztof Drewniak   }
18326540f163SKrzysztof Drewniak   copyMetadata(NewOff, &GEP);
18336540f163SKrzysztof Drewniak   NewOff->takeName(&GEP);
18346540f163SKrzysztof Drewniak   SplitUsers.insert(&GEP);
18356540f163SKrzysztof Drewniak   return {Rsrc, NewOff};
18366540f163SKrzysztof Drewniak }
18376540f163SKrzysztof Drewniak 
18386540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitPtrToIntInst(PtrToIntInst &PI) {
18396540f163SKrzysztof Drewniak   Value *Ptr = PI.getPointerOperand();
18406540f163SKrzysztof Drewniak   if (!isSplitFatPtr(Ptr->getType()))
18416540f163SKrzysztof Drewniak     return {nullptr, nullptr};
18426540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&PI);
18436540f163SKrzysztof Drewniak 
18446540f163SKrzysztof Drewniak   Type *ResTy = PI.getType();
18456540f163SKrzysztof Drewniak   unsigned Width = ResTy->getScalarSizeInBits();
18466540f163SKrzysztof Drewniak 
18476540f163SKrzysztof Drewniak   auto [Rsrc, Off] = getPtrParts(Ptr);
18482d209d96SNikita Popov   const DataLayout &DL = PI.getDataLayout();
18496540f163SKrzysztof Drewniak   unsigned FatPtrWidth = DL.getPointerSizeInBits(AMDGPUAS::BUFFER_FAT_POINTER);
18506540f163SKrzysztof Drewniak 
18510774000eSNikita Popov   Value *Res;
18520774000eSNikita Popov   if (Width <= BufferOffsetWidth) {
18530774000eSNikita Popov     Res = IRB.CreateIntCast(Off, ResTy, /*isSigned=*/false,
18540774000eSNikita Popov                             PI.getName() + ".off");
18550774000eSNikita Popov   } else {
18560774000eSNikita Popov     Value *RsrcInt = IRB.CreatePtrToInt(Rsrc, ResTy, PI.getName() + ".rsrc");
18576540f163SKrzysztof Drewniak     Value *Shl = IRB.CreateShl(
18586540f163SKrzysztof Drewniak         RsrcInt,
18590774000eSNikita Popov         ConstantExpr::getIntegerValue(ResTy, APInt(Width, BufferOffsetWidth)),
18600774000eSNikita Popov         "", Width >= FatPtrWidth, Width > FatPtrWidth);
18610774000eSNikita Popov     Value *OffCast = IRB.CreateIntCast(Off, ResTy, /*isSigned=*/false,
18620774000eSNikita Popov                                        PI.getName() + ".off");
18630774000eSNikita Popov     Res = IRB.CreateOr(Shl, OffCast);
18640774000eSNikita Popov   }
18650774000eSNikita Popov 
18660774000eSNikita Popov   copyMetadata(Res, &PI);
18676540f163SKrzysztof Drewniak   Res->takeName(&PI);
18686540f163SKrzysztof Drewniak   SplitUsers.insert(&PI);
18696540f163SKrzysztof Drewniak   PI.replaceAllUsesWith(Res);
18706540f163SKrzysztof Drewniak   return {nullptr, nullptr};
18716540f163SKrzysztof Drewniak }
18726540f163SKrzysztof Drewniak 
18736540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitIntToPtrInst(IntToPtrInst &IP) {
18746540f163SKrzysztof Drewniak   if (!isSplitFatPtr(IP.getType()))
18756540f163SKrzysztof Drewniak     return {nullptr, nullptr};
18766540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&IP);
18772d209d96SNikita Popov   const DataLayout &DL = IP.getDataLayout();
18786540f163SKrzysztof Drewniak   unsigned RsrcPtrWidth = DL.getPointerSizeInBits(AMDGPUAS::BUFFER_RESOURCE);
18796540f163SKrzysztof Drewniak   Value *Int = IP.getOperand(0);
18806540f163SKrzysztof Drewniak   Type *IntTy = Int->getType();
18816540f163SKrzysztof Drewniak   Type *RsrcIntTy = IntTy->getWithNewBitWidth(RsrcPtrWidth);
18826540f163SKrzysztof Drewniak   unsigned Width = IntTy->getScalarSizeInBits();
18836540f163SKrzysztof Drewniak 
18846540f163SKrzysztof Drewniak   auto *RetTy = cast<StructType>(IP.getType());
18856540f163SKrzysztof Drewniak   Type *RsrcTy = RetTy->getElementType(0);
18866540f163SKrzysztof Drewniak   Type *OffTy = RetTy->getElementType(1);
18876540f163SKrzysztof Drewniak   Value *RsrcPart = IRB.CreateLShr(
18886540f163SKrzysztof Drewniak       Int,
18896540f163SKrzysztof Drewniak       ConstantExpr::getIntegerValue(IntTy, APInt(Width, BufferOffsetWidth)));
18906540f163SKrzysztof Drewniak   Value *RsrcInt = IRB.CreateIntCast(RsrcPart, RsrcIntTy, /*isSigned=*/false);
18916540f163SKrzysztof Drewniak   Value *Rsrc = IRB.CreateIntToPtr(RsrcInt, RsrcTy, IP.getName() + ".rsrc");
18926540f163SKrzysztof Drewniak   Value *Off =
18936540f163SKrzysztof Drewniak       IRB.CreateIntCast(Int, OffTy, /*IsSigned=*/false, IP.getName() + ".off");
18946540f163SKrzysztof Drewniak 
18956540f163SKrzysztof Drewniak   copyMetadata(Rsrc, &IP);
18966540f163SKrzysztof Drewniak   SplitUsers.insert(&IP);
18976540f163SKrzysztof Drewniak   return {Rsrc, Off};
18986540f163SKrzysztof Drewniak }
18996540f163SKrzysztof Drewniak 
19006540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitAddrSpaceCastInst(AddrSpaceCastInst &I) {
19016540f163SKrzysztof Drewniak   if (!isSplitFatPtr(I.getType()))
19026540f163SKrzysztof Drewniak     return {nullptr, nullptr};
19036540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&I);
19046540f163SKrzysztof Drewniak   Value *In = I.getPointerOperand();
19056540f163SKrzysztof Drewniak   // No-op casts preserve parts
19066540f163SKrzysztof Drewniak   if (In->getType() == I.getType()) {
19076540f163SKrzysztof Drewniak     auto [Rsrc, Off] = getPtrParts(In);
19086540f163SKrzysztof Drewniak     SplitUsers.insert(&I);
19096540f163SKrzysztof Drewniak     return {Rsrc, Off};
19106540f163SKrzysztof Drewniak   }
19116540f163SKrzysztof Drewniak   if (I.getSrcAddressSpace() != AMDGPUAS::BUFFER_RESOURCE)
19126540f163SKrzysztof Drewniak     report_fatal_error("Only buffer resources (addrspace 8) can be cast to "
19136540f163SKrzysztof Drewniak                        "buffer fat pointers (addrspace 7)");
19146540f163SKrzysztof Drewniak   Type *OffTy = cast<StructType>(I.getType())->getElementType(1);
19156540f163SKrzysztof Drewniak   Value *ZeroOff = Constant::getNullValue(OffTy);
19166540f163SKrzysztof Drewniak   SplitUsers.insert(&I);
19176540f163SKrzysztof Drewniak   return {In, ZeroOff};
19186540f163SKrzysztof Drewniak }
19196540f163SKrzysztof Drewniak 
19206540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitICmpInst(ICmpInst &Cmp) {
19216540f163SKrzysztof Drewniak   Value *Lhs = Cmp.getOperand(0);
19226540f163SKrzysztof Drewniak   if (!isSplitFatPtr(Lhs->getType()))
19236540f163SKrzysztof Drewniak     return {nullptr, nullptr};
19246540f163SKrzysztof Drewniak   Value *Rhs = Cmp.getOperand(1);
19256540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&Cmp);
19266540f163SKrzysztof Drewniak   ICmpInst::Predicate Pred = Cmp.getPredicate();
19276540f163SKrzysztof Drewniak 
19286540f163SKrzysztof Drewniak   assert((Pred == ICmpInst::ICMP_EQ || Pred == ICmpInst::ICMP_NE) &&
19296540f163SKrzysztof Drewniak          "Pointer comparison is only equal or unequal");
19306540f163SKrzysztof Drewniak   auto [LhsRsrc, LhsOff] = getPtrParts(Lhs);
19316540f163SKrzysztof Drewniak   auto [RhsRsrc, RhsOff] = getPtrParts(Rhs);
19326540f163SKrzysztof Drewniak   Value *RsrcCmp =
19336540f163SKrzysztof Drewniak       IRB.CreateICmp(Pred, LhsRsrc, RhsRsrc, Cmp.getName() + ".rsrc");
19346540f163SKrzysztof Drewniak   copyMetadata(RsrcCmp, &Cmp);
19356540f163SKrzysztof Drewniak   Value *OffCmp = IRB.CreateICmp(Pred, LhsOff, RhsOff, Cmp.getName() + ".off");
19366540f163SKrzysztof Drewniak   copyMetadata(OffCmp, &Cmp);
19376540f163SKrzysztof Drewniak 
19386540f163SKrzysztof Drewniak   Value *Res = nullptr;
19396540f163SKrzysztof Drewniak   if (Pred == ICmpInst::ICMP_EQ)
19406540f163SKrzysztof Drewniak     Res = IRB.CreateAnd(RsrcCmp, OffCmp);
19416540f163SKrzysztof Drewniak   else if (Pred == ICmpInst::ICMP_NE)
19426540f163SKrzysztof Drewniak     Res = IRB.CreateOr(RsrcCmp, OffCmp);
19436540f163SKrzysztof Drewniak   copyMetadata(Res, &Cmp);
19446540f163SKrzysztof Drewniak   Res->takeName(&Cmp);
19456540f163SKrzysztof Drewniak   SplitUsers.insert(&Cmp);
19466540f163SKrzysztof Drewniak   Cmp.replaceAllUsesWith(Res);
19476540f163SKrzysztof Drewniak   return {nullptr, nullptr};
19486540f163SKrzysztof Drewniak }
19496540f163SKrzysztof Drewniak 
19506540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitFreezeInst(FreezeInst &I) {
19516540f163SKrzysztof Drewniak   if (!isSplitFatPtr(I.getType()))
19526540f163SKrzysztof Drewniak     return {nullptr, nullptr};
19536540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&I);
19546540f163SKrzysztof Drewniak   auto [Rsrc, Off] = getPtrParts(I.getOperand(0));
19556540f163SKrzysztof Drewniak 
19566540f163SKrzysztof Drewniak   Value *RsrcRes = IRB.CreateFreeze(Rsrc, I.getName() + ".rsrc");
19576540f163SKrzysztof Drewniak   copyMetadata(RsrcRes, &I);
19586540f163SKrzysztof Drewniak   Value *OffRes = IRB.CreateFreeze(Off, I.getName() + ".off");
19596540f163SKrzysztof Drewniak   copyMetadata(OffRes, &I);
19606540f163SKrzysztof Drewniak   SplitUsers.insert(&I);
19616540f163SKrzysztof Drewniak   return {RsrcRes, OffRes};
19626540f163SKrzysztof Drewniak }
19636540f163SKrzysztof Drewniak 
19646540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitExtractElementInst(ExtractElementInst &I) {
19656540f163SKrzysztof Drewniak   if (!isSplitFatPtr(I.getType()))
19666540f163SKrzysztof Drewniak     return {nullptr, nullptr};
19676540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&I);
19686540f163SKrzysztof Drewniak   Value *Vec = I.getVectorOperand();
19696540f163SKrzysztof Drewniak   Value *Idx = I.getIndexOperand();
19706540f163SKrzysztof Drewniak   auto [Rsrc, Off] = getPtrParts(Vec);
19716540f163SKrzysztof Drewniak 
19726540f163SKrzysztof Drewniak   Value *RsrcRes = IRB.CreateExtractElement(Rsrc, Idx, I.getName() + ".rsrc");
19736540f163SKrzysztof Drewniak   copyMetadata(RsrcRes, &I);
19746540f163SKrzysztof Drewniak   Value *OffRes = IRB.CreateExtractElement(Off, Idx, I.getName() + ".off");
19756540f163SKrzysztof Drewniak   copyMetadata(OffRes, &I);
19766540f163SKrzysztof Drewniak   SplitUsers.insert(&I);
19776540f163SKrzysztof Drewniak   return {RsrcRes, OffRes};
19786540f163SKrzysztof Drewniak }
19796540f163SKrzysztof Drewniak 
19806540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitInsertElementInst(InsertElementInst &I) {
19816540f163SKrzysztof Drewniak   // The mutated instructions temporarily don't return vectors, and so
19826540f163SKrzysztof Drewniak   // we need the generic getType() here to avoid crashes.
19836540f163SKrzysztof Drewniak   if (!isSplitFatPtr(cast<Instruction>(I).getType()))
19846540f163SKrzysztof Drewniak     return {nullptr, nullptr};
19856540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&I);
19866540f163SKrzysztof Drewniak   Value *Vec = I.getOperand(0);
19876540f163SKrzysztof Drewniak   Value *Elem = I.getOperand(1);
19886540f163SKrzysztof Drewniak   Value *Idx = I.getOperand(2);
19896540f163SKrzysztof Drewniak   auto [VecRsrc, VecOff] = getPtrParts(Vec);
19906540f163SKrzysztof Drewniak   auto [ElemRsrc, ElemOff] = getPtrParts(Elem);
19916540f163SKrzysztof Drewniak 
19926540f163SKrzysztof Drewniak   Value *RsrcRes =
19936540f163SKrzysztof Drewniak       IRB.CreateInsertElement(VecRsrc, ElemRsrc, Idx, I.getName() + ".rsrc");
19946540f163SKrzysztof Drewniak   copyMetadata(RsrcRes, &I);
19956540f163SKrzysztof Drewniak   Value *OffRes =
19966540f163SKrzysztof Drewniak       IRB.CreateInsertElement(VecOff, ElemOff, Idx, I.getName() + ".off");
19976540f163SKrzysztof Drewniak   copyMetadata(OffRes, &I);
19986540f163SKrzysztof Drewniak   SplitUsers.insert(&I);
19996540f163SKrzysztof Drewniak   return {RsrcRes, OffRes};
20006540f163SKrzysztof Drewniak }
20016540f163SKrzysztof Drewniak 
20026540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitShuffleVectorInst(ShuffleVectorInst &I) {
20036540f163SKrzysztof Drewniak   // Cast is needed for the same reason as insertelement's.
20046540f163SKrzysztof Drewniak   if (!isSplitFatPtr(cast<Instruction>(I).getType()))
20056540f163SKrzysztof Drewniak     return {nullptr, nullptr};
20066540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&I);
20076540f163SKrzysztof Drewniak 
20086540f163SKrzysztof Drewniak   Value *V1 = I.getOperand(0);
20096540f163SKrzysztof Drewniak   Value *V2 = I.getOperand(1);
20106540f163SKrzysztof Drewniak   ArrayRef<int> Mask = I.getShuffleMask();
20116540f163SKrzysztof Drewniak   auto [V1Rsrc, V1Off] = getPtrParts(V1);
20126540f163SKrzysztof Drewniak   auto [V2Rsrc, V2Off] = getPtrParts(V2);
20136540f163SKrzysztof Drewniak 
20146540f163SKrzysztof Drewniak   Value *RsrcRes =
20156540f163SKrzysztof Drewniak       IRB.CreateShuffleVector(V1Rsrc, V2Rsrc, Mask, I.getName() + ".rsrc");
20166540f163SKrzysztof Drewniak   copyMetadata(RsrcRes, &I);
20176540f163SKrzysztof Drewniak   Value *OffRes =
20186540f163SKrzysztof Drewniak       IRB.CreateShuffleVector(V1Off, V2Off, Mask, I.getName() + ".off");
20196540f163SKrzysztof Drewniak   copyMetadata(OffRes, &I);
20206540f163SKrzysztof Drewniak   SplitUsers.insert(&I);
20216540f163SKrzysztof Drewniak   return {RsrcRes, OffRes};
20226540f163SKrzysztof Drewniak }
20236540f163SKrzysztof Drewniak 
20246540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitPHINode(PHINode &PHI) {
20256540f163SKrzysztof Drewniak   if (!isSplitFatPtr(PHI.getType()))
20266540f163SKrzysztof Drewniak     return {nullptr, nullptr};
20276540f163SKrzysztof Drewniak   IRB.SetInsertPoint(*PHI.getInsertionPointAfterDef());
20286540f163SKrzysztof Drewniak   // Phi nodes will be handled in post-processing after we've visited every
20296540f163SKrzysztof Drewniak   // instruction. However, instead of just returning {nullptr, nullptr},
20306540f163SKrzysztof Drewniak   // we explicitly create the temporary extractvalue operations that are our
20316540f163SKrzysztof Drewniak   // temporary results so that they end up at the beginning of the block with
20326540f163SKrzysztof Drewniak   // the PHIs.
20336540f163SKrzysztof Drewniak   Value *TmpRsrc = IRB.CreateExtractValue(&PHI, 0, PHI.getName() + ".rsrc");
20346540f163SKrzysztof Drewniak   Value *TmpOff = IRB.CreateExtractValue(&PHI, 1, PHI.getName() + ".off");
20356540f163SKrzysztof Drewniak   Conditionals.push_back(&PHI);
20366540f163SKrzysztof Drewniak   SplitUsers.insert(&PHI);
20376540f163SKrzysztof Drewniak   return {TmpRsrc, TmpOff};
20386540f163SKrzysztof Drewniak }
20396540f163SKrzysztof Drewniak 
20406540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitSelectInst(SelectInst &SI) {
20416540f163SKrzysztof Drewniak   if (!isSplitFatPtr(SI.getType()))
20426540f163SKrzysztof Drewniak     return {nullptr, nullptr};
20436540f163SKrzysztof Drewniak   IRB.SetInsertPoint(&SI);
20446540f163SKrzysztof Drewniak 
20456540f163SKrzysztof Drewniak   Value *Cond = SI.getCondition();
20466540f163SKrzysztof Drewniak   Value *True = SI.getTrueValue();
20476540f163SKrzysztof Drewniak   Value *False = SI.getFalseValue();
20486540f163SKrzysztof Drewniak   auto [TrueRsrc, TrueOff] = getPtrParts(True);
20496540f163SKrzysztof Drewniak   auto [FalseRsrc, FalseOff] = getPtrParts(False);
20506540f163SKrzysztof Drewniak 
2051769eab47SKrzysztof Drewniak   Value *RsrcRes =
20526540f163SKrzysztof Drewniak       IRB.CreateSelect(Cond, TrueRsrc, FalseRsrc, SI.getName() + ".rsrc", &SI);
20536540f163SKrzysztof Drewniak   copyMetadata(RsrcRes, &SI);
20546540f163SKrzysztof Drewniak   Conditionals.push_back(&SI);
20556540f163SKrzysztof Drewniak   Value *OffRes =
20566540f163SKrzysztof Drewniak       IRB.CreateSelect(Cond, TrueOff, FalseOff, SI.getName() + ".off", &SI);
20576540f163SKrzysztof Drewniak   copyMetadata(OffRes, &SI);
20586540f163SKrzysztof Drewniak   SplitUsers.insert(&SI);
20596540f163SKrzysztof Drewniak   return {RsrcRes, OffRes};
20606540f163SKrzysztof Drewniak }
20616540f163SKrzysztof Drewniak 
20626540f163SKrzysztof Drewniak /// Returns true if this intrinsic needs to be removed when it is
20636540f163SKrzysztof Drewniak /// applied to `ptr addrspace(7)` values. Calls to these intrinsics are
20646540f163SKrzysztof Drewniak /// rewritten into calls to versions of that intrinsic on the resource
20656540f163SKrzysztof Drewniak /// descriptor.
20666540f163SKrzysztof Drewniak static bool isRemovablePointerIntrinsic(Intrinsic::ID IID) {
20676540f163SKrzysztof Drewniak   switch (IID) {
20686540f163SKrzysztof Drewniak   default:
20696540f163SKrzysztof Drewniak     return false;
20706540f163SKrzysztof Drewniak   case Intrinsic::ptrmask:
20716540f163SKrzysztof Drewniak   case Intrinsic::invariant_start:
20726540f163SKrzysztof Drewniak   case Intrinsic::invariant_end:
20736540f163SKrzysztof Drewniak   case Intrinsic::launder_invariant_group:
20746540f163SKrzysztof Drewniak   case Intrinsic::strip_invariant_group:
20756540f163SKrzysztof Drewniak     return true;
20766540f163SKrzysztof Drewniak   }
20776540f163SKrzysztof Drewniak }
20786540f163SKrzysztof Drewniak 
20796540f163SKrzysztof Drewniak PtrParts SplitPtrStructs::visitIntrinsicInst(IntrinsicInst &I) {
20806540f163SKrzysztof Drewniak   Intrinsic::ID IID = I.getIntrinsicID();
20816540f163SKrzysztof Drewniak   switch (IID) {
20826540f163SKrzysztof Drewniak   default:
20836540f163SKrzysztof Drewniak     break;
20846540f163SKrzysztof Drewniak   case Intrinsic::ptrmask: {
20856540f163SKrzysztof Drewniak     Value *Ptr = I.getArgOperand(0);
20866540f163SKrzysztof Drewniak     if (!isSplitFatPtr(Ptr->getType()))
20876540f163SKrzysztof Drewniak       return {nullptr, nullptr};
20886540f163SKrzysztof Drewniak     Value *Mask = I.getArgOperand(1);
20896540f163SKrzysztof Drewniak     IRB.SetInsertPoint(&I);
20906540f163SKrzysztof Drewniak     auto [Rsrc, Off] = getPtrParts(Ptr);
20916540f163SKrzysztof Drewniak     if (Mask->getType() != Off->getType())
20926540f163SKrzysztof Drewniak       report_fatal_error("offset width is not equal to index width of fat "
20936540f163SKrzysztof Drewniak                          "pointer (data layout not set up correctly?)");
20946540f163SKrzysztof Drewniak     Value *OffRes = IRB.CreateAnd(Off, Mask, I.getName() + ".off");
20956540f163SKrzysztof Drewniak     copyMetadata(OffRes, &I);
20966540f163SKrzysztof Drewniak     SplitUsers.insert(&I);
20976540f163SKrzysztof Drewniak     return {Rsrc, OffRes};
20986540f163SKrzysztof Drewniak   }
20996540f163SKrzysztof Drewniak   // Pointer annotation intrinsics that, given their object-wide nature
21006540f163SKrzysztof Drewniak   // operate on the resource part.
21016540f163SKrzysztof Drewniak   case Intrinsic::invariant_start: {
21026540f163SKrzysztof Drewniak     Value *Ptr = I.getArgOperand(1);
21036540f163SKrzysztof Drewniak     if (!isSplitFatPtr(Ptr->getType()))
21046540f163SKrzysztof Drewniak       return {nullptr, nullptr};
21056540f163SKrzysztof Drewniak     IRB.SetInsertPoint(&I);
21066540f163SKrzysztof Drewniak     auto [Rsrc, Off] = getPtrParts(Ptr);
21076540f163SKrzysztof Drewniak     Type *NewTy = PointerType::get(I.getContext(), AMDGPUAS::BUFFER_RESOURCE);
21086540f163SKrzysztof Drewniak     auto *NewRsrc = IRB.CreateIntrinsic(IID, {NewTy}, {I.getOperand(0), Rsrc});
21096540f163SKrzysztof Drewniak     copyMetadata(NewRsrc, &I);
21106540f163SKrzysztof Drewniak     NewRsrc->takeName(&I);
21116540f163SKrzysztof Drewniak     SplitUsers.insert(&I);
21126540f163SKrzysztof Drewniak     I.replaceAllUsesWith(NewRsrc);
21136540f163SKrzysztof Drewniak     return {nullptr, nullptr};
21146540f163SKrzysztof Drewniak   }
21156540f163SKrzysztof Drewniak   case Intrinsic::invariant_end: {
21166540f163SKrzysztof Drewniak     Value *RealPtr = I.getArgOperand(2);
21176540f163SKrzysztof Drewniak     if (!isSplitFatPtr(RealPtr->getType()))
21186540f163SKrzysztof Drewniak       return {nullptr, nullptr};
21196540f163SKrzysztof Drewniak     IRB.SetInsertPoint(&I);
21206540f163SKrzysztof Drewniak     Value *RealRsrc = getPtrParts(RealPtr).first;
21216540f163SKrzysztof Drewniak     Value *InvPtr = I.getArgOperand(0);
21226540f163SKrzysztof Drewniak     Value *Size = I.getArgOperand(1);
21236540f163SKrzysztof Drewniak     Value *NewRsrc = IRB.CreateIntrinsic(IID, {RealRsrc->getType()},
21246540f163SKrzysztof Drewniak                                          {InvPtr, Size, RealRsrc});
21256540f163SKrzysztof Drewniak     copyMetadata(NewRsrc, &I);
21266540f163SKrzysztof Drewniak     NewRsrc->takeName(&I);
21276540f163SKrzysztof Drewniak     SplitUsers.insert(&I);
21286540f163SKrzysztof Drewniak     I.replaceAllUsesWith(NewRsrc);
21296540f163SKrzysztof Drewniak     return {nullptr, nullptr};
21306540f163SKrzysztof Drewniak   }
21316540f163SKrzysztof Drewniak   case Intrinsic::launder_invariant_group:
21326540f163SKrzysztof Drewniak   case Intrinsic::strip_invariant_group: {
21336540f163SKrzysztof Drewniak     Value *Ptr = I.getArgOperand(0);
21346540f163SKrzysztof Drewniak     if (!isSplitFatPtr(Ptr->getType()))
21356540f163SKrzysztof Drewniak       return {nullptr, nullptr};
21366540f163SKrzysztof Drewniak     IRB.SetInsertPoint(&I);
21376540f163SKrzysztof Drewniak     auto [Rsrc, Off] = getPtrParts(Ptr);
21386540f163SKrzysztof Drewniak     Value *NewRsrc = IRB.CreateIntrinsic(IID, {Rsrc->getType()}, {Rsrc});
21396540f163SKrzysztof Drewniak     copyMetadata(NewRsrc, &I);
21406540f163SKrzysztof Drewniak     NewRsrc->takeName(&I);
21416540f163SKrzysztof Drewniak     SplitUsers.insert(&I);
21426540f163SKrzysztof Drewniak     return {NewRsrc, Off};
21436540f163SKrzysztof Drewniak   }
21446540f163SKrzysztof Drewniak   }
21456540f163SKrzysztof Drewniak   return {nullptr, nullptr};
21466540f163SKrzysztof Drewniak }
21476540f163SKrzysztof Drewniak 
21486540f163SKrzysztof Drewniak void SplitPtrStructs::processFunction(Function &F) {
21496540f163SKrzysztof Drewniak   ST = &TM->getSubtarget<GCNSubtarget>(F);
21506540f163SKrzysztof Drewniak   SmallVector<Instruction *, 0> Originals;
21516540f163SKrzysztof Drewniak   LLVM_DEBUG(dbgs() << "Splitting pointer structs in function: " << F.getName()
21526540f163SKrzysztof Drewniak                     << "\n");
21536540f163SKrzysztof Drewniak   for (Instruction &I : instructions(F))
21546540f163SKrzysztof Drewniak     Originals.push_back(&I);
21556540f163SKrzysztof Drewniak   for (Instruction *I : Originals) {
21566540f163SKrzysztof Drewniak     auto [Rsrc, Off] = visit(I);
215724c256a6SSimon Pilgrim     assert(((Rsrc && Off) || (!Rsrc && !Off)) &&
215824c256a6SSimon Pilgrim            "Can't have a resource but no offset");
21596540f163SKrzysztof Drewniak     if (Rsrc)
21606540f163SKrzysztof Drewniak       RsrcParts[I] = Rsrc;
21616540f163SKrzysztof Drewniak     if (Off)
21626540f163SKrzysztof Drewniak       OffParts[I] = Off;
21636540f163SKrzysztof Drewniak   }
21646540f163SKrzysztof Drewniak   processConditionals();
21656540f163SKrzysztof Drewniak   killAndReplaceSplitInstructions(Originals);
21666540f163SKrzysztof Drewniak 
21676540f163SKrzysztof Drewniak   // Clean up after ourselves to save on memory.
21686540f163SKrzysztof Drewniak   RsrcParts.clear();
21696540f163SKrzysztof Drewniak   OffParts.clear();
21706540f163SKrzysztof Drewniak   SplitUsers.clear();
21716540f163SKrzysztof Drewniak   Conditionals.clear();
21726540f163SKrzysztof Drewniak   ConditionalTemps.clear();
21736540f163SKrzysztof Drewniak }
21746540f163SKrzysztof Drewniak 
21756540f163SKrzysztof Drewniak namespace {
21766540f163SKrzysztof Drewniak class AMDGPULowerBufferFatPointers : public ModulePass {
21776540f163SKrzysztof Drewniak public:
21786540f163SKrzysztof Drewniak   static char ID;
21796540f163SKrzysztof Drewniak 
21806540f163SKrzysztof Drewniak   AMDGPULowerBufferFatPointers() : ModulePass(ID) {
21816540f163SKrzysztof Drewniak     initializeAMDGPULowerBufferFatPointersPass(
21826540f163SKrzysztof Drewniak         *PassRegistry::getPassRegistry());
21836540f163SKrzysztof Drewniak   }
21846540f163SKrzysztof Drewniak 
21856540f163SKrzysztof Drewniak   bool run(Module &M, const TargetMachine &TM);
21866540f163SKrzysztof Drewniak   bool runOnModule(Module &M) override;
21876540f163SKrzysztof Drewniak 
21886540f163SKrzysztof Drewniak   void getAnalysisUsage(AnalysisUsage &AU) const override;
21896540f163SKrzysztof Drewniak };
21906540f163SKrzysztof Drewniak } // namespace
21916540f163SKrzysztof Drewniak 
21926540f163SKrzysztof Drewniak /// Returns true if there are values that have a buffer fat pointer in them,
21936540f163SKrzysztof Drewniak /// which means we'll need to perform rewrites on this function. As a side
21946540f163SKrzysztof Drewniak /// effect, this will populate the type remapping cache.
21956540f163SKrzysztof Drewniak static bool containsBufferFatPointers(const Function &F,
21966540f163SKrzysztof Drewniak                                       BufferFatPtrToStructTypeMap *TypeMap) {
21976540f163SKrzysztof Drewniak   bool HasFatPointers = false;
21985ef768d2SNikita Popov   for (const BasicBlock &BB : F)
21995ef768d2SNikita Popov     for (const Instruction &I : BB)
22006540f163SKrzysztof Drewniak       HasFatPointers |= (I.getType() != TypeMap->remapType(I.getType()));
22016540f163SKrzysztof Drewniak   return HasFatPointers;
22026540f163SKrzysztof Drewniak }
22036540f163SKrzysztof Drewniak 
22046540f163SKrzysztof Drewniak static bool hasFatPointerInterface(const Function &F,
22056540f163SKrzysztof Drewniak                                    BufferFatPtrToStructTypeMap *TypeMap) {
22066540f163SKrzysztof Drewniak   Type *Ty = F.getFunctionType();
22076540f163SKrzysztof Drewniak   return Ty != TypeMap->remapType(Ty);
22086540f163SKrzysztof Drewniak }
22096540f163SKrzysztof Drewniak 
22106540f163SKrzysztof Drewniak /// Move the body of `OldF` into a new function, returning it.
22116540f163SKrzysztof Drewniak static Function *moveFunctionAdaptingType(Function *OldF, FunctionType *NewTy,
22126540f163SKrzysztof Drewniak                                           ValueToValueMapTy &CloneMap) {
22136540f163SKrzysztof Drewniak   bool IsIntrinsic = OldF->isIntrinsic();
22146540f163SKrzysztof Drewniak   Function *NewF =
22156540f163SKrzysztof Drewniak       Function::Create(NewTy, OldF->getLinkage(), OldF->getAddressSpace());
2216d0117b71SOrlando Cazalet-Hyams   NewF->IsNewDbgInfoFormat = OldF->IsNewDbgInfoFormat;
22176540f163SKrzysztof Drewniak   NewF->copyAttributesFrom(OldF);
22186540f163SKrzysztof Drewniak   NewF->copyMetadata(OldF, 0);
22196540f163SKrzysztof Drewniak   NewF->takeName(OldF);
22206540f163SKrzysztof Drewniak   NewF->updateAfterNameChange();
22216540f163SKrzysztof Drewniak   NewF->setDLLStorageClass(OldF->getDLLStorageClass());
22226540f163SKrzysztof Drewniak   OldF->getParent()->getFunctionList().insertAfter(OldF->getIterator(), NewF);
22236540f163SKrzysztof Drewniak 
22246540f163SKrzysztof Drewniak   while (!OldF->empty()) {
22256540f163SKrzysztof Drewniak     BasicBlock *BB = &OldF->front();
22266540f163SKrzysztof Drewniak     BB->removeFromParent();
22276540f163SKrzysztof Drewniak     BB->insertInto(NewF);
22286540f163SKrzysztof Drewniak     CloneMap[BB] = BB;
22296540f163SKrzysztof Drewniak     for (Instruction &I : *BB) {
22306540f163SKrzysztof Drewniak       CloneMap[&I] = &I;
22316540f163SKrzysztof Drewniak     }
22326540f163SKrzysztof Drewniak   }
22336540f163SKrzysztof Drewniak 
22346540f163SKrzysztof Drewniak   SmallVector<AttributeSet> ArgAttrs;
22356540f163SKrzysztof Drewniak   AttributeList OldAttrs = OldF->getAttributes();
22366540f163SKrzysztof Drewniak 
22376540f163SKrzysztof Drewniak   for (auto [I, OldArg, NewArg] : enumerate(OldF->args(), NewF->args())) {
22386540f163SKrzysztof Drewniak     CloneMap[&NewArg] = &OldArg;
22396540f163SKrzysztof Drewniak     NewArg.takeName(&OldArg);
22406540f163SKrzysztof Drewniak     Type *OldArgTy = OldArg.getType(), *NewArgTy = NewArg.getType();
22416540f163SKrzysztof Drewniak     // Temporarily mutate type of `NewArg` to allow RAUW to work.
22426540f163SKrzysztof Drewniak     NewArg.mutateType(OldArgTy);
22436540f163SKrzysztof Drewniak     OldArg.replaceAllUsesWith(&NewArg);
22446540f163SKrzysztof Drewniak     NewArg.mutateType(NewArgTy);
22456540f163SKrzysztof Drewniak 
22466540f163SKrzysztof Drewniak     AttributeSet ArgAttr = OldAttrs.getParamAttrs(I);
22476540f163SKrzysztof Drewniak     // Intrinsics get their attributes fixed later.
22486540f163SKrzysztof Drewniak     if (OldArgTy != NewArgTy && !IsIntrinsic)
22494f614a8fSNikita Popov       ArgAttr = ArgAttr.removeAttributes(
22504f614a8fSNikita Popov           NewF->getContext(),
22514f614a8fSNikita Popov           AttributeFuncs::typeIncompatible(NewArgTy, ArgAttr));
22526540f163SKrzysztof Drewniak     ArgAttrs.push_back(ArgAttr);
22536540f163SKrzysztof Drewniak   }
22546540f163SKrzysztof Drewniak   AttributeSet RetAttrs = OldAttrs.getRetAttrs();
22556540f163SKrzysztof Drewniak   if (OldF->getReturnType() != NewF->getReturnType() && !IsIntrinsic)
22564f614a8fSNikita Popov     RetAttrs = RetAttrs.removeAttributes(
22574f614a8fSNikita Popov         NewF->getContext(),
22584f614a8fSNikita Popov         AttributeFuncs::typeIncompatible(NewF->getReturnType(), RetAttrs));
22596540f163SKrzysztof Drewniak   NewF->setAttributes(AttributeList::get(
22606540f163SKrzysztof Drewniak       NewF->getContext(), OldAttrs.getFnAttrs(), RetAttrs, ArgAttrs));
22616540f163SKrzysztof Drewniak   return NewF;
22626540f163SKrzysztof Drewniak }
22636540f163SKrzysztof Drewniak 
22646540f163SKrzysztof Drewniak static void makeCloneInPraceMap(Function *F, ValueToValueMapTy &CloneMap) {
22656540f163SKrzysztof Drewniak   for (Argument &A : F->args())
22666540f163SKrzysztof Drewniak     CloneMap[&A] = &A;
22676540f163SKrzysztof Drewniak   for (BasicBlock &BB : *F) {
22686540f163SKrzysztof Drewniak     CloneMap[&BB] = &BB;
22696540f163SKrzysztof Drewniak     for (Instruction &I : BB)
22706540f163SKrzysztof Drewniak       CloneMap[&I] = &I;
22716540f163SKrzysztof Drewniak   }
22726540f163SKrzysztof Drewniak }
22736540f163SKrzysztof Drewniak 
22746540f163SKrzysztof Drewniak bool AMDGPULowerBufferFatPointers::run(Module &M, const TargetMachine &TM) {
22756540f163SKrzysztof Drewniak   bool Changed = false;
22766540f163SKrzysztof Drewniak   const DataLayout &DL = M.getDataLayout();
22776540f163SKrzysztof Drewniak   // Record the functions which need to be remapped.
22786540f163SKrzysztof Drewniak   // The second element of the pair indicates whether the function has to have
22796540f163SKrzysztof Drewniak   // its arguments or return types adjusted.
22806540f163SKrzysztof Drewniak   SmallVector<std::pair<Function *, bool>> NeedsRemap;
22816540f163SKrzysztof Drewniak 
22826540f163SKrzysztof Drewniak   BufferFatPtrToStructTypeMap StructTM(DL);
22836540f163SKrzysztof Drewniak   BufferFatPtrToIntTypeMap IntTM(DL);
22846540f163SKrzysztof Drewniak   for (const GlobalVariable &GV : M.globals()) {
22856540f163SKrzysztof Drewniak     if (GV.getAddressSpace() == AMDGPUAS::BUFFER_FAT_POINTER)
22866540f163SKrzysztof Drewniak       report_fatal_error("Global variables with a buffer fat pointer address "
22876540f163SKrzysztof Drewniak                          "space (7) are not supported");
22886540f163SKrzysztof Drewniak     Type *VT = GV.getValueType();
22896540f163SKrzysztof Drewniak     if (VT != StructTM.remapType(VT))
22906540f163SKrzysztof Drewniak       report_fatal_error("Global variables that contain buffer fat pointers "
22916540f163SKrzysztof Drewniak                          "(address space 7 pointers) are unsupported. Use "
22926540f163SKrzysztof Drewniak                          "buffer resource pointers (address space 8) instead.");
22936540f163SKrzysztof Drewniak   }
22946540f163SKrzysztof Drewniak 
22955ef768d2SNikita Popov   {
22965ef768d2SNikita Popov     // Collect all constant exprs and aggregates referenced by any function.
22975ef768d2SNikita Popov     SmallVector<Constant *, 8> Worklist;
22985ef768d2SNikita Popov     for (Function &F : M.functions())
22995ef768d2SNikita Popov       for (Instruction &I : instructions(F))
23005ef768d2SNikita Popov         for (Value *Op : I.operands())
23015ef768d2SNikita Popov           if (isa<ConstantExpr>(Op) || isa<ConstantAggregate>(Op))
23025ef768d2SNikita Popov             Worklist.push_back(cast<Constant>(Op));
23035ef768d2SNikita Popov 
23045ef768d2SNikita Popov     // Recursively look for any referenced buffer pointer constants.
23055ef768d2SNikita Popov     SmallPtrSet<Constant *, 8> Visited;
23065ef768d2SNikita Popov     SetVector<Constant *> BufferFatPtrConsts;
23075ef768d2SNikita Popov     while (!Worklist.empty()) {
23085ef768d2SNikita Popov       Constant *C = Worklist.pop_back_val();
23095ef768d2SNikita Popov       if (!Visited.insert(C).second)
23105ef768d2SNikita Popov         continue;
23115ef768d2SNikita Popov       if (isBufferFatPtrOrVector(C->getType()))
23125ef768d2SNikita Popov         BufferFatPtrConsts.insert(C);
23135ef768d2SNikita Popov       for (Value *Op : C->operands())
23145ef768d2SNikita Popov         if (isa<ConstantExpr>(Op) || isa<ConstantAggregate>(Op))
23155ef768d2SNikita Popov           Worklist.push_back(cast<Constant>(Op));
23165ef768d2SNikita Popov     }
23175ef768d2SNikita Popov 
23185ef768d2SNikita Popov     // Expand all constant expressions using fat buffer pointers to
23195ef768d2SNikita Popov     // instructions.
23205ef768d2SNikita Popov     Changed |= convertUsersOfConstantsToInstructions(
23215ef768d2SNikita Popov         BufferFatPtrConsts.getArrayRef(), /*RestrictToFunc=*/nullptr,
23225ef768d2SNikita Popov         /*RemoveDeadConstants=*/false, /*IncludeSelf=*/true);
23235ef768d2SNikita Popov   }
23245ef768d2SNikita Popov 
23256540f163SKrzysztof Drewniak   StoreFatPtrsAsIntsVisitor MemOpsRewrite(&IntTM, M.getContext());
2326*697c1883SKrzysztof Drewniak   LegalizeBufferContentTypesVisitor BufferContentsTypeRewrite(DL,
2327*697c1883SKrzysztof Drewniak                                                               M.getContext());
23286540f163SKrzysztof Drewniak   for (Function &F : M.functions()) {
23296540f163SKrzysztof Drewniak     bool InterfaceChange = hasFatPointerInterface(F, &StructTM);
23306540f163SKrzysztof Drewniak     bool BodyChanges = containsBufferFatPointers(F, &StructTM);
23316540f163SKrzysztof Drewniak     Changed |= MemOpsRewrite.processFunction(F);
2332*697c1883SKrzysztof Drewniak     if (InterfaceChange || BodyChanges) {
23336540f163SKrzysztof Drewniak       NeedsRemap.push_back(std::make_pair(&F, InterfaceChange));
2334*697c1883SKrzysztof Drewniak       Changed |= BufferContentsTypeRewrite.processFunction(F);
2335*697c1883SKrzysztof Drewniak     }
23366540f163SKrzysztof Drewniak   }
23376540f163SKrzysztof Drewniak   if (NeedsRemap.empty())
23386540f163SKrzysztof Drewniak     return Changed;
23396540f163SKrzysztof Drewniak 
23406540f163SKrzysztof Drewniak   SmallVector<Function *> NeedsPostProcess;
23416540f163SKrzysztof Drewniak   SmallVector<Function *> Intrinsics;
23426540f163SKrzysztof Drewniak   // Keep one big map so as to memoize constants across functions.
23436540f163SKrzysztof Drewniak   ValueToValueMapTy CloneMap;
23445ef768d2SNikita Popov   FatPtrConstMaterializer Materializer(&StructTM, CloneMap);
23456540f163SKrzysztof Drewniak 
23466540f163SKrzysztof Drewniak   ValueMapper LowerInFuncs(CloneMap, RF_None, &StructTM, &Materializer);
23476540f163SKrzysztof Drewniak   for (auto [F, InterfaceChange] : NeedsRemap) {
23486540f163SKrzysztof Drewniak     Function *NewF = F;
23496540f163SKrzysztof Drewniak     if (InterfaceChange)
23506540f163SKrzysztof Drewniak       NewF = moveFunctionAdaptingType(
23516540f163SKrzysztof Drewniak           F, cast<FunctionType>(StructTM.remapType(F->getFunctionType())),
23526540f163SKrzysztof Drewniak           CloneMap);
23536540f163SKrzysztof Drewniak     else
23546540f163SKrzysztof Drewniak       makeCloneInPraceMap(F, CloneMap);
23556540f163SKrzysztof Drewniak     LowerInFuncs.remapFunction(*NewF);
23566540f163SKrzysztof Drewniak     if (NewF->isIntrinsic())
23576540f163SKrzysztof Drewniak       Intrinsics.push_back(NewF);
23586540f163SKrzysztof Drewniak     else
23596540f163SKrzysztof Drewniak       NeedsPostProcess.push_back(NewF);
23606540f163SKrzysztof Drewniak     if (InterfaceChange) {
23616540f163SKrzysztof Drewniak       F->replaceAllUsesWith(NewF);
23626540f163SKrzysztof Drewniak       F->eraseFromParent();
23636540f163SKrzysztof Drewniak     }
23646540f163SKrzysztof Drewniak     Changed = true;
23656540f163SKrzysztof Drewniak   }
23666540f163SKrzysztof Drewniak   StructTM.clear();
23676540f163SKrzysztof Drewniak   IntTM.clear();
23686540f163SKrzysztof Drewniak   CloneMap.clear();
23696540f163SKrzysztof Drewniak 
23706540f163SKrzysztof Drewniak   SplitPtrStructs Splitter(M.getContext(), &TM);
23716540f163SKrzysztof Drewniak   for (Function *F : NeedsPostProcess)
23726540f163SKrzysztof Drewniak     Splitter.processFunction(*F);
23736540f163SKrzysztof Drewniak   for (Function *F : Intrinsics) {
23746540f163SKrzysztof Drewniak     if (isRemovablePointerIntrinsic(F->getIntrinsicID())) {
23756540f163SKrzysztof Drewniak       F->eraseFromParent();
23766540f163SKrzysztof Drewniak     } else {
23776540f163SKrzysztof Drewniak       std::optional<Function *> NewF = Intrinsic::remangleIntrinsicFunction(F);
23786540f163SKrzysztof Drewniak       if (NewF)
23796540f163SKrzysztof Drewniak         F->replaceAllUsesWith(*NewF);
23806540f163SKrzysztof Drewniak     }
23816540f163SKrzysztof Drewniak   }
23826540f163SKrzysztof Drewniak   return Changed;
23836540f163SKrzysztof Drewniak }
23846540f163SKrzysztof Drewniak 
23856540f163SKrzysztof Drewniak bool AMDGPULowerBufferFatPointers::runOnModule(Module &M) {
23866540f163SKrzysztof Drewniak   TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
23876540f163SKrzysztof Drewniak   const TargetMachine &TM = TPC.getTM<TargetMachine>();
23886540f163SKrzysztof Drewniak   return run(M, TM);
23896540f163SKrzysztof Drewniak }
23906540f163SKrzysztof Drewniak 
23916540f163SKrzysztof Drewniak char AMDGPULowerBufferFatPointers::ID = 0;
23926540f163SKrzysztof Drewniak 
23936540f163SKrzysztof Drewniak char &llvm::AMDGPULowerBufferFatPointersID = AMDGPULowerBufferFatPointers::ID;
23946540f163SKrzysztof Drewniak 
23956540f163SKrzysztof Drewniak void AMDGPULowerBufferFatPointers::getAnalysisUsage(AnalysisUsage &AU) const {
23966540f163SKrzysztof Drewniak   AU.addRequired<TargetPassConfig>();
23976540f163SKrzysztof Drewniak }
23986540f163SKrzysztof Drewniak 
23996540f163SKrzysztof Drewniak #define PASS_DESC "Lower buffer fat pointer operations to buffer resources"
24006540f163SKrzysztof Drewniak INITIALIZE_PASS_BEGIN(AMDGPULowerBufferFatPointers, DEBUG_TYPE, PASS_DESC,
24016540f163SKrzysztof Drewniak                       false, false)
24026540f163SKrzysztof Drewniak INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
24036540f163SKrzysztof Drewniak INITIALIZE_PASS_END(AMDGPULowerBufferFatPointers, DEBUG_TYPE, PASS_DESC, false,
24046540f163SKrzysztof Drewniak                     false)
24056540f163SKrzysztof Drewniak #undef PASS_DESC
24066540f163SKrzysztof Drewniak 
24076540f163SKrzysztof Drewniak ModulePass *llvm::createAMDGPULowerBufferFatPointersPass() {
24086540f163SKrzysztof Drewniak   return new AMDGPULowerBufferFatPointers();
24096540f163SKrzysztof Drewniak }
24106540f163SKrzysztof Drewniak 
24116540f163SKrzysztof Drewniak PreservedAnalyses
24126540f163SKrzysztof Drewniak AMDGPULowerBufferFatPointersPass::run(Module &M, ModuleAnalysisManager &MA) {
24136540f163SKrzysztof Drewniak   return AMDGPULowerBufferFatPointers().run(M, TM) ? PreservedAnalyses::none()
24146540f163SKrzysztof Drewniak                                                    : PreservedAnalyses::all();
24156540f163SKrzysztof Drewniak }
2416