[llvm] [Transforms][DXIL] Tool to generate resource metadata and annotations (PR #98939)

Justin Bogner via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 15 10:40:07 PDT 2024


https://github.com/bogner created https://github.com/llvm/llvm-project/pull/98939

This introduces dxil::ResourceInfo, which can generate DXIL-style metadata for resources and the constants for the DXIL 6.6+ annotateResource operation. These are done together so that it's easier to see all of the information the ResourceInfo class needs to store.

This will be used for lowering resource in the DirectX backend and is intended to help with the translation in the other direction as well. To do that, we'll need the inverse functions of `getAsMetadata` and `getAnnotateProps`.

>From 50ab7111598890ee47fd2fb6c6f54f8ff1711e1f Mon Sep 17 00:00:00 2001
From: Justin Bogner <mail at justinbogner.com>
Date: Thu, 11 Jul 2024 23:27:15 -0700
Subject: [PATCH] [Transforms][DXIL] Tool to generate resource metadata and
 annotations

This introduces dxil::ResourceInfo, which can generate DXIL-style
metadata for resources and the constants for the DXIL 6.6+
annotateResource operation. These are done together so that it's
easier to see all of the information the ResourceInfo class needs to
store.

This will be used for lowering resource in the DirectX backend and is
intended to help with the translation in the other direction as well.
To do that, we'll need the inverse functions of `getAsMetadata` and
`getAnnotateProps`.
---
 llvm/include/llvm/Support/DXILABI.h           |  19 +
 .../llvm/Transforms/Utils/DXILResource.h      | 193 +++++++++
 llvm/lib/Transforms/Utils/CMakeLists.txt      |   1 +
 llvm/lib/Transforms/Utils/DXILResource.cpp    | 372 ++++++++++++++++++
 .../unittests/Transforms/Utils/CMakeLists.txt |   1 +
 .../Transforms/Utils/DXILResourceTest.cpp     | 303 ++++++++++++++
 6 files changed, 889 insertions(+)
 create mode 100644 llvm/include/llvm/Transforms/Utils/DXILResource.h
 create mode 100644 llvm/lib/Transforms/Utils/DXILResource.cpp
 create mode 100644 llvm/unittests/Transforms/Utils/DXILResourceTest.cpp

diff --git a/llvm/include/llvm/Support/DXILABI.h b/llvm/include/llvm/Support/DXILABI.h
index 78099ae0daeca..d0bed4d5cf383 100644
--- a/llvm/include/llvm/Support/DXILABI.h
+++ b/llvm/include/llvm/Support/DXILABI.h
@@ -94,6 +94,25 @@ enum class ElementType : uint32_t {
   PackedU8x32,
 };
 
+/// Metadata tags for extra resource properties.
+enum class ExtPropTags : uint32_t {
+  ElementType = 0,
+  StructuredBufferStride = 1,
+  SamplerFeedbackKind = 2,
+  Atomic64Use = 3,
+};
+
+enum class SamplerType : uint32_t {
+  Default = 0,
+  Comparison = 1,
+  Mono = 2, // Note: Seems to be unused.
+};
+
+enum class SamplerFeedbackType : uint32_t {
+  MinMip = 0,
+  MipRegionUsed = 1,
+};
+
 } // namespace dxil
 } // namespace llvm
 
diff --git a/llvm/include/llvm/Transforms/Utils/DXILResource.h b/llvm/include/llvm/Transforms/Utils/DXILResource.h
new file mode 100644
index 0000000000000..e6e6e2d1c36a2
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/DXILResource.h
@@ -0,0 +1,193 @@
+//===- DXILResource.h - Tools to translate DXIL resources -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_DXILRESOURCE_H
+#define LLVM_TRANSFORMS_UTILS_DXILRESOURCE_H
+
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Support/DXILABI.h"
+
+namespace llvm {
+namespace dxil {
+
+struct ResourceBinding {
+  uint32_t Space;
+  uint32_t LowerBound;
+  uint32_t Size;
+
+  bool operator==(const ResourceBinding &RHS) const {
+    return std::tie(Space, LowerBound, Size) ==
+           std::tie(RHS.Space, RHS.LowerBound, RHS.Size);
+  }
+  bool operator!=(const ResourceBinding &RHS) const { return !(*this == RHS); }
+};
+
+class ResourceInfo {
+  struct UAVInfo {
+    bool GloballyCoherent;
+    bool HasCounter;
+    bool IsROV;
+
+    bool operator==(const UAVInfo &RHS) const {
+      return std::tie(GloballyCoherent, HasCounter, IsROV) ==
+             std::tie(RHS.GloballyCoherent, RHS.HasCounter, RHS.IsROV);
+    }
+    bool operator!=(const UAVInfo &RHS) const { return !(*this == RHS); }
+  };
+
+  struct StructInfo {
+    uint32_t Stride;
+    Align Alignment;
+
+    bool operator==(const StructInfo &RHS) const {
+      return std::tie(Stride, Alignment) == std::tie(RHS.Stride, RHS.Alignment);
+    }
+    bool operator!=(const StructInfo &RHS) const { return !(*this == RHS); }
+  };
+
+  struct TypedInfo {
+    dxil::ElementType ElementTy;
+    uint32_t ElementCount;
+
+    bool operator==(const TypedInfo &RHS) const {
+      return std::tie(ElementTy, ElementCount) ==
+             std::tie(RHS.ElementTy, RHS.ElementCount);
+    }
+    bool operator!=(const TypedInfo &RHS) const { return !(*this == RHS); }
+  };
+
+  struct MSInfo {
+    uint32_t Count;
+
+    bool operator==(const MSInfo &RHS) const { return Count == RHS.Count; }
+    bool operator!=(const MSInfo &RHS) const { return !(*this == RHS); }
+  };
+
+  struct FeedbackInfo {
+    dxil::SamplerFeedbackType Type;
+
+    bool operator==(const FeedbackInfo &RHS) const { return Type == RHS.Type; }
+    bool operator!=(const FeedbackInfo &RHS) const { return !(*this == RHS); }
+  };
+
+  // Universal properties.
+  Value *Symbol;
+  StringRef Name;
+
+  ResourceBinding Binding;
+  uint32_t UniqueID;
+
+  dxil::ResourceClass RC;
+  dxil::ResourceKind Kind;
+
+  // Resource class dependent properties.
+  // CBuffer, Sampler, and RawBuffer end here.
+  union {
+    UAVInfo UAVFlags;            // UAV
+    uint32_t CBufferSize;        // CBuffer
+    dxil::SamplerType SamplerTy; // Sampler
+  };
+
+  // Resource kind dependent properties.
+  union {
+    StructInfo Struct;     // StructuredBuffer
+    TypedInfo Typed;       // All SRV/UAV except Raw/StructuredBuffer
+    FeedbackInfo Feedback; // FeedbackTexture
+  };
+
+  MSInfo MultiSample;
+
+  // Conditions to check before accessing union members.
+  bool isUAV() const;
+  bool isCBuffer() const;
+  bool isSampler() const;
+  bool isStruct() const;
+  bool isTyped() const;
+  bool isFeedback() const;
+  bool isMultiSample() const;
+
+  ResourceInfo(dxil::ResourceClass RC, dxil::ResourceKind Kind, Value *Symbol,
+               StringRef Name, ResourceBinding Binding, uint32_t UniqueID)
+      : Symbol(Symbol), Name(Name), Binding(Binding), UniqueID(UniqueID),
+        RC(RC), Kind(Kind) {}
+
+public:
+  static ResourceInfo SRV(Value *Symbol, StringRef Name,
+                          ResourceBinding Binding, uint32_t UniqueID,
+                          dxil::ElementType ElementTy, uint32_t ElementCount,
+                          dxil::ResourceKind Kind);
+  static ResourceInfo RawBuffer(Value *Symbol, StringRef Name,
+                                ResourceBinding Binding, uint32_t UniqueID);
+  static ResourceInfo StructuredBuffer(Value *Symbol, StringRef Name,
+                                       ResourceBinding Binding,
+                                       uint32_t UniqueID, uint32_t Stride,
+                                       Align Alignment);
+  static ResourceInfo Texture2DMS(Value *Symbol, StringRef Name,
+                                  ResourceBinding Binding, uint32_t UniqueID,
+                                  dxil::ElementType ElementTy,
+                                  uint32_t ElementCount, uint32_t SampleCount);
+  static ResourceInfo
+  Texture2DMSArray(Value *Symbol, StringRef Name, ResourceBinding Binding,
+                   uint32_t UniqueID, dxil::ElementType ElementTy,
+                   uint32_t ElementCount, uint32_t SampleCount);
+
+  static ResourceInfo UAV(Value *Symbol, StringRef Name,
+                          ResourceBinding Binding, uint32_t UniqueID,
+                          dxil::ElementType ElementTy, uint32_t ElementCount,
+                          bool GloballyCoherent, bool IsROV,
+                          dxil::ResourceKind Kind);
+  static ResourceInfo RWRawBuffer(Value *Symbol, StringRef Name,
+                                  ResourceBinding Binding, uint32_t UniqueID,
+                                  bool GloballyCoherent, bool IsROV);
+  static ResourceInfo RWStructuredBuffer(Value *Symbol, StringRef Name,
+                                         ResourceBinding Binding,
+                                         uint32_t UniqueID, uint32_t Stride,
+                                         Align Alignment, bool GloballyCoherent,
+                                         bool IsROV, bool HasCounter);
+  static ResourceInfo RWTexture2DMS(Value *Symbol, StringRef Name,
+                                    ResourceBinding Binding, uint32_t UniqueID,
+                                    dxil::ElementType ElementTy,
+                                    uint32_t ElementCount, uint32_t SampleCount,
+                                    bool GloballyCoherent);
+  static ResourceInfo
+  RWTexture2DMSArray(Value *Symbol, StringRef Name, ResourceBinding Binding,
+                     uint32_t UniqueID, dxil::ElementType ElementTy,
+                     uint32_t ElementCount, uint32_t SampleCount,
+                     bool GloballyCoherent);
+  static ResourceInfo FeedbackTexture2D(Value *Symbol, StringRef Name,
+                                        ResourceBinding Binding,
+                                        uint32_t UniqueID,
+                                        dxil::SamplerFeedbackType FeedbackTy);
+  static ResourceInfo
+  FeedbackTexture2DArray(Value *Symbol, StringRef Name, ResourceBinding Binding,
+                         uint32_t UniqueID,
+                         dxil::SamplerFeedbackType FeedbackTy);
+
+  static ResourceInfo CBuffer(Value *Symbol, StringRef Name,
+                              ResourceBinding Binding, uint32_t UniqueID,
+                              uint32_t Size);
+
+  static ResourceInfo Sampler(Value *Symbol, StringRef Name,
+                              ResourceBinding Binding, uint32_t UniqueID,
+                              dxil::SamplerType SamplerTy);
+
+  bool operator==(const ResourceInfo &RHS) const;
+
+  MDTuple *getAsMetadata(LLVMContext &Ctx) const;
+
+  ResourceBinding getBinding() const { return Binding; }
+  std::pair<uint32_t, uint32_t> getAnnotateProps() const;
+};
+
+
+
+} // namespace dxil
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_UTILS_DXILRESOURCE_H
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index 51e8821773c3a..1b811c7cebef9 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -20,6 +20,7 @@ add_llvm_component_library(LLVMTransformUtils
   CountVisits.cpp
   Debugify.cpp
   DemoteRegToStack.cpp
+  DXILResource.cpp
   DXILUpgrade.cpp
   EntryExitInstrumenter.cpp
   EscapeEnumerator.cpp
diff --git a/llvm/lib/Transforms/Utils/DXILResource.cpp b/llvm/lib/Transforms/Utils/DXILResource.cpp
new file mode 100644
index 0000000000000..46e6599627afa
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/DXILResource.cpp
@@ -0,0 +1,372 @@
+//===- DXILResource.cpp - Tools to translate DXIL resources ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/DXILResource.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/IR/DerivedTypes.h"
+
+using namespace llvm;
+using namespace dxil;
+
+bool ResourceInfo::isUAV() const { return RC == ResourceClass::UAV; }
+
+bool ResourceInfo::isCBuffer() const {
+  return RC == ResourceClass::CBuffer;
+}
+
+bool ResourceInfo::isSampler() const {
+  return RC == ResourceClass::Sampler;
+}
+
+bool ResourceInfo::isStruct() const {
+  return Kind == ResourceKind::StructuredBuffer;
+}
+
+bool ResourceInfo::isTyped() const {
+  switch (Kind) {
+  case ResourceKind::Texture1D:
+  case ResourceKind::Texture2D:
+  case ResourceKind::Texture2DMS:
+  case ResourceKind::Texture3D:
+  case ResourceKind::TextureCube:
+  case ResourceKind::Texture1DArray:
+  case ResourceKind::Texture2DArray:
+  case ResourceKind::Texture2DMSArray:
+  case ResourceKind::TextureCubeArray:
+  case ResourceKind::TypedBuffer:
+    return true;
+  case ResourceKind::RawBuffer:
+  case ResourceKind::StructuredBuffer:
+  case ResourceKind::FeedbackTexture2D:
+  case ResourceKind::FeedbackTexture2DArray:
+  case ResourceKind::CBuffer:
+  case ResourceKind::Sampler:
+  case ResourceKind::TBuffer:
+  case ResourceKind::RTAccelerationStructure:
+    return false;
+  case ResourceKind::Invalid:
+  case ResourceKind::NumEntries:
+    llvm_unreachable("Invalid resource kind");
+  }
+}
+
+bool ResourceInfo::isFeedback() const {
+  return Kind == ResourceKind::FeedbackTexture2D ||
+         Kind == ResourceKind::FeedbackTexture2DArray;
+}
+
+bool ResourceInfo::isMultiSample() const {
+  return Kind == ResourceKind::Texture2DMS ||
+         Kind == ResourceKind::Texture2DMSArray;
+}
+
+ResourceInfo ResourceInfo::SRV(Value *Symbol, StringRef Name,
+                               ResourceBinding Binding, uint32_t UniqueID,
+                               ElementType ElementTy, uint32_t ElementCount,
+                               ResourceKind Kind) {
+  ResourceInfo RI(ResourceClass::SRV, Kind, Symbol, Name, Binding, UniqueID);
+  assert(RI.isTyped() && !(RI.isStruct() || RI.isMultiSample()) &&
+         "Invalid ResourceKind for SRV constructor.");
+  RI.Typed.ElementTy = ElementTy;
+  RI.Typed.ElementCount = ElementCount;
+  return RI;
+}
+
+ResourceInfo ResourceInfo::RawBuffer(Value *Symbol, StringRef Name,
+                                     ResourceBinding Binding,
+                                     uint32_t UniqueID) {
+  ResourceInfo RI(ResourceClass::SRV, ResourceKind::RawBuffer, Symbol, Name,
+                  Binding, UniqueID);
+  return RI;
+}
+
+ResourceInfo ResourceInfo::StructuredBuffer(Value *Symbol, StringRef Name,
+                                            ResourceBinding Binding,
+                                            uint32_t UniqueID, uint32_t Stride,
+                                            Align Alignment) {
+  ResourceInfo RI(ResourceClass::SRV, ResourceKind::StructuredBuffer, Symbol,
+                  Name, Binding, UniqueID);
+  RI.Struct.Stride = Stride;
+  RI.Struct.Alignment = Alignment;
+  return RI;
+}
+
+ResourceInfo ResourceInfo::Texture2DMS(Value *Symbol, StringRef Name,
+                                       ResourceBinding Binding,
+                                       uint32_t UniqueID, ElementType ElementTy,
+                                       uint32_t ElementCount,
+                                       uint32_t SampleCount) {
+  ResourceInfo RI(ResourceClass::SRV, ResourceKind::Texture2DMS, Symbol, Name,
+                  Binding, UniqueID);
+  RI.Typed.ElementTy = ElementTy;
+  RI.Typed.ElementCount = ElementCount;
+  RI.MultiSample.Count = SampleCount;
+  return RI;
+}
+
+ResourceInfo ResourceInfo::Texture2DMSArray(
+    Value *Symbol, StringRef Name, ResourceBinding Binding, uint32_t UniqueID,
+    ElementType ElementTy, uint32_t ElementCount, uint32_t SampleCount) {
+  ResourceInfo RI(ResourceClass::SRV, ResourceKind::Texture2DMSArray, Symbol,
+                  Name, Binding, UniqueID);
+  RI.Typed.ElementTy = ElementTy;
+  RI.Typed.ElementCount = ElementCount;
+  RI.MultiSample.Count = SampleCount;
+  return RI;
+}
+
+ResourceInfo ResourceInfo::UAV(Value *Symbol, StringRef Name,
+                               ResourceBinding Binding, uint32_t UniqueID,
+                               ElementType ElementTy, uint32_t ElementCount,
+                               bool GloballyCoherent, bool IsROV,
+                               ResourceKind Kind) {
+  ResourceInfo RI(ResourceClass::UAV, Kind, Symbol, Name, Binding, UniqueID);
+  assert(RI.isTyped() && !(RI.isStruct() || RI.isMultiSample()) &&
+         "Invalid ResourceKind for UAV constructor.");
+  RI.Typed.ElementTy = ElementTy;
+  RI.Typed.ElementCount = ElementCount;
+  RI.UAVFlags.GloballyCoherent = GloballyCoherent;
+  RI.UAVFlags.IsROV = IsROV;
+  RI.UAVFlags.HasCounter = false;
+  return RI;
+}
+
+ResourceInfo ResourceInfo::RWRawBuffer(Value *Symbol, StringRef Name,
+                                       ResourceBinding Binding,
+                                       uint32_t UniqueID, bool GloballyCoherent,
+                                       bool IsROV) {
+  ResourceInfo RI(ResourceClass::UAV, ResourceKind::RawBuffer, Symbol, Name,
+                  Binding, UniqueID);
+  RI.UAVFlags.GloballyCoherent = GloballyCoherent;
+  RI.UAVFlags.IsROV = IsROV;
+  RI.UAVFlags.HasCounter = false;
+  return RI;
+}
+
+ResourceInfo ResourceInfo::RWStructuredBuffer(Value *Symbol, StringRef Name,
+                                              ResourceBinding Binding,
+                                              uint32_t UniqueID,
+                                              uint32_t Stride, Align Alignment,
+                                              bool GloballyCoherent, bool IsROV,
+                                              bool HasCounter) {
+  ResourceInfo RI(ResourceClass::UAV, ResourceKind::StructuredBuffer, Symbol,
+                  Name, Binding, UniqueID);
+  RI.Struct.Stride = Stride;
+  RI.Struct.Alignment = Alignment;
+  RI.UAVFlags.GloballyCoherent = GloballyCoherent;
+  RI.UAVFlags.IsROV = IsROV;
+  RI.UAVFlags.HasCounter = HasCounter;
+  return RI;
+}
+
+ResourceInfo
+ResourceInfo::RWTexture2DMS(Value *Symbol, StringRef Name,
+                            ResourceBinding Binding, uint32_t UniqueID,
+                            ElementType ElementTy, uint32_t ElementCount,
+                            uint32_t SampleCount, bool GloballyCoherent) {
+  ResourceInfo RI(ResourceClass::UAV, ResourceKind::Texture2DMS, Symbol, Name,
+                  Binding, UniqueID);
+  RI.Typed.ElementTy = ElementTy;
+  RI.Typed.ElementCount = ElementCount;
+  RI.UAVFlags.GloballyCoherent = GloballyCoherent;
+  RI.UAVFlags.IsROV = false;
+  RI.UAVFlags.HasCounter = false;
+  RI.MultiSample.Count = SampleCount;
+  return RI;
+}
+
+ResourceInfo
+ResourceInfo::RWTexture2DMSArray(Value *Symbol, StringRef Name,
+                                 ResourceBinding Binding, uint32_t UniqueID,
+                                 ElementType ElementTy, uint32_t ElementCount,
+                                 uint32_t SampleCount, bool GloballyCoherent) {
+  ResourceInfo RI(ResourceClass::UAV, ResourceKind::Texture2DMSArray, Symbol,
+                  Name, Binding, UniqueID);
+  RI.Typed.ElementTy = ElementTy;
+  RI.Typed.ElementCount = ElementCount;
+  RI.UAVFlags.GloballyCoherent = GloballyCoherent;
+  RI.UAVFlags.IsROV = false;
+  RI.UAVFlags.HasCounter = false;
+  RI.MultiSample.Count = SampleCount;
+  return RI;
+}
+
+ResourceInfo ResourceInfo::FeedbackTexture2D(Value *Symbol, StringRef Name,
+                                             ResourceBinding Binding,
+                                             uint32_t UniqueID,
+                                             SamplerFeedbackType FeedbackTy) {
+  ResourceInfo RI(ResourceClass::UAV, ResourceKind::FeedbackTexture2D, Symbol,
+                  Name, Binding, UniqueID);
+  RI.UAVFlags.GloballyCoherent = false;
+  RI.UAVFlags.IsROV = false;
+  RI.UAVFlags.HasCounter = false;
+  RI.Feedback.Type = FeedbackTy;
+  return RI;
+}
+
+ResourceInfo ResourceInfo::FeedbackTexture2DArray(
+    Value *Symbol, StringRef Name, ResourceBinding Binding, uint32_t UniqueID,
+    SamplerFeedbackType FeedbackTy) {
+  ResourceInfo RI(ResourceClass::UAV, ResourceKind::FeedbackTexture2DArray,
+                  Symbol, Name, Binding, UniqueID);
+  RI.UAVFlags.GloballyCoherent = false;
+  RI.UAVFlags.IsROV = false;
+  RI.UAVFlags.HasCounter = false;
+  RI.Feedback.Type = FeedbackTy;
+  return RI;
+}
+
+ResourceInfo ResourceInfo::CBuffer(Value *Symbol, StringRef Name,
+                                   ResourceBinding Binding, uint32_t UniqueID,
+                                   uint32_t Size) {
+  ResourceInfo RI(ResourceClass::CBuffer, ResourceKind::CBuffer, Symbol, Name,
+                  Binding, UniqueID);
+  RI.CBufferSize = Size;
+  return RI;
+}
+
+ResourceInfo ResourceInfo::Sampler(Value *Symbol, StringRef Name,
+                                   ResourceBinding Binding, uint32_t UniqueID,
+                                   SamplerType SamplerTy) {
+  ResourceInfo RI(ResourceClass::Sampler, ResourceKind::Sampler, Symbol, Name,
+                  Binding, UniqueID);
+  RI.SamplerTy = SamplerTy;
+  return RI;
+}
+
+bool ResourceInfo::operator==(const ResourceInfo &RHS) const {
+  if (std::tie(Symbol, Name, Binding, UniqueID, RC, Kind) !=
+      std::tie(RHS.Symbol, RHS.Name, RHS.Binding, RHS.UniqueID, RHS.RC,
+               RHS.Kind))
+    return false;
+  if (isCBuffer())
+    return CBufferSize == RHS.CBufferSize;
+  if (isSampler())
+    return SamplerTy == RHS.SamplerTy;
+  if (isUAV() && UAVFlags != RHS.UAVFlags)
+    return false;
+
+  if (isStruct())
+    return Struct == RHS.Struct;
+  if (isFeedback())
+    return Feedback == RHS.Feedback;
+  if (isTyped() && Typed != RHS.Typed)
+    return false;
+
+  if (isMultiSample())
+    return MultiSample == RHS.MultiSample;
+
+  assert((Kind == ResourceKind::RawBuffer) && "Unhandled resource kind");
+  return true;
+}
+
+MDTuple *ResourceInfo::getAsMetadata(LLVMContext &Ctx) const {
+  SmallVector<Metadata *, 11> MDVals;
+
+  Type *I32Ty = Type::getInt32Ty(Ctx);
+  Type *I1Ty = Type::getInt1Ty(Ctx);
+  auto getIntMD = [&I32Ty](uint32_t V) {
+    return ConstantAsMetadata::get(
+        Constant::getIntegerValue(I32Ty, APInt(32, V)));
+  };
+  auto getBoolMD = [&I1Ty](uint32_t V) {
+    return ConstantAsMetadata::get(
+        Constant::getIntegerValue(I1Ty, APInt(1, V)));
+  };
+
+  MDVals.push_back(getIntMD(UniqueID));
+  MDVals.push_back(ValueAsMetadata::get(Symbol));
+  MDVals.push_back(MDString::get(Ctx, Name));
+  MDVals.push_back(getIntMD(Binding.Space));
+  MDVals.push_back(getIntMD(Binding.LowerBound));
+  MDVals.push_back(getIntMD(Binding.Size));
+
+  if (isCBuffer()) {
+    MDVals.push_back(getIntMD(CBufferSize));
+    MDVals.push_back(nullptr);
+  } else if (isSampler()) {
+    MDVals.push_back(getIntMD(llvm::to_underlying(SamplerTy)));
+    MDVals.push_back(nullptr);
+  } else {
+    MDVals.push_back(getIntMD(llvm::to_underlying(Kind)));
+
+    if (isUAV()) {
+      MDVals.push_back(getBoolMD(UAVFlags.GloballyCoherent));
+      MDVals.push_back(getBoolMD(UAVFlags.HasCounter));
+      MDVals.push_back(getBoolMD(UAVFlags.IsROV));
+    } else {
+      // All SRVs include sample count in the metadata, but it's only meaningful
+      // for multi-sampled textured. Also, UAVs can be multisampled in SM6.7+,
+      // but this just isn't reflected in the metadata at all.
+      uint32_t SampleCount = isMultiSample() ? MultiSample.Count : 0;
+      MDVals.push_back(getIntMD(SampleCount));
+    }
+
+    // Further properties are attached to a metadata list of tag-value pairs.
+    SmallVector<Metadata *> Tags;
+    if (isStruct()) {
+      Tags.push_back(
+          getIntMD(llvm::to_underlying(ExtPropTags::StructuredBufferStride)));
+      Tags.push_back(getIntMD(Struct.Stride));
+    } else if (isTyped()) {
+      Tags.push_back(getIntMD(llvm::to_underlying(ExtPropTags::ElementType)));
+      Tags.push_back(getIntMD(llvm::to_underlying(Typed.ElementTy)));
+    } else if (isFeedback()) {
+      Tags.push_back(
+          getIntMD(llvm::to_underlying(ExtPropTags::SamplerFeedbackKind)));
+      Tags.push_back(getIntMD(llvm::to_underlying(Feedback.Type)));
+    }
+    MDVals.push_back(Tags.empty() ? nullptr : MDNode::get(Ctx, Tags));
+  }
+
+  return MDNode::get(Ctx, MDVals);
+}
+
+std::pair<uint32_t, uint32_t> ResourceInfo::getAnnotateProps() const {
+  uint32_t ResourceKind = llvm::to_underlying(Kind);
+  uint32_t AlignLog2 = isStruct() ? Log2(Struct.Alignment) : 0;
+  bool IsUAV = isUAV();
+  bool IsROV = IsUAV ? UAVFlags.IsROV : 0;
+  bool IsGloballyCoherent = IsUAV ? UAVFlags.GloballyCoherent : 0;
+  uint8_t SamplerCmpOrHasCounter = 0;
+  if (IsUAV)
+    SamplerCmpOrHasCounter = UAVFlags.HasCounter;
+  else if (isSampler())
+    SamplerCmpOrHasCounter = SamplerTy == SamplerType::Comparison;
+
+  // TODO: Document this format. Currently the only reference is the
+  // implementation of dxc's DxilResourceProperties struct.
+  uint32_t Word0 = 0;
+  Word0 |= ResourceKind & 0xFF;
+  Word0 |= (AlignLog2 & 0xF) << 8;
+  Word0 |= (IsUAV & 1) << 12;
+  Word0 |= (IsROV & 1) << 13;
+  Word0 |= (IsGloballyCoherent & 1) << 14;
+  Word0 |= (SamplerCmpOrHasCounter & 1) << 15;
+
+  uint32_t Word1 = 0;
+  if (isStruct())
+    Word1 = Struct.Stride;
+  else if (isCBuffer())
+    Word1 = CBufferSize;
+  else if (isFeedback())
+    Word1 = llvm::to_underlying(Feedback.Type);
+  else if (isTyped()) {
+    uint32_t CompType = llvm::to_underlying(Typed.ElementTy);
+    uint32_t CompCount = Typed.ElementCount;
+    uint32_t SampleCount = isMultiSample() ? MultiSample.Count : 0;
+
+    Word1 |= (CompType & 0xFF) << 0;
+    Word1 |= (CompCount & 0xFF) << 8;
+    Word1 |= (SampleCount & 0xFF) << 16;
+  }
+
+  return {Word0, Word1};
+}
+
+#define DEBUG_TYPE "dxil-resource"
diff --git a/llvm/unittests/Transforms/Utils/CMakeLists.txt b/llvm/unittests/Transforms/Utils/CMakeLists.txt
index 35055baa05ee9..8a14a5b8e249e 100644
--- a/llvm/unittests/Transforms/Utils/CMakeLists.txt
+++ b/llvm/unittests/Transforms/Utils/CMakeLists.txt
@@ -18,6 +18,7 @@ add_llvm_unittest(UtilsTests
   CodeLayoutTest.cpp
   CodeMoverUtilsTest.cpp
   DebugifyTest.cpp
+  DXILResourceTest.cpp
   FunctionComparatorTest.cpp
   IntegerDivisionTest.cpp
   LocalTest.cpp
diff --git a/llvm/unittests/Transforms/Utils/DXILResourceTest.cpp b/llvm/unittests/Transforms/Utils/DXILResourceTest.cpp
new file mode 100644
index 0000000000000..ff8bdb2703c73
--- /dev/null
+++ b/llvm/unittests/Transforms/Utils/DXILResourceTest.cpp
@@ -0,0 +1,303 @@
+//===- DXILResourceTest.cpp - Unit tests for DXILResource -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/DXILResource.h"
+#include "llvm/IR/Constants.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace dxil;
+
+namespace {
+// Helper to succinctly build resource shaped metadata for tests.
+struct MDBuilder {
+  LLVMContext &Context;
+  Type *Int32Ty;
+  Type *Int1Ty;
+
+  MDBuilder(LLVMContext &Context, Type *Int32Ty, Type *Int1Ty)
+      : Context(Context), Int32Ty(Int32Ty), Int1Ty(Int1Ty) {}
+
+  template <typename... Ts>
+  void appendMDs(SmallVectorImpl<Metadata *> &MDs, int V, Ts... More) {
+    MDs.push_back(ConstantAsMetadata::get(
+        Constant::getIntegerValue(Int32Ty, APInt(32, V))));
+    appendMDs(MDs, More...);
+  }
+  template <typename... Ts>
+  void appendMDs(SmallVectorImpl<Metadata *> &MDs, unsigned int V, Ts... More) {
+    MDs.push_back(ConstantAsMetadata::get(
+        Constant::getIntegerValue(Int32Ty, APInt(32, V))));
+    appendMDs(MDs, More...);
+  }
+  template <typename... Ts>
+  void appendMDs(SmallVectorImpl<Metadata *> &MDs, bool V, Ts... More) {
+    MDs.push_back(ConstantAsMetadata::get(
+        Constant::getIntegerValue(Int1Ty, APInt(1, V))));
+    appendMDs(MDs, More...);
+  }
+  template <typename... Ts>
+  void appendMDs(SmallVectorImpl<Metadata *> &MDs, Value *V, Ts... More) {
+    MDs.push_back(ValueAsMetadata::get(V));
+    appendMDs(MDs, More...);
+  }
+  template <typename... Ts>
+  void appendMDs(SmallVectorImpl<Metadata *> &MDs, const char *V, Ts... More) {
+    MDs.push_back(MDString::get(Context, V));
+    appendMDs(MDs, More...);
+  }
+  template <typename... Ts>
+  void appendMDs(SmallVectorImpl<Metadata *> &MDs, StringRef V, Ts... More) {
+    MDs.push_back(MDString::get(Context, V));
+    appendMDs(MDs, More...);
+  }
+  template <typename... Ts>
+  void appendMDs(SmallVectorImpl<Metadata *> &MDs, nullptr_t V, Ts... More) {
+    MDs.push_back(nullptr);
+    appendMDs(MDs, More...);
+  }
+  template <typename... Ts>
+  void appendMDs(SmallVectorImpl<Metadata *> &MDs, MDTuple *V, Ts... More) {
+    MDs.push_back(V);
+    appendMDs(MDs, More...);
+  }
+  void appendMDs(SmallVectorImpl<Metadata *> &MDs) {
+    // Base case, nothing to do.
+  }
+
+  template <typename... Ts>
+  MDTuple *get(Ts... Data) {
+    SmallVector<Metadata *> MDs;
+    appendMDs(MDs, Data...);
+    return MDNode::get(Context, MDs);
+  }
+};
+
+testing::AssertionResult MDTupleEq(const char *LHSExpr, const char *RHSExpr,
+                                   MDTuple *LHS, MDTuple *RHS) {
+  if (LHS == RHS)
+    return testing::AssertionSuccess();
+  std::string LHSRepr, RHSRepr;
+  raw_string_ostream LHSS(LHSRepr), RHSS(RHSRepr);
+  LHS->printTree(LHSS);
+  RHS->printTree(RHSS);
+
+  return testing::AssertionFailure() << "Expected equality:\n"
+                                     << "  " << LHSExpr << "\n"
+                                     << "Which is:\n"
+                                     << "  " << LHSS.str() << "\n\n"
+                                     << "  " << RHSExpr << "\n"
+                                     << "Which is:\n"
+                                     << "  " << RHSS.str();
+}
+#define EXPECT_MDEQ(X, Y) EXPECT_PRED_FORMAT2(MDTupleEq, X, Y)
+}
+
+TEST(DXILResource, AnnotationsAndMetadata) {
+  LLVMContext Context;
+  Type *Int1Ty = Type::getInt1Ty(Context);
+  Type *Int32Ty = Type::getInt32Ty(Context);
+  Type *FloatTy = Type::getFloatTy(Context);
+  Type *DoubleTy = Type::getDoubleTy(Context);
+  Type *Floatx4Ty = FixedVectorType::get(FloatTy, 4);
+  Type *Floatx3Ty = FixedVectorType::get(FloatTy, 3);
+  Type *Int32x2Ty = FixedVectorType::get(Int32Ty, 2);
+
+  MDBuilder TestMD(Context, Int32Ty, Int1Ty);
+
+  // ByteAddressBuffer Buffer0;
+  Value *Symbol = UndefValue::get(
+      StructType::create(Context, {Int32Ty}, "struct.ByteAddressBuffer"));
+  ResourceInfo Resource =
+      ResourceInfo::RawBuffer(Symbol, "Buffer0", ResourceBinding{0, 0, 1},
+                              /*UniqueID=*/0);
+  std::pair<uint32_t, uint32_t> Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x0000000bU);
+  EXPECT_EQ(Props.second, 0U);
+  MDTuple *MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(0, Symbol, "Buffer0", 0, 0, 1, 11, 0, nullptr));
+
+  // RWByteAddressBuffer BufferOut : register(u3, space2);
+  Symbol = UndefValue::get(
+      StructType::create(Context, {Int32Ty}, "struct.RWByteAddressBuffer"));
+  Resource = ResourceInfo::RWRawBuffer(
+      Symbol, "BufferOut", ResourceBinding{2, 3, 1}, /*UniqueID=*/1,
+      /*GloballyCoherent=*/false, /*IsROV=*/false);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x0000100bU);
+  EXPECT_EQ(Props.second, 0U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(1, Symbol, "BufferOut", 2, 3, 1, 11, false, false,
+                             false, nullptr));
+
+  // struct BufType0 { int i; float f; double d; };
+  // StructuredBuffer<BufType0> Buffer0 : register(t0);
+  StructType *BufType0 =
+      StructType::create(Context, {Int32Ty, FloatTy, DoubleTy}, "BufType0");
+  Symbol = UndefValue::get(StructType::create(
+      Context, {BufType0}, "class.StructuredBuffer<BufType>"));
+  Resource = ResourceInfo::StructuredBuffer(
+      Symbol, "Buffer0", ResourceBinding{0, 0, 1}, /*UniqueID=*/0,
+      /*Stride=*/16, Align(8));
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x0000030cU);
+  EXPECT_EQ(Props.second, 0x00000010U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(
+      MD, TestMD.get(0, Symbol, "Buffer0", 0, 0, 1, 12, 0, TestMD.get(1, 16)));
+
+  // Texture2D<float4> ColorMapTexture : register(t2);
+  Symbol = UndefValue::get(StructType::create(
+      Context, {Floatx4Ty}, "class.Texture2D<vector<float, 4> >"));
+  Resource =
+      ResourceInfo::SRV(Symbol, "ColorMapTexture", ResourceBinding{0, 2, 1},
+                        /*UniqueID=*/2, dxil::ElementType::F32,
+                        /*ElementCount=*/4, dxil::ResourceKind::Texture2D);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x00000002U);
+  EXPECT_EQ(Props.second, 0x00000409U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(2, Symbol, "ColorMapTexture", 0, 2, 1, 2, 0,
+                             TestMD.get(0, 9)));
+
+  // Texture2DMS<float, 8> DepthBuffer : register(t0);
+  Symbol = UndefValue::get(
+      StructType::create(Context, {FloatTy}, "class.Texture2DMS<float, 8>"));
+  Resource =
+      ResourceInfo::Texture2DMS(Symbol, "DepthBuffer", ResourceBinding{0, 0, 1},
+                                /*UniqueID=*/0, dxil::ElementType::F32,
+                                /*ElementCount=*/1, /*SampleCount=*/8);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x00000003U);
+  EXPECT_EQ(Props.second, 0x00080109U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(0, Symbol, "DepthBuffer", 0, 0, 1, 3, 8,
+                             TestMD.get(0, 9)));
+
+  // FeedbackTexture2D<SAMPLER_FEEDBACK_MIN_MIP> feedbackMinMip;
+  Symbol = UndefValue::get(
+      StructType::create(Context, {Int32Ty}, "class.FeedbackTexture2D<0>"));
+  Resource = ResourceInfo::FeedbackTexture2D(
+      Symbol, "feedbackMinMip", ResourceBinding{0, 0, 1},
+      /*UniqueID=*/0, SamplerFeedbackType::MinMip);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x00001011U);
+  EXPECT_EQ(Props.second, 0U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(0, Symbol, "feedbackMinMip", 0, 0, 1, 17, false,
+                             false, false, TestMD.get(2, 0)));
+
+  // FeedbackTexture2DArray<SAMPLER_FEEDBACK_MIP_REGION_USED> feedbackMipRegion;
+  Symbol = UndefValue::get(StructType::create(
+      Context, {Int32Ty}, "class.FeedbackTexture2DArray<1>"));
+  Resource = ResourceInfo::FeedbackTexture2DArray(
+      Symbol, "feedbackMipRegion", ResourceBinding{0, 0, 1},
+      /*UniqueID=*/0, SamplerFeedbackType::MipRegionUsed);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x00001012U);
+  EXPECT_EQ(Props.second, 0x00000001U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(0, Symbol, "feedbackMipRegion", 0, 0, 1, 18, false,
+                             false, false, TestMD.get(2, 1)));
+
+  // globallycoherent RWTexture2D<int2> OutputTexture : register(u0, space2);
+  Symbol = UndefValue::get(StructType::create(
+      Context, {Int32x2Ty}, "class.RWTexture2D<vector<int, 2> >"));
+  Resource =
+      ResourceInfo::UAV(Symbol, "OutputTexture", ResourceBinding{2, 0, 1},
+                        /*UniqueID=*/0, dxil::ElementType::I32,
+                        /*ElementCount=*/2, /*GloballyCoherent=*/1, /*IsROV=*/0,
+                        dxil::ResourceKind::Texture2D);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x00005002U);
+  EXPECT_EQ(Props.second, 0x00000204U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(0, Symbol, "OutputTexture", 2, 0, 1, 2, true,
+                             false, false, TestMD.get(0, 4)));
+
+  // RasterizerOrderedBuffer<float4> ROB;
+  Symbol = UndefValue::get(
+      StructType::create(Context, {Floatx4Ty},
+                         "class.RasterizerOrderedBuffer<vector<float, 4> >"));
+  Resource = ResourceInfo::UAV(Symbol, "ROB", ResourceBinding{0, 0, 1},
+                               /*UniqueID=*/0, dxil::ElementType::F32,
+                               /*ElementCount=*/4, /*GloballyCoherent=*/0,
+                               /*IsROV=*/1, dxil::ResourceKind::TypedBuffer);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x0000300aU);
+  EXPECT_EQ(Props.second, 0x00000409U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(0, Symbol, "ROB", 0, 0, 1, 10, false, false, true,
+                             TestMD.get(0, 9)));
+
+
+  // RWStructuredBuffer<ParticleMotion> g_OutputBuffer : register(u2);
+  StructType *BufType1 = StructType::create(
+      Context, {Floatx3Ty, FloatTy, Int32Ty}, "ParticleMotion");
+  Symbol = UndefValue::get(StructType::create(
+      Context, {BufType1}, "class.StructuredBuffer<ParticleMotion>"));
+  Resource = ResourceInfo::RWStructuredBuffer(
+      Symbol, "g_OutputBuffer", ResourceBinding{0, 2, 1},
+      /*UniqueID=*/0, /*Stride=*/20, Align(4), /*GloballyCoherent=*/false,
+      /*IsROV=*/false, /*HasCounter=*/true);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x0000920cU);
+  EXPECT_EQ(Props.second, 0x00000014U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(0, Symbol, "g_OutputBuffer", 0, 2, 1, 12, false,
+                             true, false, TestMD.get(1, 20)));
+
+  // RWTexture2DMSArray<uint,8> g_rw_t2dmsa;
+  Symbol = UndefValue::get(StructType::create(
+      Context, {Int32Ty}, "class.RWTexture2DMSArray<unsigned int, 8>"));
+  Resource = ResourceInfo::RWTexture2DMSArray(
+      Symbol, "g_rw_t2dmsa", ResourceBinding{0, 0, 1},
+      /*UniqueID=*/0, dxil::ElementType::U32, /*ElementCount=*/1,
+      /*SampleCount=*/8, /*GloballyCoherent=*/false);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x00001008U);
+  EXPECT_EQ(Props.second, 0x00080105U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(0, Symbol, "g_rw_t2dmsa", 0, 0, 1, 8, false,
+                             false, false, TestMD.get(0, 5)));
+
+  // cbuffer cb0 { float4 g_X; float4 g_Y; }
+  Symbol = UndefValue::get(
+      StructType::create(Context, {Floatx4Ty, Floatx4Ty}, "cb0"));
+  Resource = ResourceInfo::CBuffer(Symbol, "cb0", ResourceBinding{0, 0, 1},
+                                   /*UniqueID=*/0, /*Size=*/32);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x0000000dU);
+  EXPECT_EQ(Props.second, 0x00000020U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD, TestMD.get(0, Symbol, "cb0", 0, 0, 1, 32, nullptr));
+
+  // SamplerState ColorMapSampler : register(s0);
+  Symbol = UndefValue::get(
+      StructType::create(Context, {Int32Ty}, "struct.SamplerState"));
+  Resource =
+      ResourceInfo::Sampler(Symbol, "ColorMapSampler", ResourceBinding{0, 0, 1},
+                            /*UniqueID=*/0, dxil::SamplerType::Default);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x0000000eU);
+  EXPECT_EQ(Props.second, 0U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD,
+              TestMD.get(0, Symbol, "ColorMapSampler", 0, 0, 1, 0, nullptr));
+
+  // SamplerComparisonState ShadowSampler {...};
+  Resource =
+      ResourceInfo::Sampler(Symbol, "CmpSampler", ResourceBinding{0, 0, 1},
+                            /*UniqueID=*/0, dxil::SamplerType::Comparison);
+  Props = Resource.getAnnotateProps();
+  EXPECT_EQ(Props.first, 0x0000800eU);
+  EXPECT_EQ(Props.second, 0U);
+  MD = Resource.getAsMetadata(Context);
+  EXPECT_MDEQ(MD,
+              TestMD.get(0, Symbol, "CmpSampler", 0, 0, 1, 1, nullptr));
+}



More information about the llvm-commits mailing list