LLVM 20.0.0git
DXILResourceAccess.cpp
Go to the documentation of this file.
1//===- DXILResourceAccess.cpp - Resource access via load/store ------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "DirectX.h"
12#include "llvm/IR/Dominators.h"
13#include "llvm/IR/IRBuilder.h"
16#include "llvm/IR/Intrinsics.h"
17#include "llvm/IR/IntrinsicsDirectX.h"
19
20#define DEBUG_TYPE "dxil-resource-access"
21
22using namespace llvm;
23
26 assert(!PrevOffset && "Non-constant GEP chains not handled yet");
27
28 const DataLayout &DL = GEP->getDataLayout();
29
30 uint64_t ScalarSize = 1;
31 if (RTI.isTyped()) {
32 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
33 // We need the size of an element in bytes so that we can calculate the
34 // offset in elements given a total offset in bytes.
35 Type *ScalarType = ContainedType->getScalarType();
36 ScalarSize = DL.getTypeSizeInBits(ScalarType) / 8;
37 }
38
39 APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
40 if (GEP->accumulateConstantOffset(DL, ConstantOffset)) {
41 APInt Scaled = ConstantOffset.udiv(ScalarSize);
42 return ConstantInt::get(Type::getInt32Ty(GEP->getContext()), Scaled);
43 }
44
45 auto IndexIt = GEP->idx_begin();
46 assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&
47 "GEP is not indexing through pointer");
48 ++IndexIt;
49 Value *Offset = *IndexIt;
50 assert(++IndexIt == GEP->idx_end() && "Too many indices in GEP");
51 return Offset;
52}
53
56 IRBuilder<> Builder(SI);
57 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
58 Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
59
60 Value *V = SI->getValueOperand();
61 if (V->getType() == ContainedType) {
62 // V is already the right type.
63 assert(!Offset && "store of whole element has offset?");
64 } else if (V->getType() == ContainedType->getScalarType()) {
65 // We're storing a scalar, so we need to load the current value and only
66 // replace the relevant part.
67 auto *Load = Builder.CreateIntrinsic(
68 LoadType, Intrinsic::dx_resource_load_typedbuffer,
69 {II->getOperand(0), II->getOperand(1)});
70 auto *Struct = Builder.CreateExtractValue(Load, {0});
71
72 // If we have an offset from seeing a GEP earlier, use that. Otherwise, 0.
73 if (!Offset)
74 Offset = ConstantInt::get(Builder.getInt32Ty(), 0);
75 V = Builder.CreateInsertElement(Struct, V, Offset);
76 } else {
77 llvm_unreachable("Store to typed resource has invalid type");
78 }
79
80 auto *Inst = Builder.CreateIntrinsic(
81 Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,
82 {II->getOperand(0), II->getOperand(1), V});
83 SI->replaceAllUsesWith(Inst);
84}
85
87 IRBuilder<> Builder(SI);
88
89 if (!Offset)
90 Offset = ConstantInt::get(Builder.getInt32Ty(), 0);
91 Value *V = SI->getValueOperand();
92 // TODO: break up larger types
93 auto *Inst = Builder.CreateIntrinsic(
94 Builder.getVoidTy(), Intrinsic::dx_resource_store_rawbuffer,
95 {II->getOperand(0), II->getOperand(1), Offset, V});
96 SI->replaceAllUsesWith(Inst);
97}
98
101 switch (RTI.getResourceKind()) {
102 case dxil::ResourceKind::TypedBuffer:
103 return createTypedBufferStore(II, SI, Offset, RTI);
104 case dxil::ResourceKind::RawBuffer:
105 case dxil::ResourceKind::StructuredBuffer:
106 return createRawStore(II, SI, Offset);
107 case dxil::ResourceKind::Texture1D:
108 case dxil::ResourceKind::Texture2D:
109 case dxil::ResourceKind::Texture2DMS:
110 case dxil::ResourceKind::Texture3D:
111 case dxil::ResourceKind::TextureCube:
112 case dxil::ResourceKind::Texture1DArray:
113 case dxil::ResourceKind::Texture2DArray:
114 case dxil::ResourceKind::Texture2DMSArray:
115 case dxil::ResourceKind::TextureCubeArray:
116 case dxil::ResourceKind::FeedbackTexture2D:
117 case dxil::ResourceKind::FeedbackTexture2DArray:
118 report_fatal_error("DXIL Load not implemented yet",
119 /*gen_crash_diag=*/false);
120 return;
121 case dxil::ResourceKind::CBuffer:
122 case dxil::ResourceKind::Sampler:
123 case dxil::ResourceKind::TBuffer:
124 case dxil::ResourceKind::RTAccelerationStructure:
125 case dxil::ResourceKind::Invalid:
126 case dxil::ResourceKind::NumEntries:
127 llvm_unreachable("Invalid resource kind for store");
128 }
129 llvm_unreachable("Unhandled case in switch");
130}
131
134 IRBuilder<> Builder(LI);
135 Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
136 Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
137
138 Value *V =
139 Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,
140 {II->getOperand(0), II->getOperand(1)});
141 V = Builder.CreateExtractValue(V, {0});
142
143 if (Offset)
144 V = Builder.CreateExtractElement(V, Offset);
145
146 LI->replaceAllUsesWith(V);
147}
148
150 IRBuilder<> Builder(LI);
151 // TODO: break up larger types
152 Type *LoadType = StructType::get(LI->getType(), Builder.getInt1Ty());
153 if (!Offset)
154 Offset = ConstantInt::get(Builder.getInt32Ty(), 0);
155 Value *V =
156 Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_rawbuffer,
157 {II->getOperand(0), II->getOperand(1), Offset});
158 V = Builder.CreateExtractValue(V, {0});
159
160 LI->replaceAllUsesWith(V);
161}
162
165 switch (RTI.getResourceKind()) {
166 case dxil::ResourceKind::TypedBuffer:
167 return createTypedBufferLoad(II, LI, Offset, RTI);
168 case dxil::ResourceKind::RawBuffer:
169 case dxil::ResourceKind::StructuredBuffer:
170 return createRawLoad(II, LI, Offset);
171 case dxil::ResourceKind::Texture1D:
172 case dxil::ResourceKind::Texture2D:
173 case dxil::ResourceKind::Texture2DMS:
174 case dxil::ResourceKind::Texture3D:
175 case dxil::ResourceKind::TextureCube:
176 case dxil::ResourceKind::Texture1DArray:
177 case dxil::ResourceKind::Texture2DArray:
178 case dxil::ResourceKind::Texture2DMSArray:
179 case dxil::ResourceKind::TextureCubeArray:
180 case dxil::ResourceKind::FeedbackTexture2D:
181 case dxil::ResourceKind::FeedbackTexture2DArray:
182 case dxil::ResourceKind::CBuffer:
183 case dxil::ResourceKind::TBuffer:
184 // TODO: handle these
185 return;
186 case dxil::ResourceKind::Sampler:
187 case dxil::ResourceKind::RTAccelerationStructure:
188 case dxil::ResourceKind::Invalid:
189 case dxil::ResourceKind::NumEntries:
190 llvm_unreachable("Invalid resource kind for load");
191 }
192 llvm_unreachable("Unhandled case in switch");
193}
194
196 // Process users keeping track of indexing accumulated from GEPs.
197 struct AccessAndOffset {
198 User *Access;
199 Value *Offset;
200 };
202 for (User *U : II->users())
203 Worklist.push_back({U, nullptr});
204
206 while (!Worklist.empty()) {
207 AccessAndOffset Current = Worklist.back();
208 Worklist.pop_back();
209
210 if (auto *GEP = dyn_cast<GetElementPtrInst>(Current.Access)) {
211 IRBuilder<> Builder(GEP);
212
213 Value *Offset = calculateGEPOffset(GEP, Current.Offset, RTI);
214 for (User *U : GEP->users())
215 Worklist.push_back({U, Offset});
216 DeadInsts.push_back(GEP);
217
218 } else if (auto *SI = dyn_cast<StoreInst>(Current.Access)) {
219 assert(SI->getValueOperand() != II && "Pointer escaped!");
220 createStoreIntrinsic(II, SI, Current.Offset, RTI);
221 DeadInsts.push_back(SI);
222
223 } else if (auto *LI = dyn_cast<LoadInst>(Current.Access)) {
224 createLoadIntrinsic(II, LI, Current.Offset, RTI);
225 DeadInsts.push_back(LI);
226
227 } else
228 llvm_unreachable("Unhandled instruction - pointer escaped?");
229 }
230
231 // Traverse the now-dead instructions in RPO and remove them.
232 for (Instruction *Dead : llvm::reverse(DeadInsts))
233 Dead->eraseFromParent();
234 II->eraseFromParent();
235}
236
238 bool Changed = false;
240 for (BasicBlock &BB : F)
241 for (Instruction &I : BB)
242 if (auto *II = dyn_cast<IntrinsicInst>(&I))
243 if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
244 auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
245 Resources.emplace_back(II, DRTM[HandleTy]);
246 }
247
248 for (auto &[II, RI] : Resources)
249 replaceAccess(II, RI);
250
251 return Changed;
252}
253
257 DXILResourceTypeMap *DRTM =
258 MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent());
259 assert(DRTM && "DXILResourceTypeAnalysis must be available");
260
261 bool MadeChanges = transformResourcePointers(F, *DRTM);
262 if (!MadeChanges)
263 return PreservedAnalyses::all();
264
268 return PA;
269}
270
271namespace {
272class DXILResourceAccessLegacy : public FunctionPass {
273public:
274 bool runOnFunction(Function &F) override {
275 DXILResourceTypeMap &DRTM =
276 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
277
278 return transformResourcePointers(F, DRTM);
279 }
280 StringRef getPassName() const override { return "DXIL Resource Access"; }
281 DXILResourceAccessLegacy() : FunctionPass(ID) {}
282
283 static char ID; // Pass identification.
284 void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
287 }
288};
289char DXILResourceAccessLegacy::ID = 0;
290} // end anonymous namespace
291
292INITIALIZE_PASS_BEGIN(DXILResourceAccessLegacy, DEBUG_TYPE,
293 "DXIL Resource Access", false, false)
295INITIALIZE_PASS_END(DXILResourceAccessLegacy, DEBUG_TYPE,
296 "DXIL Resource Access", false, false)
297
299 return new DXILResourceAccessLegacy();
300}
@ Scaled
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static Value * calculateGEPOffset(GetElementPtrInst *GEP, Value *PrevOffset, dxil::ResourceTypeInfo &RTI)
static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM)
static void createRawLoad(IntrinsicInst *II, LoadInst *LI, Value *Offset)
static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, Value *Offset, dxil::ResourceTypeInfo &RTI)
static void createTypedBufferLoad(IntrinsicInst *II, LoadInst *LI, Value *Offset, dxil::ResourceTypeInfo &RTI)
static void createStoreIntrinsic(IntrinsicInst *II, StoreInst *SI, Value *Offset, dxil::ResourceTypeInfo &RTI)
static void createTypedBufferStore(IntrinsicInst *II, StoreInst *SI, Value *Offset, dxil::ResourceTypeInfo &RTI)
DXIL Resource Access
static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI)
static void createRawStore(IntrinsicInst *II, StoreInst *SI, Value *Offset)
static bool runOnFunction(Function &F, bool PostInlining)
#define DEBUG_TYPE
Hexagon Common GEP
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
uint64_t IntrinsicInst * II
FunctionAnalysisManager FAM
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition: PassSupport.h:55
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:57
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:52
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
@ Struct
Class for arbitrary precision integers.
Definition: APInt.h:78
APInt udiv(const APInt &RHS) const
Unsigned division operation.
Definition: APInt.cpp:1547
A container for analyses that lazily runs them and caches their results.
Definition: PassManager.h:253
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Definition: PassManager.h:410
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
LLVM Basic Block Representation.
Definition: BasicBlock.h:61
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM)
A parsed version of the target data layout string in and methods for querying it.
Definition: DataLayout.h:63
Analysis pass which computes a DominatorTree.
Definition: Dominators.h:279
Legacy analysis pass which computes a DominatorTree.
Definition: Dominators.h:317
FunctionPass class - This class is used to implement most global optimizations.
Definition: Pass.h:310
an instruction for type-safe pointer arithmetic to access elements of arrays and structs
Definition: Instructions.h:933
Value * CreateInsertElement(Type *VecTy, Value *NewElt, Value *Idx, const Twine &Name="")
Definition: IRBuilder.h:2511
IntegerType * getInt1Ty()
Fetch the type representing a single bit.
Definition: IRBuilder.h:530
Value * CreateExtractElement(Value *Vec, Value *Idx, const Twine &Name="")
Definition: IRBuilder.h:2499
Value * CreateExtractValue(Value *Agg, ArrayRef< unsigned > Idxs, const Twine &Name="")
Definition: IRBuilder.h:2555
IntegerType * getInt32Ty()
Fetch the type representing a 32-bit integer.
Definition: IRBuilder.h:545
CallInst * CreateIntrinsic(Intrinsic::ID ID, ArrayRef< Type * > Types, ArrayRef< Value * > Args, FMFSource FMFSource={}, const Twine &Name="")
Create a call to intrinsic ID with Args, mangled using Types.
Definition: IRBuilder.cpp:900
Type * getVoidTy()
Fetch the type representing void.
Definition: IRBuilder.h:583
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition: IRBuilder.h:2705
A wrapper class for inspecting calls to intrinsic functions.
Definition: IntrinsicInst.h:48
An instruction for reading from memory.
Definition: Instructions.h:176
An analysis over an "inner" IR unit that provides access to an analysis manager over a "outer" IR uni...
Definition: PassManager.h:692
A set of analyses that are preserved following a run of a transformation pass.
Definition: Analysis.h:111
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition: Analysis.h:117
void preserve()
Mark an analysis as preserved.
Definition: Analysis.h:131
bool empty() const
Definition: SmallVector.h:81
reference emplace_back(ArgTypes &&... Args)
Definition: SmallVector.h:937
void push_back(const T &Elt)
Definition: SmallVector.h:413
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1196
An instruction for storing to memory.
Definition: Instructions.h:292
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
static StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition: Type.cpp:406
Type * getTypeParameter(unsigned i) const
Definition: DerivedTypes.h:792
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
static IntegerType * getInt32Ty(LLVMContext &C)
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
Definition: Type.h:355
LLVM Value Representation.
Definition: Value.h:74
Type * getType() const
All values are typed, get the type of this value.
Definition: Value.h:255
void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition: Value.cpp:534
TargetExtType * getHandleTy() const
Definition: DXILResource.h:273
dxil::ResourceKind getResourceKind() const
Definition: DXILResource.h:295
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Offset
Definition: DWP.cpp:480
auto reverse(ContainerTy &&C)
Definition: STLExtras.h:420
FunctionPass * createDXILResourceAccessLegacyPass()
Pass to update resource accesses to use load/store directly.
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
Definition: Error.cpp:167