1 | //===- DXILResource.cpp - Representations of DXIL resources ---------------===// |
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 "llvm/Analysis/DXILResource.h" |
10 | #include "llvm/ADT/APInt.h" |
11 | #include "llvm/ADT/STLExtras.h" |
12 | #include "llvm/ADT/SmallString.h" |
13 | #include "llvm/ADT/SmallVector.h" |
14 | #include "llvm/IR/Constants.h" |
15 | #include "llvm/IR/DerivedTypes.h" |
16 | #include "llvm/IR/DiagnosticInfo.h" |
17 | #include "llvm/IR/Instructions.h" |
18 | #include "llvm/IR/Intrinsics.h" |
19 | #include "llvm/IR/IntrinsicsDirectX.h" |
20 | #include "llvm/IR/Metadata.h" |
21 | #include "llvm/IR/Module.h" |
22 | #include "llvm/InitializePasses.h" |
23 | #include "llvm/Support/FormatVariadic.h" |
24 | #include <cstdint> |
25 | #include <optional> |
26 | |
27 | #define DEBUG_TYPE "dxil-resource" |
28 | |
29 | using namespace llvm; |
30 | using namespace dxil; |
31 | |
32 | static StringRef getResourceClassName(ResourceClass RC) { |
33 | switch (RC) { |
34 | case ResourceClass::SRV: |
35 | return "SRV" ; |
36 | case ResourceClass::UAV: |
37 | return "UAV" ; |
38 | case ResourceClass::CBuffer: |
39 | return "CBuffer" ; |
40 | case ResourceClass::Sampler: |
41 | return "Sampler" ; |
42 | } |
43 | llvm_unreachable("Unhandled ResourceClass" ); |
44 | } |
45 | |
46 | static StringRef getResourceKindName(ResourceKind RK) { |
47 | switch (RK) { |
48 | case ResourceKind::Texture1D: |
49 | return "Texture1D" ; |
50 | case ResourceKind::Texture2D: |
51 | return "Texture2D" ; |
52 | case ResourceKind::Texture2DMS: |
53 | return "Texture2DMS" ; |
54 | case ResourceKind::Texture3D: |
55 | return "Texture3D" ; |
56 | case ResourceKind::TextureCube: |
57 | return "TextureCube" ; |
58 | case ResourceKind::Texture1DArray: |
59 | return "Texture1DArray" ; |
60 | case ResourceKind::Texture2DArray: |
61 | return "Texture2DArray" ; |
62 | case ResourceKind::Texture2DMSArray: |
63 | return "Texture2DMSArray" ; |
64 | case ResourceKind::TextureCubeArray: |
65 | return "TextureCubeArray" ; |
66 | case ResourceKind::TypedBuffer: |
67 | return "Buffer" ; |
68 | case ResourceKind::RawBuffer: |
69 | return "RawBuffer" ; |
70 | case ResourceKind::StructuredBuffer: |
71 | return "StructuredBuffer" ; |
72 | case ResourceKind::CBuffer: |
73 | return "CBuffer" ; |
74 | case ResourceKind::Sampler: |
75 | return "Sampler" ; |
76 | case ResourceKind::TBuffer: |
77 | return "TBuffer" ; |
78 | case ResourceKind::RTAccelerationStructure: |
79 | return "RTAccelerationStructure" ; |
80 | case ResourceKind::FeedbackTexture2D: |
81 | return "FeedbackTexture2D" ; |
82 | case ResourceKind::FeedbackTexture2DArray: |
83 | return "FeedbackTexture2DArray" ; |
84 | case ResourceKind::NumEntries: |
85 | case ResourceKind::Invalid: |
86 | return "<invalid>" ; |
87 | } |
88 | llvm_unreachable("Unhandled ResourceKind" ); |
89 | } |
90 | |
91 | static StringRef getElementTypeName(ElementType ET) { |
92 | switch (ET) { |
93 | case ElementType::I1: |
94 | return "i1" ; |
95 | case ElementType::I16: |
96 | return "i16" ; |
97 | case ElementType::U16: |
98 | return "u16" ; |
99 | case ElementType::I32: |
100 | return "i32" ; |
101 | case ElementType::U32: |
102 | return "u32" ; |
103 | case ElementType::I64: |
104 | return "i64" ; |
105 | case ElementType::U64: |
106 | return "u64" ; |
107 | case ElementType::F16: |
108 | return "f16" ; |
109 | case ElementType::F32: |
110 | return "f32" ; |
111 | case ElementType::F64: |
112 | return "f64" ; |
113 | case ElementType::SNormF16: |
114 | return "snorm_f16" ; |
115 | case ElementType::UNormF16: |
116 | return "unorm_f16" ; |
117 | case ElementType::SNormF32: |
118 | return "snorm_f32" ; |
119 | case ElementType::UNormF32: |
120 | return "unorm_f32" ; |
121 | case ElementType::SNormF64: |
122 | return "snorm_f64" ; |
123 | case ElementType::UNormF64: |
124 | return "unorm_f64" ; |
125 | case ElementType::PackedS8x32: |
126 | return "p32i8" ; |
127 | case ElementType::PackedU8x32: |
128 | return "p32u8" ; |
129 | case ElementType::Invalid: |
130 | return "<invalid>" ; |
131 | } |
132 | llvm_unreachable("Unhandled ElementType" ); |
133 | } |
134 | |
135 | static StringRef getElementTypeNameForTemplate(ElementType ET) { |
136 | switch (ET) { |
137 | case ElementType::I1: |
138 | return "bool" ; |
139 | case ElementType::I16: |
140 | return "int16_t" ; |
141 | case ElementType::U16: |
142 | return "uint16_t" ; |
143 | case ElementType::I32: |
144 | return "int32_t" ; |
145 | case ElementType::U32: |
146 | return "uint32_t" ; |
147 | case ElementType::I64: |
148 | return "int64_t" ; |
149 | case ElementType::U64: |
150 | return "uint32_t" ; |
151 | case ElementType::F16: |
152 | case ElementType::SNormF16: |
153 | case ElementType::UNormF16: |
154 | return "half" ; |
155 | case ElementType::F32: |
156 | case ElementType::SNormF32: |
157 | case ElementType::UNormF32: |
158 | return "float" ; |
159 | case ElementType::F64: |
160 | case ElementType::SNormF64: |
161 | case ElementType::UNormF64: |
162 | return "double" ; |
163 | case ElementType::PackedS8x32: |
164 | return "int8_t4_packed" ; |
165 | case ElementType::PackedU8x32: |
166 | return "uint8_t4_packed" ; |
167 | case ElementType::Invalid: |
168 | return "<invalid>" ; |
169 | } |
170 | llvm_unreachable("Unhandled ElementType" ); |
171 | } |
172 | |
173 | static StringRef getSamplerTypeName(SamplerType ST) { |
174 | switch (ST) { |
175 | case SamplerType::Default: |
176 | return "Default" ; |
177 | case SamplerType::Comparison: |
178 | return "Comparison" ; |
179 | case SamplerType::Mono: |
180 | return "Mono" ; |
181 | } |
182 | llvm_unreachable("Unhandled SamplerType" ); |
183 | } |
184 | |
185 | static StringRef getSamplerFeedbackTypeName(SamplerFeedbackType SFT) { |
186 | switch (SFT) { |
187 | case SamplerFeedbackType::MinMip: |
188 | return "MinMip" ; |
189 | case SamplerFeedbackType::MipRegionUsed: |
190 | return "MipRegionUsed" ; |
191 | } |
192 | llvm_unreachable("Unhandled SamplerFeedbackType" ); |
193 | } |
194 | |
195 | static dxil::ElementType toDXILElementType(Type *Ty, bool IsSigned) { |
196 | // TODO: Handle unorm, snorm, and packed. |
197 | Ty = Ty->getScalarType(); |
198 | |
199 | if (Ty->isIntegerTy()) { |
200 | switch (Ty->getIntegerBitWidth()) { |
201 | case 16: |
202 | return IsSigned ? ElementType::I16 : ElementType::U16; |
203 | case 32: |
204 | return IsSigned ? ElementType::I32 : ElementType::U32; |
205 | case 64: |
206 | return IsSigned ? ElementType::I64 : ElementType::U64; |
207 | case 1: |
208 | default: |
209 | return ElementType::Invalid; |
210 | } |
211 | } else if (Ty->isFloatTy()) { |
212 | return ElementType::F32; |
213 | } else if (Ty->isDoubleTy()) { |
214 | return ElementType::F64; |
215 | } else if (Ty->isHalfTy()) { |
216 | return ElementType::F16; |
217 | } |
218 | |
219 | return ElementType::Invalid; |
220 | } |
221 | |
222 | ResourceTypeInfo::ResourceTypeInfo(TargetExtType *HandleTy, |
223 | const dxil::ResourceClass RC_, |
224 | const dxil::ResourceKind Kind_) |
225 | : HandleTy(HandleTy) { |
226 | // If we're provided a resource class and kind, trust them. |
227 | if (Kind_ != dxil::ResourceKind::Invalid) { |
228 | RC = RC_; |
229 | Kind = Kind_; |
230 | return; |
231 | } |
232 | |
233 | if (auto *Ty = dyn_cast<RawBufferExtType>(Val: HandleTy)) { |
234 | RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV; |
235 | Kind = Ty->isStructured() ? ResourceKind::StructuredBuffer |
236 | : ResourceKind::RawBuffer; |
237 | } else if (auto *Ty = dyn_cast<TypedBufferExtType>(Val: HandleTy)) { |
238 | RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV; |
239 | Kind = ResourceKind::TypedBuffer; |
240 | } else if (auto *Ty = dyn_cast<TextureExtType>(Val: HandleTy)) { |
241 | RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV; |
242 | Kind = Ty->getDimension(); |
243 | } else if (auto *Ty = dyn_cast<MSTextureExtType>(Val: HandleTy)) { |
244 | RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV; |
245 | Kind = Ty->getDimension(); |
246 | } else if (auto *Ty = dyn_cast<FeedbackTextureExtType>(Val: HandleTy)) { |
247 | RC = ResourceClass::UAV; |
248 | Kind = Ty->getDimension(); |
249 | } else if (isa<CBufferExtType>(Val: HandleTy)) { |
250 | RC = ResourceClass::CBuffer; |
251 | Kind = ResourceKind::CBuffer; |
252 | } else if (isa<SamplerExtType>(Val: HandleTy)) { |
253 | RC = ResourceClass::Sampler; |
254 | Kind = ResourceKind::Sampler; |
255 | } else |
256 | llvm_unreachable("Unknown handle type" ); |
257 | } |
258 | |
259 | static void formatTypeName(SmallString<64> &Dest, StringRef Name, |
260 | bool IsWriteable, bool IsROV, |
261 | Type *ContainedType = nullptr, |
262 | bool IsSigned = true) { |
263 | raw_svector_ostream DestStream(Dest); |
264 | if (IsWriteable) |
265 | DestStream << (IsROV ? "RasterizerOrdered" : "RW" ); |
266 | DestStream << Name; |
267 | |
268 | if (!ContainedType) |
269 | return; |
270 | |
271 | StringRef ElementName; |
272 | ElementType ET = toDXILElementType(Ty: ContainedType, IsSigned); |
273 | if (ET != ElementType::Invalid) { |
274 | ElementName = getElementTypeNameForTemplate(ET); |
275 | } else { |
276 | assert(isa<StructType>(ContainedType) && |
277 | "invalid element type for raw buffer" ); |
278 | StructType *ST = cast<StructType>(Val: ContainedType); |
279 | if (!ST->hasName()) |
280 | return; |
281 | ElementName = ST->getStructName(); |
282 | } |
283 | |
284 | DestStream << "<" << ElementName; |
285 | if (const FixedVectorType *VTy = dyn_cast<FixedVectorType>(Val: ContainedType)) |
286 | DestStream << VTy->getNumElements(); |
287 | DestStream << ">" ; |
288 | } |
289 | |
290 | static StructType *getOrCreateElementStruct(Type *ElemType, StringRef Name) { |
291 | StructType *Ty = StructType::getTypeByName(C&: ElemType->getContext(), Name); |
292 | if (Ty && Ty->getNumElements() == 1 && Ty->getElementType(N: 0) == ElemType) |
293 | return Ty; |
294 | return StructType::create(Elements: ElemType, Name); |
295 | } |
296 | |
297 | StructType *ResourceTypeInfo::createElementStruct(StringRef CBufferName) { |
298 | SmallString<64> TypeName; |
299 | |
300 | switch (Kind) { |
301 | case ResourceKind::Texture1D: |
302 | case ResourceKind::Texture2D: |
303 | case ResourceKind::Texture3D: |
304 | case ResourceKind::TextureCube: |
305 | case ResourceKind::Texture1DArray: |
306 | case ResourceKind::Texture2DArray: |
307 | case ResourceKind::TextureCubeArray: { |
308 | auto *RTy = cast<TextureExtType>(Val: HandleTy); |
309 | formatTypeName(Dest&: TypeName, Name: getResourceKindName(RK: Kind), IsWriteable: RTy->isWriteable(), |
310 | IsROV: RTy->isROV(), ContainedType: RTy->getResourceType(), IsSigned: RTy->isSigned()); |
311 | return getOrCreateElementStruct(ElemType: RTy->getResourceType(), Name: TypeName); |
312 | } |
313 | case ResourceKind::Texture2DMS: |
314 | case ResourceKind::Texture2DMSArray: { |
315 | auto *RTy = cast<MSTextureExtType>(Val: HandleTy); |
316 | formatTypeName(Dest&: TypeName, Name: getResourceKindName(RK: Kind), IsWriteable: RTy->isWriteable(), |
317 | /*IsROV=*/false, ContainedType: RTy->getResourceType(), IsSigned: RTy->isSigned()); |
318 | return getOrCreateElementStruct(ElemType: RTy->getResourceType(), Name: TypeName); |
319 | } |
320 | case ResourceKind::TypedBuffer: { |
321 | auto *RTy = cast<TypedBufferExtType>(Val: HandleTy); |
322 | formatTypeName(Dest&: TypeName, Name: getResourceKindName(RK: Kind), IsWriteable: RTy->isWriteable(), |
323 | IsROV: RTy->isROV(), ContainedType: RTy->getResourceType(), IsSigned: RTy->isSigned()); |
324 | return getOrCreateElementStruct(ElemType: RTy->getResourceType(), Name: TypeName); |
325 | } |
326 | case ResourceKind::RawBuffer: { |
327 | auto *RTy = cast<RawBufferExtType>(Val: HandleTy); |
328 | formatTypeName(Dest&: TypeName, Name: "ByteAddressBuffer" , IsWriteable: RTy->isWriteable(), |
329 | IsROV: RTy->isROV()); |
330 | return getOrCreateElementStruct(ElemType: Type::getInt32Ty(C&: HandleTy->getContext()), |
331 | Name: TypeName); |
332 | } |
333 | case ResourceKind::StructuredBuffer: { |
334 | auto *RTy = cast<RawBufferExtType>(Val: HandleTy); |
335 | Type *Ty = RTy->getResourceType(); |
336 | formatTypeName(Dest&: TypeName, Name: "StructuredBuffer" , IsWriteable: RTy->isWriteable(), |
337 | IsROV: RTy->isROV(), ContainedType: RTy->getResourceType(), IsSigned: true); |
338 | return getOrCreateElementStruct(ElemType: Ty, Name: TypeName); |
339 | } |
340 | case ResourceKind::FeedbackTexture2D: |
341 | case ResourceKind::FeedbackTexture2DArray: { |
342 | auto *RTy = cast<FeedbackTextureExtType>(Val: HandleTy); |
343 | TypeName = formatv(Fmt: "{0}<{1}>" , Vals: getResourceKindName(RK: Kind), |
344 | Vals: llvm::to_underlying(E: RTy->getFeedbackType())); |
345 | return getOrCreateElementStruct(ElemType: Type::getInt32Ty(C&: HandleTy->getContext()), |
346 | Name: TypeName); |
347 | } |
348 | case ResourceKind::CBuffer: { |
349 | auto *RTy = cast<CBufferExtType>(Val: HandleTy); |
350 | LayoutExtType *LayoutType = cast<LayoutExtType>(Val: RTy->getResourceType()); |
351 | StructType *Ty = cast<StructType>(Val: LayoutType->getWrappedType()); |
352 | SmallString<64> Name = getResourceKindName(RK: Kind); |
353 | if (!CBufferName.empty()) { |
354 | Name.append(RHS: "." ); |
355 | Name.append(RHS: CBufferName); |
356 | } |
357 | return StructType::create(Elements: Ty->elements(), Name); |
358 | } |
359 | case ResourceKind::Sampler: { |
360 | auto *RTy = cast<SamplerExtType>(Val: HandleTy); |
361 | TypeName = formatv(Fmt: "SamplerState<{0}>" , |
362 | Vals: llvm::to_underlying(E: RTy->getSamplerType())); |
363 | return getOrCreateElementStruct(ElemType: Type::getInt32Ty(C&: HandleTy->getContext()), |
364 | Name: TypeName); |
365 | } |
366 | case ResourceKind::TBuffer: |
367 | case ResourceKind::RTAccelerationStructure: |
368 | llvm_unreachable("Unhandled resource kind" ); |
369 | case ResourceKind::Invalid: |
370 | case ResourceKind::NumEntries: |
371 | llvm_unreachable("Invalid resource kind" ); |
372 | } |
373 | llvm_unreachable("Unhandled ResourceKind enum" ); |
374 | } |
375 | |
376 | bool ResourceTypeInfo::isUAV() const { return RC == ResourceClass::UAV; } |
377 | |
378 | bool ResourceTypeInfo::isCBuffer() const { |
379 | return RC == ResourceClass::CBuffer; |
380 | } |
381 | |
382 | bool ResourceTypeInfo::isSampler() const { |
383 | return RC == ResourceClass::Sampler; |
384 | } |
385 | |
386 | bool ResourceTypeInfo::isStruct() const { |
387 | return Kind == ResourceKind::StructuredBuffer; |
388 | } |
389 | |
390 | bool ResourceTypeInfo::isTyped() const { |
391 | switch (Kind) { |
392 | case ResourceKind::Texture1D: |
393 | case ResourceKind::Texture2D: |
394 | case ResourceKind::Texture2DMS: |
395 | case ResourceKind::Texture3D: |
396 | case ResourceKind::TextureCube: |
397 | case ResourceKind::Texture1DArray: |
398 | case ResourceKind::Texture2DArray: |
399 | case ResourceKind::Texture2DMSArray: |
400 | case ResourceKind::TextureCubeArray: |
401 | case ResourceKind::TypedBuffer: |
402 | return true; |
403 | case ResourceKind::RawBuffer: |
404 | case ResourceKind::StructuredBuffer: |
405 | case ResourceKind::FeedbackTexture2D: |
406 | case ResourceKind::FeedbackTexture2DArray: |
407 | case ResourceKind::CBuffer: |
408 | case ResourceKind::Sampler: |
409 | case ResourceKind::TBuffer: |
410 | case ResourceKind::RTAccelerationStructure: |
411 | return false; |
412 | case ResourceKind::Invalid: |
413 | case ResourceKind::NumEntries: |
414 | llvm_unreachable("Invalid resource kind" ); |
415 | } |
416 | llvm_unreachable("Unhandled ResourceKind enum" ); |
417 | } |
418 | |
419 | bool ResourceTypeInfo::isFeedback() const { |
420 | return Kind == ResourceKind::FeedbackTexture2D || |
421 | Kind == ResourceKind::FeedbackTexture2DArray; |
422 | } |
423 | |
424 | bool ResourceTypeInfo::isMultiSample() const { |
425 | return Kind == ResourceKind::Texture2DMS || |
426 | Kind == ResourceKind::Texture2DMSArray; |
427 | } |
428 | |
429 | static bool isROV(dxil::ResourceKind Kind, TargetExtType *Ty) { |
430 | switch (Kind) { |
431 | case ResourceKind::Texture1D: |
432 | case ResourceKind::Texture2D: |
433 | case ResourceKind::Texture3D: |
434 | case ResourceKind::TextureCube: |
435 | case ResourceKind::Texture1DArray: |
436 | case ResourceKind::Texture2DArray: |
437 | case ResourceKind::TextureCubeArray: |
438 | return cast<TextureExtType>(Val: Ty)->isROV(); |
439 | case ResourceKind::TypedBuffer: |
440 | return cast<TypedBufferExtType>(Val: Ty)->isROV(); |
441 | case ResourceKind::RawBuffer: |
442 | case ResourceKind::StructuredBuffer: |
443 | return cast<RawBufferExtType>(Val: Ty)->isROV(); |
444 | case ResourceKind::Texture2DMS: |
445 | case ResourceKind::Texture2DMSArray: |
446 | case ResourceKind::FeedbackTexture2D: |
447 | case ResourceKind::FeedbackTexture2DArray: |
448 | return false; |
449 | case ResourceKind::CBuffer: |
450 | case ResourceKind::Sampler: |
451 | case ResourceKind::TBuffer: |
452 | case ResourceKind::RTAccelerationStructure: |
453 | case ResourceKind::Invalid: |
454 | case ResourceKind::NumEntries: |
455 | llvm_unreachable("Resource cannot be ROV" ); |
456 | } |
457 | llvm_unreachable("Unhandled ResourceKind enum" ); |
458 | } |
459 | |
460 | ResourceTypeInfo::UAVInfo ResourceTypeInfo::getUAV() const { |
461 | assert(isUAV() && "Not a UAV" ); |
462 | return {.IsROV: isROV(Kind, Ty: HandleTy)}; |
463 | } |
464 | |
465 | uint32_t ResourceTypeInfo::getCBufferSize(const DataLayout &DL) const { |
466 | assert(isCBuffer() && "Not a CBuffer" ); |
467 | |
468 | Type *ElTy = cast<CBufferExtType>(Val: HandleTy)->getResourceType(); |
469 | |
470 | if (auto *LayoutTy = dyn_cast<LayoutExtType>(Val: ElTy)) |
471 | return LayoutTy->getSize(); |
472 | |
473 | // TODO: What should we do with unannotated arrays? |
474 | return DL.getTypeAllocSize(Ty: ElTy); |
475 | } |
476 | |
477 | dxil::SamplerType ResourceTypeInfo::getSamplerType() const { |
478 | assert(isSampler() && "Not a Sampler" ); |
479 | return cast<SamplerExtType>(Val: HandleTy)->getSamplerType(); |
480 | } |
481 | |
482 | ResourceTypeInfo::StructInfo |
483 | ResourceTypeInfo::getStruct(const DataLayout &DL) const { |
484 | assert(isStruct() && "Not a Struct" ); |
485 | |
486 | Type *ElTy = cast<RawBufferExtType>(Val: HandleTy)->getResourceType(); |
487 | |
488 | uint32_t Stride = DL.getTypeAllocSize(Ty: ElTy); |
489 | MaybeAlign Alignment; |
490 | if (auto *STy = dyn_cast<StructType>(Val: ElTy)) |
491 | Alignment = DL.getStructLayout(Ty: STy)->getAlignment(); |
492 | uint32_t AlignLog2 = Alignment ? Log2(A: *Alignment) : 0; |
493 | return {.Stride: Stride, .AlignLog2: AlignLog2}; |
494 | } |
495 | |
496 | static std::pair<Type *, bool> getTypedElementType(dxil::ResourceKind Kind, |
497 | TargetExtType *Ty) { |
498 | switch (Kind) { |
499 | case ResourceKind::Texture1D: |
500 | case ResourceKind::Texture2D: |
501 | case ResourceKind::Texture3D: |
502 | case ResourceKind::TextureCube: |
503 | case ResourceKind::Texture1DArray: |
504 | case ResourceKind::Texture2DArray: |
505 | case ResourceKind::TextureCubeArray: { |
506 | auto *RTy = cast<TextureExtType>(Val: Ty); |
507 | return {RTy->getResourceType(), RTy->isSigned()}; |
508 | } |
509 | case ResourceKind::Texture2DMS: |
510 | case ResourceKind::Texture2DMSArray: { |
511 | auto *RTy = cast<MSTextureExtType>(Val: Ty); |
512 | return {RTy->getResourceType(), RTy->isSigned()}; |
513 | } |
514 | case ResourceKind::TypedBuffer: { |
515 | auto *RTy = cast<TypedBufferExtType>(Val: Ty); |
516 | return {RTy->getResourceType(), RTy->isSigned()}; |
517 | } |
518 | case ResourceKind::RawBuffer: |
519 | case ResourceKind::StructuredBuffer: |
520 | case ResourceKind::FeedbackTexture2D: |
521 | case ResourceKind::FeedbackTexture2DArray: |
522 | case ResourceKind::CBuffer: |
523 | case ResourceKind::Sampler: |
524 | case ResourceKind::TBuffer: |
525 | case ResourceKind::RTAccelerationStructure: |
526 | case ResourceKind::Invalid: |
527 | case ResourceKind::NumEntries: |
528 | llvm_unreachable("Resource is not typed" ); |
529 | } |
530 | llvm_unreachable("Unhandled ResourceKind enum" ); |
531 | } |
532 | |
533 | ResourceTypeInfo::TypedInfo ResourceTypeInfo::getTyped() const { |
534 | assert(isTyped() && "Not typed" ); |
535 | |
536 | auto [ElTy, IsSigned] = getTypedElementType(Kind, Ty: HandleTy); |
537 | dxil::ElementType ET = toDXILElementType(Ty: ElTy, IsSigned); |
538 | uint32_t Count = 1; |
539 | if (auto *VTy = dyn_cast<FixedVectorType>(Val: ElTy)) |
540 | Count = VTy->getNumElements(); |
541 | return {.ElementTy: ET, .ElementCount: Count}; |
542 | } |
543 | |
544 | dxil::SamplerFeedbackType ResourceTypeInfo::getFeedbackType() const { |
545 | assert(isFeedback() && "Not Feedback" ); |
546 | return cast<FeedbackTextureExtType>(Val: HandleTy)->getFeedbackType(); |
547 | } |
548 | uint32_t ResourceTypeInfo::getMultiSampleCount() const { |
549 | assert(isMultiSample() && "Not MultiSampled" ); |
550 | return cast<MSTextureExtType>(Val: HandleTy)->getSampleCount(); |
551 | } |
552 | |
553 | bool ResourceTypeInfo::operator==(const ResourceTypeInfo &RHS) const { |
554 | return HandleTy == RHS.HandleTy; |
555 | } |
556 | |
557 | bool ResourceTypeInfo::operator<(const ResourceTypeInfo &RHS) const { |
558 | // An empty datalayout is sufficient for sorting purposes. |
559 | DataLayout DummyDL; |
560 | if (std::tie(args: RC, args: Kind) < std::tie(args: RHS.RC, args: RHS.Kind)) |
561 | return true; |
562 | if (isCBuffer() && RHS.isCBuffer() && |
563 | getCBufferSize(DL: DummyDL) < RHS.getCBufferSize(DL: DummyDL)) |
564 | return true; |
565 | if (isSampler() && RHS.isSampler() && getSamplerType() < RHS.getSamplerType()) |
566 | return true; |
567 | if (isUAV() && RHS.isUAV() && getUAV() < RHS.getUAV()) |
568 | return true; |
569 | if (isStruct() && RHS.isStruct() && |
570 | getStruct(DL: DummyDL) < RHS.getStruct(DL: DummyDL)) |
571 | return true; |
572 | if (isFeedback() && RHS.isFeedback() && |
573 | getFeedbackType() < RHS.getFeedbackType()) |
574 | return true; |
575 | if (isTyped() && RHS.isTyped() && getTyped() < RHS.getTyped()) |
576 | return true; |
577 | if (isMultiSample() && RHS.isMultiSample() && |
578 | getMultiSampleCount() < RHS.getMultiSampleCount()) |
579 | return true; |
580 | return false; |
581 | } |
582 | |
583 | void ResourceTypeInfo::print(raw_ostream &OS, const DataLayout &DL) const { |
584 | OS << " Class: " << getResourceClassName(RC) << "\n" |
585 | << " Kind: " << getResourceKindName(RK: Kind) << "\n" ; |
586 | |
587 | if (isCBuffer()) { |
588 | OS << " CBuffer size: " << getCBufferSize(DL) << "\n" ; |
589 | } else if (isSampler()) { |
590 | OS << " Sampler Type: " << getSamplerTypeName(ST: getSamplerType()) << "\n" ; |
591 | } else { |
592 | if (isUAV()) { |
593 | UAVInfo UAVFlags = getUAV(); |
594 | OS << " IsROV: " << UAVFlags.IsROV << "\n" ; |
595 | } |
596 | if (isMultiSample()) |
597 | OS << " Sample Count: " << getMultiSampleCount() << "\n" ; |
598 | |
599 | if (isStruct()) { |
600 | StructInfo Struct = getStruct(DL); |
601 | OS << " Buffer Stride: " << Struct.Stride << "\n" ; |
602 | OS << " Alignment: " << Struct.AlignLog2 << "\n" ; |
603 | } else if (isTyped()) { |
604 | TypedInfo Typed = getTyped(); |
605 | OS << " Element Type: " << getElementTypeName(ET: Typed.ElementTy) << "\n" |
606 | << " Element Count: " << Typed.ElementCount << "\n" ; |
607 | } else if (isFeedback()) |
608 | OS << " Feedback Type: " << getSamplerFeedbackTypeName(SFT: getFeedbackType()) |
609 | << "\n" ; |
610 | } |
611 | } |
612 | |
613 | GlobalVariable *ResourceInfo::createSymbol(Module &M, StructType *Ty) { |
614 | assert(!Symbol && "Symbol has already been created" ); |
615 | Symbol = new GlobalVariable(M, Ty, /*isConstant=*/true, |
616 | GlobalValue::ExternalLinkage, |
617 | /*Initializer=*/nullptr, Name); |
618 | return Symbol; |
619 | } |
620 | |
621 | MDTuple *ResourceInfo::getAsMetadata(Module &M, |
622 | dxil::ResourceTypeInfo &RTI) const { |
623 | LLVMContext &Ctx = M.getContext(); |
624 | const DataLayout &DL = M.getDataLayout(); |
625 | |
626 | SmallVector<Metadata *, 11> MDVals; |
627 | |
628 | Type *I32Ty = Type::getInt32Ty(C&: Ctx); |
629 | Type *I1Ty = Type::getInt1Ty(C&: Ctx); |
630 | auto getIntMD = [&I32Ty](uint32_t V) { |
631 | return ConstantAsMetadata::get( |
632 | C: Constant::getIntegerValue(Ty: I32Ty, V: APInt(32, V))); |
633 | }; |
634 | auto getBoolMD = [&I1Ty](uint32_t V) { |
635 | return ConstantAsMetadata::get( |
636 | C: Constant::getIntegerValue(Ty: I1Ty, V: APInt(1, V))); |
637 | }; |
638 | |
639 | MDVals.push_back(Elt: getIntMD(Binding.RecordID)); |
640 | assert(Symbol && "Cannot yet create useful resource metadata without symbol" ); |
641 | MDVals.push_back(Elt: ValueAsMetadata::get(V: Symbol)); |
642 | MDVals.push_back(Elt: MDString::get(Context&: Ctx, Str: Name)); |
643 | MDVals.push_back(Elt: getIntMD(Binding.Space)); |
644 | MDVals.push_back(Elt: getIntMD(Binding.LowerBound)); |
645 | MDVals.push_back(Elt: getIntMD(Binding.Size)); |
646 | |
647 | if (RTI.isCBuffer()) { |
648 | MDVals.push_back(Elt: getIntMD(RTI.getCBufferSize(DL))); |
649 | MDVals.push_back(Elt: nullptr); |
650 | } else if (RTI.isSampler()) { |
651 | MDVals.push_back(Elt: getIntMD(llvm::to_underlying(E: RTI.getSamplerType()))); |
652 | MDVals.push_back(Elt: nullptr); |
653 | } else { |
654 | MDVals.push_back(Elt: getIntMD(llvm::to_underlying(E: RTI.getResourceKind()))); |
655 | |
656 | if (RTI.isUAV()) { |
657 | ResourceTypeInfo::UAVInfo UAVFlags = RTI.getUAV(); |
658 | MDVals.push_back(Elt: getBoolMD(GloballyCoherent)); |
659 | MDVals.push_back(Elt: getBoolMD(hasCounter())); |
660 | MDVals.push_back(Elt: getBoolMD(UAVFlags.IsROV)); |
661 | } else { |
662 | // All SRVs include sample count in the metadata, but it's only meaningful |
663 | // for multi-sampled textured. Also, UAVs can be multisampled in SM6.7+, |
664 | // but this just isn't reflected in the metadata at all. |
665 | uint32_t SampleCount = |
666 | RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0; |
667 | MDVals.push_back(Elt: getIntMD(SampleCount)); |
668 | } |
669 | |
670 | // Further properties are attached to a metadata list of tag-value pairs. |
671 | SmallVector<Metadata *> Tags; |
672 | if (RTI.isStruct()) { |
673 | Tags.push_back( |
674 | Elt: getIntMD(llvm::to_underlying(E: ExtPropTags::StructuredBufferStride))); |
675 | Tags.push_back(Elt: getIntMD(RTI.getStruct(DL).Stride)); |
676 | } else if (RTI.isTyped()) { |
677 | Tags.push_back(Elt: getIntMD(llvm::to_underlying(E: ExtPropTags::ElementType))); |
678 | Tags.push_back(Elt: getIntMD(llvm::to_underlying(E: RTI.getTyped().ElementTy))); |
679 | } else if (RTI.isFeedback()) { |
680 | Tags.push_back( |
681 | Elt: getIntMD(llvm::to_underlying(E: ExtPropTags::SamplerFeedbackKind))); |
682 | Tags.push_back(Elt: getIntMD(llvm::to_underlying(E: RTI.getFeedbackType()))); |
683 | } |
684 | MDVals.push_back(Elt: Tags.empty() ? nullptr : MDNode::get(Context&: Ctx, MDs: Tags)); |
685 | } |
686 | |
687 | return MDNode::get(Context&: Ctx, MDs: MDVals); |
688 | } |
689 | |
690 | std::pair<uint32_t, uint32_t> |
691 | ResourceInfo::getAnnotateProps(Module &M, dxil::ResourceTypeInfo &RTI) const { |
692 | const DataLayout &DL = M.getDataLayout(); |
693 | |
694 | uint32_t ResourceKind = llvm::to_underlying(E: RTI.getResourceKind()); |
695 | uint32_t AlignLog2 = RTI.isStruct() ? RTI.getStruct(DL).AlignLog2 : 0; |
696 | bool IsUAV = RTI.isUAV(); |
697 | ResourceTypeInfo::UAVInfo UAVFlags = |
698 | IsUAV ? RTI.getUAV() : ResourceTypeInfo::UAVInfo{}; |
699 | bool IsROV = IsUAV && UAVFlags.IsROV; |
700 | bool IsGloballyCoherent = IsUAV && GloballyCoherent; |
701 | uint8_t SamplerCmpOrHasCounter = 0; |
702 | if (IsUAV) |
703 | SamplerCmpOrHasCounter = hasCounter(); |
704 | else if (RTI.isSampler()) |
705 | SamplerCmpOrHasCounter = RTI.getSamplerType() == SamplerType::Comparison; |
706 | |
707 | // TODO: Document this format. Currently the only reference is the |
708 | // implementation of dxc's DxilResourceProperties struct. |
709 | uint32_t Word0 = 0; |
710 | Word0 |= ResourceKind & 0xFF; |
711 | Word0 |= (AlignLog2 & 0xF) << 8; |
712 | Word0 |= (IsUAV & 1) << 12; |
713 | Word0 |= (IsROV & 1) << 13; |
714 | Word0 |= (IsGloballyCoherent & 1) << 14; |
715 | Word0 |= (SamplerCmpOrHasCounter & 1) << 15; |
716 | |
717 | uint32_t Word1 = 0; |
718 | if (RTI.isStruct()) |
719 | Word1 = RTI.getStruct(DL).Stride; |
720 | else if (RTI.isCBuffer()) |
721 | Word1 = RTI.getCBufferSize(DL); |
722 | else if (RTI.isFeedback()) |
723 | Word1 = llvm::to_underlying(E: RTI.getFeedbackType()); |
724 | else if (RTI.isTyped()) { |
725 | ResourceTypeInfo::TypedInfo Typed = RTI.getTyped(); |
726 | uint32_t CompType = llvm::to_underlying(E: Typed.ElementTy); |
727 | uint32_t CompCount = Typed.ElementCount; |
728 | uint32_t SampleCount = RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0; |
729 | |
730 | Word1 |= (CompType & 0xFF) << 0; |
731 | Word1 |= (CompCount & 0xFF) << 8; |
732 | Word1 |= (SampleCount & 0xFF) << 16; |
733 | } |
734 | |
735 | return {Word0, Word1}; |
736 | } |
737 | |
738 | void ResourceInfo::print(raw_ostream &OS, dxil::ResourceTypeInfo &RTI, |
739 | const DataLayout &DL) const { |
740 | if (!Name.empty()) |
741 | OS << " Name: " << Name << "\n" ; |
742 | |
743 | if (Symbol) { |
744 | OS << " Symbol: " ; |
745 | Symbol->printAsOperand(O&: OS); |
746 | OS << "\n" ; |
747 | } |
748 | |
749 | OS << " Binding:\n" |
750 | << " Record ID: " << Binding.RecordID << "\n" |
751 | << " Space: " << Binding.Space << "\n" |
752 | << " Lower Bound: " << Binding.LowerBound << "\n" |
753 | << " Size: " << Binding.Size << "\n" ; |
754 | |
755 | OS << " Globally Coherent: " << GloballyCoherent << "\n" ; |
756 | OS << " Counter Direction: " ; |
757 | |
758 | switch (CounterDirection) { |
759 | case ResourceCounterDirection::Increment: |
760 | OS << "Increment\n" ; |
761 | break; |
762 | case ResourceCounterDirection::Decrement: |
763 | OS << "Decrement\n" ; |
764 | break; |
765 | case ResourceCounterDirection::Unknown: |
766 | OS << "Unknown\n" ; |
767 | break; |
768 | case ResourceCounterDirection::Invalid: |
769 | OS << "Invalid\n" ; |
770 | break; |
771 | } |
772 | |
773 | RTI.print(OS, DL); |
774 | } |
775 | |
776 | //===----------------------------------------------------------------------===// |
777 | |
778 | bool DXILResourceTypeMap::invalidate(Module &M, const PreservedAnalyses &PA, |
779 | ModuleAnalysisManager::Invalidator &Inv) { |
780 | // Passes that introduce resource types must explicitly invalidate this pass. |
781 | auto PAC = PA.getChecker<DXILResourceTypeAnalysis>(); |
782 | return !PAC.preservedWhenStateless(); |
783 | } |
784 | |
785 | //===----------------------------------------------------------------------===// |
786 | static bool isUpdateCounterIntrinsic(Function &F) { |
787 | return F.getIntrinsicID() == Intrinsic::dx_resource_updatecounter; |
788 | } |
789 | |
790 | StringRef dxil::getResourceNameFromBindingCall(CallInst *CI) { |
791 | Value *Op = nullptr; |
792 | switch (CI->getCalledFunction()->getIntrinsicID()) { |
793 | default: |
794 | llvm_unreachable("unexpected handle creation intrinsic" ); |
795 | case Intrinsic::dx_resource_handlefrombinding: |
796 | case Intrinsic::dx_resource_handlefromimplicitbinding: |
797 | Op = CI->getArgOperand(i: 5); |
798 | break; |
799 | } |
800 | |
801 | auto *GV = dyn_cast<llvm::GlobalVariable>(Val: Op); |
802 | if (!GV) |
803 | return "" ; |
804 | |
805 | auto *CA = dyn_cast<ConstantDataArray>(Val: GV->getInitializer()); |
806 | assert(CA && CA->isString() && "expected constant string" ); |
807 | StringRef Name = CA->getAsString(); |
808 | // strip trailing 0 |
809 | if (Name.ends_with(Suffix: '\0')) |
810 | Name = Name.drop_back(N: 1); |
811 | return Name; |
812 | } |
813 | |
814 | void DXILResourceMap::populateResourceInfos(Module &M, |
815 | DXILResourceTypeMap &DRTM) { |
816 | SmallVector<std::tuple<CallInst *, ResourceInfo, ResourceTypeInfo>> CIToInfos; |
817 | |
818 | for (Function &F : M.functions()) { |
819 | if (!F.isDeclaration()) |
820 | continue; |
821 | LLVM_DEBUG(dbgs() << "Function: " << F.getName() << "\n" ); |
822 | Intrinsic::ID ID = F.getIntrinsicID(); |
823 | switch (ID) { |
824 | default: |
825 | continue; |
826 | case Intrinsic::dx_resource_handlefrombinding: { |
827 | auto *HandleTy = cast<TargetExtType>(Val: F.getReturnType()); |
828 | ResourceTypeInfo &RTI = DRTM[HandleTy]; |
829 | |
830 | for (User *U : F.users()) |
831 | if (CallInst *CI = dyn_cast<CallInst>(Val: U)) { |
832 | LLVM_DEBUG(dbgs() << " Visiting: " << *U << "\n" ); |
833 | uint32_t Space = |
834 | cast<ConstantInt>(Val: CI->getArgOperand(i: 0))->getZExtValue(); |
835 | uint32_t LowerBound = |
836 | cast<ConstantInt>(Val: CI->getArgOperand(i: 1))->getZExtValue(); |
837 | uint32_t Size = |
838 | cast<ConstantInt>(Val: CI->getArgOperand(i: 2))->getZExtValue(); |
839 | StringRef Name = getResourceNameFromBindingCall(CI); |
840 | |
841 | ResourceInfo RI = |
842 | ResourceInfo{/*RecordID=*/0, Space, LowerBound, |
843 | Size, HandleTy, Name}; |
844 | |
845 | CIToInfos.emplace_back(Args&: CI, Args&: RI, Args&: RTI); |
846 | } |
847 | |
848 | break; |
849 | } |
850 | } |
851 | } |
852 | |
853 | llvm::stable_sort(Range&: CIToInfos, C: [](auto &LHS, auto &RHS) { |
854 | const auto &[LCI, LRI, LRTI] = LHS; |
855 | const auto &[RCI, RRI, RRTI] = RHS; |
856 | // Sort by resource class first for grouping purposes, and then by the |
857 | // binding and type so we can remove duplicates. |
858 | ResourceClass LRC = LRTI.getResourceClass(); |
859 | ResourceClass RRC = RRTI.getResourceClass(); |
860 | |
861 | return std::tie(LRC, LRI, LRTI) < std::tie(RRC, RRI, RRTI); |
862 | }); |
863 | for (auto [CI, RI, RTI] : CIToInfos) { |
864 | if (Infos.empty() || RI != Infos.back()) |
865 | Infos.push_back(Elt: RI); |
866 | CallMap[CI] = Infos.size() - 1; |
867 | } |
868 | |
869 | unsigned Size = Infos.size(); |
870 | // In DXC, Record ID is unique per resource type. Match that. |
871 | FirstUAV = FirstCBuffer = FirstSampler = Size; |
872 | uint32_t NextID = 0; |
873 | for (unsigned I = 0, E = Size; I != E; ++I) { |
874 | ResourceInfo &RI = Infos[I]; |
875 | ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()]; |
876 | if (RTI.isUAV() && FirstUAV == Size) { |
877 | FirstUAV = I; |
878 | NextID = 0; |
879 | } else if (RTI.isCBuffer() && FirstCBuffer == Size) { |
880 | FirstCBuffer = I; |
881 | NextID = 0; |
882 | } else if (RTI.isSampler() && FirstSampler == Size) { |
883 | FirstSampler = I; |
884 | NextID = 0; |
885 | } |
886 | |
887 | // We need to make sure the types of resource are ordered even if some are |
888 | // missing. |
889 | FirstCBuffer = std::min(l: {FirstCBuffer, FirstSampler}); |
890 | FirstUAV = std::min(l: {FirstUAV, FirstCBuffer}); |
891 | |
892 | // Adjust the resource binding to use the next ID. |
893 | RI.setBindingID(NextID++); |
894 | } |
895 | } |
896 | |
897 | void DXILResourceMap::populateCounterDirections(Module &M) { |
898 | for (Function &F : M.functions()) { |
899 | if (!isUpdateCounterIntrinsic(F)) |
900 | continue; |
901 | |
902 | LLVM_DEBUG(dbgs() << "Update Counter Function: " << F.getName() << "\n" ); |
903 | |
904 | for (const User *U : F.users()) { |
905 | const CallInst *CI = dyn_cast<CallInst>(Val: U); |
906 | assert(CI && "Users of dx_resource_updateCounter must be call instrs" ); |
907 | |
908 | // Determine if the use is an increment or decrement |
909 | Value *CountArg = CI->getArgOperand(i: 1); |
910 | ConstantInt *CountValue = cast<ConstantInt>(Val: CountArg); |
911 | int64_t CountLiteral = CountValue->getSExtValue(); |
912 | |
913 | // 0 is an unknown direction and shouldn't result in an insert |
914 | if (CountLiteral == 0) |
915 | continue; |
916 | |
917 | ResourceCounterDirection Direction = ResourceCounterDirection::Decrement; |
918 | if (CountLiteral > 0) |
919 | Direction = ResourceCounterDirection::Increment; |
920 | |
921 | // Collect all potential creation points for the handle arg |
922 | Value *HandleArg = CI->getArgOperand(i: 0); |
923 | SmallVector<ResourceInfo *> RBInfos = findByUse(Key: HandleArg); |
924 | for (ResourceInfo *RBInfo : RBInfos) { |
925 | if (RBInfo->CounterDirection == ResourceCounterDirection::Unknown) |
926 | RBInfo->CounterDirection = Direction; |
927 | else if (RBInfo->CounterDirection != Direction) { |
928 | RBInfo->CounterDirection = ResourceCounterDirection::Invalid; |
929 | HasInvalidDirection = true; |
930 | } |
931 | } |
932 | } |
933 | } |
934 | } |
935 | |
936 | void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) { |
937 | populateResourceInfos(M, DRTM); |
938 | populateCounterDirections(M); |
939 | } |
940 | |
941 | void DXILResourceMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM, |
942 | const DataLayout &DL) const { |
943 | for (unsigned I = 0, E = Infos.size(); I != E; ++I) { |
944 | OS << "Resource " << I << ":\n" ; |
945 | const dxil::ResourceInfo &RI = Infos[I]; |
946 | RI.print(OS, RTI&: DRTM[RI.getHandleTy()], DL); |
947 | OS << "\n" ; |
948 | } |
949 | |
950 | for (const auto &[CI, Index] : CallMap) { |
951 | OS << "Call bound to " << Index << ":" ; |
952 | CI->print(O&: OS); |
953 | OS << "\n" ; |
954 | } |
955 | } |
956 | |
957 | SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) { |
958 | if (const PHINode *Phi = dyn_cast<PHINode>(Val: Key)) { |
959 | SmallVector<dxil::ResourceInfo *> Children; |
960 | for (const Value *V : Phi->operands()) { |
961 | Children.append(RHS: findByUse(Key: V)); |
962 | } |
963 | return Children; |
964 | } |
965 | |
966 | const CallInst *CI = dyn_cast<CallInst>(Val: Key); |
967 | if (!CI) |
968 | return {}; |
969 | |
970 | switch (CI->getIntrinsicID()) { |
971 | // Found the create, return the binding |
972 | case Intrinsic::dx_resource_handlefrombinding: { |
973 | auto Pos = CallMap.find(Val: CI); |
974 | assert(Pos != CallMap.end() && "HandleFromBinding must be in resource map" ); |
975 | return {&Infos[Pos->second]}; |
976 | } |
977 | default: |
978 | break; |
979 | } |
980 | |
981 | // Check if any of the parameters are the resource we are following. If so |
982 | // keep searching. If none of them are return an empty list |
983 | const Type *UseType = CI->getType(); |
984 | SmallVector<dxil::ResourceInfo *> Children; |
985 | for (const Value *V : CI->args()) { |
986 | if (V->getType() != UseType) |
987 | continue; |
988 | |
989 | Children.append(RHS: findByUse(Key: V)); |
990 | } |
991 | |
992 | return Children; |
993 | } |
994 | |
995 | //===----------------------------------------------------------------------===// |
996 | |
997 | void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) { |
998 | struct Binding { |
999 | ResourceClass RC; |
1000 | uint32_t Space; |
1001 | uint32_t LowerBound; |
1002 | uint32_t UpperBound; |
1003 | Value *Name; |
1004 | Binding(ResourceClass RC, uint32_t Space, uint32_t LowerBound, |
1005 | uint32_t UpperBound, Value *Name) |
1006 | : RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound), |
1007 | Name(Name) {} |
1008 | }; |
1009 | SmallVector<Binding> Bindings; |
1010 | |
1011 | // collect all of the llvm.dx.resource.handlefrombinding calls; |
1012 | // make a note if there is llvm.dx.resource.handlefromimplicitbinding |
1013 | for (Function &F : M.functions()) { |
1014 | if (!F.isDeclaration()) |
1015 | continue; |
1016 | |
1017 | switch (F.getIntrinsicID()) { |
1018 | default: |
1019 | continue; |
1020 | case Intrinsic::dx_resource_handlefrombinding: { |
1021 | auto *HandleTy = cast<TargetExtType>(Val: F.getReturnType()); |
1022 | ResourceTypeInfo &RTI = DRTM[HandleTy]; |
1023 | |
1024 | for (User *U : F.users()) |
1025 | if (CallInst *CI = dyn_cast<CallInst>(Val: U)) { |
1026 | uint32_t Space = |
1027 | cast<ConstantInt>(Val: CI->getArgOperand(i: 0))->getZExtValue(); |
1028 | uint32_t LowerBound = |
1029 | cast<ConstantInt>(Val: CI->getArgOperand(i: 1))->getZExtValue(); |
1030 | int32_t Size = |
1031 | cast<ConstantInt>(Val: CI->getArgOperand(i: 2))->getZExtValue(); |
1032 | Value *Name = CI->getArgOperand(i: 5); |
1033 | |
1034 | // negative size means unbounded resource array; |
1035 | // upper bound register overflow should be detected in Sema |
1036 | assert((Size < 0 || (unsigned)LowerBound + Size - 1 <= UINT32_MAX) && |
1037 | "upper bound register overflow" ); |
1038 | uint32_t UpperBound = Size < 0 ? UINT32_MAX : LowerBound + Size - 1; |
1039 | Bindings.emplace_back(Args: RTI.getResourceClass(), Args&: Space, Args&: LowerBound, |
1040 | Args&: UpperBound, Args&: Name); |
1041 | } |
1042 | break; |
1043 | } |
1044 | case Intrinsic::dx_resource_handlefromimplicitbinding: { |
1045 | ImplicitBinding = true; |
1046 | break; |
1047 | } |
1048 | } |
1049 | } |
1050 | |
1051 | // sort all the collected bindings |
1052 | llvm::stable_sort(Range&: Bindings, C: [](auto &LHS, auto &RHS) { |
1053 | return std::tie(LHS.RC, LHS.Space, LHS.LowerBound) < |
1054 | std::tie(RHS.RC, RHS.Space, RHS.LowerBound); |
1055 | }); |
1056 | |
1057 | // remove duplicates |
1058 | Binding *NewEnd = llvm::unique(R&: Bindings, P: [](auto &LHS, auto &RHS) { |
1059 | return std::tie(LHS.RC, LHS.Space, LHS.LowerBound, LHS.UpperBound, |
1060 | LHS.Name) == std::tie(RHS.RC, RHS.Space, RHS.LowerBound, |
1061 | RHS.UpperBound, RHS.Name); |
1062 | }); |
1063 | if (NewEnd != Bindings.end()) |
1064 | Bindings.erase(CI: NewEnd); |
1065 | |
1066 | // Go over the sorted bindings and build up lists of free register ranges |
1067 | // for each binding type and used spaces. Bindings are sorted by resource |
1068 | // class, space, and lower bound register slot. |
1069 | BindingSpaces *BS = &SRVSpaces; |
1070 | for (const Binding &B : Bindings) { |
1071 | if (BS->RC != B.RC) |
1072 | // move to the next resource class spaces |
1073 | BS = &getBindingSpaces(RC: B.RC); |
1074 | |
1075 | RegisterSpace *S = BS->Spaces.empty() ? &BS->Spaces.emplace_back(Args: B.Space) |
1076 | : &BS->Spaces.back(); |
1077 | assert(S->Space <= B.Space && "bindings not sorted correctly?" ); |
1078 | if (B.Space != S->Space) |
1079 | // add new space |
1080 | S = &BS->Spaces.emplace_back(Args: B.Space); |
1081 | |
1082 | // the space is full - set flag to report overlapping binding later |
1083 | if (S->FreeRanges.empty()) { |
1084 | OverlappingBinding = true; |
1085 | continue; |
1086 | } |
1087 | |
1088 | // adjust the last free range lower bound, split it in two, or remove it |
1089 | BindingRange &LastFreeRange = S->FreeRanges.back(); |
1090 | assert(LastFreeRange.UpperBound == UINT32_MAX); |
1091 | if (LastFreeRange.LowerBound == B.LowerBound) { |
1092 | if (B.UpperBound < UINT32_MAX) |
1093 | LastFreeRange.LowerBound = B.UpperBound + 1; |
1094 | else |
1095 | S->FreeRanges.pop_back(); |
1096 | } else if (LastFreeRange.LowerBound < B.LowerBound) { |
1097 | LastFreeRange.UpperBound = B.LowerBound - 1; |
1098 | if (B.UpperBound < UINT32_MAX) |
1099 | S->FreeRanges.emplace_back(Args: B.UpperBound + 1, UINT32_MAX); |
1100 | } else { |
1101 | OverlappingBinding = true; |
1102 | if (B.UpperBound < UINT32_MAX) |
1103 | LastFreeRange.LowerBound = |
1104 | std::max(a: LastFreeRange.LowerBound, b: B.UpperBound + 1); |
1105 | else |
1106 | S->FreeRanges.pop_back(); |
1107 | } |
1108 | } |
1109 | } |
1110 | |
1111 | // returns std::nulopt if binding could not be found in given space |
1112 | std::optional<uint32_t> |
1113 | DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC, |
1114 | uint32_t Space, int32_t Size) { |
1115 | BindingSpaces &BS = getBindingSpaces(RC); |
1116 | RegisterSpace &RS = BS.getOrInsertSpace(Space); |
1117 | return RS.findAvailableBinding(Size); |
1118 | } |
1119 | |
1120 | DXILResourceBindingInfo::RegisterSpace & |
1121 | DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) { |
1122 | for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) { |
1123 | if (I->Space == Space) |
1124 | return *I; |
1125 | if (I->Space < Space) |
1126 | continue; |
1127 | return *Spaces.insert(I, Elt: Space); |
1128 | } |
1129 | return Spaces.emplace_back(Args&: Space); |
1130 | } |
1131 | |
1132 | std::optional<uint32_t> |
1133 | DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) { |
1134 | assert((Size == -1 || Size > 0) && "invalid size" ); |
1135 | |
1136 | if (FreeRanges.empty()) |
1137 | return std::nullopt; |
1138 | |
1139 | // unbounded array |
1140 | if (Size == -1) { |
1141 | BindingRange &Last = FreeRanges.back(); |
1142 | if (Last.UpperBound != UINT32_MAX) |
1143 | // this space is already occupied by an unbounded array |
1144 | return std::nullopt; |
1145 | uint32_t RegSlot = Last.LowerBound; |
1146 | FreeRanges.pop_back(); |
1147 | return RegSlot; |
1148 | } |
1149 | |
1150 | // single resource or fixed-size array |
1151 | for (BindingRange &R : FreeRanges) { |
1152 | // compare the size as uint64_t to prevent overflow for range (0, |
1153 | // UINT32_MAX) |
1154 | if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size) |
1155 | continue; |
1156 | uint32_t RegSlot = R.LowerBound; |
1157 | // This might create a range where (LowerBound == UpperBound + 1). When |
1158 | // that happens, the next time this function is called the range will |
1159 | // skipped over by the check above (at this point Size is always > 0). |
1160 | R.LowerBound += Size; |
1161 | return RegSlot; |
1162 | } |
1163 | |
1164 | return std::nullopt; |
1165 | } |
1166 | |
1167 | //===----------------------------------------------------------------------===// |
1168 | |
1169 | AnalysisKey DXILResourceTypeAnalysis::Key; |
1170 | AnalysisKey DXILResourceAnalysis::Key; |
1171 | AnalysisKey DXILResourceBindingAnalysis::Key; |
1172 | |
1173 | DXILResourceMap DXILResourceAnalysis::run(Module &M, |
1174 | ModuleAnalysisManager &AM) { |
1175 | DXILResourceMap Data; |
1176 | DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(IR&: M); |
1177 | Data.populate(M, DRTM); |
1178 | return Data; |
1179 | } |
1180 | |
1181 | DXILResourceBindingInfo |
1182 | DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) { |
1183 | DXILResourceBindingInfo Data; |
1184 | DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(IR&: M); |
1185 | Data.populate(M, DRTM); |
1186 | return Data; |
1187 | } |
1188 | |
1189 | PreservedAnalyses DXILResourcePrinterPass::run(Module &M, |
1190 | ModuleAnalysisManager &AM) { |
1191 | DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(IR&: M); |
1192 | DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(IR&: M); |
1193 | |
1194 | DRM.print(OS, DRTM, DL: M.getDataLayout()); |
1195 | return PreservedAnalyses::all(); |
1196 | } |
1197 | |
1198 | void DXILResourceTypeWrapperPass::anchor() {} |
1199 | |
1200 | DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass() |
1201 | : ImmutablePass(ID) {} |
1202 | |
1203 | INITIALIZE_PASS(DXILResourceTypeWrapperPass, "dxil-resource-type" , |
1204 | "DXIL Resource Type Analysis" , false, true) |
1205 | char DXILResourceTypeWrapperPass::ID = 0; |
1206 | |
1207 | ModulePass *llvm::createDXILResourceTypeWrapperPassPass() { |
1208 | return new DXILResourceTypeWrapperPass(); |
1209 | } |
1210 | |
1211 | DXILResourceWrapperPass::DXILResourceWrapperPass() : ModulePass(ID) {} |
1212 | |
1213 | DXILResourceWrapperPass::~DXILResourceWrapperPass() = default; |
1214 | |
1215 | void DXILResourceWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { |
1216 | AU.addRequiredTransitive<DXILResourceTypeWrapperPass>(); |
1217 | AU.setPreservesAll(); |
1218 | } |
1219 | |
1220 | bool DXILResourceWrapperPass::runOnModule(Module &M) { |
1221 | Map.reset(p: new DXILResourceMap()); |
1222 | |
1223 | DRTM = &getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap(); |
1224 | Map->populate(M, DRTM&: *DRTM); |
1225 | |
1226 | return false; |
1227 | } |
1228 | |
1229 | void DXILResourceWrapperPass::releaseMemory() { Map.reset(); } |
1230 | |
1231 | void DXILResourceWrapperPass::print(raw_ostream &OS, const Module *M) const { |
1232 | if (!Map) { |
1233 | OS << "No resource map has been built!\n" ; |
1234 | return; |
1235 | } |
1236 | Map->print(OS, DRTM&: *DRTM, DL: M->getDataLayout()); |
1237 | } |
1238 | |
1239 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
1240 | LLVM_DUMP_METHOD |
1241 | void DXILResourceWrapperPass::dump() const { print(dbgs(), nullptr); } |
1242 | #endif |
1243 | |
1244 | INITIALIZE_PASS(DXILResourceWrapperPass, "dxil-resources" , |
1245 | "DXIL Resources Analysis" , false, true) |
1246 | char DXILResourceWrapperPass::ID = 0; |
1247 | |
1248 | ModulePass *llvm::createDXILResourceWrapperPassPass() { |
1249 | return new DXILResourceWrapperPass(); |
1250 | } |
1251 | |
1252 | DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass() |
1253 | : ModulePass(ID) {} |
1254 | |
1255 | DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default; |
1256 | |
1257 | void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { |
1258 | AU.addRequiredTransitive<DXILResourceTypeWrapperPass>(); |
1259 | AU.setPreservesAll(); |
1260 | } |
1261 | |
1262 | bool DXILResourceBindingWrapperPass::runOnModule(Module &M) { |
1263 | BindingInfo.reset(p: new DXILResourceBindingInfo()); |
1264 | |
1265 | DXILResourceTypeMap &DRTM = |
1266 | getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap(); |
1267 | BindingInfo->populate(M, DRTM); |
1268 | |
1269 | return false; |
1270 | } |
1271 | |
1272 | void DXILResourceBindingWrapperPass::releaseMemory() { BindingInfo.reset(); } |
1273 | |
1274 | INITIALIZE_PASS(DXILResourceBindingWrapperPass, "dxil-resource-binding" , |
1275 | "DXIL Resource Binding Analysis" , false, true) |
1276 | char DXILResourceBindingWrapperPass::ID = 0; |
1277 | |
1278 | ModulePass *llvm::createDXILResourceBindingWrapperPassPass() { |
1279 | return new DXILResourceWrapperPass(); |
1280 | } |
1281 | |