diff --git a/llvm/lib/Target/DirectX/DXILMetadata.h b/llvm/lib/Target/DirectX/DXILMetadata.h --- a/llvm/lib/Target/DirectX/DXILMetadata.h +++ b/llvm/lib/Target/DirectX/DXILMetadata.h @@ -13,6 +13,8 @@ #ifndef LLVM_TARGET_DIRECTX_DXILMETADATA_H #define LLVM_TARGET_DIRECTX_DXILMETADATA_H +#include + namespace llvm { class Module; class NamedMDNode; @@ -31,6 +33,7 @@ }; void createShaderModelMD(Module &M); +void createEntryMD(Module &M, const uint64_t ShaderFlags); } // namespace dxil } // namespace llvm diff --git a/llvm/lib/Target/DirectX/DXILMetadata.cpp b/llvm/lib/Target/DirectX/DXILMetadata.cpp --- a/llvm/lib/Target/DirectX/DXILMetadata.cpp +++ b/llvm/lib/Target/DirectX/DXILMetadata.cpp @@ -12,6 +12,7 @@ #include "DXILMetadata.h" #include "llvm/ADT/Triple.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" @@ -27,7 +28,6 @@ auto &Ctx = Entry->getParent()->getContext(); IRBuilder<> B(Ctx); Metadata *MDVals[2]; - MDVals[0] = ConstantAsMetadata::get(B.getInt32(ValidatorVer.getMajor())); MDVals[1] = ConstantAsMetadata::get(B.getInt32(ValidatorVer.getMinor().value_or(0))); @@ -80,3 +80,230 @@ Vals[2] = ConstantAsMetadata::get(B.getInt32(Ver.getMinor().value_or(0))); Entry->addOperand(MDNode::get(Ctx, Vals)); } + +static uint32_t getShaderStage(Triple::EnvironmentType Env) { + return (uint32_t)Env - (uint32_t)llvm::Triple::Pixel; +} + +namespace { + +struct EntryProps { + Triple::EnvironmentType ShaderKind; + // FIXME: support more shader profiles. + // See https://github.com/llvm/llvm-project/issues/57927. + struct { + unsigned NumThreads[3]; + } CS; + + EntryProps(Function &F, Triple::EnvironmentType ModuleShaderKind) + : ShaderKind(ModuleShaderKind) { + + if (ShaderKind == Triple::EnvironmentType::Library) { + Attribute EntryAttr = F.getFnAttribute("hlsl.shader"); + StringRef EntryProfile = EntryAttr.getValueAsString(); + Triple T("", "", "", EntryProfile); + ShaderKind = T.getEnvironment(); + } + + if (ShaderKind == Triple::EnvironmentType::Compute) { + auto NumThreadsStr = + F.getFnAttribute("hlsl.numthreads").getValueAsString(); + SmallVector NumThreads; + NumThreadsStr.split(NumThreads, ','); + assert(NumThreads.size() == 3 && "invalid numthreads"); + auto Zip = + llvm::zip(NumThreads, MutableArrayRef(CS.NumThreads)); + for (auto It : Zip) { + StringRef Str = std::get<0>(It); + APInt V; + [[maybe_unused]] bool Result = Str.getAsInteger(10, V); + assert(!Result && "Failed to parse numthreads"); + + unsigned &Num = std::get<1>(It); + Num = V.getLimitedValue(); + } + } + } + + MDTuple *emitDXILEntryProps(uint64_t RawShaderFlag, LLVMContext &Ctx, + bool IsLib) { + std::vector MDVals; + + if (RawShaderFlag != 0) + appendShaderFlags(MDVals, RawShaderFlag, Ctx); + + // Add shader kind for lib entrys. + if (IsLib && ShaderKind != Triple::EnvironmentType::Library) + appendShaderKind(MDVals, Ctx); + + if (ShaderKind == Triple::EnvironmentType::Compute) + appendNumThreads(MDVals, Ctx); + // FIXME: support more props. + // See https://github.com/llvm/llvm-project/issues/57948. + return MDNode::get(Ctx, MDVals); + } + + static MDTuple *emitEntryPropsForEmptyEntry(uint64_t RawShaderFlag, + LLVMContext &Ctx) { + if (RawShaderFlag == 0) + return nullptr; + + std::vector MDVals; + + appendShaderFlags(MDVals, RawShaderFlag, Ctx); + // FIXME: support more props. + // See https://github.com/llvm/llvm-project/issues/57948. + return MDNode::get(Ctx, MDVals); + } + +private: + enum EntryPropsTag { + ShaderFlagsTag = 0, + GSStateTag, + DSStateTag, + HSStateTag, + NumThreadsTag, + AutoBindingSpaceTag, + RayPayloadSizeTag, + RayAttribSizeTag, + ShaderKindTag, + MSStateTag, + ASStateTag, + WaveSizeTag, + EntryRootSigTag, + }; + + void appendNumThreads(std::vector &MDVals, LLVMContext &Ctx) { + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), NumThreadsTag))); + + std::vector NumThreadVals; + for (auto Num : ArrayRef(CS.NumThreads)) + NumThreadVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), Num))); + MDVals.emplace_back(MDNode::get(Ctx, NumThreadVals)); + } + + static void appendShaderFlags(std::vector &MDVals, + uint64_t RawShaderFlag, LLVMContext &Ctx) { + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), ShaderFlagsTag))); + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt64Ty(Ctx), RawShaderFlag))); + } + + void appendShaderKind(std::vector &MDVals, LLVMContext &Ctx) { + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), ShaderKindTag))); + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), getShaderStage(ShaderKind)))); + } +}; + +class EntryMD { + Function &F; + LLVMContext &Ctx; + EntryProps Props; + +public: + EntryMD(Function &F, Triple::EnvironmentType ModuleShaderKind) + : F(F), Ctx(F.getContext()), Props(F, ModuleShaderKind) {} + + MDTuple *emitEntryTuple(MDTuple *Resources, uint64_t RawShaderFlag) { + // FIXME: add signature for profile other than CS. + // See https://github.com/llvm/llvm-project/issues/57928. + MDTuple *Signatures = nullptr; + return emitDxilEntryPointTuple( + &F, F.getName().str(), Signatures, Resources, + Props.emitDXILEntryProps(RawShaderFlag, Ctx, /*IsLib*/ false), Ctx); + } + + MDTuple *emitEntryTupleForLib(uint64_t RawShaderFlag) { + // FIXME: add signature for profile other than CS. + // See https://github.com/llvm/llvm-project/issues/57928. + MDTuple *Signatures = nullptr; + return emitDxilEntryPointTuple( + &F, F.getName().str(), Signatures, + /*entry in lib doesn't need resources metadata*/ nullptr, + Props.emitDXILEntryProps(RawShaderFlag, Ctx, /*IsLib*/ true), Ctx); + } + + // Library will have empty entry metadata which only store the resource table + // metadata. + static MDTuple *emitEmptyEntryForLib(MDTuple *Resources, + uint64_t RawShaderFlag, + LLVMContext &Ctx) { + return emitDxilEntryPointTuple( + nullptr, "", nullptr, Resources, + EntryProps::emitEntryPropsForEmptyEntry(RawShaderFlag, Ctx), Ctx); + } + +private: + static MDTuple *emitDxilEntryPointTuple(Function *Fn, const std::string &Name, + MDTuple *Signatures, + MDTuple *Resources, + MDTuple *Properties, + LLVMContext &Ctx) { + Metadata *MDVals[5]; + MDVals[0] = Fn ? ValueAsMetadata::get(Fn) : nullptr; + MDVals[1] = MDString::get(Ctx, Name.c_str()); + MDVals[2] = Signatures; + MDVals[3] = Resources; + MDVals[4] = Properties; + return MDNode::get(Ctx, MDVals); + } +}; +} // namespace + +void dxil::createEntryMD(Module &M, const uint64_t ShaderFlags) { + SmallVector EntryList; + for (auto &F : M.functions()) { + if (!F.hasFnAttribute("hlsl.shader")) + continue; + EntryList.emplace_back(&F); + } + + auto &Ctx = M.getContext(); + // FIXME: generate metadata for resource. + // See https://github.com/llvm/llvm-project/issues/57926. + MDTuple *MDResources = nullptr; + if (auto *NamedResources = M.getNamedMetadata("dx.resources")) + MDResources = dyn_cast(NamedResources->getOperand(0)); + + std::vector Entries; + Triple T = Triple(M.getTargetTriple()); + switch (T.getEnvironment()) { + case Triple::EnvironmentType::Library: { + // Add empty entry to put resource metadata. + MDTuple *EmptyEntry = + EntryMD::emitEmptyEntryForLib(MDResources, ShaderFlags, Ctx); + Entries.emplace_back(EmptyEntry); + + for (Function *Entry : EntryList) { + EntryMD MD(*Entry, T.getEnvironment()); + Entries.emplace_back(MD.emitEntryTupleForLib(0)); + } + } break; + case Triple::EnvironmentType::Compute: + case Triple::EnvironmentType::Amplification: + case Triple::EnvironmentType::Mesh: + case Triple::EnvironmentType::Vertex: + case Triple::EnvironmentType::Hull: + case Triple::EnvironmentType::Domain: + case Triple::EnvironmentType::Geometry: + case Triple::EnvironmentType::Pixel: { + assert(EntryList.size() == 1 && + "non-lib profiles should only have one entry"); + EntryMD MD(*EntryList.front(), T.getEnvironment()); + Entries.emplace_back(MD.emitEntryTuple(MDResources, ShaderFlags)); + } break; + default: + assert(0 && "invalid profile"); + break; + } + + NamedMDNode *EntryPointsNamedMD = + M.getOrInsertNamedMetadata("dx.entryPoints"); + for (auto *Entry : Entries) + EntryPointsNamedMD->addOperand(Entry); +} diff --git a/llvm/lib/Target/DirectX/DXILResource.cpp b/llvm/lib/Target/DirectX/DXILResource.cpp --- a/llvm/lib/Target/DirectX/DXILResource.cpp +++ b/llvm/lib/Target/DirectX/DXILResource.cpp @@ -324,8 +324,13 @@ if (!UAVMDs.empty()) ResourceMDs[1] = MDNode::get(M.getContext(), UAVMDs); - NamedMDNode *DXResMD = M.getOrInsertNamedMetadata("dx.resources"); - DXResMD->addOperand(MDNode::get(M.getContext(), ResourceMDs)); + bool HasResource = ResourceMDs[0] != nullptr || ResourceMDs[1] != nullptr || + ResourceMDs[2] != nullptr || ResourceMDs[3] != nullptr; + + if (HasResource) { + NamedMDNode *DXResMD = M.getOrInsertNamedMetadata("dx.resources"); + DXResMD->addOperand(MDNode::get(M.getContext(), ResourceMDs)); + } NamedMDNode *Entry = M.getNamedMetadata("hlsl.uavs"); if (Entry) diff --git a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp --- a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp +++ b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp @@ -11,6 +11,7 @@ #include "DXILMetadata.h" #include "DXILResource.h" #include "DXILResourceAnalysis.h" +#include "DXILShaderFlags.h" #include "DirectX.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" @@ -20,6 +21,7 @@ #include "llvm/Pass.h" using namespace llvm; +using namespace llvm::dxil; namespace { class DXILTranslateMetadata : public ModulePass { @@ -32,6 +34,7 @@ void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); AU.addRequired(); + AU.addRequired(); } bool runOnModule(Module &M) override; @@ -49,6 +52,11 @@ const dxil::Resources &Res = getAnalysis().getDXILResource(); Res.write(M); + + const uint64_t Flags = + (uint64_t)(getAnalysis().getShaderFlags()); + dxil::createEntryMD(M, Flags); + return false; } @@ -61,5 +69,6 @@ INITIALIZE_PASS_BEGIN(DXILTranslateMetadata, "dxil-metadata-emit", "DXIL Metadata Emit", false, false) INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapper) +INITIALIZE_PASS_DEPENDENCY(ShaderFlagsAnalysisWrapper) INITIALIZE_PASS_END(DXILTranslateMetadata, "dxil-metadata-emit", "DXIL Metadata Emit", false, false) diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-as.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-as.ll --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-as.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-as.ll @@ -3,3 +3,10 @@ ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"as", i32 6, i32 0} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="amplification" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll @@ -3,3 +3,10 @@ ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"cs", i32 6, i32 6} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.numthreads"="1,2,1" "hlsl.shader"="compute" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-gs.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-gs.ll --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-gs.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-gs.ll @@ -3,3 +3,10 @@ ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"gs", i32 6, i32 6} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="geometry" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-hs.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-hs.ll --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-hs.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-hs.ll @@ -3,3 +3,10 @@ ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"hs", i32 6, i32 6} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="hull" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ms.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ms.ll --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ms.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ms.ll @@ -3,3 +3,10 @@ ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"ms", i32 6, i32 6} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="mesh" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ps.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ps.ll --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ps.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-ps.ll @@ -3,3 +3,10 @@ ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"ps", i32 5, i32 0} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="pixel" } diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-vs.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-vs.ll --- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-vs.ll +++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-vs.ll @@ -3,3 +3,10 @@ ; CHECK: !dx.shaderModel = !{![[SM:[0-9]+]]} ; CHECK: ![[SM]] = !{!"vs", i32 0, i32 0} + +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.shader"="vertex" } diff --git a/llvm/test/CodeGen/DirectX/UAVMetadata.ll b/llvm/test/CodeGen/DirectX/UAVMetadata.ll --- a/llvm/test/CodeGen/DirectX/UAVMetadata.ll +++ b/llvm/test/CodeGen/DirectX/UAVMetadata.ll @@ -3,7 +3,7 @@ ; RUN: llc %s --filetype=asm -o - < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,PRINT 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" +target triple = "dxil-pc-shadermodel6.0-library" %"class.hlsl::RWBuffer" = type { ptr } diff --git a/llvm/test/CodeGen/DirectX/empty_cs_entry.ll b/llvm/test/CodeGen/DirectX/empty_cs_entry.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/DirectX/empty_cs_entry.ll @@ -0,0 +1,17 @@ +; RUN: opt -S -dxil-metadata-emit < %s | FileCheck %s +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-unknown-shadermodel6.7-compute" + +;CHECK:!dx.entryPoints = !{![[entry:[0-9]+]]} + +;CHECK:![[entry]] = !{ptr @entry, !"entry", null, null, ![[extra:[0-9]+]]} +;CHECK:![[extra]] = !{i32 4, ![[numthreads:[0-9]+]]} +;CHECK:![[numthreads]] = !{i32 1, i32 2, i32 1} + +; Function Attrs: noinline nounwind +define void @entry() #0 { +entry: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.numthreads"="1,2,1" "hlsl.shader"="compute" } diff --git a/llvm/test/CodeGen/DirectX/lib_entry.ll b/llvm/test/CodeGen/DirectX/lib_entry.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/DirectX/lib_entry.ll @@ -0,0 +1,22 @@ +; RUN: opt -S -dxil-metadata-emit < %s | FileCheck %s +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-unknown-shadermodel6.7-library" + +;CHECK:!dx.entryPoints = !{![[empty_entry:[0-9]+]], ![[entry:[0-9]+]]} + +; Make sure generate empty entry for lib profile. +;CHECK:![[empty_entry]] = !{null, !"", null, null, ![[shader_flags:[0-9]+]]} +; Make sure double is marked for shader flags. +;CHECK:![[shader_flags]] = !{i32 0, i64 1} +;CHECK:![[entry]] = !{ptr @entry, !"entry", null, null, ![[extra:[0-9]+]]} +;CHECK:![[extra]] = !{i32 8, i32 5, i32 4, ![[numthreads:[0-9]+]]} +;CHECK:![[numthreads]] = !{i32 1, i32 2, i32 1} + +; Function Attrs: noinline nounwind +define void @entry() #0 { +entry: + %0 = fpext float 2.000000e+00 to double + ret void +} + +attributes #0 = { noinline nounwind "hlsl.numthreads"="1,2,1" "hlsl.shader"="compute" }