xref: /llvm-project/llvm/lib/Target/DirectX/DXILResourceAccess.cpp (revision 2f39d138dc38a1fdf4754e4e26dd0aeb7409b13d)
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