Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
213799 views
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
9
#include "DXILResourceAccess.h"
10
#include "DirectX.h"
11
#include "llvm/Analysis/DXILResource.h"
12
#include "llvm/IR/Dominators.h"
13
#include "llvm/IR/IRBuilder.h"
14
#include "llvm/IR/Instructions.h"
15
#include "llvm/IR/IntrinsicInst.h"
16
#include "llvm/IR/Intrinsics.h"
17
#include "llvm/IR/IntrinsicsDirectX.h"
18
#include "llvm/InitializePasses.h"
19
20
#define DEBUG_TYPE "dxil-resource-access"
21
22
using namespace llvm;
23
24
static Value *calculateGEPOffset(GetElementPtrInst *GEP, Value *PrevOffset,
25
dxil::ResourceTypeInfo &RTI) {
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
54
static void createTypedBufferStore(IntrinsicInst *II, StoreInst *SI,
55
Value *Offset, dxil::ResourceTypeInfo &RTI) {
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
86
static void createRawStore(IntrinsicInst *II, StoreInst *SI, Value *Offset) {
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
99
static void createStoreIntrinsic(IntrinsicInst *II, StoreInst *SI,
100
Value *Offset, dxil::ResourceTypeInfo &RTI) {
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
reportFatalUsageError("DXIL Load not implemented yet");
119
return;
120
case dxil::ResourceKind::CBuffer:
121
case dxil::ResourceKind::Sampler:
122
case dxil::ResourceKind::TBuffer:
123
case dxil::ResourceKind::RTAccelerationStructure:
124
case dxil::ResourceKind::Invalid:
125
case dxil::ResourceKind::NumEntries:
126
llvm_unreachable("Invalid resource kind for store");
127
}
128
llvm_unreachable("Unhandled case in switch");
129
}
130
131
static void createTypedBufferLoad(IntrinsicInst *II, LoadInst *LI,
132
Value *Offset, dxil::ResourceTypeInfo &RTI) {
133
IRBuilder<> Builder(LI);
134
Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
135
Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
136
137
Value *V =
138
Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,
139
{II->getOperand(0), II->getOperand(1)});
140
V = Builder.CreateExtractValue(V, {0});
141
142
if (Offset)
143
V = Builder.CreateExtractElement(V, Offset);
144
145
// If we loaded a <1 x ...> instead of a scalar (presumably to feed a
146
// shufflevector), then make sure we're maintaining the resulting type.
147
if (auto *VT = dyn_cast<FixedVectorType>(LI->getType()))
148
if (VT->getNumElements() == 1 && !isa<FixedVectorType>(V->getType()))
149
V = Builder.CreateInsertElement(PoisonValue::get(VT), V,
150
Builder.getInt32(0));
151
152
LI->replaceAllUsesWith(V);
153
}
154
155
static void createRawLoad(IntrinsicInst *II, LoadInst *LI, Value *Offset) {
156
IRBuilder<> Builder(LI);
157
// TODO: break up larger types
158
Type *LoadType = StructType::get(LI->getType(), Builder.getInt1Ty());
159
if (!Offset)
160
Offset = ConstantInt::get(Builder.getInt32Ty(), 0);
161
Value *V =
162
Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_rawbuffer,
163
{II->getOperand(0), II->getOperand(1), Offset});
164
V = Builder.CreateExtractValue(V, {0});
165
166
LI->replaceAllUsesWith(V);
167
}
168
169
static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, Value *Offset,
170
dxil::ResourceTypeInfo &RTI) {
171
switch (RTI.getResourceKind()) {
172
case dxil::ResourceKind::TypedBuffer:
173
return createTypedBufferLoad(II, LI, Offset, RTI);
174
case dxil::ResourceKind::RawBuffer:
175
case dxil::ResourceKind::StructuredBuffer:
176
return createRawLoad(II, LI, Offset);
177
case dxil::ResourceKind::Texture1D:
178
case dxil::ResourceKind::Texture2D:
179
case dxil::ResourceKind::Texture2DMS:
180
case dxil::ResourceKind::Texture3D:
181
case dxil::ResourceKind::TextureCube:
182
case dxil::ResourceKind::Texture1DArray:
183
case dxil::ResourceKind::Texture2DArray:
184
case dxil::ResourceKind::Texture2DMSArray:
185
case dxil::ResourceKind::TextureCubeArray:
186
case dxil::ResourceKind::FeedbackTexture2D:
187
case dxil::ResourceKind::FeedbackTexture2DArray:
188
case dxil::ResourceKind::CBuffer:
189
case dxil::ResourceKind::TBuffer:
190
// TODO: handle these
191
return;
192
case dxil::ResourceKind::Sampler:
193
case dxil::ResourceKind::RTAccelerationStructure:
194
case dxil::ResourceKind::Invalid:
195
case dxil::ResourceKind::NumEntries:
196
llvm_unreachable("Invalid resource kind for load");
197
}
198
llvm_unreachable("Unhandled case in switch");
199
}
200
201
static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
202
// Process users keeping track of indexing accumulated from GEPs.
203
struct AccessAndOffset {
204
User *Access;
205
Value *Offset;
206
};
207
SmallVector<AccessAndOffset> Worklist;
208
for (User *U : II->users())
209
Worklist.push_back({U, nullptr});
210
211
SmallVector<Instruction *> DeadInsts;
212
while (!Worklist.empty()) {
213
AccessAndOffset Current = Worklist.back();
214
Worklist.pop_back();
215
216
if (auto *GEP = dyn_cast<GetElementPtrInst>(Current.Access)) {
217
IRBuilder<> Builder(GEP);
218
219
Value *Offset = calculateGEPOffset(GEP, Current.Offset, RTI);
220
for (User *U : GEP->users())
221
Worklist.push_back({U, Offset});
222
DeadInsts.push_back(GEP);
223
224
} else if (auto *SI = dyn_cast<StoreInst>(Current.Access)) {
225
assert(SI->getValueOperand() != II && "Pointer escaped!");
226
createStoreIntrinsic(II, SI, Current.Offset, RTI);
227
DeadInsts.push_back(SI);
228
229
} else if (auto *LI = dyn_cast<LoadInst>(Current.Access)) {
230
createLoadIntrinsic(II, LI, Current.Offset, RTI);
231
DeadInsts.push_back(LI);
232
233
} else
234
llvm_unreachable("Unhandled instruction - pointer escaped?");
235
}
236
237
// Traverse the now-dead instructions in RPO and remove them.
238
for (Instruction *Dead : llvm::reverse(DeadInsts))
239
Dead->eraseFromParent();
240
II->eraseFromParent();
241
}
242
243
static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM) {
244
bool Changed = false;
245
SmallVector<std::pair<IntrinsicInst *, dxil::ResourceTypeInfo>> Resources;
246
for (BasicBlock &BB : F)
247
for (Instruction &I : BB)
248
if (auto *II = dyn_cast<IntrinsicInst>(&I))
249
if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
250
auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
251
Resources.emplace_back(II, DRTM[HandleTy]);
252
}
253
254
for (auto &[II, RI] : Resources)
255
replaceAccess(II, RI);
256
257
return Changed;
258
}
259
260
PreservedAnalyses DXILResourceAccess::run(Function &F,
261
FunctionAnalysisManager &FAM) {
262
auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
263
DXILResourceTypeMap *DRTM =
264
MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent());
265
assert(DRTM && "DXILResourceTypeAnalysis must be available");
266
267
bool MadeChanges = transformResourcePointers(F, *DRTM);
268
if (!MadeChanges)
269
return PreservedAnalyses::all();
270
271
PreservedAnalyses PA;
272
PA.preserve<DXILResourceTypeAnalysis>();
273
PA.preserve<DominatorTreeAnalysis>();
274
return PA;
275
}
276
277
namespace {
278
class DXILResourceAccessLegacy : public FunctionPass {
279
public:
280
bool runOnFunction(Function &F) override {
281
DXILResourceTypeMap &DRTM =
282
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
283
284
return transformResourcePointers(F, DRTM);
285
}
286
StringRef getPassName() const override { return "DXIL Resource Access"; }
287
DXILResourceAccessLegacy() : FunctionPass(ID) {}
288
289
static char ID; // Pass identification.
290
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
291
AU.addRequired<DXILResourceTypeWrapperPass>();
292
AU.addPreserved<DominatorTreeWrapperPass>();
293
}
294
};
295
char DXILResourceAccessLegacy::ID = 0;
296
} // end anonymous namespace
297
298
INITIALIZE_PASS_BEGIN(DXILResourceAccessLegacy, DEBUG_TYPE,
299
"DXIL Resource Access", false, false)
300
INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
301
INITIALIZE_PASS_END(DXILResourceAccessLegacy, DEBUG_TYPE,
302
"DXIL Resource Access", false, false)
303
304
FunctionPass *llvm::createDXILResourceAccessLegacyPass() {
305
return new DXILResourceAccessLegacy();
306
}
307
308