10fca76d5SJustin Bogner //===- DXILResourceAccess.cpp - Resource access via load/store ------------===// 20fca76d5SJustin Bogner // 30fca76d5SJustin Bogner // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40fca76d5SJustin Bogner // See https://llvm.org/LICENSE.txt for license information. 50fca76d5SJustin Bogner // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60fca76d5SJustin Bogner // 70fca76d5SJustin Bogner //===----------------------------------------------------------------------===// 80fca76d5SJustin Bogner 90fca76d5SJustin Bogner #include "DXILResourceAccess.h" 100fca76d5SJustin Bogner #include "DirectX.h" 110fca76d5SJustin Bogner #include "llvm/Analysis/DXILResource.h" 120fca76d5SJustin Bogner #include "llvm/IR/Dominators.h" 130fca76d5SJustin Bogner #include "llvm/IR/IRBuilder.h" 140fca76d5SJustin Bogner #include "llvm/IR/Instructions.h" 150fca76d5SJustin Bogner #include "llvm/IR/IntrinsicInst.h" 160fca76d5SJustin Bogner #include "llvm/IR/Intrinsics.h" 170fca76d5SJustin Bogner #include "llvm/IR/IntrinsicsDirectX.h" 180fca76d5SJustin Bogner #include "llvm/InitializePasses.h" 190fca76d5SJustin Bogner 200fca76d5SJustin Bogner #define DEBUG_TYPE "dxil-resource-access" 210fca76d5SJustin Bogner 220fca76d5SJustin Bogner using namespace llvm; 230fca76d5SJustin Bogner 24*2f39d138SJustin Bogner static Value *calculateGEPOffset(GetElementPtrInst *GEP, Value *PrevOffset, 250fca76d5SJustin Bogner dxil::ResourceTypeInfo &RTI) { 26*2f39d138SJustin Bogner assert(!PrevOffset && "Non-constant GEP chains not handled yet"); 270fca76d5SJustin Bogner 28*2f39d138SJustin Bogner const DataLayout &DL = GEP->getDataLayout(); 290fca76d5SJustin Bogner 30*2f39d138SJustin Bogner uint64_t ScalarSize = 1; 31*2f39d138SJustin Bogner if (RTI.isTyped()) { 32*2f39d138SJustin Bogner Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0); 33*2f39d138SJustin Bogner // We need the size of an element in bytes so that we can calculate the 34*2f39d138SJustin Bogner // offset in elements given a total offset in bytes. 350fca76d5SJustin Bogner Type *ScalarType = ContainedType->getScalarType(); 36*2f39d138SJustin Bogner ScalarSize = DL.getTypeSizeInBits(ScalarType) / 8; 37*2f39d138SJustin Bogner } 380fca76d5SJustin Bogner 390fca76d5SJustin Bogner APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0); 400fca76d5SJustin Bogner if (GEP->accumulateConstantOffset(DL, ConstantOffset)) { 410fca76d5SJustin Bogner APInt Scaled = ConstantOffset.udiv(ScalarSize); 42*2f39d138SJustin Bogner return ConstantInt::get(Type::getInt32Ty(GEP->getContext()), Scaled); 43*2f39d138SJustin Bogner } 44*2f39d138SJustin Bogner 450fca76d5SJustin Bogner auto IndexIt = GEP->idx_begin(); 460fca76d5SJustin Bogner assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 && 470fca76d5SJustin Bogner "GEP is not indexing through pointer"); 480fca76d5SJustin Bogner ++IndexIt; 49*2f39d138SJustin Bogner Value *Offset = *IndexIt; 500fca76d5SJustin Bogner assert(++IndexIt == GEP->idx_end() && "Too many indices in GEP"); 51*2f39d138SJustin Bogner return Offset; 520fca76d5SJustin Bogner } 530fca76d5SJustin Bogner 54*2f39d138SJustin Bogner static void createTypedBufferStore(IntrinsicInst *II, StoreInst *SI, 55*2f39d138SJustin Bogner Value *Offset, dxil::ResourceTypeInfo &RTI) { 560fca76d5SJustin Bogner IRBuilder<> Builder(SI); 57*2f39d138SJustin Bogner Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0); 58*2f39d138SJustin Bogner Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty()); 590fca76d5SJustin Bogner 600fca76d5SJustin Bogner Value *V = SI->getValueOperand(); 610fca76d5SJustin Bogner if (V->getType() == ContainedType) { 620fca76d5SJustin Bogner // V is already the right type. 63*2f39d138SJustin Bogner assert(!Offset && "store of whole element has offset?"); 64*2f39d138SJustin Bogner } else if (V->getType() == ContainedType->getScalarType()) { 650fca76d5SJustin Bogner // We're storing a scalar, so we need to load the current value and only 660fca76d5SJustin Bogner // replace the relevant part. 670fca76d5SJustin Bogner auto *Load = Builder.CreateIntrinsic( 682c7c07dfSJustin Bogner LoadType, Intrinsic::dx_resource_load_typedbuffer, 690fca76d5SJustin Bogner {II->getOperand(0), II->getOperand(1)}); 702c7c07dfSJustin Bogner auto *Struct = Builder.CreateExtractValue(Load, {0}); 712c7c07dfSJustin Bogner 72*2f39d138SJustin Bogner // If we have an offset from seeing a GEP earlier, use that. Otherwise, 0. 73*2f39d138SJustin Bogner if (!Offset) 74*2f39d138SJustin Bogner Offset = ConstantInt::get(Builder.getInt32Ty(), 0); 75*2f39d138SJustin Bogner V = Builder.CreateInsertElement(Struct, V, Offset); 760fca76d5SJustin Bogner } else { 770fca76d5SJustin Bogner llvm_unreachable("Store to typed resource has invalid type"); 780fca76d5SJustin Bogner } 790fca76d5SJustin Bogner 800fca76d5SJustin Bogner auto *Inst = Builder.CreateIntrinsic( 81aa07f922SJustin Bogner Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer, 820fca76d5SJustin Bogner {II->getOperand(0), II->getOperand(1), V}); 830fca76d5SJustin Bogner SI->replaceAllUsesWith(Inst); 84*2f39d138SJustin Bogner } 850fca76d5SJustin Bogner 86*2f39d138SJustin Bogner static void createRawStore(IntrinsicInst *II, StoreInst *SI, Value *Offset) { 87*2f39d138SJustin Bogner IRBuilder<> Builder(SI); 88*2f39d138SJustin Bogner 89*2f39d138SJustin Bogner if (!Offset) 90*2f39d138SJustin Bogner Offset = ConstantInt::get(Builder.getInt32Ty(), 0); 91*2f39d138SJustin Bogner Value *V = SI->getValueOperand(); 92*2f39d138SJustin Bogner // TODO: break up larger types 93*2f39d138SJustin Bogner auto *Inst = Builder.CreateIntrinsic( 94*2f39d138SJustin Bogner Builder.getVoidTy(), Intrinsic::dx_resource_store_rawbuffer, 95*2f39d138SJustin Bogner {II->getOperand(0), II->getOperand(1), Offset, V}); 96*2f39d138SJustin Bogner SI->replaceAllUsesWith(Inst); 97*2f39d138SJustin Bogner } 98*2f39d138SJustin Bogner 99*2f39d138SJustin Bogner static void createStoreIntrinsic(IntrinsicInst *II, StoreInst *SI, 100*2f39d138SJustin Bogner Value *Offset, dxil::ResourceTypeInfo &RTI) { 101*2f39d138SJustin Bogner switch (RTI.getResourceKind()) { 102*2f39d138SJustin Bogner case dxil::ResourceKind::TypedBuffer: 103*2f39d138SJustin Bogner return createTypedBufferStore(II, SI, Offset, RTI); 104*2f39d138SJustin Bogner case dxil::ResourceKind::RawBuffer: 105*2f39d138SJustin Bogner case dxil::ResourceKind::StructuredBuffer: 106*2f39d138SJustin Bogner return createRawStore(II, SI, Offset); 107*2f39d138SJustin Bogner case dxil::ResourceKind::Texture1D: 108*2f39d138SJustin Bogner case dxil::ResourceKind::Texture2D: 109*2f39d138SJustin Bogner case dxil::ResourceKind::Texture2DMS: 110*2f39d138SJustin Bogner case dxil::ResourceKind::Texture3D: 111*2f39d138SJustin Bogner case dxil::ResourceKind::TextureCube: 112*2f39d138SJustin Bogner case dxil::ResourceKind::Texture1DArray: 113*2f39d138SJustin Bogner case dxil::ResourceKind::Texture2DArray: 114*2f39d138SJustin Bogner case dxil::ResourceKind::Texture2DMSArray: 115*2f39d138SJustin Bogner case dxil::ResourceKind::TextureCubeArray: 116*2f39d138SJustin Bogner case dxil::ResourceKind::FeedbackTexture2D: 117*2f39d138SJustin Bogner case dxil::ResourceKind::FeedbackTexture2DArray: 118*2f39d138SJustin Bogner report_fatal_error("DXIL Load not implemented yet", 119*2f39d138SJustin Bogner /*gen_crash_diag=*/false); 120*2f39d138SJustin Bogner return; 121*2f39d138SJustin Bogner case dxil::ResourceKind::CBuffer: 122*2f39d138SJustin Bogner case dxil::ResourceKind::Sampler: 123*2f39d138SJustin Bogner case dxil::ResourceKind::TBuffer: 124*2f39d138SJustin Bogner case dxil::ResourceKind::RTAccelerationStructure: 125*2f39d138SJustin Bogner case dxil::ResourceKind::Invalid: 126*2f39d138SJustin Bogner case dxil::ResourceKind::NumEntries: 127*2f39d138SJustin Bogner llvm_unreachable("Invalid resource kind for store"); 128*2f39d138SJustin Bogner } 129*2f39d138SJustin Bogner llvm_unreachable("Unhandled case in switch"); 130*2f39d138SJustin Bogner } 131*2f39d138SJustin Bogner 132*2f39d138SJustin Bogner static void createTypedBufferLoad(IntrinsicInst *II, LoadInst *LI, 133*2f39d138SJustin Bogner Value *Offset, dxil::ResourceTypeInfo &RTI) { 1340fca76d5SJustin Bogner IRBuilder<> Builder(LI); 135*2f39d138SJustin Bogner Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0); 136*2f39d138SJustin Bogner Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty()); 137*2f39d138SJustin Bogner 138*2f39d138SJustin Bogner Value *V = 139*2f39d138SJustin Bogner Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer, 1400fca76d5SJustin Bogner {II->getOperand(0), II->getOperand(1)}); 1412c7c07dfSJustin Bogner V = Builder.CreateExtractValue(V, {0}); 1422c7c07dfSJustin Bogner 143*2f39d138SJustin Bogner if (Offset) 144*2f39d138SJustin Bogner V = Builder.CreateExtractElement(V, Offset); 1450fca76d5SJustin Bogner 1460fca76d5SJustin Bogner LI->replaceAllUsesWith(V); 147*2f39d138SJustin Bogner } 148*2f39d138SJustin Bogner 149*2f39d138SJustin Bogner static void createRawLoad(IntrinsicInst *II, LoadInst *LI, Value *Offset) { 150*2f39d138SJustin Bogner IRBuilder<> Builder(LI); 151*2f39d138SJustin Bogner // TODO: break up larger types 152*2f39d138SJustin Bogner Type *LoadType = StructType::get(LI->getType(), Builder.getInt1Ty()); 153*2f39d138SJustin Bogner if (!Offset) 154*2f39d138SJustin Bogner Offset = ConstantInt::get(Builder.getInt32Ty(), 0); 155*2f39d138SJustin Bogner Value *V = 156*2f39d138SJustin Bogner Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_rawbuffer, 157*2f39d138SJustin Bogner {II->getOperand(0), II->getOperand(1), Offset}); 158*2f39d138SJustin Bogner V = Builder.CreateExtractValue(V, {0}); 159*2f39d138SJustin Bogner 160*2f39d138SJustin Bogner LI->replaceAllUsesWith(V); 161*2f39d138SJustin Bogner } 162*2f39d138SJustin Bogner 163*2f39d138SJustin Bogner static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, Value *Offset, 164*2f39d138SJustin Bogner dxil::ResourceTypeInfo &RTI) { 165*2f39d138SJustin Bogner switch (RTI.getResourceKind()) { 166*2f39d138SJustin Bogner case dxil::ResourceKind::TypedBuffer: 167*2f39d138SJustin Bogner return createTypedBufferLoad(II, LI, Offset, RTI); 168*2f39d138SJustin Bogner case dxil::ResourceKind::RawBuffer: 169*2f39d138SJustin Bogner case dxil::ResourceKind::StructuredBuffer: 170*2f39d138SJustin Bogner return createRawLoad(II, LI, Offset); 171*2f39d138SJustin Bogner case dxil::ResourceKind::Texture1D: 172*2f39d138SJustin Bogner case dxil::ResourceKind::Texture2D: 173*2f39d138SJustin Bogner case dxil::ResourceKind::Texture2DMS: 174*2f39d138SJustin Bogner case dxil::ResourceKind::Texture3D: 175*2f39d138SJustin Bogner case dxil::ResourceKind::TextureCube: 176*2f39d138SJustin Bogner case dxil::ResourceKind::Texture1DArray: 177*2f39d138SJustin Bogner case dxil::ResourceKind::Texture2DArray: 178*2f39d138SJustin Bogner case dxil::ResourceKind::Texture2DMSArray: 179*2f39d138SJustin Bogner case dxil::ResourceKind::TextureCubeArray: 180*2f39d138SJustin Bogner case dxil::ResourceKind::FeedbackTexture2D: 181*2f39d138SJustin Bogner case dxil::ResourceKind::FeedbackTexture2DArray: 182*2f39d138SJustin Bogner case dxil::ResourceKind::CBuffer: 183*2f39d138SJustin Bogner case dxil::ResourceKind::TBuffer: 184*2f39d138SJustin Bogner // TODO: handle these 185*2f39d138SJustin Bogner return; 186*2f39d138SJustin Bogner case dxil::ResourceKind::Sampler: 187*2f39d138SJustin Bogner case dxil::ResourceKind::RTAccelerationStructure: 188*2f39d138SJustin Bogner case dxil::ResourceKind::Invalid: 189*2f39d138SJustin Bogner case dxil::ResourceKind::NumEntries: 190*2f39d138SJustin Bogner llvm_unreachable("Invalid resource kind for load"); 191*2f39d138SJustin Bogner } 192*2f39d138SJustin Bogner llvm_unreachable("Unhandled case in switch"); 193*2f39d138SJustin Bogner } 194*2f39d138SJustin Bogner 195*2f39d138SJustin Bogner static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) { 196*2f39d138SJustin Bogner // Process users keeping track of indexing accumulated from GEPs. 197*2f39d138SJustin Bogner struct AccessAndOffset { 198*2f39d138SJustin Bogner User *Access; 199*2f39d138SJustin Bogner Value *Offset; 200*2f39d138SJustin Bogner }; 201*2f39d138SJustin Bogner SmallVector<AccessAndOffset> Worklist; 202*2f39d138SJustin Bogner for (User *U : II->users()) 203*2f39d138SJustin Bogner Worklist.push_back({U, nullptr}); 204*2f39d138SJustin Bogner 205*2f39d138SJustin Bogner SmallVector<Instruction *> DeadInsts; 206*2f39d138SJustin Bogner while (!Worklist.empty()) { 207*2f39d138SJustin Bogner AccessAndOffset Current = Worklist.back(); 208*2f39d138SJustin Bogner Worklist.pop_back(); 209*2f39d138SJustin Bogner 210*2f39d138SJustin Bogner if (auto *GEP = dyn_cast<GetElementPtrInst>(Current.Access)) { 211*2f39d138SJustin Bogner IRBuilder<> Builder(GEP); 212*2f39d138SJustin Bogner 213*2f39d138SJustin Bogner Value *Offset = calculateGEPOffset(GEP, Current.Offset, RTI); 214*2f39d138SJustin Bogner for (User *U : GEP->users()) 215*2f39d138SJustin Bogner Worklist.push_back({U, Offset}); 216*2f39d138SJustin Bogner DeadInsts.push_back(GEP); 217*2f39d138SJustin Bogner 218*2f39d138SJustin Bogner } else if (auto *SI = dyn_cast<StoreInst>(Current.Access)) { 219*2f39d138SJustin Bogner assert(SI->getValueOperand() != II && "Pointer escaped!"); 220*2f39d138SJustin Bogner createStoreIntrinsic(II, SI, Current.Offset, RTI); 221*2f39d138SJustin Bogner DeadInsts.push_back(SI); 222*2f39d138SJustin Bogner 223*2f39d138SJustin Bogner } else if (auto *LI = dyn_cast<LoadInst>(Current.Access)) { 224*2f39d138SJustin Bogner createLoadIntrinsic(II, LI, Current.Offset, RTI); 2250fca76d5SJustin Bogner DeadInsts.push_back(LI); 2260fca76d5SJustin Bogner 2270fca76d5SJustin Bogner } else 2280fca76d5SJustin Bogner llvm_unreachable("Unhandled instruction - pointer escaped?"); 2290fca76d5SJustin Bogner } 2300fca76d5SJustin Bogner 2310fca76d5SJustin Bogner // Traverse the now-dead instructions in RPO and remove them. 2320fca76d5SJustin Bogner for (Instruction *Dead : llvm::reverse(DeadInsts)) 2330fca76d5SJustin Bogner Dead->eraseFromParent(); 2340fca76d5SJustin Bogner II->eraseFromParent(); 2350fca76d5SJustin Bogner } 2360fca76d5SJustin Bogner 2370fca76d5SJustin Bogner static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM) { 2380fca76d5SJustin Bogner bool Changed = false; 2390fca76d5SJustin Bogner SmallVector<std::pair<IntrinsicInst *, dxil::ResourceTypeInfo>> Resources; 2400fca76d5SJustin Bogner for (BasicBlock &BB : F) 2410fca76d5SJustin Bogner for (Instruction &I : BB) 2420fca76d5SJustin Bogner if (auto *II = dyn_cast<IntrinsicInst>(&I)) 2430fca76d5SJustin Bogner if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) { 2440fca76d5SJustin Bogner auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType()); 2450fca76d5SJustin Bogner Resources.emplace_back(II, DRTM[HandleTy]); 2460fca76d5SJustin Bogner } 2470fca76d5SJustin Bogner 248*2f39d138SJustin Bogner for (auto &[II, RI] : Resources) 249*2f39d138SJustin Bogner replaceAccess(II, RI); 2500fca76d5SJustin Bogner 2510fca76d5SJustin Bogner return Changed; 2520fca76d5SJustin Bogner } 2530fca76d5SJustin Bogner 2540fca76d5SJustin Bogner PreservedAnalyses DXILResourceAccess::run(Function &F, 2550fca76d5SJustin Bogner FunctionAnalysisManager &FAM) { 2560fca76d5SJustin Bogner auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F); 2570fca76d5SJustin Bogner DXILResourceTypeMap *DRTM = 2580fca76d5SJustin Bogner MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent()); 2590fca76d5SJustin Bogner assert(DRTM && "DXILResourceTypeAnalysis must be available"); 2600fca76d5SJustin Bogner 2610fca76d5SJustin Bogner bool MadeChanges = transformResourcePointers(F, *DRTM); 2620fca76d5SJustin Bogner if (!MadeChanges) 2630fca76d5SJustin Bogner return PreservedAnalyses::all(); 2640fca76d5SJustin Bogner 2650fca76d5SJustin Bogner PreservedAnalyses PA; 2660fca76d5SJustin Bogner PA.preserve<DXILResourceTypeAnalysis>(); 2670fca76d5SJustin Bogner PA.preserve<DominatorTreeAnalysis>(); 2680fca76d5SJustin Bogner return PA; 2690fca76d5SJustin Bogner } 2700fca76d5SJustin Bogner 2710fca76d5SJustin Bogner namespace { 2720fca76d5SJustin Bogner class DXILResourceAccessLegacy : public FunctionPass { 2730fca76d5SJustin Bogner public: 2740fca76d5SJustin Bogner bool runOnFunction(Function &F) override { 2750fca76d5SJustin Bogner DXILResourceTypeMap &DRTM = 2760fca76d5SJustin Bogner getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap(); 2770fca76d5SJustin Bogner 2780fca76d5SJustin Bogner return transformResourcePointers(F, DRTM); 2790fca76d5SJustin Bogner } 2800fca76d5SJustin Bogner StringRef getPassName() const override { return "DXIL Resource Access"; } 2810fca76d5SJustin Bogner DXILResourceAccessLegacy() : FunctionPass(ID) {} 2820fca76d5SJustin Bogner 2830fca76d5SJustin Bogner static char ID; // Pass identification. 2840fca76d5SJustin Bogner void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { 2850fca76d5SJustin Bogner AU.addRequired<DXILResourceTypeWrapperPass>(); 2860fca76d5SJustin Bogner AU.addPreserved<DominatorTreeWrapperPass>(); 2870fca76d5SJustin Bogner } 2880fca76d5SJustin Bogner }; 2890fca76d5SJustin Bogner char DXILResourceAccessLegacy::ID = 0; 2900fca76d5SJustin Bogner } // end anonymous namespace 2910fca76d5SJustin Bogner 2920fca76d5SJustin Bogner INITIALIZE_PASS_BEGIN(DXILResourceAccessLegacy, DEBUG_TYPE, 2930fca76d5SJustin Bogner "DXIL Resource Access", false, false) 2940fca76d5SJustin Bogner INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass) 2950fca76d5SJustin Bogner INITIALIZE_PASS_END(DXILResourceAccessLegacy, DEBUG_TYPE, 2960fca76d5SJustin Bogner "DXIL Resource Access", false, false) 2970fca76d5SJustin Bogner 2980fca76d5SJustin Bogner FunctionPass *llvm::createDXILResourceAccessLegacyPass() { 2990fca76d5SJustin Bogner return new DXILResourceAccessLegacy(); 3000fca76d5SJustin Bogner } 301