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
29using namespace llvm;
30using namespace dxil;
31
32static 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
46static 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
91static 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
135static 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
173static 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
185static 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
195static 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
222ResourceTypeInfo::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
259static 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
290static 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
297StructType *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
376bool ResourceTypeInfo::isUAV() const { return RC == ResourceClass::UAV; }
377
378bool ResourceTypeInfo::isCBuffer() const {
379 return RC == ResourceClass::CBuffer;
380}
381
382bool ResourceTypeInfo::isSampler() const {
383 return RC == ResourceClass::Sampler;
384}
385
386bool ResourceTypeInfo::isStruct() const {
387 return Kind == ResourceKind::StructuredBuffer;
388}
389
390bool 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
419bool ResourceTypeInfo::isFeedback() const {
420 return Kind == ResourceKind::FeedbackTexture2D ||
421 Kind == ResourceKind::FeedbackTexture2DArray;
422}
423
424bool ResourceTypeInfo::isMultiSample() const {
425 return Kind == ResourceKind::Texture2DMS ||
426 Kind == ResourceKind::Texture2DMSArray;
427}
428
429static 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
460ResourceTypeInfo::UAVInfo ResourceTypeInfo::getUAV() const {
461 assert(isUAV() && "Not a UAV");
462 return {.IsROV: isROV(Kind, Ty: HandleTy)};
463}
464
465uint32_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
477dxil::SamplerType ResourceTypeInfo::getSamplerType() const {
478 assert(isSampler() && "Not a Sampler");
479 return cast<SamplerExtType>(Val: HandleTy)->getSamplerType();
480}
481
482ResourceTypeInfo::StructInfo
483ResourceTypeInfo::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
496static 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
533ResourceTypeInfo::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
544dxil::SamplerFeedbackType ResourceTypeInfo::getFeedbackType() const {
545 assert(isFeedback() && "Not Feedback");
546 return cast<FeedbackTextureExtType>(Val: HandleTy)->getFeedbackType();
547}
548uint32_t ResourceTypeInfo::getMultiSampleCount() const {
549 assert(isMultiSample() && "Not MultiSampled");
550 return cast<MSTextureExtType>(Val: HandleTy)->getSampleCount();
551}
552
553bool ResourceTypeInfo::operator==(const ResourceTypeInfo &RHS) const {
554 return HandleTy == RHS.HandleTy;
555}
556
557bool 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
583void 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
613GlobalVariable *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
621MDTuple *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
690std::pair<uint32_t, uint32_t>
691ResourceInfo::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
738void 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
778bool 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//===----------------------------------------------------------------------===//
786static bool isUpdateCounterIntrinsic(Function &F) {
787 return F.getIntrinsicID() == Intrinsic::dx_resource_updatecounter;
788}
789
790StringRef 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
814void 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
897void 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
936void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) {
937 populateResourceInfos(M, DRTM);
938 populateCounterDirections(M);
939}
940
941void 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
957SmallVector<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
997void 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
1112std::optional<uint32_t>
1113DXILResourceBindingInfo::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
1120DXILResourceBindingInfo::RegisterSpace &
1121DXILResourceBindingInfo::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
1132std::optional<uint32_t>
1133DXILResourceBindingInfo::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
1169AnalysisKey DXILResourceTypeAnalysis::Key;
1170AnalysisKey DXILResourceAnalysis::Key;
1171AnalysisKey DXILResourceBindingAnalysis::Key;
1172
1173DXILResourceMap 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
1181DXILResourceBindingInfo
1182DXILResourceBindingAnalysis::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
1189PreservedAnalyses 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
1198void DXILResourceTypeWrapperPass::anchor() {}
1199
1200DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass()
1201 : ImmutablePass(ID) {}
1202
1203INITIALIZE_PASS(DXILResourceTypeWrapperPass, "dxil-resource-type",
1204 "DXIL Resource Type Analysis", false, true)
1205char DXILResourceTypeWrapperPass::ID = 0;
1206
1207ModulePass *llvm::createDXILResourceTypeWrapperPassPass() {
1208 return new DXILResourceTypeWrapperPass();
1209}
1210
1211DXILResourceWrapperPass::DXILResourceWrapperPass() : ModulePass(ID) {}
1212
1213DXILResourceWrapperPass::~DXILResourceWrapperPass() = default;
1214
1215void DXILResourceWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
1216 AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
1217 AU.setPreservesAll();
1218}
1219
1220bool 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
1229void DXILResourceWrapperPass::releaseMemory() { Map.reset(); }
1230
1231void 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)
1240LLVM_DUMP_METHOD
1241void DXILResourceWrapperPass::dump() const { print(dbgs(), nullptr); }
1242#endif
1243
1244INITIALIZE_PASS(DXILResourceWrapperPass, "dxil-resources",
1245 "DXIL Resources Analysis", false, true)
1246char DXILResourceWrapperPass::ID = 0;
1247
1248ModulePass *llvm::createDXILResourceWrapperPassPass() {
1249 return new DXILResourceWrapperPass();
1250}
1251
1252DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass()
1253 : ModulePass(ID) {}
1254
1255DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default;
1256
1257void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
1258 AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
1259 AU.setPreservesAll();
1260}
1261
1262bool 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
1272void DXILResourceBindingWrapperPass::releaseMemory() { BindingInfo.reset(); }
1273
1274INITIALIZE_PASS(DXILResourceBindingWrapperPass, "dxil-resource-binding",
1275 "DXIL Resource Binding Analysis", false, true)
1276char DXILResourceBindingWrapperPass::ID = 0;
1277
1278ModulePass *llvm::createDXILResourceBindingWrapperPassPass() {
1279 return new DXILResourceWrapperPass();
1280}
1281