xref: /freebsd/contrib/llvm-project/llvm/lib/Analysis/DXILResource.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
getResourceClassName(ResourceClass RC)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 
getResourceKindName(ResourceKind RK)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 
getElementTypeName(ElementType ET)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 
getElementTypeNameForTemplate(ElementType ET)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 
getSamplerTypeName(SamplerType ST)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 
getSamplerFeedbackTypeName(SamplerFeedbackType SFT)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 
toDXILElementType(Type * Ty,bool IsSigned)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 
ResourceTypeInfo(TargetExtType * HandleTy,const dxil::ResourceClass RC_,const dxil::ResourceKind Kind_)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>(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>(HandleTy)) {
238     RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
239     Kind = ResourceKind::TypedBuffer;
240   } else if (auto *Ty = dyn_cast<TextureExtType>(HandleTy)) {
241     RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
242     Kind = Ty->getDimension();
243   } else if (auto *Ty = dyn_cast<MSTextureExtType>(HandleTy)) {
244     RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;
245     Kind = Ty->getDimension();
246   } else if (auto *Ty = dyn_cast<FeedbackTextureExtType>(HandleTy)) {
247     RC = ResourceClass::UAV;
248     Kind = Ty->getDimension();
249   } else if (isa<CBufferExtType>(HandleTy)) {
250     RC = ResourceClass::CBuffer;
251     Kind = ResourceKind::CBuffer;
252   } else if (isa<SamplerExtType>(HandleTy)) {
253     RC = ResourceClass::Sampler;
254     Kind = ResourceKind::Sampler;
255   } else
256     llvm_unreachable("Unknown handle type");
257 }
258 
formatTypeName(SmallString<64> & Dest,StringRef Name,bool IsWriteable,bool IsROV,Type * ContainedType=nullptr,bool IsSigned=true)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(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>(ContainedType);
279     if (!ST->hasName())
280       return;
281     ElementName = ST->getStructName();
282   }
283 
284   DestStream << "<" << ElementName;
285   if (const FixedVectorType *VTy = dyn_cast<FixedVectorType>(ContainedType))
286     DestStream << VTy->getNumElements();
287   DestStream << ">";
288 }
289 
getOrCreateElementStruct(Type * ElemType,StringRef Name)290 static StructType *getOrCreateElementStruct(Type *ElemType, StringRef Name) {
291   StructType *Ty = StructType::getTypeByName(ElemType->getContext(), Name);
292   if (Ty && Ty->getNumElements() == 1 && Ty->getElementType(0) == ElemType)
293     return Ty;
294   return StructType::create(ElemType, Name);
295 }
296 
createElementStruct(StringRef CBufferName)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>(HandleTy);
309     formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),
310                    RTy->isROV(), RTy->getResourceType(), RTy->isSigned());
311     return getOrCreateElementStruct(RTy->getResourceType(), TypeName);
312   }
313   case ResourceKind::Texture2DMS:
314   case ResourceKind::Texture2DMSArray: {
315     auto *RTy = cast<MSTextureExtType>(HandleTy);
316     formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),
317                    /*IsROV=*/false, RTy->getResourceType(), RTy->isSigned());
318     return getOrCreateElementStruct(RTy->getResourceType(), TypeName);
319   }
320   case ResourceKind::TypedBuffer: {
321     auto *RTy = cast<TypedBufferExtType>(HandleTy);
322     formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),
323                    RTy->isROV(), RTy->getResourceType(), RTy->isSigned());
324     return getOrCreateElementStruct(RTy->getResourceType(), TypeName);
325   }
326   case ResourceKind::RawBuffer: {
327     auto *RTy = cast<RawBufferExtType>(HandleTy);
328     formatTypeName(TypeName, "ByteAddressBuffer", RTy->isWriteable(),
329                    RTy->isROV());
330     return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),
331                                     TypeName);
332   }
333   case ResourceKind::StructuredBuffer: {
334     auto *RTy = cast<RawBufferExtType>(HandleTy);
335     Type *Ty = RTy->getResourceType();
336     formatTypeName(TypeName, "StructuredBuffer", RTy->isWriteable(),
337                    RTy->isROV(), RTy->getResourceType(), true);
338     return getOrCreateElementStruct(Ty, TypeName);
339   }
340   case ResourceKind::FeedbackTexture2D:
341   case ResourceKind::FeedbackTexture2DArray: {
342     auto *RTy = cast<FeedbackTextureExtType>(HandleTy);
343     TypeName = formatv("{0}<{1}>", getResourceKindName(Kind),
344                        llvm::to_underlying(RTy->getFeedbackType()));
345     return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),
346                                     TypeName);
347   }
348   case ResourceKind::CBuffer: {
349     auto *RTy = cast<CBufferExtType>(HandleTy);
350     LayoutExtType *LayoutType = cast<LayoutExtType>(RTy->getResourceType());
351     StructType *Ty = cast<StructType>(LayoutType->getWrappedType());
352     SmallString<64> Name = getResourceKindName(Kind);
353     if (!CBufferName.empty()) {
354       Name.append(".");
355       Name.append(CBufferName);
356     }
357     return StructType::create(Ty->elements(), Name);
358   }
359   case ResourceKind::Sampler: {
360     auto *RTy = cast<SamplerExtType>(HandleTy);
361     TypeName = formatv("SamplerState<{0}>",
362                        llvm::to_underlying(RTy->getSamplerType()));
363     return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),
364                                     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 
isUAV() const376 bool ResourceTypeInfo::isUAV() const { return RC == ResourceClass::UAV; }
377 
isCBuffer() const378 bool ResourceTypeInfo::isCBuffer() const {
379   return RC == ResourceClass::CBuffer;
380 }
381 
isSampler() const382 bool ResourceTypeInfo::isSampler() const {
383   return RC == ResourceClass::Sampler;
384 }
385 
isStruct() const386 bool ResourceTypeInfo::isStruct() const {
387   return Kind == ResourceKind::StructuredBuffer;
388 }
389 
isTyped() const390 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 
isFeedback() const419 bool ResourceTypeInfo::isFeedback() const {
420   return Kind == ResourceKind::FeedbackTexture2D ||
421          Kind == ResourceKind::FeedbackTexture2DArray;
422 }
423 
isMultiSample() const424 bool ResourceTypeInfo::isMultiSample() const {
425   return Kind == ResourceKind::Texture2DMS ||
426          Kind == ResourceKind::Texture2DMSArray;
427 }
428 
isROV(dxil::ResourceKind Kind,TargetExtType * Ty)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>(Ty)->isROV();
439   case ResourceKind::TypedBuffer:
440     return cast<TypedBufferExtType>(Ty)->isROV();
441   case ResourceKind::RawBuffer:
442   case ResourceKind::StructuredBuffer:
443     return cast<RawBufferExtType>(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 
getUAV() const460 ResourceTypeInfo::UAVInfo ResourceTypeInfo::getUAV() const {
461   assert(isUAV() && "Not a UAV");
462   return {isROV(Kind, HandleTy)};
463 }
464 
getCBufferSize(const DataLayout & DL) const465 uint32_t ResourceTypeInfo::getCBufferSize(const DataLayout &DL) const {
466   assert(isCBuffer() && "Not a CBuffer");
467 
468   Type *ElTy = cast<CBufferExtType>(HandleTy)->getResourceType();
469 
470   if (auto *LayoutTy = dyn_cast<LayoutExtType>(ElTy))
471     return LayoutTy->getSize();
472 
473   // TODO: What should we do with unannotated arrays?
474   return DL.getTypeAllocSize(ElTy);
475 }
476 
getSamplerType() const477 dxil::SamplerType ResourceTypeInfo::getSamplerType() const {
478   assert(isSampler() && "Not a Sampler");
479   return cast<SamplerExtType>(HandleTy)->getSamplerType();
480 }
481 
482 ResourceTypeInfo::StructInfo
getStruct(const DataLayout & DL) const483 ResourceTypeInfo::getStruct(const DataLayout &DL) const {
484   assert(isStruct() && "Not a Struct");
485 
486   Type *ElTy = cast<RawBufferExtType>(HandleTy)->getResourceType();
487 
488   uint32_t Stride = DL.getTypeAllocSize(ElTy);
489   MaybeAlign Alignment;
490   if (auto *STy = dyn_cast<StructType>(ElTy))
491     Alignment = DL.getStructLayout(STy)->getAlignment();
492   uint32_t AlignLog2 = Alignment ? Log2(*Alignment) : 0;
493   return {Stride, AlignLog2};
494 }
495 
getTypedElementType(dxil::ResourceKind Kind,TargetExtType * Ty)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>(Ty);
507     return {RTy->getResourceType(), RTy->isSigned()};
508   }
509   case ResourceKind::Texture2DMS:
510   case ResourceKind::Texture2DMSArray: {
511     auto *RTy = cast<MSTextureExtType>(Ty);
512     return {RTy->getResourceType(), RTy->isSigned()};
513   }
514   case ResourceKind::TypedBuffer: {
515     auto *RTy = cast<TypedBufferExtType>(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 
getTyped() const533 ResourceTypeInfo::TypedInfo ResourceTypeInfo::getTyped() const {
534   assert(isTyped() && "Not typed");
535 
536   auto [ElTy, IsSigned] = getTypedElementType(Kind, HandleTy);
537   dxil::ElementType ET = toDXILElementType(ElTy, IsSigned);
538   uint32_t Count = 1;
539   if (auto *VTy = dyn_cast<FixedVectorType>(ElTy))
540     Count = VTy->getNumElements();
541   return {ET, Count};
542 }
543 
getFeedbackType() const544 dxil::SamplerFeedbackType ResourceTypeInfo::getFeedbackType() const {
545   assert(isFeedback() && "Not Feedback");
546   return cast<FeedbackTextureExtType>(HandleTy)->getFeedbackType();
547 }
getMultiSampleCount() const548 uint32_t ResourceTypeInfo::getMultiSampleCount() const {
549   assert(isMultiSample() && "Not MultiSampled");
550   return cast<MSTextureExtType>(HandleTy)->getSampleCount();
551 }
552 
operator ==(const ResourceTypeInfo & RHS) const553 bool ResourceTypeInfo::operator==(const ResourceTypeInfo &RHS) const {
554   return HandleTy == RHS.HandleTy;
555 }
556 
operator <(const ResourceTypeInfo & RHS) const557 bool ResourceTypeInfo::operator<(const ResourceTypeInfo &RHS) const {
558   // An empty datalayout is sufficient for sorting purposes.
559   DataLayout DummyDL;
560   if (std::tie(RC, Kind) < std::tie(RHS.RC, RHS.Kind))
561     return true;
562   if (isCBuffer() && RHS.isCBuffer() &&
563       getCBufferSize(DummyDL) < RHS.getCBufferSize(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(DummyDL) < RHS.getStruct(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 
print(raw_ostream & OS,const DataLayout & DL) const583 void ResourceTypeInfo::print(raw_ostream &OS, const DataLayout &DL) const {
584   OS << "  Class: " << getResourceClassName(RC) << "\n"
585      << "  Kind: " << getResourceKindName(Kind) << "\n";
586 
587   if (isCBuffer()) {
588     OS << "  CBuffer size: " << getCBufferSize(DL) << "\n";
589   } else if (isSampler()) {
590     OS << "  Sampler Type: " << getSamplerTypeName(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(Typed.ElementTy) << "\n"
606          << "  Element Count: " << Typed.ElementCount << "\n";
607     } else if (isFeedback())
608       OS << "  Feedback Type: " << getSamplerFeedbackTypeName(getFeedbackType())
609          << "\n";
610   }
611 }
612 
createSymbol(Module & M,StructType * Ty)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 
getAsMetadata(Module & M,dxil::ResourceTypeInfo & RTI) const621 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(Ctx);
629   Type *I1Ty = Type::getInt1Ty(Ctx);
630   auto getIntMD = [&I32Ty](uint32_t V) {
631     return ConstantAsMetadata::get(
632         Constant::getIntegerValue(I32Ty, APInt(32, V)));
633   };
634   auto getBoolMD = [&I1Ty](uint32_t V) {
635     return ConstantAsMetadata::get(
636         Constant::getIntegerValue(I1Ty, APInt(1, V)));
637   };
638 
639   MDVals.push_back(getIntMD(Binding.RecordID));
640   assert(Symbol && "Cannot yet create useful resource metadata without symbol");
641   MDVals.push_back(ValueAsMetadata::get(Symbol));
642   MDVals.push_back(MDString::get(Ctx, Name));
643   MDVals.push_back(getIntMD(Binding.Space));
644   MDVals.push_back(getIntMD(Binding.LowerBound));
645   MDVals.push_back(getIntMD(Binding.Size));
646 
647   if (RTI.isCBuffer()) {
648     MDVals.push_back(getIntMD(RTI.getCBufferSize(DL)));
649     MDVals.push_back(nullptr);
650   } else if (RTI.isSampler()) {
651     MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getSamplerType())));
652     MDVals.push_back(nullptr);
653   } else {
654     MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getResourceKind())));
655 
656     if (RTI.isUAV()) {
657       ResourceTypeInfo::UAVInfo UAVFlags = RTI.getUAV();
658       MDVals.push_back(getBoolMD(GloballyCoherent));
659       MDVals.push_back(getBoolMD(hasCounter()));
660       MDVals.push_back(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(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           getIntMD(llvm::to_underlying(ExtPropTags::StructuredBufferStride)));
675       Tags.push_back(getIntMD(RTI.getStruct(DL).Stride));
676     } else if (RTI.isTyped()) {
677       Tags.push_back(getIntMD(llvm::to_underlying(ExtPropTags::ElementType)));
678       Tags.push_back(getIntMD(llvm::to_underlying(RTI.getTyped().ElementTy)));
679     } else if (RTI.isFeedback()) {
680       Tags.push_back(
681           getIntMD(llvm::to_underlying(ExtPropTags::SamplerFeedbackKind)));
682       Tags.push_back(getIntMD(llvm::to_underlying(RTI.getFeedbackType())));
683     }
684     MDVals.push_back(Tags.empty() ? nullptr : MDNode::get(Ctx, Tags));
685   }
686 
687   return MDNode::get(Ctx, MDVals);
688 }
689 
690 std::pair<uint32_t, uint32_t>
getAnnotateProps(Module & M,dxil::ResourceTypeInfo & RTI) const691 ResourceInfo::getAnnotateProps(Module &M, dxil::ResourceTypeInfo &RTI) const {
692   const DataLayout &DL = M.getDataLayout();
693 
694   uint32_t ResourceKind = llvm::to_underlying(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(RTI.getFeedbackType());
724   else if (RTI.isTyped()) {
725     ResourceTypeInfo::TypedInfo Typed = RTI.getTyped();
726     uint32_t CompType = llvm::to_underlying(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 
print(raw_ostream & OS,dxil::ResourceTypeInfo & RTI,const DataLayout & DL) const738 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(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 
invalidate(Module & M,const PreservedAnalyses & PA,ModuleAnalysisManager::Invalidator & Inv)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 //===----------------------------------------------------------------------===//
isUpdateCounterIntrinsic(Function & F)786 static bool isUpdateCounterIntrinsic(Function &F) {
787   return F.getIntrinsicID() == Intrinsic::dx_resource_updatecounter;
788 }
789 
getResourceNameFromBindingCall(CallInst * CI)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(5);
798     break;
799   }
800 
801   auto *GV = dyn_cast<llvm::GlobalVariable>(Op);
802   if (!GV)
803     return "";
804 
805   auto *CA = dyn_cast<ConstantDataArray>(GV->getInitializer());
806   assert(CA && CA->isString() && "expected constant string");
807   StringRef Name = CA->getAsString();
808   // strip trailing 0
809   if (Name.ends_with('\0'))
810     Name = Name.drop_back(1);
811   return Name;
812 }
813 
populateResourceInfos(Module & M,DXILResourceTypeMap & DRTM)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>(F.getReturnType());
828       ResourceTypeInfo &RTI = DRTM[HandleTy];
829 
830       for (User *U : F.users())
831         if (CallInst *CI = dyn_cast<CallInst>(U)) {
832           LLVM_DEBUG(dbgs() << "  Visiting: " << *U << "\n");
833           uint32_t Space =
834               cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
835           uint32_t LowerBound =
836               cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();
837           uint32_t Size =
838               cast<ConstantInt>(CI->getArgOperand(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(CI, RI, RTI);
846         }
847 
848       break;
849     }
850     }
851   }
852 
853   llvm::stable_sort(CIToInfos, [](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(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({FirstCBuffer, FirstSampler});
890     FirstUAV = std::min({FirstUAV, FirstCBuffer});
891 
892     // Adjust the resource binding to use the next ID.
893     RI.setBindingID(NextID++);
894   }
895 }
896 
populateCounterDirections(Module & M)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>(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(1);
910       ConstantInt *CountValue = cast<ConstantInt>(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(0);
923       SmallVector<ResourceInfo *> RBInfos = findByUse(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 
populate(Module & M,DXILResourceTypeMap & DRTM)936 void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) {
937   populateResourceInfos(M, DRTM);
938   populateCounterDirections(M);
939 }
940 
print(raw_ostream & OS,DXILResourceTypeMap & DRTM,const DataLayout & DL) const941 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, 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(OS);
953     OS << "\n";
954   }
955 }
956 
findByUse(const Value * Key)957 SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {
958   if (const PHINode *Phi = dyn_cast<PHINode>(Key)) {
959     SmallVector<dxil::ResourceInfo *> Children;
960     for (const Value *V : Phi->operands()) {
961       Children.append(findByUse(V));
962     }
963     return Children;
964   }
965 
966   const CallInst *CI = dyn_cast<CallInst>(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(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(findByUse(V));
990   }
991 
992   return Children;
993 }
994 
995 //===----------------------------------------------------------------------===//
996 
populate(Module & M,DXILResourceTypeMap & DRTM)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>(F.getReturnType());
1022       ResourceTypeInfo &RTI = DRTM[HandleTy];
1023 
1024       for (User *U : F.users())
1025         if (CallInst *CI = dyn_cast<CallInst>(U)) {
1026           uint32_t Space =
1027               cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
1028           uint32_t LowerBound =
1029               cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();
1030           int32_t Size =
1031               cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
1032           Value *Name = CI->getArgOperand(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(RTI.getResourceClass(), Space, LowerBound,
1040                                 UpperBound, 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(Bindings, [](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(Bindings, [](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(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(B.RC);
1074 
1075     RegisterSpace *S = BS->Spaces.empty() ? &BS->Spaces.emplace_back(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(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(B.UpperBound + 1, UINT32_MAX);
1100     } else {
1101       OverlappingBinding = true;
1102       if (B.UpperBound < UINT32_MAX)
1103         LastFreeRange.LowerBound =
1104             std::max(LastFreeRange.LowerBound, 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>
findAvailableBinding(dxil::ResourceClass RC,uint32_t Space,int32_t Size)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 &
getOrInsertSpace(uint32_t Space)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, Space);
1128   }
1129   return Spaces.emplace_back(Space);
1130 }
1131 
1132 std::optional<uint32_t>
findAvailableBinding(int32_t Size)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 
run(Module & M,ModuleAnalysisManager & AM)1173 DXILResourceMap DXILResourceAnalysis::run(Module &M,
1174                                           ModuleAnalysisManager &AM) {
1175   DXILResourceMap Data;
1176   DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
1177   Data.populate(M, DRTM);
1178   return Data;
1179 }
1180 
1181 DXILResourceBindingInfo
run(Module & M,ModuleAnalysisManager & AM)1182 DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
1183   DXILResourceBindingInfo Data;
1184   DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
1185   Data.populate(M, DRTM);
1186   return Data;
1187 }
1188 
run(Module & M,ModuleAnalysisManager & AM)1189 PreservedAnalyses DXILResourcePrinterPass::run(Module &M,
1190                                                ModuleAnalysisManager &AM) {
1191   DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(M);
1192   DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
1193 
1194   DRM.print(OS, DRTM, M.getDataLayout());
1195   return PreservedAnalyses::all();
1196 }
1197 
anchor()1198 void DXILResourceTypeWrapperPass::anchor() {}
1199 
DXILResourceTypeWrapperPass()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 
createDXILResourceTypeWrapperPassPass()1207 ModulePass *llvm::createDXILResourceTypeWrapperPassPass() {
1208   return new DXILResourceTypeWrapperPass();
1209 }
1210 
DXILResourceWrapperPass()1211 DXILResourceWrapperPass::DXILResourceWrapperPass() : ModulePass(ID) {}
1212 
1213 DXILResourceWrapperPass::~DXILResourceWrapperPass() = default;
1214 
getAnalysisUsage(AnalysisUsage & AU) const1215 void DXILResourceWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
1216   AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
1217   AU.setPreservesAll();
1218 }
1219 
runOnModule(Module & M)1220 bool DXILResourceWrapperPass::runOnModule(Module &M) {
1221   Map.reset(new DXILResourceMap());
1222 
1223   DRTM = &getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
1224   Map->populate(M, *DRTM);
1225 
1226   return false;
1227 }
1228 
releaseMemory()1229 void DXILResourceWrapperPass::releaseMemory() { Map.reset(); }
1230 
print(raw_ostream & OS,const Module * M) const1231 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, M->getDataLayout());
1237 }
1238 
1239 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
1240 LLVM_DUMP_METHOD
dump() const1241 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 
createDXILResourceWrapperPassPass()1248 ModulePass *llvm::createDXILResourceWrapperPassPass() {
1249   return new DXILResourceWrapperPass();
1250 }
1251 
DXILResourceBindingWrapperPass()1252 DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass()
1253     : ModulePass(ID) {}
1254 
1255 DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default;
1256 
getAnalysisUsage(AnalysisUsage & AU) const1257 void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
1258   AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
1259   AU.setPreservesAll();
1260 }
1261 
runOnModule(Module & M)1262 bool DXILResourceBindingWrapperPass::runOnModule(Module &M) {
1263   BindingInfo.reset(new DXILResourceBindingInfo());
1264 
1265   DXILResourceTypeMap &DRTM =
1266       getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
1267   BindingInfo->populate(M, DRTM);
1268 
1269   return false;
1270 }
1271 
releaseMemory()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 
createDXILResourceBindingWrapperPassPass()1278 ModulePass *llvm::createDXILResourceBindingWrapperPassPass() {
1279   return new DXILResourceWrapperPass();
1280 }
1281