| //===- 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); | |
| } |