xref: /llvm-project/llvm/lib/Analysis/DXILResource.cpp (revision aa07f922103ebe8e78c8da4c754b43af3c129f3e)
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/SmallString.h"
12 #include "llvm/IR/Constants.h"
13 #include "llvm/IR/DerivedTypes.h"
14 #include "llvm/IR/DiagnosticInfo.h"
15 #include "llvm/IR/Instructions.h"
16 #include "llvm/IR/Intrinsics.h"
17 #include "llvm/IR/IntrinsicsDirectX.h"
18 #include "llvm/IR/Metadata.h"
19 #include "llvm/IR/Module.h"
20 #include "llvm/InitializePasses.h"
21 #include "llvm/Support/FormatVariadic.h"
22 
23 #define DEBUG_TYPE "dxil-resource"
24 
25 using namespace llvm;
26 using namespace dxil;
27 
28 static StringRef getResourceClassName(ResourceClass RC) {
29   switch (RC) {
30   case ResourceClass::SRV:
31     return "SRV";
32   case ResourceClass::UAV:
33     return "UAV";
34   case ResourceClass::CBuffer:
35     return "CBuffer";
36   case ResourceClass::Sampler:
37     return "Sampler";
38   }
39   llvm_unreachable("Unhandled ResourceClass");
40 }
41 
42 static StringRef getResourceKindName(ResourceKind RK) {
43   switch (RK) {
44   case ResourceKind::Texture1D:
45     return "Texture1D";
46   case ResourceKind::Texture2D:
47     return "Texture2D";
48   case ResourceKind::Texture2DMS:
49     return "Texture2DMS";
50   case ResourceKind::Texture3D:
51     return "Texture3D";
52   case ResourceKind::TextureCube:
53     return "TextureCube";
54   case ResourceKind::Texture1DArray:
55     return "Texture1DArray";
56   case ResourceKind::Texture2DArray:
57     return "Texture2DArray";
58   case ResourceKind::Texture2DMSArray:
59     return "Texture2DMSArray";
60   case ResourceKind::TextureCubeArray:
61     return "TextureCubeArray";
62   case ResourceKind::TypedBuffer:
63     return "TypedBuffer";
64   case ResourceKind::RawBuffer:
65     return "RawBuffer";
66   case ResourceKind::StructuredBuffer:
67     return "StructuredBuffer";
68   case ResourceKind::CBuffer:
69     return "CBuffer";
70   case ResourceKind::Sampler:
71     return "Sampler";
72   case ResourceKind::TBuffer:
73     return "TBuffer";
74   case ResourceKind::RTAccelerationStructure:
75     return "RTAccelerationStructure";
76   case ResourceKind::FeedbackTexture2D:
77     return "FeedbackTexture2D";
78   case ResourceKind::FeedbackTexture2DArray:
79     return "FeedbackTexture2DArray";
80   case ResourceKind::NumEntries:
81   case ResourceKind::Invalid:
82     return "<invalid>";
83   }
84   llvm_unreachable("Unhandled ResourceKind");
85 }
86 
87 static StringRef getElementTypeName(ElementType ET) {
88   switch (ET) {
89   case ElementType::I1:
90     return "i1";
91   case ElementType::I16:
92     return "i16";
93   case ElementType::U16:
94     return "u16";
95   case ElementType::I32:
96     return "i32";
97   case ElementType::U32:
98     return "u32";
99   case ElementType::I64:
100     return "i64";
101   case ElementType::U64:
102     return "u64";
103   case ElementType::F16:
104     return "f16";
105   case ElementType::F32:
106     return "f32";
107   case ElementType::F64:
108     return "f64";
109   case ElementType::SNormF16:
110     return "snorm_f16";
111   case ElementType::UNormF16:
112     return "unorm_f16";
113   case ElementType::SNormF32:
114     return "snorm_f32";
115   case ElementType::UNormF32:
116     return "unorm_f32";
117   case ElementType::SNormF64:
118     return "snorm_f64";
119   case ElementType::UNormF64:
120     return "unorm_f64";
121   case ElementType::PackedS8x32:
122     return "p32i8";
123   case ElementType::PackedU8x32:
124     return "p32u8";
125   case ElementType::Invalid:
126     return "<invalid>";
127   }
128   llvm_unreachable("Unhandled ElementType");
129 }
130 
131 static StringRef getSamplerTypeName(SamplerType ST) {
132   switch (ST) {
133   case SamplerType::Default:
134     return "Default";
135   case SamplerType::Comparison:
136     return "Comparison";
137   case SamplerType::Mono:
138     return "Mono";
139   }
140   llvm_unreachable("Unhandled SamplerType");
141 }
142 
143 static StringRef getSamplerFeedbackTypeName(SamplerFeedbackType SFT) {
144   switch (SFT) {
145   case SamplerFeedbackType::MinMip:
146     return "MinMip";
147   case SamplerFeedbackType::MipRegionUsed:
148     return "MipRegionUsed";
149   }
150   llvm_unreachable("Unhandled SamplerFeedbackType");
151 }
152 
153 static dxil::ElementType toDXILElementType(Type *Ty, bool IsSigned) {
154   // TODO: Handle unorm, snorm, and packed.
155   Ty = Ty->getScalarType();
156 
157   if (Ty->isIntegerTy()) {
158     switch (Ty->getIntegerBitWidth()) {
159     case 16:
160       return IsSigned ? ElementType::I16 : ElementType::U16;
161     case 32:
162       return IsSigned ? ElementType::I32 : ElementType::U32;
163     case 64:
164       return IsSigned ? ElementType::I64 : ElementType::U64;
165     case 1:
166     default:
167       return ElementType::Invalid;
168     }
169   } else if (Ty->isFloatTy()) {
170     return ElementType::F32;
171   } else if (Ty->isDoubleTy()) {
172     return ElementType::F64;
173   } else if (Ty->isHalfTy()) {
174     return ElementType::F16;
175   }
176 
177   return ElementType::Invalid;
178 }
179 
180 ResourceTypeInfo::ResourceTypeInfo(TargetExtType *HandleTy,
181                                    const dxil::ResourceClass RC_,
182                                    const dxil::ResourceKind Kind_,
183                                    bool GloballyCoherent, bool HasCounter)
184     : HandleTy(HandleTy), GloballyCoherent(GloballyCoherent),
185       HasCounter(HasCounter) {
186   // If we're provided a resource class and kind, trust them.
187   if (Kind_ != dxil::ResourceKind::Invalid) {
188     RC = RC_;
189     Kind = Kind_;
190     return;
191   }
192 
193   if (auto *Ty = dyn_cast<RawBufferExtType>(HandleTy)) {
194     RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
195     Kind = Ty->isStructured() ? ResourceKind::StructuredBuffer
196                               : ResourceKind::RawBuffer;
197   } else if (auto *Ty = dyn_cast<TypedBufferExtType>(HandleTy)) {
198     RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
199     Kind = ResourceKind::TypedBuffer;
200   } else if (auto *Ty = dyn_cast<TextureExtType>(HandleTy)) {
201     RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
202     Kind = Ty->getDimension();
203   } else if (auto *Ty = dyn_cast<MSTextureExtType>(HandleTy)) {
204     RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
205     Kind = Ty->getDimension();
206   } else if (auto *Ty = dyn_cast<FeedbackTextureExtType>(HandleTy)) {
207     RC = ResourceClass::UAV;
208     Kind = Ty->getDimension();
209   } else if (isa<CBufferExtType>(HandleTy)) {
210     RC = ResourceClass::CBuffer;
211     Kind = ResourceKind::CBuffer;
212   } else if (isa<SamplerExtType>(HandleTy)) {
213     RC = ResourceClass::Sampler;
214     Kind = ResourceKind::Sampler;
215   } else
216     llvm_unreachable("Unknown handle type");
217 }
218 
219 static void formatTypeName(SmallString<64> &Dest, StringRef Name,
220                            bool isWriteable, bool isROV) {
221   Dest = isWriteable ? (isROV ? "RasterizerOrdered" : "RW") : "";
222   Dest += Name;
223 }
224 
225 StructType *ResourceTypeInfo::createElementStruct() {
226   SmallString<64> TypeName;
227 
228   switch (Kind) {
229   case ResourceKind::Texture1D:
230   case ResourceKind::Texture2D:
231   case ResourceKind::Texture3D:
232   case ResourceKind::TextureCube:
233   case ResourceKind::Texture1DArray:
234   case ResourceKind::Texture2DArray:
235   case ResourceKind::TextureCubeArray: {
236     auto *RTy = cast<TextureExtType>(HandleTy);
237     formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),
238                    RTy->isROV());
239     return StructType::create(RTy->getResourceType(), TypeName);
240   }
241   case ResourceKind::Texture2DMS:
242   case ResourceKind::Texture2DMSArray: {
243     auto *RTy = cast<MSTextureExtType>(HandleTy);
244     formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),
245                    /*IsROV=*/false);
246     return StructType::create(RTy->getResourceType(), TypeName);
247   }
248   case ResourceKind::TypedBuffer: {
249     auto *RTy = cast<TypedBufferExtType>(HandleTy);
250     formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),
251                    RTy->isROV());
252     return StructType::create(RTy->getResourceType(), TypeName);
253   }
254   case ResourceKind::RawBuffer: {
255     auto *RTy = cast<RawBufferExtType>(HandleTy);
256     formatTypeName(TypeName, "ByteAddressBuffer", RTy->isWriteable(),
257                    RTy->isROV());
258     return StructType::create(Type::getInt32Ty(HandleTy->getContext()),
259                               TypeName);
260   }
261   case ResourceKind::StructuredBuffer: {
262     auto *RTy = cast<RawBufferExtType>(HandleTy);
263     formatTypeName(TypeName, "StructuredBuffer", RTy->isWriteable(),
264                    RTy->isROV());
265     return StructType::create(RTy->getResourceType(), TypeName);
266   }
267   case ResourceKind::FeedbackTexture2D:
268   case ResourceKind::FeedbackTexture2DArray: {
269     auto *RTy = cast<FeedbackTextureExtType>(HandleTy);
270     TypeName = formatv("{0}<{1}>", getResourceKindName(Kind),
271                        llvm::to_underlying(RTy->getFeedbackType()));
272     return StructType::create(Type::getInt32Ty(HandleTy->getContext()),
273                               TypeName);
274   }
275   case ResourceKind::CBuffer:
276     return StructType::create(HandleTy->getContext(), "cbuffer");
277   case ResourceKind::Sampler: {
278     auto *RTy = cast<SamplerExtType>(HandleTy);
279     TypeName = formatv("SamplerState<{0}>",
280                        llvm::to_underlying(RTy->getSamplerType()));
281     return StructType::create(Type::getInt32Ty(HandleTy->getContext()),
282                               TypeName);
283   }
284   case ResourceKind::TBuffer:
285   case ResourceKind::RTAccelerationStructure:
286     llvm_unreachable("Unhandled resource kind");
287   case ResourceKind::Invalid:
288   case ResourceKind::NumEntries:
289     llvm_unreachable("Invalid resource kind");
290   }
291   llvm_unreachable("Unhandled ResourceKind enum");
292 }
293 
294 bool ResourceTypeInfo::isUAV() const { return RC == ResourceClass::UAV; }
295 
296 bool ResourceTypeInfo::isCBuffer() const {
297   return RC == ResourceClass::CBuffer;
298 }
299 
300 bool ResourceTypeInfo::isSampler() const {
301   return RC == ResourceClass::Sampler;
302 }
303 
304 bool ResourceTypeInfo::isStruct() const {
305   return Kind == ResourceKind::StructuredBuffer;
306 }
307 
308 bool ResourceTypeInfo::isTyped() const {
309   switch (Kind) {
310   case ResourceKind::Texture1D:
311   case ResourceKind::Texture2D:
312   case ResourceKind::Texture2DMS:
313   case ResourceKind::Texture3D:
314   case ResourceKind::TextureCube:
315   case ResourceKind::Texture1DArray:
316   case ResourceKind::Texture2DArray:
317   case ResourceKind::Texture2DMSArray:
318   case ResourceKind::TextureCubeArray:
319   case ResourceKind::TypedBuffer:
320     return true;
321   case ResourceKind::RawBuffer:
322   case ResourceKind::StructuredBuffer:
323   case ResourceKind::FeedbackTexture2D:
324   case ResourceKind::FeedbackTexture2DArray:
325   case ResourceKind::CBuffer:
326   case ResourceKind::Sampler:
327   case ResourceKind::TBuffer:
328   case ResourceKind::RTAccelerationStructure:
329     return false;
330   case ResourceKind::Invalid:
331   case ResourceKind::NumEntries:
332     llvm_unreachable("Invalid resource kind");
333   }
334   llvm_unreachable("Unhandled ResourceKind enum");
335 }
336 
337 bool ResourceTypeInfo::isFeedback() const {
338   return Kind == ResourceKind::FeedbackTexture2D ||
339          Kind == ResourceKind::FeedbackTexture2DArray;
340 }
341 
342 bool ResourceTypeInfo::isMultiSample() const {
343   return Kind == ResourceKind::Texture2DMS ||
344          Kind == ResourceKind::Texture2DMSArray;
345 }
346 
347 static bool isROV(dxil::ResourceKind Kind, TargetExtType *Ty) {
348   switch (Kind) {
349   case ResourceKind::Texture1D:
350   case ResourceKind::Texture2D:
351   case ResourceKind::Texture3D:
352   case ResourceKind::TextureCube:
353   case ResourceKind::Texture1DArray:
354   case ResourceKind::Texture2DArray:
355   case ResourceKind::TextureCubeArray:
356     return cast<TextureExtType>(Ty)->isROV();
357   case ResourceKind::TypedBuffer:
358     return cast<TypedBufferExtType>(Ty)->isROV();
359   case ResourceKind::RawBuffer:
360   case ResourceKind::StructuredBuffer:
361     return cast<RawBufferExtType>(Ty)->isROV();
362   case ResourceKind::Texture2DMS:
363   case ResourceKind::Texture2DMSArray:
364   case ResourceKind::FeedbackTexture2D:
365   case ResourceKind::FeedbackTexture2DArray:
366     return false;
367   case ResourceKind::CBuffer:
368   case ResourceKind::Sampler:
369   case ResourceKind::TBuffer:
370   case ResourceKind::RTAccelerationStructure:
371   case ResourceKind::Invalid:
372   case ResourceKind::NumEntries:
373     llvm_unreachable("Resource cannot be ROV");
374   }
375   llvm_unreachable("Unhandled ResourceKind enum");
376 }
377 
378 ResourceTypeInfo::UAVInfo ResourceTypeInfo::getUAV() const {
379   assert(isUAV() && "Not a UAV");
380   return {GloballyCoherent, HasCounter, isROV(Kind, HandleTy)};
381 }
382 
383 uint32_t ResourceTypeInfo::getCBufferSize(const DataLayout &DL) const {
384   assert(isCBuffer() && "Not a CBuffer");
385   return cast<CBufferExtType>(HandleTy)->getCBufferSize();
386 }
387 
388 dxil::SamplerType ResourceTypeInfo::getSamplerType() const {
389   assert(isSampler() && "Not a Sampler");
390   return cast<SamplerExtType>(HandleTy)->getSamplerType();
391 }
392 
393 ResourceTypeInfo::StructInfo
394 ResourceTypeInfo::getStruct(const DataLayout &DL) const {
395   assert(isStruct() && "Not a Struct");
396 
397   Type *ElTy = cast<RawBufferExtType>(HandleTy)->getResourceType();
398 
399   uint32_t Stride = DL.getTypeAllocSize(ElTy);
400   MaybeAlign Alignment;
401   if (auto *STy = dyn_cast<StructType>(ElTy))
402     Alignment = DL.getStructLayout(STy)->getAlignment();
403   uint32_t AlignLog2 = Alignment ? Log2(*Alignment) : 0;
404   return {Stride, AlignLog2};
405 }
406 
407 static std::pair<Type *, bool> getTypedElementType(dxil::ResourceKind Kind,
408                                                    TargetExtType *Ty) {
409   switch (Kind) {
410   case ResourceKind::Texture1D:
411   case ResourceKind::Texture2D:
412   case ResourceKind::Texture3D:
413   case ResourceKind::TextureCube:
414   case ResourceKind::Texture1DArray:
415   case ResourceKind::Texture2DArray:
416   case ResourceKind::TextureCubeArray: {
417     auto *RTy = cast<TextureExtType>(Ty);
418     return {RTy->getResourceType(), RTy->isSigned()};
419   }
420   case ResourceKind::Texture2DMS:
421   case ResourceKind::Texture2DMSArray: {
422     auto *RTy = cast<MSTextureExtType>(Ty);
423     return {RTy->getResourceType(), RTy->isSigned()};
424   }
425   case ResourceKind::TypedBuffer: {
426     auto *RTy = cast<TypedBufferExtType>(Ty);
427     return {RTy->getResourceType(), RTy->isSigned()};
428   }
429   case ResourceKind::RawBuffer:
430   case ResourceKind::StructuredBuffer:
431   case ResourceKind::FeedbackTexture2D:
432   case ResourceKind::FeedbackTexture2DArray:
433   case ResourceKind::CBuffer:
434   case ResourceKind::Sampler:
435   case ResourceKind::TBuffer:
436   case ResourceKind::RTAccelerationStructure:
437   case ResourceKind::Invalid:
438   case ResourceKind::NumEntries:
439     llvm_unreachable("Resource is not typed");
440   }
441   llvm_unreachable("Unhandled ResourceKind enum");
442 }
443 
444 ResourceTypeInfo::TypedInfo ResourceTypeInfo::getTyped() const {
445   assert(isTyped() && "Not typed");
446 
447   auto [ElTy, IsSigned] = getTypedElementType(Kind, HandleTy);
448   dxil::ElementType ET = toDXILElementType(ElTy, IsSigned);
449   uint32_t Count = 1;
450   if (auto *VTy = dyn_cast<FixedVectorType>(ElTy))
451     Count = VTy->getNumElements();
452   return {ET, Count};
453 }
454 
455 dxil::SamplerFeedbackType ResourceTypeInfo::getFeedbackType() const {
456   assert(isFeedback() && "Not Feedback");
457   return cast<FeedbackTextureExtType>(HandleTy)->getFeedbackType();
458 }
459 uint32_t ResourceTypeInfo::getMultiSampleCount() const {
460   assert(isMultiSample() && "Not MultiSampled");
461   return cast<MSTextureExtType>(HandleTy)->getSampleCount();
462 }
463 
464 bool ResourceTypeInfo::operator==(const ResourceTypeInfo &RHS) const {
465   return std::tie(HandleTy, GloballyCoherent, HasCounter) ==
466          std::tie(RHS.HandleTy, RHS.GloballyCoherent, RHS.HasCounter);
467 }
468 
469 bool ResourceTypeInfo::operator<(const ResourceTypeInfo &RHS) const {
470   // An empty datalayout is sufficient for sorting purposes.
471   DataLayout DummyDL;
472   if (std::tie(RC, Kind) < std::tie(RHS.RC, RHS.Kind))
473     return true;
474   if (isCBuffer() && RHS.isCBuffer() &&
475       getCBufferSize(DummyDL) < RHS.getCBufferSize(DummyDL))
476     return true;
477   if (isSampler() && RHS.isSampler() && getSamplerType() < RHS.getSamplerType())
478     return true;
479   if (isUAV() && RHS.isUAV() && getUAV() < RHS.getUAV())
480     return true;
481   if (isStruct() && RHS.isStruct() &&
482       getStruct(DummyDL) < RHS.getStruct(DummyDL))
483     return true;
484   if (isFeedback() && RHS.isFeedback() &&
485       getFeedbackType() < RHS.getFeedbackType())
486     return true;
487   if (isTyped() && RHS.isTyped() && getTyped() < RHS.getTyped())
488     return true;
489   if (isMultiSample() && RHS.isMultiSample() &&
490       getMultiSampleCount() < RHS.getMultiSampleCount())
491     return true;
492   return false;
493 }
494 
495 void ResourceTypeInfo::print(raw_ostream &OS, const DataLayout &DL) const {
496   OS << "  Class: " << getResourceClassName(RC) << "\n"
497      << "  Kind: " << getResourceKindName(Kind) << "\n";
498 
499   if (isCBuffer()) {
500     OS << "  CBuffer size: " << getCBufferSize(DL) << "\n";
501   } else if (isSampler()) {
502     OS << "  Sampler Type: " << getSamplerTypeName(getSamplerType()) << "\n";
503   } else {
504     if (isUAV()) {
505       UAVInfo UAVFlags = getUAV();
506       OS << "  Globally Coherent: " << UAVFlags.GloballyCoherent << "\n"
507          << "  HasCounter: " << UAVFlags.HasCounter << "\n"
508          << "  IsROV: " << UAVFlags.IsROV << "\n";
509     }
510     if (isMultiSample())
511       OS << "  Sample Count: " << getMultiSampleCount() << "\n";
512 
513     if (isStruct()) {
514       StructInfo Struct = getStruct(DL);
515       OS << "  Buffer Stride: " << Struct.Stride << "\n";
516       OS << "  Alignment: " << Struct.AlignLog2 << "\n";
517     } else if (isTyped()) {
518       TypedInfo Typed = getTyped();
519       OS << "  Element Type: " << getElementTypeName(Typed.ElementTy) << "\n"
520          << "  Element Count: " << Typed.ElementCount << "\n";
521     } else if (isFeedback())
522       OS << "  Feedback Type: " << getSamplerFeedbackTypeName(getFeedbackType())
523          << "\n";
524   }
525 }
526 
527 GlobalVariable *ResourceBindingInfo::createSymbol(Module &M, StructType *Ty,
528                                                   StringRef Name) {
529   assert(!Symbol && "Symbol has already been created");
530   Symbol = new GlobalVariable(M, Ty, /*isConstant=*/true,
531                               GlobalValue::ExternalLinkage,
532                               /*Initializer=*/nullptr, Name);
533   return Symbol;
534 }
535 
536 MDTuple *ResourceBindingInfo::getAsMetadata(Module &M,
537                                             dxil::ResourceTypeInfo &RTI) const {
538   LLVMContext &Ctx = M.getContext();
539   const DataLayout &DL = M.getDataLayout();
540 
541   SmallVector<Metadata *, 11> MDVals;
542 
543   Type *I32Ty = Type::getInt32Ty(Ctx);
544   Type *I1Ty = Type::getInt1Ty(Ctx);
545   auto getIntMD = [&I32Ty](uint32_t V) {
546     return ConstantAsMetadata::get(
547         Constant::getIntegerValue(I32Ty, APInt(32, V)));
548   };
549   auto getBoolMD = [&I1Ty](uint32_t V) {
550     return ConstantAsMetadata::get(
551         Constant::getIntegerValue(I1Ty, APInt(1, V)));
552   };
553 
554   MDVals.push_back(getIntMD(Binding.RecordID));
555   assert(Symbol && "Cannot yet create useful resource metadata without symbol");
556   MDVals.push_back(ValueAsMetadata::get(Symbol));
557   MDVals.push_back(MDString::get(Ctx, Symbol->getName()));
558   MDVals.push_back(getIntMD(Binding.Space));
559   MDVals.push_back(getIntMD(Binding.LowerBound));
560   MDVals.push_back(getIntMD(Binding.Size));
561 
562   if (RTI.isCBuffer()) {
563     MDVals.push_back(getIntMD(RTI.getCBufferSize(DL)));
564     MDVals.push_back(nullptr);
565   } else if (RTI.isSampler()) {
566     MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getSamplerType())));
567     MDVals.push_back(nullptr);
568   } else {
569     MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getResourceKind())));
570 
571     if (RTI.isUAV()) {
572       ResourceTypeInfo::UAVInfo UAVFlags = RTI.getUAV();
573       MDVals.push_back(getBoolMD(UAVFlags.GloballyCoherent));
574       MDVals.push_back(getBoolMD(UAVFlags.HasCounter));
575       MDVals.push_back(getBoolMD(UAVFlags.IsROV));
576     } else {
577       // All SRVs include sample count in the metadata, but it's only meaningful
578       // for multi-sampled textured. Also, UAVs can be multisampled in SM6.7+,
579       // but this just isn't reflected in the metadata at all.
580       uint32_t SampleCount =
581           RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0;
582       MDVals.push_back(getIntMD(SampleCount));
583     }
584 
585     // Further properties are attached to a metadata list of tag-value pairs.
586     SmallVector<Metadata *> Tags;
587     if (RTI.isStruct()) {
588       Tags.push_back(
589           getIntMD(llvm::to_underlying(ExtPropTags::StructuredBufferStride)));
590       Tags.push_back(getIntMD(RTI.getStruct(DL).Stride));
591     } else if (RTI.isTyped()) {
592       Tags.push_back(getIntMD(llvm::to_underlying(ExtPropTags::ElementType)));
593       Tags.push_back(getIntMD(llvm::to_underlying(RTI.getTyped().ElementTy)));
594     } else if (RTI.isFeedback()) {
595       Tags.push_back(
596           getIntMD(llvm::to_underlying(ExtPropTags::SamplerFeedbackKind)));
597       Tags.push_back(getIntMD(llvm::to_underlying(RTI.getFeedbackType())));
598     }
599     MDVals.push_back(Tags.empty() ? nullptr : MDNode::get(Ctx, Tags));
600   }
601 
602   return MDNode::get(Ctx, MDVals);
603 }
604 
605 std::pair<uint32_t, uint32_t>
606 ResourceBindingInfo::getAnnotateProps(Module &M,
607                                       dxil::ResourceTypeInfo &RTI) const {
608   const DataLayout &DL = M.getDataLayout();
609 
610   uint32_t ResourceKind = llvm::to_underlying(RTI.getResourceKind());
611   uint32_t AlignLog2 = RTI.isStruct() ? RTI.getStruct(DL).AlignLog2 : 0;
612   bool IsUAV = RTI.isUAV();
613   ResourceTypeInfo::UAVInfo UAVFlags =
614       IsUAV ? RTI.getUAV() : ResourceTypeInfo::UAVInfo{};
615   bool IsROV = IsUAV && UAVFlags.IsROV;
616   bool IsGloballyCoherent = IsUAV && UAVFlags.GloballyCoherent;
617   uint8_t SamplerCmpOrHasCounter = 0;
618   if (IsUAV)
619     SamplerCmpOrHasCounter = UAVFlags.HasCounter;
620   else if (RTI.isSampler())
621     SamplerCmpOrHasCounter = RTI.getSamplerType() == SamplerType::Comparison;
622 
623   // TODO: Document this format. Currently the only reference is the
624   // implementation of dxc's DxilResourceProperties struct.
625   uint32_t Word0 = 0;
626   Word0 |= ResourceKind & 0xFF;
627   Word0 |= (AlignLog2 & 0xF) << 8;
628   Word0 |= (IsUAV & 1) << 12;
629   Word0 |= (IsROV & 1) << 13;
630   Word0 |= (IsGloballyCoherent & 1) << 14;
631   Word0 |= (SamplerCmpOrHasCounter & 1) << 15;
632 
633   uint32_t Word1 = 0;
634   if (RTI.isStruct())
635     Word1 = RTI.getStruct(DL).Stride;
636   else if (RTI.isCBuffer())
637     Word1 = RTI.getCBufferSize(DL);
638   else if (RTI.isFeedback())
639     Word1 = llvm::to_underlying(RTI.getFeedbackType());
640   else if (RTI.isTyped()) {
641     ResourceTypeInfo::TypedInfo Typed = RTI.getTyped();
642     uint32_t CompType = llvm::to_underlying(Typed.ElementTy);
643     uint32_t CompCount = Typed.ElementCount;
644     uint32_t SampleCount = RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0;
645 
646     Word1 |= (CompType & 0xFF) << 0;
647     Word1 |= (CompCount & 0xFF) << 8;
648     Word1 |= (SampleCount & 0xFF) << 16;
649   }
650 
651   return {Word0, Word1};
652 }
653 
654 void ResourceBindingInfo::print(raw_ostream &OS, dxil::ResourceTypeInfo &RTI,
655                                 const DataLayout &DL) const {
656   if (Symbol) {
657     OS << "  Symbol: ";
658     Symbol->printAsOperand(OS);
659     OS << "\n";
660   }
661 
662   OS << "  Binding:\n"
663      << "    Record ID: " << Binding.RecordID << "\n"
664      << "    Space: " << Binding.Space << "\n"
665      << "    Lower Bound: " << Binding.LowerBound << "\n"
666      << "    Size: " << Binding.Size << "\n";
667 
668   RTI.print(OS, DL);
669 }
670 
671 //===----------------------------------------------------------------------===//
672 
673 bool DXILResourceTypeMap::invalidate(Module &M, const PreservedAnalyses &PA,
674                                      ModuleAnalysisManager::Invalidator &Inv) {
675   // Passes that introduce resource types must explicitly invalidate this pass.
676   auto PAC = PA.getChecker<DXILResourceTypeAnalysis>();
677   return !PAC.preservedWhenStateless();
678 }
679 
680 //===----------------------------------------------------------------------===//
681 
682 void DXILBindingMap::populate(Module &M, DXILResourceTypeMap &DRTM) {
683   SmallVector<std::tuple<CallInst *, ResourceBindingInfo, ResourceTypeInfo>>
684       CIToInfos;
685 
686   for (Function &F : M.functions()) {
687     if (!F.isDeclaration())
688       continue;
689     LLVM_DEBUG(dbgs() << "Function: " << F.getName() << "\n");
690     Intrinsic::ID ID = F.getIntrinsicID();
691     switch (ID) {
692     default:
693       continue;
694     case Intrinsic::dx_resource_handlefrombinding: {
695       auto *HandleTy = cast<TargetExtType>(F.getReturnType());
696       ResourceTypeInfo &RTI = DRTM[HandleTy];
697 
698       for (User *U : F.users())
699         if (CallInst *CI = dyn_cast<CallInst>(U)) {
700           LLVM_DEBUG(dbgs() << "  Visiting: " << *U << "\n");
701           uint32_t Space =
702               cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
703           uint32_t LowerBound =
704               cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();
705           uint32_t Size =
706               cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
707           ResourceBindingInfo RBI = ResourceBindingInfo{
708               /*RecordID=*/0, Space, LowerBound, Size, HandleTy};
709 
710           CIToInfos.emplace_back(CI, RBI, RTI);
711         }
712 
713       break;
714     }
715     }
716   }
717 
718   llvm::stable_sort(CIToInfos, [](auto &LHS, auto &RHS) {
719     const auto &[LCI, LRBI, LRTI] = LHS;
720     const auto &[RCI, RRBI, RRTI] = RHS;
721     // Sort by resource class first for grouping purposes, and then by the
722     // binding and type so we can remove duplicates.
723     ResourceClass LRC = LRTI.getResourceClass();
724     ResourceClass RRC = RRTI.getResourceClass();
725 
726     return std::tie(LRC, LRBI, LRTI) < std::tie(RRC, RRBI, RRTI);
727   });
728   for (auto [CI, RBI, RTI] : CIToInfos) {
729     if (Infos.empty() || RBI != Infos.back())
730       Infos.push_back(RBI);
731     CallMap[CI] = Infos.size() - 1;
732   }
733 
734   unsigned Size = Infos.size();
735   // In DXC, Record ID is unique per resource type. Match that.
736   FirstUAV = FirstCBuffer = FirstSampler = Size;
737   uint32_t NextID = 0;
738   for (unsigned I = 0, E = Size; I != E; ++I) {
739     ResourceBindingInfo &RBI = Infos[I];
740     ResourceTypeInfo &RTI = DRTM[RBI.getHandleTy()];
741     if (RTI.isUAV() && FirstUAV == Size) {
742       FirstUAV = I;
743       NextID = 0;
744     } else if (RTI.isCBuffer() && FirstCBuffer == Size) {
745       FirstCBuffer = I;
746       NextID = 0;
747     } else if (RTI.isSampler() && FirstSampler == Size) {
748       FirstSampler = I;
749       NextID = 0;
750     }
751 
752     // Adjust the resource binding to use the next ID.
753     RBI.setBindingID(NextID++);
754   }
755 }
756 
757 void DXILBindingMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM,
758                            const DataLayout &DL) const {
759   for (unsigned I = 0, E = Infos.size(); I != E; ++I) {
760     OS << "Binding " << I << ":\n";
761     const dxil::ResourceBindingInfo &RBI = Infos[I];
762     RBI.print(OS, DRTM[RBI.getHandleTy()], DL);
763     OS << "\n";
764   }
765 
766   for (const auto &[CI, Index] : CallMap) {
767     OS << "Call bound to " << Index << ":";
768     CI->print(OS);
769     OS << "\n";
770   }
771 }
772 
773 //===----------------------------------------------------------------------===//
774 
775 AnalysisKey DXILResourceTypeAnalysis::Key;
776 AnalysisKey DXILResourceBindingAnalysis::Key;
777 
778 DXILBindingMap DXILResourceBindingAnalysis::run(Module &M,
779                                                 ModuleAnalysisManager &AM) {
780   DXILBindingMap Data;
781   DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
782   Data.populate(M, DRTM);
783   return Data;
784 }
785 
786 PreservedAnalyses
787 DXILResourceBindingPrinterPass::run(Module &M, ModuleAnalysisManager &AM) {
788   DXILBindingMap &DBM = AM.getResult<DXILResourceBindingAnalysis>(M);
789   DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
790 
791   DBM.print(OS, DRTM, M.getDataLayout());
792   return PreservedAnalyses::all();
793 }
794 
795 void DXILResourceTypeWrapperPass::anchor() {}
796 
797 DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass() : ImmutablePass(ID) {
798   initializeDXILResourceTypeWrapperPassPass(*PassRegistry::getPassRegistry());
799 }
800 
801 INITIALIZE_PASS(DXILResourceTypeWrapperPass, "dxil-resource-type",
802                 "DXIL Resource Type Analysis", false, true)
803 char DXILResourceTypeWrapperPass::ID = 0;
804 
805 ModulePass *llvm::createDXILResourceTypeWrapperPassPass() {
806   return new DXILResourceTypeWrapperPass();
807 }
808 
809 DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass()
810     : ModulePass(ID) {
811   initializeDXILResourceBindingWrapperPassPass(
812       *PassRegistry::getPassRegistry());
813 }
814 
815 DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default;
816 
817 void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
818   AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
819   AU.setPreservesAll();
820 }
821 
822 bool DXILResourceBindingWrapperPass::runOnModule(Module &M) {
823   Map.reset(new DXILBindingMap());
824 
825   DRTM = &getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
826   Map->populate(M, *DRTM);
827 
828   return false;
829 }
830 
831 void DXILResourceBindingWrapperPass::releaseMemory() { Map.reset(); }
832 
833 void DXILResourceBindingWrapperPass::print(raw_ostream &OS,
834                                            const Module *M) const {
835   if (!Map) {
836     OS << "No resource map has been built!\n";
837     return;
838   }
839   Map->print(OS, *DRTM, M->getDataLayout());
840 }
841 
842 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
843 LLVM_DUMP_METHOD
844 void DXILResourceBindingWrapperPass::dump() const { print(dbgs(), nullptr); }
845 #endif
846 
847 INITIALIZE_PASS(DXILResourceBindingWrapperPass, "dxil-resource-binding",
848                 "DXIL Resource Binding Analysis", false, true)
849 char DXILResourceBindingWrapperPass::ID = 0;
850 
851 ModulePass *llvm::createDXILResourceBindingWrapperPassPass() {
852   return new DXILResourceBindingWrapperPass();
853 }
854