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 @@ -28,6 +28,11 @@ Constant::getIntegerValue(IntegerType::get(Ctx, 32), APInt(32, v))); } +static ConstantAsMetadata *Uint64ToConstMD(uint64_t V, LLVMContext &Ctx) { + return ConstantAsMetadata::get( + Constant::getIntegerValue(IntegerType::get(Ctx, 64), APInt(64, V))); +} + constexpr StringLiteral ValVerKey = "dx.valver"; constexpr unsigned DXILVersionNumFields = 2; @@ -49,6 +54,146 @@ DXILValidatorVersionMD->addOperand(MDNode::get(Ctx, MDVals)); } +namespace { +struct DxilFunctionProps { + Triple::EnvironmentType ShaderKind; + union { + // Compute shader. + struct { + unsigned NumThreads[3]; + } CS; + } ShaderProps; +}; + +const unsigned DXILEntryPointNumFields = 5; +// Entry point function symbol. +const unsigned DXILEntryPointFunction = 0; +// Entry point unmangled name. +const unsigned DXILEntryPointName = 1; +// Entry point signature tuple. +const unsigned DXILEntryPointSignatures = 2; +// Entry point resource tuple. +const unsigned DXILEntryPointResources = 3; +// Entry point properties tuple. +const unsigned DXILEntryPointProperties = 4; +} // namespace + +static MDTuple *emitDXILEntryProps(DxilFunctionProps &Props, LLVMContext &Ctx, + uint64_t RawShaderFlag) { + std::vector MDVals; + + if (RawShaderFlag != 0) { + static const unsigned DxilShaderFlagsTag = 0; + MDVals.emplace_back(Uint32ToConstMD(DxilShaderFlagsTag, Ctx)); + MDVals.emplace_back(Uint64ToConstMD(RawShaderFlag, Ctx)); + } + + if (Props.ShaderKind == Triple::EnvironmentType::Compute) { + auto &CS = Props.ShaderProps.CS; + static const unsigned DXILNumThreadsTag = 4; + MDVals.emplace_back(Uint32ToConstMD(DXILNumThreadsTag, Ctx)); + + std::vector NumThreadVals; + NumThreadVals.emplace_back(Uint32ToConstMD(CS.NumThreads[0], Ctx)); + NumThreadVals.emplace_back(Uint32ToConstMD(CS.NumThreads[1], Ctx)); + NumThreadVals.emplace_back(Uint32ToConstMD(CS.NumThreads[2], Ctx)); + MDVals.emplace_back(MDNode::get(Ctx, NumThreadVals)); + } + + return MDNode::get(Ctx, MDVals); +} + +static MDTuple *emitDxilEntryPointTuple(Function *Fn, const std::string &Name, + MDTuple *Signatures, MDTuple *Resources, + MDTuple *Properties, LLVMContext &Ctx) { + Metadata *MDVals[DXILEntryPointNumFields]; + MDVals[DXILEntryPointFunction] = Fn ? ValueAsMetadata::get(Fn) : nullptr; + MDVals[DXILEntryPointName] = MDString::get(Ctx, Name.c_str()); + MDVals[DXILEntryPointSignatures] = Signatures; + MDVals[DXILEntryPointResources] = Resources; + MDVals[DXILEntryPointProperties] = Properties; + return MDNode::get(Ctx, MDVals); +} + +static uint64_t collectShaderFlags(Module &M) { + uint64_t Flags = 0; + // FIXME: build shader flags. + return Flags; +} + +static void emitDXILEntry(Module &M) { + SmallVector> EntryList; + constexpr StringLiteral DXILEntryKey = "dx.shader"; + for (auto &F : M.functions()) { + if (!F.hasFnAttribute(DXILEntryKey)) + continue; + Attribute EntryAttr = F.getFnAttribute(DXILEntryKey); + StringRef EntryProfile = EntryAttr.getValueAsString(); + Triple T("", "", "", EntryProfile); + DxilFunctionProps Props; + Props.ShaderKind = T.getEnvironment(); + if (Props.ShaderKind == Triple::EnvironmentType::Compute) { + constexpr StringLiteral NumThreadsKey = "dx.numthreads"; + auto NumThreadsStr = F.getFnAttribute(NumThreadsKey).getValueAsString(); + SmallVector NumThreads; + NumThreadsStr.split(NumThreads, ','); + assert(NumThreads.size() == 3 && "invalid numthreads"); + Props.ShaderProps.CS.NumThreads[0] = std::stoi(NumThreads[0].str()); + Props.ShaderProps.CS.NumThreads[1] = std::stoi(NumThreads[1].str()); + Props.ShaderProps.CS.NumThreads[2] = std::stoi(NumThreads[2].str()); + } + EntryList.emplace_back(std::make_pair(&F, Props)); + } + + auto &Ctx = M.getContext(); + // FIXME: generate metadata for resource. + MDTuple *MDResources = nullptr; + + uint64_t ShaderFlags = collectShaderFlags(M); + std::vector Entries; + + Triple T = Triple(M.getTargetTriple()); + switch (T.getEnvironment()) { + default: + llvm_unreachable("invalid profile"); + break; + case Triple::EnvironmentType::Library: { + // Add empty entry to put resource metadata. + MDTuple *EmptyEntry = + emitDxilEntryPointTuple(nullptr, "", nullptr, nullptr, nullptr, Ctx); + Entries.emplace_back(EmptyEntry); + } break; + // FIXME: support more shader profiles. + case Triple::EnvironmentType::Compute: + assert(EntryList.size() == 1 && + "non-lib profiles should only have one entry"); + break; + } + + for (auto &It : EntryList) { + Function *F = It.first; + DxilFunctionProps &Props = It.second; + MDTuple *MDProps = emitDXILEntryProps(Props, Ctx, ShaderFlags); + // FIXME add signature for profile other than CS. + MDTuple *Sig = nullptr; + + MDTuple *SubEntry = emitDxilEntryPointTuple(F, F->getName().str(), Sig, + nullptr, MDProps, Ctx); + + Entries.emplace_back(SubEntry); + } + + // Set resource for first entry. + Entries.front()->replaceOperandWith(DXILEntryPointResources, MDResources); + + constexpr StringLiteral DXILEntryPointsMDName = "dx.entryPoints"; + NamedMDNode *EntryPointsNamedMD = + M.getOrInsertNamedMetadata(DXILEntryPointsMDName); + for (auto *Entry : Entries) { + EntryPointsNamedMD->addOperand(Entry); + } +} + static VersionTuple loadDXILValidatorVersion(MDNode *ValVerMD) { if (ValVerMD->getNumOperands() != DXILVersionNumFields) return VersionTuple(); @@ -107,6 +252,7 @@ ValidatorVer = ValVer; } emitDXILValidatorVersion(M, ValidatorVer); + emitDXILEntry(M); cleanModule(M); return false; } 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 "dx.numthreads"="1,2,1" "dx.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 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 "dx.numthreads"="1,2,1" "dx.shader"="compute" }