diff --git a/llvm/lib/Target/DirectX/CMakeLists.txt b/llvm/lib/Target/DirectX/CMakeLists.txt index dbefb4dac8ec63..e698107e370878 100644 --- a/llvm/lib/Target/DirectX/CMakeLists.txt +++ b/llvm/lib/Target/DirectX/CMakeLists.txt @@ -21,6 +21,7 @@ add_llvm_target(DirectXCodeGen DXILOpBuilder.cpp DXILOpLowering.cpp DXILPrepare.cpp + DXILResource.cpp DXILTranslateMetadata.cpp PointerTypeAnalysis.cpp diff --git a/llvm/lib/Target/DirectX/DXILResource.cpp b/llvm/lib/Target/DirectX/DXILResource.cpp new file mode 100644 index 00000000000000..910db4311ec920 --- /dev/null +++ b/llvm/lib/Target/DirectX/DXILResource.cpp @@ -0,0 +1,158 @@ +//===- DXILResource.cpp - DXIL Resource helper objects --------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file This file contains helper objects for working with DXIL Resources. +/// +//===----------------------------------------------------------------------===// + +#include "DXILResource.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" + +using namespace llvm; +using namespace llvm::dxil; + +GlobalVariable *FrontendResource::getGlobalVariable() { + return cast( + cast(Entry->getOperand(0))->getValue()); +} + +StringRef FrontendResource::getSourceType() { + return cast(Entry->getOperand(1))->getString(); +} + +Constant *FrontendResource::getID() { + return cast(Entry->getOperand(2))->getValue(); +} + +void Resources::collectUAVs() { + NamedMDNode *Entry = Mod.getNamedMetadata("hlsl.uavs"); + if (!Entry || Entry->getNumOperands() == 0) + return; + + uint32_t Counter = 0; + for (auto *UAV : Entry->operands()) { + UAVs.push_back(UAVResource(Counter++, FrontendResource(cast(UAV)))); + } +} + +ResourceBase::ResourceBase(uint32_t I, FrontendResource R) + : ID(I), GV(R.getGlobalVariable()), Name(""), Space(0), LowerBound(0), + RangeSize(1) { + if (auto *ArrTy = dyn_cast(GV->getInitializer()->getType())) + RangeSize = ArrTy->getNumElements(); +} + +UAVResource::UAVResource(uint32_t I, FrontendResource R) + : ResourceBase(I, R), Shape(Kinds::Invalid), GloballyCoherent(false), + HasCounter(false), IsROV(false), ExtProps() { + parseSourceType(R.getSourceType()); +} + +// FIXME: Capture this in HLSL source. I would go do this right now, but I want +// to get this in first so that I can make sure to capture all the extra +// information we need to remove the source type string from here (See issue: +// https://github.com/llvm/llvm-project/issues/57991). +void UAVResource::parseSourceType(StringRef S) { + IsROV = S.startswith("RasterizerOrdered"); + if (IsROV) + S = S.substr(strlen("RasterizerOrdered")); + if (S.startswith("RW")) + S = S.substr(strlen("RW")); + + // Note: I'm deliberately not handling any of the Texture buffer types at the + // moment. I want to resolve the issue above before adding Texture or Sampler + // support. + Shape = StringSwitch(S) + .StartsWith("Buffer<", Kinds::TypedBuffer) + .StartsWith("ByteAddressBuffer<", Kinds::RawBuffer) + .StartsWith("StructuredBuffer<", Kinds::StructuredBuffer) + .Default(Kinds::Invalid); + assert(Shape != Kinds::Invalid && "Unsupported buffer type"); + + S = S.substr(S.find("<") + 1); + + constexpr size_t PrefixLen = StringRef("vector<").size(); + if (S.startswith("vector<")) + S = S.substr(PrefixLen, S.find(",") - PrefixLen); + else + S = S.substr(0, S.find(">")); + + ComponentType ElTy = StringSwitch(S) + .Case("bool", ComponentType::I1) + .Case("int16_t", ComponentType::I16) + .Case("uint16_t", ComponentType::U16) + .Case("int32_t", ComponentType::I32) + .Case("uint32_t", ComponentType::U32) + .Case("int64_t", ComponentType::I64) + .Case("uint64_t", ComponentType::U64) + .Case("half", ComponentType::F16) + .Case("float", ComponentType::F32) + .Case("double", ComponentType::F64) + .Default(ComponentType::Invalid); + if (ElTy != ComponentType::Invalid) + ExtProps.ElementType = ElTy; +} + +MDNode *ResourceBase::ExtendedProperties::write(LLVMContext &Ctx) { + IRBuilder<> B(Ctx); + SmallVector Entries; + if (ElementType) { + Entries.emplace_back( + ConstantAsMetadata::get(B.getInt32(TypedBufferElementType))); + Entries.emplace_back(ConstantAsMetadata::get( + B.getInt32(static_cast(*ElementType)))); + } + if (Entries.empty()) + return nullptr; + return MDNode::get(Ctx, Entries); +} + +void ResourceBase::write(LLVMContext &Ctx, + MutableArrayRef Entries) { + IRBuilder<> B(Ctx); + Entries[0] = ConstantAsMetadata::get(B.getInt32(ID)); + Entries[1] = ConstantAsMetadata::get(GV); + Entries[2] = MDString::get(Ctx, Name); + Entries[3] = ConstantAsMetadata::get(B.getInt32(Space)); + Entries[4] = ConstantAsMetadata::get(B.getInt32(LowerBound)); + Entries[5] = ConstantAsMetadata::get(B.getInt32(RangeSize)); +} + +MDNode *UAVResource::write() { + auto &Ctx = GV->getContext(); + IRBuilder<> B(Ctx); + Metadata *Entries[11]; + ResourceBase::write(Ctx, Entries); + Entries[6] = + ConstantAsMetadata::get(B.getInt32(static_cast(Shape))); + Entries[7] = ConstantAsMetadata::get(B.getInt1(GloballyCoherent)); + Entries[8] = ConstantAsMetadata::get(B.getInt1(HasCounter)); + Entries[9] = ConstantAsMetadata::get(B.getInt1(IsROV)); + Entries[10] = ExtProps.write(Ctx); + return MDNode::get(Ctx, Entries); +} + +void Resources::write() { + Metadata *ResourceMDs[4] = {nullptr, nullptr, nullptr, nullptr}; + SmallVector UAVMDs; + for (auto &UAV : UAVs) + UAVMDs.emplace_back(UAV.write()); + + if (!UAVMDs.empty()) + ResourceMDs[1] = MDNode::get(Mod.getContext(), UAVMDs); + + NamedMDNode *DXResMD = Mod.getOrInsertNamedMetadata("dx.resources"); + DXResMD->addOperand(MDNode::get(Mod.getContext(), ResourceMDs)); + + NamedMDNode *Entry = Mod.getNamedMetadata("hlsl.uavs"); + if (Entry) + Entry->eraseFromParent(); +} diff --git a/llvm/lib/Target/DirectX/DXILResource.h b/llvm/lib/Target/DirectX/DXILResource.h new file mode 100644 index 00000000000000..332a89fa0c7e5d --- /dev/null +++ b/llvm/lib/Target/DirectX/DXILResource.h @@ -0,0 +1,157 @@ +//===- DXILResource.h - DXIL Resource helper objects ----------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file This file contains helper objects for working with DXIL Resources. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TARGET_DIRECTX_DXILRESOURCE_H +#define LLVM_TARGET_DIRECTX_DXILRESOURCE_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/Metadata.h" +#include + +namespace llvm { +class Module; +class GlobalVariable; + +namespace dxil { + +// FIXME: Ultimately this class and some of these utilities should be moved into +// a new LLVMFrontendHLSL library so that they can be reused in Clang. +// See issue https://github.com/llvm/llvm-project/issues/58000. +class FrontendResource { + MDNode *Entry; + +public: + FrontendResource(MDNode *E) : Entry(E) { + assert(Entry->getNumOperands() == 3 && "Unexpected metadata shape"); + } + + GlobalVariable *getGlobalVariable(); + StringRef getSourceType(); + Constant *getID(); +}; + +class ResourceBase { +protected: + uint32_t ID; + GlobalVariable *GV; + StringRef Name; + uint32_t Space; + uint32_t LowerBound; + uint32_t RangeSize; + ResourceBase(uint32_t I, FrontendResource R); + + void write(LLVMContext &Ctx, MutableArrayRef Entries); + + // The value ordering of this enumeration is part of the DXIL ABI. Elements + // can only be added to the end, and not removed. + enum class Kinds : uint32_t { + Invalid = 0, + Texture1D, + Texture2D, + Texture2DMS, + Texture3D, + TextureCube, + Texture1DArray, + Texture2DArray, + Texture2DMSArray, + TextureCubeArray, + TypedBuffer, + RawBuffer, + StructuredBuffer, + CBuffer, + Sampler, + TBuffer, + RTAccelerationStructure, + FeedbackTexture2D, + FeedbackTexture2DArray, + NumEntries, + }; + + // The value ordering of this enumeration is part of the DXIL ABI. Elements + // can only be added to the end, and not removed. + enum class ComponentType : uint32_t { + Invalid = 0, + I1, + I16, + U16, + I32, + U32, + I64, + U64, + F16, + F32, + F64, + SNormF16, + UNormF16, + SNormF32, + UNormF32, + SNormF64, + UNormF64, + PackedS8x32, + PackedU8x32, + LastEntry + }; + +public: + struct ExtendedProperties { + llvm::Optional ElementType; + + // The value ordering of this enumeration is part of the DXIL ABI. Elements + // can only be added to the end, and not removed. + enum Tags : uint32_t { + TypedBufferElementType = 0, + StructuredBufferElementStride, + SamplerFeedbackKind, + Atomic64Use + }; + + MDNode *write(LLVMContext &Ctx); + }; +}; + +class UAVResource : public ResourceBase { + ResourceBase::Kinds Shape; + bool GloballyCoherent; + bool HasCounter; + bool IsROV; + ResourceBase::ExtendedProperties ExtProps; + + void parseSourceType(StringRef S); + +public: + UAVResource(uint32_t I, FrontendResource R); + + MDNode *write(); +}; + +// FIXME: Fully computing the resource structures requires analyzing the IR +// because some flags are set based on what operations are performed on the +// resource. This partial patch handles some of the leg work, but not all of it. +// See issue https://github.com/llvm/llvm-project/issues/57936. +class Resources { + Module &Mod; + llvm::SmallVector UAVs; + + void collectUAVs(); + +public: + Resources(Module &M) : Mod(M) { collectUAVs(); } + + void write(); +}; + +} // namespace dxil +} // namespace llvm + +#endif // LLVM_TARGET_DIRECTX_DXILRESOURCE_H diff --git a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp index eafb17a6ccaf79..54409cce70114e 100644 --- a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp +++ b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp @@ -9,6 +9,7 @@ //===----------------------------------------------------------------------===// #include "DXILMetadata.h" +#include "DXILResource.h" #include "DirectX.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" @@ -38,6 +39,9 @@ bool DXILTranslateMetadata::runOnModule(Module &M) { if (ValVerMD.isEmpty()) ValVerMD.update(VersionTuple(1, 0)); dxil::createShaderModelMD(M); + + dxil::Resources Res(M); + Res.write(); return false; } diff --git a/llvm/test/CodeGen/DirectX/UAVMetadata.ll b/llvm/test/CodeGen/DirectX/UAVMetadata.ll new file mode 100644 index 00000000000000..b4edc7d13f4911 --- /dev/null +++ b/llvm/test/CodeGen/DirectX/UAVMetadata.ll @@ -0,0 +1,60 @@ +; RUN: opt -S -dxil-metadata-emit < %s | FileCheck %s +; ModuleID = '/home/cbieneman/dev/shuffle.hlsl' +target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-pc-shadermodel6.0-compute" + +%"class.hlsl::RWBuffer" = type { ptr } + +@Zero = local_unnamed_addr global %"class.hlsl::RWBuffer" zeroinitializer, align 4 +@One = local_unnamed_addr global %"class.hlsl::RWBuffer" zeroinitializer, align 4 +@Two = local_unnamed_addr global %"class.hlsl::RWBuffer" zeroinitializer, align 4 +@Three = local_unnamed_addr global [2 x %"class.hlsl::RWBuffer"] zeroinitializer, align 4 +@Four = local_unnamed_addr global %"class.hlsl::RWBuffer" zeroinitializer, align 4 +@Five = local_unnamed_addr global %"class.hlsl::RWBuffer" zeroinitializer, align 4 +@Six = local_unnamed_addr global %"class.hlsl::RWBuffer" zeroinitializer, align 4 +@Seven = local_unnamed_addr global %"class.hlsl::RWBuffer" zeroinitializer, align 4 +@Eight = local_unnamed_addr global %"class.hlsl::RWBuffer" zeroinitializer, align 4 +@Nine = local_unnamed_addr global %"class.hlsl::RWBuffer" zeroinitializer, align 4 + + +!hlsl.uavs = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9} + +!0 = !{ptr @Zero, !"RWBuffer", i32 0} +!1 = !{ptr @One, !"Buffer>", i32 1} +!2 = !{ptr @Two, !"Buffer", i32 2} +!3 = !{ptr @Three, !"Buffer", i32 3} +!4 = !{ptr @Four, !"ByteAddressBuffer", i32 4} +!5 = !{ptr @Five, !"StructuredBuffer", i32 5} +!6 = !{ptr @Six, !"RasterizerOrderedBuffer", i32 6} +!7 = !{ptr @Seven, !"RasterizerOrderedStructuredBuffer", i32 7} +!8 = !{ptr @Eight, !"RasterizerOrderedByteAddressBuffer", i32 8} +!9 = !{ptr @Nine, !"RWBuffer", i32 9} + + +; CHECK: !dx.resources = !{[[ResList:[!][0-9]+]]} + +; CHECK: [[ResList]] = !{null, [[UAVList:[!][0-9]+]], null, null} +; CHECK: [[UAVList]] = !{[[Zero:[!][0-9]+]], [[One:[!][0-9]+]], +; CHECK-SAME: [[Two:[!][0-9]+]], [[Three:[!][0-9]+]], [[Four:[!][0-9]+]], +; CHECK-SAME: [[Five:[!][0-9]+]], [[Six:[!][0-9]+]], [[Seven:[!][0-9]+]], +; CHECK-SAME: [[Eight:[!][0-9]+]], [[Nine:[!][0-9]+]]} +; CHECK: [[Zero]] = !{i32 0, ptr @Zero, !"", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, [[Half:[!][0-9]+]]} +; CHECK: [[Half]] = !{i32 0, i32 8} +; CHECK: [[One]] = !{i32 1, ptr @One, !"", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, [[Float:[!][0-9]+]]} +; CHECK: [[Float]] = !{i32 0, i32 9} +; CHECK: [[Two]] = !{i32 2, ptr @Two, !"", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, [[Double:[!][0-9]+]]} +; CHECK: [[Double]] = !{i32 0, i32 10} +; CHECK: [[Three]] = !{i32 3, ptr @Three, !"", i32 0, i32 0, i32 2, i32 10, i1 false, i1 false, i1 false, [[Bool:[!][0-9]+]]} +; CHECK: [[Bool]] = !{i32 0, i32 1} +; CHECK: [[Four]] = !{i32 4, ptr @Four, !"", i32 0, i32 0, i32 1, i32 11, i1 false, i1 false, i1 false, [[I16:[!][0-9]+]]} +; CHECK: [[I16]] = !{i32 0, i32 2} +; CHECK: [[Five]] = !{i32 5, ptr @Five, !"", i32 0, i32 0, i32 1, i32 12, i1 false, i1 false, i1 false, [[U16:[!][0-9]+]]} +; CHECK: [[U16]] = !{i32 0, i32 3} +; CHECK: [[Six]] = !{i32 6, ptr @Six, !"", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 true, [[I32:[!][0-9]+]]} +; CHECK: [[I32]] = !{i32 0, i32 4} +; CHECK: [[Seven]] = !{i32 7, ptr @Seven, !"", i32 0, i32 0, i32 1, i32 12, i1 false, i1 false, i1 true, [[U32:[!][0-9]+]]} +; CHECK: [[U32]] = !{i32 0, i32 5} +; CHECK: [[Eight]] = !{i32 8, ptr @Eight, !"", i32 0, i32 0, i32 1, i32 11, i1 false, i1 false, i1 true, [[I64:[!][0-9]+]]} +; CHECK: [[I64]] = !{i32 0, i32 6} +; CHECK: [[Nine]] = !{i32 9, ptr @Nine, !"", i32 0, i32 0, i32 1, i32 10, i1 false, i1 false, i1 false, [[U64:[!][0-9]+]]} +; CHECK: [[U64]] = !{i32 0, i32 7}