| //===- DXILMetadata.cpp - DXIL Metadata 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 metadata. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #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" |
| #include "llvm/Support/VersionTuple.h" |
| |
| using namespace llvm; |
| using namespace llvm::dxil; |
| |
| ValidatorVersionMD::ValidatorVersionMD(Module &M) |
| : Entry(M.getOrInsertNamedMetadata("dx.valver")) {} |
| |
| void ValidatorVersionMD::update(VersionTuple ValidatorVer) { |
| 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))); |
| |
| if (isEmpty()) |
| Entry->addOperand(MDNode::get(Ctx, MDVals)); |
| else |
| Entry->setOperand(0, MDNode::get(Ctx, MDVals)); |
| } |
| |
| bool ValidatorVersionMD::isEmpty() { return Entry->getNumOperands() == 0; } |
| |
| static StringRef getShortShaderStage(Triple::EnvironmentType Env) { |
| switch (Env) { |
| case Triple::Pixel: |
| return "ps"; |
| case Triple::Vertex: |
| return "vs"; |
| case Triple::Geometry: |
| return "gs"; |
| case Triple::Hull: |
| return "hs"; |
| case Triple::Domain: |
| return "ds"; |
| case Triple::Compute: |
| return "cs"; |
| case Triple::Library: |
| return "lib"; |
| case Triple::Mesh: |
| return "ms"; |
| case Triple::Amplification: |
| return "as"; |
| default: |
| break; |
| } |
| llvm_unreachable("Unsupported environment for DXIL generation."); |
| return ""; |
| } |
| |
| void dxil::createShaderModelMD(Module &M) { |
| NamedMDNode *Entry = M.getOrInsertNamedMetadata("dx.shaderModel"); |
| Triple TT(M.getTargetTriple()); |
| VersionTuple Ver = TT.getOSVersion(); |
| LLVMContext &Ctx = M.getContext(); |
| IRBuilder<> B(Ctx); |
| |
| Metadata *Vals[3]; |
| Vals[0] = MDString::get(Ctx, getShortShaderStage(TT.getEnvironment())); |
| Vals[1] = ConstantAsMetadata::get(B.getInt32(Ver.getMajor())); |
| 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<StringRef> NumThreads; |
| NumThreadsStr.split(NumThreads, ','); |
| assert(NumThreads.size() == 3 && "invalid numthreads"); |
| auto Zip = |
| llvm::zip(NumThreads, MutableArrayRef<unsigned>(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<Metadata *> 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<Metadata *> 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<Metadata *> &MDVals, LLVMContext &Ctx) { |
| MDVals.emplace_back(ConstantAsMetadata::get( |
| ConstantInt::get(Type::getInt32Ty(Ctx), NumThreadsTag))); |
| |
| std::vector<Metadata *> NumThreadVals; |
| for (auto Num : ArrayRef<unsigned>(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<Metadata *> &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<Metadata *> &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<Function *> 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<MDTuple>(NamedResources->getOperand(0)); |
| |
| std::vector<MDNode *> 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); |
| } |