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 @@ -31,6 +31,7 @@ }; void createShaderModelMD(Module &M); +void createEntryMD(Module &M); } // 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,231 @@ Vals[2] = ConstantAsMetadata::get(B.getInt32(Ver.getMinor().value_or(0))); Entry->addOperand(MDNode::get(Ctx, Vals)); } + +static uint32_t getEnumShaderStage(Triple::EnvironmentType Env) { + // Must match D3D11_SHADER_VERSION_TYPE + enum ShaderKind { + Pixel = 0, + Vertex, + Geometry, + Hull, + Domain, + Compute, + Library, + RayGeneration, + Intersection, + AnyHit, + ClosestHit, + Miss, + Callable, + Mesh, + Amplification, + Invalid, + }; + switch (Env) { + case Triple::Pixel: + return ShaderKind::Pixel; + case Triple::Vertex: + return ShaderKind::Vertex; + case Triple::Geometry: + return ShaderKind::Geometry; + case Triple::Hull: + return ShaderKind::Hull; + case Triple::Domain: + return ShaderKind::Domain; + case Triple::Compute: + return ShaderKind::Compute; + case Triple::Library: + return ShaderKind::Library; + case Triple::Mesh: + return ShaderKind::Mesh; + case Triple::Amplification: + return ShaderKind::Amplification; + default: + break; + } + llvm_unreachable("Unsupported environment for DXIL generation."); + return ShaderKind::Invalid; +} + +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) { + Attribute EntryAttr = F.getFnAttribute("hlsl.shader"); + StringRef EntryProfile = EntryAttr.getValueAsString(); + Triple T("", "", "", EntryProfile); + ShaderKind = T.getEnvironment(); + if (ShaderKind == Triple::EnvironmentType::Compute) { + constexpr StringLiteral NumThreadsKey = "hlsl.numthreads"; + auto NumThreadsStr = F.getFnAttribute(NumThreadsKey).getValueAsString(); + SmallVector NumThreads; + NumThreadsStr.split(NumThreads, ','); + assert(NumThreads.size() == 3 && "invalid numthreads"); + for (int I = 0; I < 3; ++I) { + APInt V; + if (NumThreads[I].getAsInteger(10, V)) + assert(0 && "invalid num thread"); + CS.NumThreads[I] = 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); + } + +private: + enum EntryPropsTag { + DXILShaderFlagsTag = 0, + DXILNumThreadsTag = 4, + DXILShaderKindTag = 8, + }; + void appendNumThreads(std::vector &MDVals, LLVMContext &Ctx) { + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), DXILNumThreadsTag))); + + std::vector NumThreadVals; + for (int I = 0; I < 3; ++I) + NumThreadVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), CS.NumThreads[I]))); + MDVals.emplace_back(MDNode::get(Ctx, NumThreadVals)); + } + void appendShaderFlags(std::vector &MDVals, + uint64_t RawShaderFlag, LLVMContext &Ctx) { + MDVals.emplace_back(ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(Ctx), DXILShaderFlagsTag))); + 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), DXILShaderKindTag))); + MDVals.emplace_back(ConstantAsMetadata::get(ConstantInt::get( + Type::getInt32Ty(Ctx), getEnumShaderStage(ShaderKind)))); + } +}; + +class EntryMD { + Function &F; + LLVMContext &Ctx; + EntryProps Props; + +public: + EntryMD(Function &F) : F(F), Ctx(F.getContext()), Props(F) {} + 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 metadta*/ 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, LLVMContext &Ctx) { + return emitDxilEntryPointTuple(nullptr, "", nullptr, Resources, nullptr, + 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) { + 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)); + + // FIXME: build shader flags. + // See https://github.com/llvm/llvm-project/issues/57925 + uint64_t ShaderFlags = 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, Ctx); + Entries.emplace_back(EmptyEntry); + + for (Function *Entry : EntryList) { + EntryMD MD(*Entry); + Entries.emplace_back(MD.emitEntryTupleForLib(ShaderFlags)); + } + } 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()); + 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/DXILTranslateMetadata.cpp b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp --- a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp +++ b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp @@ -48,6 +48,8 @@ dxil::Resources &Res = getAnalysis().getDXILResource(); Res.write(M); + + dxil::createEntryMD(M); return 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 @@ -2,7 +2,7 @@ ; RUN: opt -S --passes="print-dxil-resource" < %s 2>&1 | FileCheck %s --check-prefix=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, ![[res:[0-9]+]], ![[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,20 @@ +; 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, null} + +;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: + ret void +} + +attributes #0 = { noinline nounwind "hlsl.numthreads"="1,2,1" "hlsl.shader"="compute" }