| //===- DXILTranslateMetadata.cpp - Pass to emit DXIL metadata -------------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "DXILTranslateMetadata.h" | |
| #include "DXILRootSignature.h" | |
| #include "DXILShaderFlags.h" | |
| #include "DirectX.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/Twine.h" | |
| #include "llvm/Analysis/DXILMetadataAnalysis.h" | |
| #include "llvm/Analysis/DXILResource.h" | |
| #include "llvm/IR/BasicBlock.h" | |
| #include "llvm/IR/Constants.h" | |
| #include "llvm/IR/DiagnosticInfo.h" | |
| #include "llvm/IR/DiagnosticPrinter.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/IR/LLVMContext.h" | |
| #include "llvm/IR/MDBuilder.h" | |
| #include "llvm/IR/Metadata.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/InitializePasses.h" | |
| #include "llvm/Pass.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/VersionTuple.h" | |
| #include "llvm/TargetParser/Triple.h" | |
| #include <cstdint> | |
| using namespace llvm; | |
| using namespace llvm::dxil; | |
| namespace { | |
| /// A simple wrapper of DiagnosticInfo that generates module-level diagnostic | |
| /// for the DXILValidateMetadata pass | |
| class DiagnosticInfoValidateMD : public DiagnosticInfo { | |
| private: | |
| const Twine &Msg; | |
| const Module &Mod; | |
| public: | |
| /// \p M is the module for which the diagnostic is being emitted. \p Msg is | |
| /// the message to show. Note that this class does not copy this message, so | |
| /// this reference must be valid for the whole life time of the diagnostic. | |
| DiagnosticInfoValidateMD(const Module &M, | |
| const Twine &Msg LLVM_LIFETIME_BOUND, | |
| DiagnosticSeverity Severity = DS_Error) | |
| : DiagnosticInfo(DK_Unsupported, Severity), Msg(Msg), Mod(M) {} | |
| void print(DiagnosticPrinter &DP) const override { | |
| DP << Mod.getName() << ": " << Msg << '\n'; | |
| } | |
| }; | |
| static void reportError(Module &M, Twine Message, | |
| DiagnosticSeverity Severity = DS_Error) { | |
| M.getContext().diagnose(DiagnosticInfoValidateMD(M, Message, Severity)); | |
| } | |
| static void reportLoopError(Module &M, Twine Message, | |
| DiagnosticSeverity Severity = DS_Error) { | |
| reportError(M, Twine("Invalid \"llvm.loop\" metadata: ") + Message, Severity); | |
| } | |
| enum class EntryPropsTag { | |
| ShaderFlags = 0, | |
| GSState, | |
| DSState, | |
| HSState, | |
| NumThreads, | |
| AutoBindingSpace, | |
| RayPayloadSize, | |
| RayAttribSize, | |
| ShaderKind, | |
| MSState, | |
| ASStateTag, | |
| WaveSize, | |
| EntryRootSig, | |
| WaveRange = 23, | |
| }; | |
| } // namespace | |
| static NamedMDNode *emitResourceMetadata(Module &M, DXILResourceMap &DRM, | |
| DXILResourceTypeMap &DRTM) { | |
| LLVMContext &Context = M.getContext(); | |
| for (ResourceInfo &RI : DRM) | |
| if (!RI.hasSymbol()) | |
| RI.createSymbol(M, | |
| DRTM[RI.getHandleTy()].createElementStruct(RI.getName())); | |
| SmallVector<Metadata *> SRVs, UAVs, CBufs, Smps; | |
| for (const ResourceInfo &RI : DRM.srvs()) | |
| SRVs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()])); | |
| for (const ResourceInfo &RI : DRM.uavs()) | |
| UAVs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()])); | |
| for (const ResourceInfo &RI : DRM.cbuffers()) | |
| CBufs.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()])); | |
| for (const ResourceInfo &RI : DRM.samplers()) | |
| Smps.push_back(RI.getAsMetadata(M, DRTM[RI.getHandleTy()])); | |
| Metadata *SRVMD = SRVs.empty() ? nullptr : MDNode::get(Context, SRVs); | |
| Metadata *UAVMD = UAVs.empty() ? nullptr : MDNode::get(Context, UAVs); | |
| Metadata *CBufMD = CBufs.empty() ? nullptr : MDNode::get(Context, CBufs); | |
| Metadata *SmpMD = Smps.empty() ? nullptr : MDNode::get(Context, Smps); | |
| if (DRM.empty()) | |
| return nullptr; | |
| NamedMDNode *ResourceMD = M.getOrInsertNamedMetadata("dx.resources"); | |
| ResourceMD->addOperand( | |
| MDNode::get(M.getContext(), {SRVMD, UAVMD, CBufMD, SmpMD})); | |
| return ResourceMD; | |
| } | |
| 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"; | |
| case Triple::RootSignature: | |
| return "rootsig"; | |
| default: | |
| break; | |
| } | |
| llvm_unreachable("Unsupported environment for DXIL generation."); | |
| } | |
| static uint32_t getShaderStage(Triple::EnvironmentType Env) { | |
| return (uint32_t)Env - (uint32_t)llvm::Triple::Pixel; | |
| } | |
| static SmallVector<Metadata *> | |
| getTagValueAsMetadata(EntryPropsTag Tag, uint64_t Value, LLVMContext &Ctx) { | |
| SmallVector<Metadata *> MDVals; | |
| MDVals.emplace_back(ConstantAsMetadata::get( | |
| ConstantInt::get(Type::getInt32Ty(Ctx), static_cast<int>(Tag)))); | |
| switch (Tag) { | |
| case EntryPropsTag::ShaderFlags: | |
| MDVals.emplace_back(ConstantAsMetadata::get( | |
| ConstantInt::get(Type::getInt64Ty(Ctx), Value))); | |
| break; | |
| case EntryPropsTag::ShaderKind: | |
| MDVals.emplace_back(ConstantAsMetadata::get( | |
| ConstantInt::get(Type::getInt32Ty(Ctx), Value))); | |
| break; | |
| case EntryPropsTag::GSState: | |
| case EntryPropsTag::DSState: | |
| case EntryPropsTag::HSState: | |
| case EntryPropsTag::NumThreads: | |
| case EntryPropsTag::AutoBindingSpace: | |
| case EntryPropsTag::RayPayloadSize: | |
| case EntryPropsTag::RayAttribSize: | |
| case EntryPropsTag::MSState: | |
| case EntryPropsTag::ASStateTag: | |
| case EntryPropsTag::WaveSize: | |
| case EntryPropsTag::EntryRootSig: | |
| case EntryPropsTag::WaveRange: | |
| llvm_unreachable("NYI: Unhandled entry property tag"); | |
| } | |
| return MDVals; | |
| } | |
| static MDTuple *getEntryPropAsMetadata(Module &M, const EntryProperties &EP, | |
| uint64_t EntryShaderFlags, | |
| const ModuleMetadataInfo &MMDI) { | |
| SmallVector<Metadata *> MDVals; | |
| LLVMContext &Ctx = EP.Entry->getContext(); | |
| if (EntryShaderFlags != 0) | |
| MDVals.append(getTagValueAsMetadata(EntryPropsTag::ShaderFlags, | |
| EntryShaderFlags, Ctx)); | |
| if (EP.Entry != nullptr) { | |
| // FIXME: support more props. | |
| // See https://github.com/llvm/llvm-project/issues/57948. | |
| // Add shader kind for lib entries. | |
| if (MMDI.ShaderProfile == Triple::EnvironmentType::Library && | |
| EP.ShaderStage != Triple::EnvironmentType::Library) | |
| MDVals.append(getTagValueAsMetadata(EntryPropsTag::ShaderKind, | |
| getShaderStage(EP.ShaderStage), Ctx)); | |
| if (EP.ShaderStage == Triple::EnvironmentType::Compute) { | |
| // Handle mandatory "hlsl.numthreads" | |
| MDVals.emplace_back(ConstantAsMetadata::get(ConstantInt::get( | |
| Type::getInt32Ty(Ctx), static_cast<int>(EntryPropsTag::NumThreads)))); | |
| Metadata *NumThreadVals[] = {ConstantAsMetadata::get(ConstantInt::get( | |
| Type::getInt32Ty(Ctx), EP.NumThreadsX)), | |
| ConstantAsMetadata::get(ConstantInt::get( | |
| Type::getInt32Ty(Ctx), EP.NumThreadsY)), | |
| ConstantAsMetadata::get(ConstantInt::get( | |
| Type::getInt32Ty(Ctx), EP.NumThreadsZ))}; | |
| MDVals.emplace_back(MDNode::get(Ctx, NumThreadVals)); | |
| // Handle optional "hlsl.wavesize". The fields are optionally represented | |
| // if they are non-zero. | |
| if (EP.WaveSizeMin != 0) { | |
| bool IsWaveRange = VersionTuple(6, 8) <= MMDI.ShaderModelVersion; | |
| bool IsWaveSize = | |
| !IsWaveRange && VersionTuple(6, 6) <= MMDI.ShaderModelVersion; | |
| if (!IsWaveRange && !IsWaveSize) { | |
| reportError(M, "Shader model 6.6 or greater is required to specify " | |
| "the \"hlsl.wavesize\" function attribute"); | |
| return nullptr; | |
| } | |
| // A range is being specified if EP.WaveSizeMax != 0 | |
| if (EP.WaveSizeMax && !IsWaveRange) { | |
| reportError( | |
| M, "Shader model 6.8 or greater is required to specify " | |
| "wave size range values of the \"hlsl.wavesize\" function " | |
| "attribute"); | |
| return nullptr; | |
| } | |
| EntryPropsTag Tag = | |
| IsWaveSize ? EntryPropsTag::WaveSize : EntryPropsTag::WaveRange; | |
| MDVals.emplace_back(ConstantAsMetadata::get( | |
| ConstantInt::get(Type::getInt32Ty(Ctx), static_cast<int>(Tag)))); | |
| SmallVector<Metadata *> WaveSizeVals = {ConstantAsMetadata::get( | |
| ConstantInt::get(Type::getInt32Ty(Ctx), EP.WaveSizeMin))}; | |
| if (IsWaveRange) { | |
| WaveSizeVals.push_back(ConstantAsMetadata::get( | |
| ConstantInt::get(Type::getInt32Ty(Ctx), EP.WaveSizeMax))); | |
| WaveSizeVals.push_back(ConstantAsMetadata::get( | |
| ConstantInt::get(Type::getInt32Ty(Ctx), EP.WaveSizePref))); | |
| } | |
| MDVals.emplace_back(MDNode::get(Ctx, WaveSizeVals)); | |
| } | |
| } | |
| } | |
| if (MDVals.empty()) | |
| return nullptr; | |
| return MDNode::get(Ctx, MDVals); | |
| } | |
| static MDTuple *constructEntryMetadata(const Function *EntryFn, | |
| MDTuple *Signatures, MDNode *Resources, | |
| MDTuple *Properties, LLVMContext &Ctx) { | |
| // Each entry point metadata record specifies: | |
| // * reference to the entry point function global symbol | |
| // * unmangled name | |
| // * list of signatures | |
| // * list of resources | |
| // * list of tag-value pairs of shader capabilities and other properties | |
| Metadata *MDVals[5]; | |
| MDVals[0] = | |
| EntryFn ? ValueAsMetadata::get(const_cast<Function *>(EntryFn)) : nullptr; | |
| MDVals[1] = MDString::get(Ctx, EntryFn ? EntryFn->getName() : ""); | |
| MDVals[2] = Signatures; | |
| MDVals[3] = Resources; | |
| MDVals[4] = Properties; | |
| return MDNode::get(Ctx, MDVals); | |
| } | |
| static MDTuple *emitEntryMD(Module &M, const EntryProperties &EP, | |
| MDTuple *Signatures, MDNode *MDResources, | |
| const uint64_t EntryShaderFlags, | |
| const ModuleMetadataInfo &MMDI) { | |
| MDTuple *Properties = getEntryPropAsMetadata(M, EP, EntryShaderFlags, MMDI); | |
| return constructEntryMetadata(EP.Entry, Signatures, MDResources, Properties, | |
| EP.Entry->getContext()); | |
| } | |
| static void emitValidatorVersionMD(Module &M, const ModuleMetadataInfo &MMDI) { | |
| if (MMDI.ValidatorVersion.empty()) | |
| return; | |
| LLVMContext &Ctx = M.getContext(); | |
| IRBuilder<> IRB(Ctx); | |
| Metadata *MDVals[2]; | |
| MDVals[0] = | |
| ConstantAsMetadata::get(IRB.getInt32(MMDI.ValidatorVersion.getMajor())); | |
| MDVals[1] = ConstantAsMetadata::get( | |
| IRB.getInt32(MMDI.ValidatorVersion.getMinor().value_or(0))); | |
| NamedMDNode *ValVerNode = M.getOrInsertNamedMetadata("dx.valver"); | |
| // Set validator version obtained from DXIL Metadata Analysis pass | |
| ValVerNode->clearOperands(); | |
| ValVerNode->addOperand(MDNode::get(Ctx, MDVals)); | |
| } | |
| static void emitShaderModelVersionMD(Module &M, | |
| const ModuleMetadataInfo &MMDI) { | |
| LLVMContext &Ctx = M.getContext(); | |
| IRBuilder<> IRB(Ctx); | |
| Metadata *SMVals[3]; | |
| VersionTuple SM = MMDI.ShaderModelVersion; | |
| SMVals[0] = MDString::get(Ctx, getShortShaderStage(MMDI.ShaderProfile)); | |
| SMVals[1] = ConstantAsMetadata::get(IRB.getInt32(SM.getMajor())); | |
| SMVals[2] = ConstantAsMetadata::get(IRB.getInt32(SM.getMinor().value_or(0))); | |
| NamedMDNode *SMMDNode = M.getOrInsertNamedMetadata("dx.shaderModel"); | |
| SMMDNode->addOperand(MDNode::get(Ctx, SMVals)); | |
| } | |
| static void emitDXILVersionTupleMD(Module &M, const ModuleMetadataInfo &MMDI) { | |
| LLVMContext &Ctx = M.getContext(); | |
| IRBuilder<> IRB(Ctx); | |
| VersionTuple DXILVer = MMDI.DXILVersion; | |
| Metadata *DXILVals[2]; | |
| DXILVals[0] = ConstantAsMetadata::get(IRB.getInt32(DXILVer.getMajor())); | |
| DXILVals[1] = | |
| ConstantAsMetadata::get(IRB.getInt32(DXILVer.getMinor().value_or(0))); | |
| NamedMDNode *DXILVerMDNode = M.getOrInsertNamedMetadata("dx.version"); | |
| DXILVerMDNode->addOperand(MDNode::get(Ctx, DXILVals)); | |
| } | |
| static MDTuple *emitTopLevelLibraryNode(Module &M, MDNode *RMD, | |
| uint64_t ShaderFlags) { | |
| LLVMContext &Ctx = M.getContext(); | |
| MDTuple *Properties = nullptr; | |
| if (ShaderFlags != 0) { | |
| SmallVector<Metadata *> MDVals; | |
| MDVals.append( | |
| getTagValueAsMetadata(EntryPropsTag::ShaderFlags, ShaderFlags, Ctx)); | |
| Properties = MDNode::get(Ctx, MDVals); | |
| } | |
| // Library has an entry metadata with resource table metadata and all other | |
| // MDNodes as null. | |
| return constructEntryMetadata(nullptr, nullptr, RMD, Properties, Ctx); | |
| } | |
| static void translateBranchMetadata(Module &M, Instruction *BBTerminatorInst) { | |
| MDNode *HlslControlFlowMD = | |
| BBTerminatorInst->getMetadata("hlsl.controlflow.hint"); | |
| if (!HlslControlFlowMD) | |
| return; | |
| assert(HlslControlFlowMD->getNumOperands() == 2 && | |
| "invalid operands for hlsl.controlflow.hint"); | |
| MDBuilder MDHelper(M.getContext()); | |
| llvm::Metadata *HintsStr = MDHelper.createString("dx.controlflow.hints"); | |
| llvm::Metadata *HintsValue = MDHelper.createConstant( | |
| mdconst::extract<ConstantInt>(HlslControlFlowMD->getOperand(1))); | |
| MDNode *MDNode = llvm::MDNode::get(M.getContext(), {HintsStr, HintsValue}); | |
| BBTerminatorInst->setMetadata("dx.controlflow.hints", MDNode); | |
| BBTerminatorInst->setMetadata("hlsl.controlflow.hint", nullptr); | |
| } | |
| // Determines if the metadata node will be compatible with DXIL's loop metadata | |
| // representation. | |
| // | |
| // Reports an error for compatible metadata that is ill-formed. | |
| static bool isLoopMDCompatible(Module &M, Metadata *MD) { | |
| // DXIL only accepts the following loop hints: | |
| std::array<StringLiteral, 3> ValidHintNames = {"llvm.loop.unroll.count", | |
| "llvm.loop.unroll.disable", | |
| "llvm.loop.unroll.full"}; | |
| MDNode *HintMD = dyn_cast<MDNode>(MD); | |
| if (!HintMD || HintMD->getNumOperands() == 0) | |
| return false; | |
| auto *HintStr = dyn_cast<MDString>(HintMD->getOperand(0)); | |
| if (!HintStr) | |
| return false; | |
| if (!llvm::is_contained(ValidHintNames, HintStr->getString())) | |
| return false; | |
| auto ValidCountNode = [](MDNode *CountMD) -> bool { | |
| if (CountMD->getNumOperands() == 2) | |
| if (auto *Count = dyn_cast<ConstantAsMetadata>(CountMD->getOperand(1))) | |
| if (isa<ConstantInt>(Count->getValue())) | |
| return true; | |
| return false; | |
| }; | |
| if (HintStr->getString() == "llvm.loop.unroll.count") { | |
| if (!ValidCountNode(HintMD)) { | |
| reportLoopError(M, "\"llvm.loop.unroll.count\" must have 2 operands and " | |
| "the second must be a constant integer"); | |
| return false; | |
| } | |
| } else if (HintMD->getNumOperands() != 1) { | |
| reportLoopError( | |
| M, "\"llvm.loop.unroll.disable\" and \"llvm.loop.unroll.full\" " | |
| "must be provided as a single operand"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| static void translateLoopMetadata(Module &M, Instruction *I, MDNode *BaseMD) { | |
| // A distinct node has the self-referential form: !0 = !{ !0, ... } | |
| auto IsDistinctNode = [](MDNode *Node) -> bool { | |
| return Node && Node->getNumOperands() != 0 && Node == Node->getOperand(0); | |
| }; | |
| // Set metadata to null to remove empty/ill-formed metadata from instruction | |
| if (BaseMD->getNumOperands() == 0 || !IsDistinctNode(BaseMD)) | |
| return I->setMetadata("llvm.loop", nullptr); | |
| // It is valid to have a chain of self-refential loop metadata nodes, as | |
| // below. We will collapse these into just one when we reconstruct the | |
| // metadata. | |
| // | |
| // Eg: | |
| // !0 = !{!0, !1} | |
| // !1 = !{!1, !2} | |
| // !2 = !{!"llvm.loop.unroll.disable"} | |
| // | |
| // So, traverse down a potential self-referential chain | |
| while (1 < BaseMD->getNumOperands() && | |
| IsDistinctNode(dyn_cast<MDNode>(BaseMD->getOperand(1)))) | |
| BaseMD = dyn_cast<MDNode>(BaseMD->getOperand(1)); | |
| // To reconstruct a distinct node we create a temporary node that we will | |
| // then update to create a self-reference. | |
| llvm::TempMDTuple TempNode = llvm::MDNode::getTemporary(M.getContext(), {}); | |
| SmallVector<Metadata *> CompatibleOperands = {TempNode.get()}; | |
| // Iterate and reconstruct the metadata nodes that contains any hints, | |
| // stripping any unrecognized metadata. | |
| ArrayRef<MDOperand> Operands = BaseMD->operands(); | |
| for (auto &Op : Operands.drop_front()) | |
| if (isLoopMDCompatible(M, Op.get())) | |
| CompatibleOperands.push_back(Op.get()); | |
| if (2 < CompatibleOperands.size()) | |
| reportLoopError(M, "Provided conflicting hints"); | |
| MDNode *CompatibleLoopMD = MDNode::get(M.getContext(), CompatibleOperands); | |
| TempNode->replaceAllUsesWith(CompatibleLoopMD); | |
| I->setMetadata("llvm.loop", CompatibleLoopMD); | |
| } | |
| using InstructionMDList = std::array<unsigned, 7>; | |
| static InstructionMDList getCompatibleInstructionMDs(llvm::Module &M) { | |
| return { | |
| M.getMDKindID("dx.nonuniform"), M.getMDKindID("dx.controlflow.hints"), | |
| M.getMDKindID("dx.precise"), llvm::LLVMContext::MD_range, | |
| llvm::LLVMContext::MD_alias_scope, llvm::LLVMContext::MD_noalias, | |
| M.getMDKindID("llvm.loop")}; | |
| } | |
| static void translateInstructionMetadata(Module &M) { | |
| // construct allowlist of valid metadata node kinds | |
| InstructionMDList DXILCompatibleMDs = getCompatibleInstructionMDs(M); | |
| unsigned char MDLoopKind = M.getContext().getMDKindID("llvm.loop"); | |
| for (Function &F : M) { | |
| for (BasicBlock &BB : F) { | |
| // This needs to be done first so that "hlsl.controlflow.hints" isn't | |
| // removed in the allow-list below | |
| if (auto *I = BB.getTerminator()) | |
| translateBranchMetadata(M, I); | |
| for (auto &I : make_early_inc_range(BB)) { | |
| if (isa<UncondBrInst, CondBrInst>(I)) | |
| if (MDNode *LoopMD = I.getMetadata(MDLoopKind)) | |
| translateLoopMetadata(M, &I, LoopMD); | |
| I.dropUnknownNonDebugMetadata(DXILCompatibleMDs); | |
| } | |
| } | |
| } | |
| } | |
| static void cleanModuleFlags(Module &M) { | |
| NamedMDNode *MDFlags = M.getModuleFlagsMetadata(); | |
| if (!MDFlags) | |
| return; | |
| SmallVector<llvm::Module::ModuleFlagEntry> FlagEntries; | |
| M.getModuleFlagsMetadata(FlagEntries); | |
| bool Updated = false; | |
| for (auto &Flag : FlagEntries) { | |
| // llvm 3.7 only supports behavior up to AppendUnique. | |
| if (Flag.Behavior <= Module::ModFlagBehavior::AppendUnique) | |
| continue; | |
| Flag.Behavior = Module::ModFlagBehavior::Warning; | |
| Updated = true; | |
| } | |
| if (!Updated) | |
| return; | |
| MDFlags->eraseFromParent(); | |
| for (auto &Flag : FlagEntries) | |
| M.addModuleFlag(Flag.Behavior, Flag.Key->getString(), Flag.Val); | |
| } | |
| using GlobalMDList = std::array<StringLiteral, 7>; | |
| // The following are compatible with DXIL but not emit with clang, they can | |
| // be added when applicable: | |
| // dx.typeAnnotations, dx.viewIDState, dx.dxrPayloadAnnotations | |
| static GlobalMDList CompatibleNamedModuleMDs = { | |
| "llvm.ident", "llvm.module.flags", "dx.resources", "dx.valver", | |
| "dx.shaderModel", "dx.version", "dx.entryPoints", | |
| }; | |
| static void translateGlobalMetadata(Module &M, DXILResourceMap &DRM, | |
| DXILResourceTypeMap &DRTM, | |
| const ModuleShaderFlags &ShaderFlags, | |
| const ModuleMetadataInfo &MMDI) { | |
| LLVMContext &Ctx = M.getContext(); | |
| IRBuilder<> IRB(Ctx); | |
| SmallVector<MDNode *> EntryFnMDNodes; | |
| emitValidatorVersionMD(M, MMDI); | |
| emitShaderModelVersionMD(M, MMDI); | |
| emitDXILVersionTupleMD(M, MMDI); | |
| NamedMDNode *NamedResourceMD = emitResourceMetadata(M, DRM, DRTM); | |
| auto *ResourceMD = | |
| (NamedResourceMD != nullptr) ? NamedResourceMD->getOperand(0) : nullptr; | |
| // FIXME: Add support to construct Signatures | |
| // See https://github.com/llvm/llvm-project/issues/57928 | |
| MDTuple *Signatures = nullptr; | |
| if (MMDI.ShaderProfile == Triple::EnvironmentType::Library) { | |
| // Get the combined shader flag mask of all functions in the library to be | |
| // used as shader flags mask value associated with top-level library entry | |
| // metadata. | |
| uint64_t CombinedMask = ShaderFlags.getCombinedFlags(); | |
| EntryFnMDNodes.emplace_back( | |
| emitTopLevelLibraryNode(M, ResourceMD, CombinedMask)); | |
| } else if (1 < MMDI.EntryPropertyVec.size()) | |
| reportError(M, "Non-library shader: One and only one entry expected"); | |
| for (const EntryProperties &EntryProp : MMDI.EntryPropertyVec) { | |
| uint64_t EntryShaderFlags = 0; | |
| if (MMDI.ShaderProfile != Triple::EnvironmentType::Library) { | |
| EntryShaderFlags = ShaderFlags.getFunctionFlags(EntryProp.Entry); | |
| if (EntryProp.ShaderStage != MMDI.ShaderProfile) | |
| reportError( | |
| M, "Shader stage '" + | |
| Twine(getShortShaderStage(EntryProp.ShaderStage)) + | |
| "' for entry '" + Twine(EntryProp.Entry->getName()) + | |
| "' different from specified target profile '" + | |
| Twine(Triple::getEnvironmentTypeName(MMDI.ShaderProfile) + | |
| "'")); | |
| } | |
| EntryFnMDNodes.emplace_back(emitEntryMD( | |
| M, EntryProp, Signatures, ResourceMD, EntryShaderFlags, MMDI)); | |
| } | |
| NamedMDNode *EntryPointsNamedMD = | |
| M.getOrInsertNamedMetadata("dx.entryPoints"); | |
| for (auto *Entry : EntryFnMDNodes) | |
| EntryPointsNamedMD->addOperand(Entry); | |
| cleanModuleFlags(M); | |
| // Finally, strip all module metadata that is not explicitly specified in the | |
| // allow-list | |
| SmallVector<NamedMDNode *> ToStrip; | |
| for (NamedMDNode &NamedMD : M.named_metadata()) | |
| if (!NamedMD.getName().starts_with("llvm.dbg.") && | |
| !llvm::is_contained(CompatibleNamedModuleMDs, NamedMD.getName())) | |
| ToStrip.push_back(&NamedMD); | |
| for (NamedMDNode *NamedMD : ToStrip) | |
| NamedMD->eraseFromParent(); | |
| } | |
| PreservedAnalyses DXILTranslateMetadata::run(Module &M, | |
| ModuleAnalysisManager &MAM) { | |
| DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M); | |
| DXILResourceTypeMap &DRTM = MAM.getResult<DXILResourceTypeAnalysis>(M); | |
| const ModuleShaderFlags &ShaderFlags = MAM.getResult<ShaderFlagsAnalysis>(M); | |
| const dxil::ModuleMetadataInfo MMDI = MAM.getResult<DXILMetadataAnalysis>(M); | |
| translateGlobalMetadata(M, DRM, DRTM, ShaderFlags, MMDI); | |
| translateInstructionMetadata(M); | |
| return PreservedAnalyses::all(); | |
| } | |
| void DXILTranslateMetadataLegacy::getAnalysisUsage(AnalysisUsage &AU) const { | |
| AU.addRequired<DXILResourceTypeWrapperPass>(); | |
| AU.addRequired<DXILResourceWrapperPass>(); | |
| AU.addRequired<ShaderFlagsAnalysisWrapper>(); | |
| AU.addRequired<DXILMetadataAnalysisWrapperPass>(); | |
| AU.addRequired<RootSignatureAnalysisWrapper>(); | |
| AU.addPreserved<DXILMetadataAnalysisWrapperPass>(); | |
| AU.addPreserved<DXILResourceBindingWrapperPass>(); | |
| AU.addPreserved<DXILResourceWrapperPass>(); | |
| AU.addPreserved<RootSignatureAnalysisWrapper>(); | |
| AU.addPreserved<ShaderFlagsAnalysisWrapper>(); | |
| } | |
| bool DXILTranslateMetadataLegacy::runOnModule(Module &M) { | |
| DXILResourceMap &DRM = | |
| getAnalysis<DXILResourceWrapperPass>().getResourceMap(); | |
| DXILResourceTypeMap &DRTM = | |
| getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap(); | |
| const ModuleShaderFlags &ShaderFlags = | |
| getAnalysis<ShaderFlagsAnalysisWrapper>().getShaderFlags(); | |
| dxil::ModuleMetadataInfo MMDI = | |
| getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata(); | |
| translateGlobalMetadata(M, DRM, DRTM, ShaderFlags, MMDI); | |
| translateInstructionMetadata(M); | |
| return true; | |
| } | |
| char DXILTranslateMetadataLegacy::ID = 0; | |
| ModulePass *llvm::createDXILTranslateMetadataLegacyPass() { | |
| return new DXILTranslateMetadataLegacy(); | |
| } | |
| INITIALIZE_PASS_BEGIN(DXILTranslateMetadataLegacy, "dxil-translate-metadata", | |
| "DXIL Translate Metadata", false, false) | |
| INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass) | |
| INITIALIZE_PASS_DEPENDENCY(ShaderFlagsAnalysisWrapper) | |
| INITIALIZE_PASS_DEPENDENCY(RootSignatureAnalysisWrapper) | |
| INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass) | |
| INITIALIZE_PASS_END(DXILTranslateMetadataLegacy, "dxil-translate-metadata", | |
| "DXIL Translate Metadata", false, false) |