1*700637cbSDimitry Andric //===- DXILResourceAccess.cpp - Resource access via load/store ------------===//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric
9*700637cbSDimitry Andric #include "DXILResourceAccess.h"
10*700637cbSDimitry Andric #include "DirectX.h"
11*700637cbSDimitry Andric #include "llvm/Analysis/DXILResource.h"
12*700637cbSDimitry Andric #include "llvm/IR/Dominators.h"
13*700637cbSDimitry Andric #include "llvm/IR/IRBuilder.h"
14*700637cbSDimitry Andric #include "llvm/IR/Instructions.h"
15*700637cbSDimitry Andric #include "llvm/IR/IntrinsicInst.h"
16*700637cbSDimitry Andric #include "llvm/IR/Intrinsics.h"
17*700637cbSDimitry Andric #include "llvm/IR/IntrinsicsDirectX.h"
18*700637cbSDimitry Andric #include "llvm/InitializePasses.h"
19*700637cbSDimitry Andric
20*700637cbSDimitry Andric #define DEBUG_TYPE "dxil-resource-access"
21*700637cbSDimitry Andric
22*700637cbSDimitry Andric using namespace llvm;
23*700637cbSDimitry Andric
calculateGEPOffset(GetElementPtrInst * GEP,Value * PrevOffset,dxil::ResourceTypeInfo & RTI)24*700637cbSDimitry Andric static Value *calculateGEPOffset(GetElementPtrInst *GEP, Value *PrevOffset,
25*700637cbSDimitry Andric dxil::ResourceTypeInfo &RTI) {
26*700637cbSDimitry Andric assert(!PrevOffset && "Non-constant GEP chains not handled yet");
27*700637cbSDimitry Andric
28*700637cbSDimitry Andric const DataLayout &DL = GEP->getDataLayout();
29*700637cbSDimitry Andric
30*700637cbSDimitry Andric uint64_t ScalarSize = 1;
31*700637cbSDimitry Andric if (RTI.isTyped()) {
32*700637cbSDimitry Andric Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
33*700637cbSDimitry Andric // We need the size of an element in bytes so that we can calculate the
34*700637cbSDimitry Andric // offset in elements given a total offset in bytes.
35*700637cbSDimitry Andric Type *ScalarType = ContainedType->getScalarType();
36*700637cbSDimitry Andric ScalarSize = DL.getTypeSizeInBits(ScalarType) / 8;
37*700637cbSDimitry Andric }
38*700637cbSDimitry Andric
39*700637cbSDimitry Andric APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
40*700637cbSDimitry Andric if (GEP->accumulateConstantOffset(DL, ConstantOffset)) {
41*700637cbSDimitry Andric APInt Scaled = ConstantOffset.udiv(ScalarSize);
42*700637cbSDimitry Andric return ConstantInt::get(Type::getInt32Ty(GEP->getContext()), Scaled);
43*700637cbSDimitry Andric }
44*700637cbSDimitry Andric
45*700637cbSDimitry Andric auto IndexIt = GEP->idx_begin();
46*700637cbSDimitry Andric assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&
47*700637cbSDimitry Andric "GEP is not indexing through pointer");
48*700637cbSDimitry Andric ++IndexIt;
49*700637cbSDimitry Andric Value *Offset = *IndexIt;
50*700637cbSDimitry Andric assert(++IndexIt == GEP->idx_end() && "Too many indices in GEP");
51*700637cbSDimitry Andric return Offset;
52*700637cbSDimitry Andric }
53*700637cbSDimitry Andric
createTypedBufferStore(IntrinsicInst * II,StoreInst * SI,Value * Offset,dxil::ResourceTypeInfo & RTI)54*700637cbSDimitry Andric static void createTypedBufferStore(IntrinsicInst *II, StoreInst *SI,
55*700637cbSDimitry Andric Value *Offset, dxil::ResourceTypeInfo &RTI) {
56*700637cbSDimitry Andric IRBuilder<> Builder(SI);
57*700637cbSDimitry Andric Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
58*700637cbSDimitry Andric Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
59*700637cbSDimitry Andric
60*700637cbSDimitry Andric Value *V = SI->getValueOperand();
61*700637cbSDimitry Andric if (V->getType() == ContainedType) {
62*700637cbSDimitry Andric // V is already the right type.
63*700637cbSDimitry Andric assert(!Offset && "store of whole element has offset?");
64*700637cbSDimitry Andric } else if (V->getType() == ContainedType->getScalarType()) {
65*700637cbSDimitry Andric // We're storing a scalar, so we need to load the current value and only
66*700637cbSDimitry Andric // replace the relevant part.
67*700637cbSDimitry Andric auto *Load = Builder.CreateIntrinsic(
68*700637cbSDimitry Andric LoadType, Intrinsic::dx_resource_load_typedbuffer,
69*700637cbSDimitry Andric {II->getOperand(0), II->getOperand(1)});
70*700637cbSDimitry Andric auto *Struct = Builder.CreateExtractValue(Load, {0});
71*700637cbSDimitry Andric
72*700637cbSDimitry Andric // If we have an offset from seeing a GEP earlier, use that. Otherwise, 0.
73*700637cbSDimitry Andric if (!Offset)
74*700637cbSDimitry Andric Offset = ConstantInt::get(Builder.getInt32Ty(), 0);
75*700637cbSDimitry Andric V = Builder.CreateInsertElement(Struct, V, Offset);
76*700637cbSDimitry Andric } else {
77*700637cbSDimitry Andric llvm_unreachable("Store to typed resource has invalid type");
78*700637cbSDimitry Andric }
79*700637cbSDimitry Andric
80*700637cbSDimitry Andric auto *Inst = Builder.CreateIntrinsic(
81*700637cbSDimitry Andric Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,
82*700637cbSDimitry Andric {II->getOperand(0), II->getOperand(1), V});
83*700637cbSDimitry Andric SI->replaceAllUsesWith(Inst);
84*700637cbSDimitry Andric }
85*700637cbSDimitry Andric
createRawStore(IntrinsicInst * II,StoreInst * SI,Value * Offset)86*700637cbSDimitry Andric static void createRawStore(IntrinsicInst *II, StoreInst *SI, Value *Offset) {
87*700637cbSDimitry Andric IRBuilder<> Builder(SI);
88*700637cbSDimitry Andric
89*700637cbSDimitry Andric if (!Offset)
90*700637cbSDimitry Andric Offset = ConstantInt::get(Builder.getInt32Ty(), 0);
91*700637cbSDimitry Andric Value *V = SI->getValueOperand();
92*700637cbSDimitry Andric // TODO: break up larger types
93*700637cbSDimitry Andric auto *Inst = Builder.CreateIntrinsic(
94*700637cbSDimitry Andric Builder.getVoidTy(), Intrinsic::dx_resource_store_rawbuffer,
95*700637cbSDimitry Andric {II->getOperand(0), II->getOperand(1), Offset, V});
96*700637cbSDimitry Andric SI->replaceAllUsesWith(Inst);
97*700637cbSDimitry Andric }
98*700637cbSDimitry Andric
createStoreIntrinsic(IntrinsicInst * II,StoreInst * SI,Value * Offset,dxil::ResourceTypeInfo & RTI)99*700637cbSDimitry Andric static void createStoreIntrinsic(IntrinsicInst *II, StoreInst *SI,
100*700637cbSDimitry Andric Value *Offset, dxil::ResourceTypeInfo &RTI) {
101*700637cbSDimitry Andric switch (RTI.getResourceKind()) {
102*700637cbSDimitry Andric case dxil::ResourceKind::TypedBuffer:
103*700637cbSDimitry Andric return createTypedBufferStore(II, SI, Offset, RTI);
104*700637cbSDimitry Andric case dxil::ResourceKind::RawBuffer:
105*700637cbSDimitry Andric case dxil::ResourceKind::StructuredBuffer:
106*700637cbSDimitry Andric return createRawStore(II, SI, Offset);
107*700637cbSDimitry Andric case dxil::ResourceKind::Texture1D:
108*700637cbSDimitry Andric case dxil::ResourceKind::Texture2D:
109*700637cbSDimitry Andric case dxil::ResourceKind::Texture2DMS:
110*700637cbSDimitry Andric case dxil::ResourceKind::Texture3D:
111*700637cbSDimitry Andric case dxil::ResourceKind::TextureCube:
112*700637cbSDimitry Andric case dxil::ResourceKind::Texture1DArray:
113*700637cbSDimitry Andric case dxil::ResourceKind::Texture2DArray:
114*700637cbSDimitry Andric case dxil::ResourceKind::Texture2DMSArray:
115*700637cbSDimitry Andric case dxil::ResourceKind::TextureCubeArray:
116*700637cbSDimitry Andric case dxil::ResourceKind::FeedbackTexture2D:
117*700637cbSDimitry Andric case dxil::ResourceKind::FeedbackTexture2DArray:
118*700637cbSDimitry Andric reportFatalUsageError("DXIL Load not implemented yet");
119*700637cbSDimitry Andric return;
120*700637cbSDimitry Andric case dxil::ResourceKind::CBuffer:
121*700637cbSDimitry Andric case dxil::ResourceKind::Sampler:
122*700637cbSDimitry Andric case dxil::ResourceKind::TBuffer:
123*700637cbSDimitry Andric case dxil::ResourceKind::RTAccelerationStructure:
124*700637cbSDimitry Andric case dxil::ResourceKind::Invalid:
125*700637cbSDimitry Andric case dxil::ResourceKind::NumEntries:
126*700637cbSDimitry Andric llvm_unreachable("Invalid resource kind for store");
127*700637cbSDimitry Andric }
128*700637cbSDimitry Andric llvm_unreachable("Unhandled case in switch");
129*700637cbSDimitry Andric }
130*700637cbSDimitry Andric
createTypedBufferLoad(IntrinsicInst * II,LoadInst * LI,Value * Offset,dxil::ResourceTypeInfo & RTI)131*700637cbSDimitry Andric static void createTypedBufferLoad(IntrinsicInst *II, LoadInst *LI,
132*700637cbSDimitry Andric Value *Offset, dxil::ResourceTypeInfo &RTI) {
133*700637cbSDimitry Andric IRBuilder<> Builder(LI);
134*700637cbSDimitry Andric Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
135*700637cbSDimitry Andric Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
136*700637cbSDimitry Andric
137*700637cbSDimitry Andric Value *V =
138*700637cbSDimitry Andric Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,
139*700637cbSDimitry Andric {II->getOperand(0), II->getOperand(1)});
140*700637cbSDimitry Andric V = Builder.CreateExtractValue(V, {0});
141*700637cbSDimitry Andric
142*700637cbSDimitry Andric if (Offset)
143*700637cbSDimitry Andric V = Builder.CreateExtractElement(V, Offset);
144*700637cbSDimitry Andric
145*700637cbSDimitry Andric // If we loaded a <1 x ...> instead of a scalar (presumably to feed a
146*700637cbSDimitry Andric // shufflevector), then make sure we're maintaining the resulting type.
147*700637cbSDimitry Andric if (auto *VT = dyn_cast<FixedVectorType>(LI->getType()))
148*700637cbSDimitry Andric if (VT->getNumElements() == 1 && !isa<FixedVectorType>(V->getType()))
149*700637cbSDimitry Andric V = Builder.CreateInsertElement(PoisonValue::get(VT), V,
150*700637cbSDimitry Andric Builder.getInt32(0));
151*700637cbSDimitry Andric
152*700637cbSDimitry Andric LI->replaceAllUsesWith(V);
153*700637cbSDimitry Andric }
154*700637cbSDimitry Andric
createRawLoad(IntrinsicInst * II,LoadInst * LI,Value * Offset)155*700637cbSDimitry Andric static void createRawLoad(IntrinsicInst *II, LoadInst *LI, Value *Offset) {
156*700637cbSDimitry Andric IRBuilder<> Builder(LI);
157*700637cbSDimitry Andric // TODO: break up larger types
158*700637cbSDimitry Andric Type *LoadType = StructType::get(LI->getType(), Builder.getInt1Ty());
159*700637cbSDimitry Andric if (!Offset)
160*700637cbSDimitry Andric Offset = ConstantInt::get(Builder.getInt32Ty(), 0);
161*700637cbSDimitry Andric Value *V =
162*700637cbSDimitry Andric Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_rawbuffer,
163*700637cbSDimitry Andric {II->getOperand(0), II->getOperand(1), Offset});
164*700637cbSDimitry Andric V = Builder.CreateExtractValue(V, {0});
165*700637cbSDimitry Andric
166*700637cbSDimitry Andric LI->replaceAllUsesWith(V);
167*700637cbSDimitry Andric }
168*700637cbSDimitry Andric
createLoadIntrinsic(IntrinsicInst * II,LoadInst * LI,Value * Offset,dxil::ResourceTypeInfo & RTI)169*700637cbSDimitry Andric static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, Value *Offset,
170*700637cbSDimitry Andric dxil::ResourceTypeInfo &RTI) {
171*700637cbSDimitry Andric switch (RTI.getResourceKind()) {
172*700637cbSDimitry Andric case dxil::ResourceKind::TypedBuffer:
173*700637cbSDimitry Andric return createTypedBufferLoad(II, LI, Offset, RTI);
174*700637cbSDimitry Andric case dxil::ResourceKind::RawBuffer:
175*700637cbSDimitry Andric case dxil::ResourceKind::StructuredBuffer:
176*700637cbSDimitry Andric return createRawLoad(II, LI, Offset);
177*700637cbSDimitry Andric case dxil::ResourceKind::Texture1D:
178*700637cbSDimitry Andric case dxil::ResourceKind::Texture2D:
179*700637cbSDimitry Andric case dxil::ResourceKind::Texture2DMS:
180*700637cbSDimitry Andric case dxil::ResourceKind::Texture3D:
181*700637cbSDimitry Andric case dxil::ResourceKind::TextureCube:
182*700637cbSDimitry Andric case dxil::ResourceKind::Texture1DArray:
183*700637cbSDimitry Andric case dxil::ResourceKind::Texture2DArray:
184*700637cbSDimitry Andric case dxil::ResourceKind::Texture2DMSArray:
185*700637cbSDimitry Andric case dxil::ResourceKind::TextureCubeArray:
186*700637cbSDimitry Andric case dxil::ResourceKind::FeedbackTexture2D:
187*700637cbSDimitry Andric case dxil::ResourceKind::FeedbackTexture2DArray:
188*700637cbSDimitry Andric case dxil::ResourceKind::CBuffer:
189*700637cbSDimitry Andric case dxil::ResourceKind::TBuffer:
190*700637cbSDimitry Andric // TODO: handle these
191*700637cbSDimitry Andric return;
192*700637cbSDimitry Andric case dxil::ResourceKind::Sampler:
193*700637cbSDimitry Andric case dxil::ResourceKind::RTAccelerationStructure:
194*700637cbSDimitry Andric case dxil::ResourceKind::Invalid:
195*700637cbSDimitry Andric case dxil::ResourceKind::NumEntries:
196*700637cbSDimitry Andric llvm_unreachable("Invalid resource kind for load");
197*700637cbSDimitry Andric }
198*700637cbSDimitry Andric llvm_unreachable("Unhandled case in switch");
199*700637cbSDimitry Andric }
200*700637cbSDimitry Andric
replaceAccess(IntrinsicInst * II,dxil::ResourceTypeInfo & RTI)201*700637cbSDimitry Andric static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
202*700637cbSDimitry Andric // Process users keeping track of indexing accumulated from GEPs.
203*700637cbSDimitry Andric struct AccessAndOffset {
204*700637cbSDimitry Andric User *Access;
205*700637cbSDimitry Andric Value *Offset;
206*700637cbSDimitry Andric };
207*700637cbSDimitry Andric SmallVector<AccessAndOffset> Worklist;
208*700637cbSDimitry Andric for (User *U : II->users())
209*700637cbSDimitry Andric Worklist.push_back({U, nullptr});
210*700637cbSDimitry Andric
211*700637cbSDimitry Andric SmallVector<Instruction *> DeadInsts;
212*700637cbSDimitry Andric while (!Worklist.empty()) {
213*700637cbSDimitry Andric AccessAndOffset Current = Worklist.back();
214*700637cbSDimitry Andric Worklist.pop_back();
215*700637cbSDimitry Andric
216*700637cbSDimitry Andric if (auto *GEP = dyn_cast<GetElementPtrInst>(Current.Access)) {
217*700637cbSDimitry Andric IRBuilder<> Builder(GEP);
218*700637cbSDimitry Andric
219*700637cbSDimitry Andric Value *Offset = calculateGEPOffset(GEP, Current.Offset, RTI);
220*700637cbSDimitry Andric for (User *U : GEP->users())
221*700637cbSDimitry Andric Worklist.push_back({U, Offset});
222*700637cbSDimitry Andric DeadInsts.push_back(GEP);
223*700637cbSDimitry Andric
224*700637cbSDimitry Andric } else if (auto *SI = dyn_cast<StoreInst>(Current.Access)) {
225*700637cbSDimitry Andric assert(SI->getValueOperand() != II && "Pointer escaped!");
226*700637cbSDimitry Andric createStoreIntrinsic(II, SI, Current.Offset, RTI);
227*700637cbSDimitry Andric DeadInsts.push_back(SI);
228*700637cbSDimitry Andric
229*700637cbSDimitry Andric } else if (auto *LI = dyn_cast<LoadInst>(Current.Access)) {
230*700637cbSDimitry Andric createLoadIntrinsic(II, LI, Current.Offset, RTI);
231*700637cbSDimitry Andric DeadInsts.push_back(LI);
232*700637cbSDimitry Andric
233*700637cbSDimitry Andric } else
234*700637cbSDimitry Andric llvm_unreachable("Unhandled instruction - pointer escaped?");
235*700637cbSDimitry Andric }
236*700637cbSDimitry Andric
237*700637cbSDimitry Andric // Traverse the now-dead instructions in RPO and remove them.
238*700637cbSDimitry Andric for (Instruction *Dead : llvm::reverse(DeadInsts))
239*700637cbSDimitry Andric Dead->eraseFromParent();
240*700637cbSDimitry Andric II->eraseFromParent();
241*700637cbSDimitry Andric }
242*700637cbSDimitry Andric
transformResourcePointers(Function & F,DXILResourceTypeMap & DRTM)243*700637cbSDimitry Andric static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM) {
244*700637cbSDimitry Andric bool Changed = false;
245*700637cbSDimitry Andric SmallVector<std::pair<IntrinsicInst *, dxil::ResourceTypeInfo>> Resources;
246*700637cbSDimitry Andric for (BasicBlock &BB : F)
247*700637cbSDimitry Andric for (Instruction &I : BB)
248*700637cbSDimitry Andric if (auto *II = dyn_cast<IntrinsicInst>(&I))
249*700637cbSDimitry Andric if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
250*700637cbSDimitry Andric auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
251*700637cbSDimitry Andric Resources.emplace_back(II, DRTM[HandleTy]);
252*700637cbSDimitry Andric }
253*700637cbSDimitry Andric
254*700637cbSDimitry Andric for (auto &[II, RI] : Resources)
255*700637cbSDimitry Andric replaceAccess(II, RI);
256*700637cbSDimitry Andric
257*700637cbSDimitry Andric return Changed;
258*700637cbSDimitry Andric }
259*700637cbSDimitry Andric
run(Function & F,FunctionAnalysisManager & FAM)260*700637cbSDimitry Andric PreservedAnalyses DXILResourceAccess::run(Function &F,
261*700637cbSDimitry Andric FunctionAnalysisManager &FAM) {
262*700637cbSDimitry Andric auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
263*700637cbSDimitry Andric DXILResourceTypeMap *DRTM =
264*700637cbSDimitry Andric MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent());
265*700637cbSDimitry Andric assert(DRTM && "DXILResourceTypeAnalysis must be available");
266*700637cbSDimitry Andric
267*700637cbSDimitry Andric bool MadeChanges = transformResourcePointers(F, *DRTM);
268*700637cbSDimitry Andric if (!MadeChanges)
269*700637cbSDimitry Andric return PreservedAnalyses::all();
270*700637cbSDimitry Andric
271*700637cbSDimitry Andric PreservedAnalyses PA;
272*700637cbSDimitry Andric PA.preserve<DXILResourceTypeAnalysis>();
273*700637cbSDimitry Andric PA.preserve<DominatorTreeAnalysis>();
274*700637cbSDimitry Andric return PA;
275*700637cbSDimitry Andric }
276*700637cbSDimitry Andric
277*700637cbSDimitry Andric namespace {
278*700637cbSDimitry Andric class DXILResourceAccessLegacy : public FunctionPass {
279*700637cbSDimitry Andric public:
runOnFunction(Function & F)280*700637cbSDimitry Andric bool runOnFunction(Function &F) override {
281*700637cbSDimitry Andric DXILResourceTypeMap &DRTM =
282*700637cbSDimitry Andric getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
283*700637cbSDimitry Andric
284*700637cbSDimitry Andric return transformResourcePointers(F, DRTM);
285*700637cbSDimitry Andric }
getPassName() const286*700637cbSDimitry Andric StringRef getPassName() const override { return "DXIL Resource Access"; }
DXILResourceAccessLegacy()287*700637cbSDimitry Andric DXILResourceAccessLegacy() : FunctionPass(ID) {}
288*700637cbSDimitry Andric
289*700637cbSDimitry Andric static char ID; // Pass identification.
getAnalysisUsage(llvm::AnalysisUsage & AU) const290*700637cbSDimitry Andric void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
291*700637cbSDimitry Andric AU.addRequired<DXILResourceTypeWrapperPass>();
292*700637cbSDimitry Andric AU.addPreserved<DominatorTreeWrapperPass>();
293*700637cbSDimitry Andric }
294*700637cbSDimitry Andric };
295*700637cbSDimitry Andric char DXILResourceAccessLegacy::ID = 0;
296*700637cbSDimitry Andric } // end anonymous namespace
297*700637cbSDimitry Andric
298*700637cbSDimitry Andric INITIALIZE_PASS_BEGIN(DXILResourceAccessLegacy, DEBUG_TYPE,
299*700637cbSDimitry Andric "DXIL Resource Access", false, false)
INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)300*700637cbSDimitry Andric INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
301*700637cbSDimitry Andric INITIALIZE_PASS_END(DXILResourceAccessLegacy, DEBUG_TYPE,
302*700637cbSDimitry Andric "DXIL Resource Access", false, false)
303*700637cbSDimitry Andric
304*700637cbSDimitry Andric FunctionPass *llvm::createDXILResourceAccessLegacyPass() {
305*700637cbSDimitry Andric return new DXILResourceAccessLegacy();
306*700637cbSDimitry Andric }
307