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