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..df01fb457977a --- /dev/null +++ b/llvm/include/llvm/Transforms/Utils/DXILResource.h @@ -0,0 +1,191 @@ +//===- 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 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..7281c7ad04531 --- /dev/null +++ b/llvm/lib/Transforms/Utils/DXILResource.cpp @@ -0,0 +1,369 @@ +//===- 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 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 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 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..d9303ffe68f6a --- /dev/null +++ b/llvm/unittests/Transforms/Utils/DXILResourceTest.cpp @@ -0,0 +1,300 @@ +//===- 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 + void appendMDs(SmallVectorImpl &MDs, int V, Ts... More) { + MDs.push_back(ConstantAsMetadata::get( + Constant::getIntegerValue(Int32Ty, APInt(32, V)))); + appendMDs(MDs, More...); + } + template + void appendMDs(SmallVectorImpl &MDs, unsigned int V, Ts... More) { + MDs.push_back(ConstantAsMetadata::get( + Constant::getIntegerValue(Int32Ty, APInt(32, V)))); + appendMDs(MDs, More...); + } + template + void appendMDs(SmallVectorImpl &MDs, bool V, Ts... More) { + MDs.push_back(ConstantAsMetadata::get( + Constant::getIntegerValue(Int1Ty, APInt(1, V)))); + appendMDs(MDs, More...); + } + template + void appendMDs(SmallVectorImpl &MDs, Value *V, Ts... More) { + MDs.push_back(ValueAsMetadata::get(V)); + appendMDs(MDs, More...); + } + template + void appendMDs(SmallVectorImpl &MDs, const char *V, Ts... More) { + MDs.push_back(MDString::get(Context, V)); + appendMDs(MDs, More...); + } + template + void appendMDs(SmallVectorImpl &MDs, StringRef V, Ts... More) { + MDs.push_back(MDString::get(Context, V)); + appendMDs(MDs, More...); + } + template + void appendMDs(SmallVectorImpl &MDs, nullptr_t V, Ts... More) { + MDs.push_back(nullptr); + appendMDs(MDs, More...); + } + template + void appendMDs(SmallVectorImpl &MDs, MDTuple *V, Ts... More) { + MDs.push_back(V); + appendMDs(MDs, More...); + } + void appendMDs(SmallVectorImpl &MDs) { + // Base case, nothing to do. + } + + template MDTuple *get(Ts... Data) { + SmallVector 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) +} // namespace + +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 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 Buffer0 : register(t0); + StructType *BufType0 = + StructType::create(Context, {Int32Ty, FloatTy, DoubleTy}, "BufType0"); + Symbol = UndefValue::get(StructType::create( + Context, {BufType0}, "class.StructuredBuffer")); + 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 ColorMapTexture : register(t2); + Symbol = UndefValue::get(StructType::create( + Context, {Floatx4Ty}, "class.Texture2D >")); + 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 DepthBuffer : register(t0); + Symbol = UndefValue::get( + StructType::create(Context, {FloatTy}, "class.Texture2DMS")); + 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 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 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 OutputTexture : register(u0, space2); + Symbol = UndefValue::get(StructType::create( + Context, {Int32x2Ty}, "class.RWTexture2D >")); + 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 ROB; + Symbol = UndefValue::get( + StructType::create(Context, {Floatx4Ty}, + "class.RasterizerOrderedBuffer >")); + 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 g_OutputBuffer : register(u2); + StructType *BufType1 = StructType::create( + Context, {Floatx3Ty, FloatTy, Int32Ty}, "ParticleMotion"); + Symbol = UndefValue::get(StructType::create( + Context, {BufType1}, "class.StructuredBuffer")); + 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 g_rw_t2dmsa; + Symbol = UndefValue::get(StructType::create( + Context, {Int32Ty}, "class.RWTexture2DMSArray")); + 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)); +}