1bdd1243dSDimitry Andric //===- DXILMetadata.cpp - DXIL Metadata helper objects --------------------===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric /// 9bdd1243dSDimitry Andric /// \file This file contains helper objects for working with DXIL metadata. 10bdd1243dSDimitry Andric /// 11bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 12bdd1243dSDimitry Andric 13bdd1243dSDimitry Andric #include "DXILMetadata.h" 14bdd1243dSDimitry Andric #include "llvm/IR/Constants.h" 15bdd1243dSDimitry Andric #include "llvm/IR/IRBuilder.h" 16bdd1243dSDimitry Andric #include "llvm/IR/Metadata.h" 17bdd1243dSDimitry Andric #include "llvm/IR/Module.h" 18bdd1243dSDimitry Andric #include "llvm/Support/VersionTuple.h" 19*06c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.h" 20bdd1243dSDimitry Andric 21bdd1243dSDimitry Andric using namespace llvm; 22bdd1243dSDimitry Andric using namespace llvm::dxil; 23bdd1243dSDimitry Andric 24bdd1243dSDimitry Andric ValidatorVersionMD::ValidatorVersionMD(Module &M) 25bdd1243dSDimitry Andric : Entry(M.getOrInsertNamedMetadata("dx.valver")) {} 26bdd1243dSDimitry Andric 27bdd1243dSDimitry Andric void ValidatorVersionMD::update(VersionTuple ValidatorVer) { 28bdd1243dSDimitry Andric auto &Ctx = Entry->getParent()->getContext(); 29bdd1243dSDimitry Andric IRBuilder<> B(Ctx); 30bdd1243dSDimitry Andric Metadata *MDVals[2]; 31bdd1243dSDimitry Andric MDVals[0] = ConstantAsMetadata::get(B.getInt32(ValidatorVer.getMajor())); 32bdd1243dSDimitry Andric MDVals[1] = 33bdd1243dSDimitry Andric ConstantAsMetadata::get(B.getInt32(ValidatorVer.getMinor().value_or(0))); 34bdd1243dSDimitry Andric 35bdd1243dSDimitry Andric if (isEmpty()) 36bdd1243dSDimitry Andric Entry->addOperand(MDNode::get(Ctx, MDVals)); 37bdd1243dSDimitry Andric else 38bdd1243dSDimitry Andric Entry->setOperand(0, MDNode::get(Ctx, MDVals)); 39bdd1243dSDimitry Andric } 40bdd1243dSDimitry Andric 41bdd1243dSDimitry Andric bool ValidatorVersionMD::isEmpty() { return Entry->getNumOperands() == 0; } 42bdd1243dSDimitry Andric 43bdd1243dSDimitry Andric static StringRef getShortShaderStage(Triple::EnvironmentType Env) { 44bdd1243dSDimitry Andric switch (Env) { 45bdd1243dSDimitry Andric case Triple::Pixel: 46bdd1243dSDimitry Andric return "ps"; 47bdd1243dSDimitry Andric case Triple::Vertex: 48bdd1243dSDimitry Andric return "vs"; 49bdd1243dSDimitry Andric case Triple::Geometry: 50bdd1243dSDimitry Andric return "gs"; 51bdd1243dSDimitry Andric case Triple::Hull: 52bdd1243dSDimitry Andric return "hs"; 53bdd1243dSDimitry Andric case Triple::Domain: 54bdd1243dSDimitry Andric return "ds"; 55bdd1243dSDimitry Andric case Triple::Compute: 56bdd1243dSDimitry Andric return "cs"; 57bdd1243dSDimitry Andric case Triple::Library: 58bdd1243dSDimitry Andric return "lib"; 59bdd1243dSDimitry Andric case Triple::Mesh: 60bdd1243dSDimitry Andric return "ms"; 61bdd1243dSDimitry Andric case Triple::Amplification: 62bdd1243dSDimitry Andric return "as"; 63bdd1243dSDimitry Andric default: 64bdd1243dSDimitry Andric break; 65bdd1243dSDimitry Andric } 66bdd1243dSDimitry Andric llvm_unreachable("Unsupported environment for DXIL generation."); 67bdd1243dSDimitry Andric return ""; 68bdd1243dSDimitry Andric } 69bdd1243dSDimitry Andric 70bdd1243dSDimitry Andric void dxil::createShaderModelMD(Module &M) { 71bdd1243dSDimitry Andric NamedMDNode *Entry = M.getOrInsertNamedMetadata("dx.shaderModel"); 72bdd1243dSDimitry Andric Triple TT(M.getTargetTriple()); 73bdd1243dSDimitry Andric VersionTuple Ver = TT.getOSVersion(); 74bdd1243dSDimitry Andric LLVMContext &Ctx = M.getContext(); 75bdd1243dSDimitry Andric IRBuilder<> B(Ctx); 76bdd1243dSDimitry Andric 77bdd1243dSDimitry Andric Metadata *Vals[3]; 78bdd1243dSDimitry Andric Vals[0] = MDString::get(Ctx, getShortShaderStage(TT.getEnvironment())); 79bdd1243dSDimitry Andric Vals[1] = ConstantAsMetadata::get(B.getInt32(Ver.getMajor())); 80bdd1243dSDimitry Andric Vals[2] = ConstantAsMetadata::get(B.getInt32(Ver.getMinor().value_or(0))); 81bdd1243dSDimitry Andric Entry->addOperand(MDNode::get(Ctx, Vals)); 82bdd1243dSDimitry Andric } 83bdd1243dSDimitry Andric 84bdd1243dSDimitry Andric static uint32_t getShaderStage(Triple::EnvironmentType Env) { 85bdd1243dSDimitry Andric return (uint32_t)Env - (uint32_t)llvm::Triple::Pixel; 86bdd1243dSDimitry Andric } 87bdd1243dSDimitry Andric 88bdd1243dSDimitry Andric namespace { 89bdd1243dSDimitry Andric 90bdd1243dSDimitry Andric struct EntryProps { 91bdd1243dSDimitry Andric Triple::EnvironmentType ShaderKind; 92bdd1243dSDimitry Andric // FIXME: support more shader profiles. 93bdd1243dSDimitry Andric // See https://github.com/llvm/llvm-project/issues/57927. 94bdd1243dSDimitry Andric struct { 95bdd1243dSDimitry Andric unsigned NumThreads[3]; 96bdd1243dSDimitry Andric } CS; 97bdd1243dSDimitry Andric 98bdd1243dSDimitry Andric EntryProps(Function &F, Triple::EnvironmentType ModuleShaderKind) 99bdd1243dSDimitry Andric : ShaderKind(ModuleShaderKind) { 100bdd1243dSDimitry Andric 101bdd1243dSDimitry Andric if (ShaderKind == Triple::EnvironmentType::Library) { 102bdd1243dSDimitry Andric Attribute EntryAttr = F.getFnAttribute("hlsl.shader"); 103bdd1243dSDimitry Andric StringRef EntryProfile = EntryAttr.getValueAsString(); 104bdd1243dSDimitry Andric Triple T("", "", "", EntryProfile); 105bdd1243dSDimitry Andric ShaderKind = T.getEnvironment(); 106bdd1243dSDimitry Andric } 107bdd1243dSDimitry Andric 108bdd1243dSDimitry Andric if (ShaderKind == Triple::EnvironmentType::Compute) { 109bdd1243dSDimitry Andric auto NumThreadsStr = 110bdd1243dSDimitry Andric F.getFnAttribute("hlsl.numthreads").getValueAsString(); 111bdd1243dSDimitry Andric SmallVector<StringRef> NumThreads; 112bdd1243dSDimitry Andric NumThreadsStr.split(NumThreads, ','); 113bdd1243dSDimitry Andric assert(NumThreads.size() == 3 && "invalid numthreads"); 114bdd1243dSDimitry Andric auto Zip = 115bdd1243dSDimitry Andric llvm::zip(NumThreads, MutableArrayRef<unsigned>(CS.NumThreads)); 116bdd1243dSDimitry Andric for (auto It : Zip) { 117bdd1243dSDimitry Andric StringRef Str = std::get<0>(It); 118bdd1243dSDimitry Andric APInt V; 119bdd1243dSDimitry Andric [[maybe_unused]] bool Result = Str.getAsInteger(10, V); 120bdd1243dSDimitry Andric assert(!Result && "Failed to parse numthreads"); 121bdd1243dSDimitry Andric 122bdd1243dSDimitry Andric unsigned &Num = std::get<1>(It); 123bdd1243dSDimitry Andric Num = V.getLimitedValue(); 124bdd1243dSDimitry Andric } 125bdd1243dSDimitry Andric } 126bdd1243dSDimitry Andric } 127bdd1243dSDimitry Andric 128bdd1243dSDimitry Andric MDTuple *emitDXILEntryProps(uint64_t RawShaderFlag, LLVMContext &Ctx, 129bdd1243dSDimitry Andric bool IsLib) { 130bdd1243dSDimitry Andric std::vector<Metadata *> MDVals; 131bdd1243dSDimitry Andric 132bdd1243dSDimitry Andric if (RawShaderFlag != 0) 133bdd1243dSDimitry Andric appendShaderFlags(MDVals, RawShaderFlag, Ctx); 134bdd1243dSDimitry Andric 135bdd1243dSDimitry Andric // Add shader kind for lib entrys. 136bdd1243dSDimitry Andric if (IsLib && ShaderKind != Triple::EnvironmentType::Library) 137bdd1243dSDimitry Andric appendShaderKind(MDVals, Ctx); 138bdd1243dSDimitry Andric 139bdd1243dSDimitry Andric if (ShaderKind == Triple::EnvironmentType::Compute) 140bdd1243dSDimitry Andric appendNumThreads(MDVals, Ctx); 141bdd1243dSDimitry Andric // FIXME: support more props. 142bdd1243dSDimitry Andric // See https://github.com/llvm/llvm-project/issues/57948. 143bdd1243dSDimitry Andric return MDNode::get(Ctx, MDVals); 144bdd1243dSDimitry Andric } 145bdd1243dSDimitry Andric 146bdd1243dSDimitry Andric static MDTuple *emitEntryPropsForEmptyEntry(uint64_t RawShaderFlag, 147bdd1243dSDimitry Andric LLVMContext &Ctx) { 148bdd1243dSDimitry Andric if (RawShaderFlag == 0) 149bdd1243dSDimitry Andric return nullptr; 150bdd1243dSDimitry Andric 151bdd1243dSDimitry Andric std::vector<Metadata *> MDVals; 152bdd1243dSDimitry Andric 153bdd1243dSDimitry Andric appendShaderFlags(MDVals, RawShaderFlag, Ctx); 154bdd1243dSDimitry Andric // FIXME: support more props. 155bdd1243dSDimitry Andric // See https://github.com/llvm/llvm-project/issues/57948. 156bdd1243dSDimitry Andric return MDNode::get(Ctx, MDVals); 157bdd1243dSDimitry Andric } 158bdd1243dSDimitry Andric 159bdd1243dSDimitry Andric private: 160bdd1243dSDimitry Andric enum EntryPropsTag { 161bdd1243dSDimitry Andric ShaderFlagsTag = 0, 162bdd1243dSDimitry Andric GSStateTag, 163bdd1243dSDimitry Andric DSStateTag, 164bdd1243dSDimitry Andric HSStateTag, 165bdd1243dSDimitry Andric NumThreadsTag, 166bdd1243dSDimitry Andric AutoBindingSpaceTag, 167bdd1243dSDimitry Andric RayPayloadSizeTag, 168bdd1243dSDimitry Andric RayAttribSizeTag, 169bdd1243dSDimitry Andric ShaderKindTag, 170bdd1243dSDimitry Andric MSStateTag, 171bdd1243dSDimitry Andric ASStateTag, 172bdd1243dSDimitry Andric WaveSizeTag, 173bdd1243dSDimitry Andric EntryRootSigTag, 174bdd1243dSDimitry Andric }; 175bdd1243dSDimitry Andric 176bdd1243dSDimitry Andric void appendNumThreads(std::vector<Metadata *> &MDVals, LLVMContext &Ctx) { 177bdd1243dSDimitry Andric MDVals.emplace_back(ConstantAsMetadata::get( 178bdd1243dSDimitry Andric ConstantInt::get(Type::getInt32Ty(Ctx), NumThreadsTag))); 179bdd1243dSDimitry Andric 180bdd1243dSDimitry Andric std::vector<Metadata *> NumThreadVals; 181bdd1243dSDimitry Andric for (auto Num : ArrayRef<unsigned>(CS.NumThreads)) 182bdd1243dSDimitry Andric NumThreadVals.emplace_back(ConstantAsMetadata::get( 183bdd1243dSDimitry Andric ConstantInt::get(Type::getInt32Ty(Ctx), Num))); 184bdd1243dSDimitry Andric MDVals.emplace_back(MDNode::get(Ctx, NumThreadVals)); 185bdd1243dSDimitry Andric } 186bdd1243dSDimitry Andric 187bdd1243dSDimitry Andric static void appendShaderFlags(std::vector<Metadata *> &MDVals, 188bdd1243dSDimitry Andric uint64_t RawShaderFlag, LLVMContext &Ctx) { 189bdd1243dSDimitry Andric MDVals.emplace_back(ConstantAsMetadata::get( 190bdd1243dSDimitry Andric ConstantInt::get(Type::getInt32Ty(Ctx), ShaderFlagsTag))); 191bdd1243dSDimitry Andric MDVals.emplace_back(ConstantAsMetadata::get( 192bdd1243dSDimitry Andric ConstantInt::get(Type::getInt64Ty(Ctx), RawShaderFlag))); 193bdd1243dSDimitry Andric } 194bdd1243dSDimitry Andric 195bdd1243dSDimitry Andric void appendShaderKind(std::vector<Metadata *> &MDVals, LLVMContext &Ctx) { 196bdd1243dSDimitry Andric MDVals.emplace_back(ConstantAsMetadata::get( 197bdd1243dSDimitry Andric ConstantInt::get(Type::getInt32Ty(Ctx), ShaderKindTag))); 198bdd1243dSDimitry Andric MDVals.emplace_back(ConstantAsMetadata::get( 199bdd1243dSDimitry Andric ConstantInt::get(Type::getInt32Ty(Ctx), getShaderStage(ShaderKind)))); 200bdd1243dSDimitry Andric } 201bdd1243dSDimitry Andric }; 202bdd1243dSDimitry Andric 203bdd1243dSDimitry Andric class EntryMD { 204bdd1243dSDimitry Andric Function &F; 205bdd1243dSDimitry Andric LLVMContext &Ctx; 206bdd1243dSDimitry Andric EntryProps Props; 207bdd1243dSDimitry Andric 208bdd1243dSDimitry Andric public: 209bdd1243dSDimitry Andric EntryMD(Function &F, Triple::EnvironmentType ModuleShaderKind) 210bdd1243dSDimitry Andric : F(F), Ctx(F.getContext()), Props(F, ModuleShaderKind) {} 211bdd1243dSDimitry Andric 212bdd1243dSDimitry Andric MDTuple *emitEntryTuple(MDTuple *Resources, uint64_t RawShaderFlag) { 213bdd1243dSDimitry Andric // FIXME: add signature for profile other than CS. 214bdd1243dSDimitry Andric // See https://github.com/llvm/llvm-project/issues/57928. 215bdd1243dSDimitry Andric MDTuple *Signatures = nullptr; 216bdd1243dSDimitry Andric return emitDxilEntryPointTuple( 217bdd1243dSDimitry Andric &F, F.getName().str(), Signatures, Resources, 218bdd1243dSDimitry Andric Props.emitDXILEntryProps(RawShaderFlag, Ctx, /*IsLib*/ false), Ctx); 219bdd1243dSDimitry Andric } 220bdd1243dSDimitry Andric 221bdd1243dSDimitry Andric MDTuple *emitEntryTupleForLib(uint64_t RawShaderFlag) { 222bdd1243dSDimitry Andric // FIXME: add signature for profile other than CS. 223bdd1243dSDimitry Andric // See https://github.com/llvm/llvm-project/issues/57928. 224bdd1243dSDimitry Andric MDTuple *Signatures = nullptr; 225bdd1243dSDimitry Andric return emitDxilEntryPointTuple( 226bdd1243dSDimitry Andric &F, F.getName().str(), Signatures, 227bdd1243dSDimitry Andric /*entry in lib doesn't need resources metadata*/ nullptr, 228bdd1243dSDimitry Andric Props.emitDXILEntryProps(RawShaderFlag, Ctx, /*IsLib*/ true), Ctx); 229bdd1243dSDimitry Andric } 230bdd1243dSDimitry Andric 231bdd1243dSDimitry Andric // Library will have empty entry metadata which only store the resource table 232bdd1243dSDimitry Andric // metadata. 233bdd1243dSDimitry Andric static MDTuple *emitEmptyEntryForLib(MDTuple *Resources, 234bdd1243dSDimitry Andric uint64_t RawShaderFlag, 235bdd1243dSDimitry Andric LLVMContext &Ctx) { 236bdd1243dSDimitry Andric return emitDxilEntryPointTuple( 237bdd1243dSDimitry Andric nullptr, "", nullptr, Resources, 238bdd1243dSDimitry Andric EntryProps::emitEntryPropsForEmptyEntry(RawShaderFlag, Ctx), Ctx); 239bdd1243dSDimitry Andric } 240bdd1243dSDimitry Andric 241bdd1243dSDimitry Andric private: 242bdd1243dSDimitry Andric static MDTuple *emitDxilEntryPointTuple(Function *Fn, const std::string &Name, 243bdd1243dSDimitry Andric MDTuple *Signatures, 244bdd1243dSDimitry Andric MDTuple *Resources, 245bdd1243dSDimitry Andric MDTuple *Properties, 246bdd1243dSDimitry Andric LLVMContext &Ctx) { 247bdd1243dSDimitry Andric Metadata *MDVals[5]; 248bdd1243dSDimitry Andric MDVals[0] = Fn ? ValueAsMetadata::get(Fn) : nullptr; 249bdd1243dSDimitry Andric MDVals[1] = MDString::get(Ctx, Name.c_str()); 250bdd1243dSDimitry Andric MDVals[2] = Signatures; 251bdd1243dSDimitry Andric MDVals[3] = Resources; 252bdd1243dSDimitry Andric MDVals[4] = Properties; 253bdd1243dSDimitry Andric return MDNode::get(Ctx, MDVals); 254bdd1243dSDimitry Andric } 255bdd1243dSDimitry Andric }; 256bdd1243dSDimitry Andric } // namespace 257bdd1243dSDimitry Andric 258bdd1243dSDimitry Andric void dxil::createEntryMD(Module &M, const uint64_t ShaderFlags) { 259bdd1243dSDimitry Andric SmallVector<Function *> EntryList; 260bdd1243dSDimitry Andric for (auto &F : M.functions()) { 261bdd1243dSDimitry Andric if (!F.hasFnAttribute("hlsl.shader")) 262bdd1243dSDimitry Andric continue; 263bdd1243dSDimitry Andric EntryList.emplace_back(&F); 264bdd1243dSDimitry Andric } 265bdd1243dSDimitry Andric 266bdd1243dSDimitry Andric auto &Ctx = M.getContext(); 267bdd1243dSDimitry Andric // FIXME: generate metadata for resource. 268bdd1243dSDimitry Andric // See https://github.com/llvm/llvm-project/issues/57926. 269bdd1243dSDimitry Andric MDTuple *MDResources = nullptr; 270bdd1243dSDimitry Andric if (auto *NamedResources = M.getNamedMetadata("dx.resources")) 271bdd1243dSDimitry Andric MDResources = dyn_cast<MDTuple>(NamedResources->getOperand(0)); 272bdd1243dSDimitry Andric 273bdd1243dSDimitry Andric std::vector<MDNode *> Entries; 274bdd1243dSDimitry Andric Triple T = Triple(M.getTargetTriple()); 275bdd1243dSDimitry Andric switch (T.getEnvironment()) { 276bdd1243dSDimitry Andric case Triple::EnvironmentType::Library: { 277bdd1243dSDimitry Andric // Add empty entry to put resource metadata. 278bdd1243dSDimitry Andric MDTuple *EmptyEntry = 279bdd1243dSDimitry Andric EntryMD::emitEmptyEntryForLib(MDResources, ShaderFlags, Ctx); 280bdd1243dSDimitry Andric Entries.emplace_back(EmptyEntry); 281bdd1243dSDimitry Andric 282bdd1243dSDimitry Andric for (Function *Entry : EntryList) { 283bdd1243dSDimitry Andric EntryMD MD(*Entry, T.getEnvironment()); 284bdd1243dSDimitry Andric Entries.emplace_back(MD.emitEntryTupleForLib(0)); 285bdd1243dSDimitry Andric } 286bdd1243dSDimitry Andric } break; 287bdd1243dSDimitry Andric case Triple::EnvironmentType::Compute: 288bdd1243dSDimitry Andric case Triple::EnvironmentType::Amplification: 289bdd1243dSDimitry Andric case Triple::EnvironmentType::Mesh: 290bdd1243dSDimitry Andric case Triple::EnvironmentType::Vertex: 291bdd1243dSDimitry Andric case Triple::EnvironmentType::Hull: 292bdd1243dSDimitry Andric case Triple::EnvironmentType::Domain: 293bdd1243dSDimitry Andric case Triple::EnvironmentType::Geometry: 294bdd1243dSDimitry Andric case Triple::EnvironmentType::Pixel: { 295bdd1243dSDimitry Andric assert(EntryList.size() == 1 && 296bdd1243dSDimitry Andric "non-lib profiles should only have one entry"); 297bdd1243dSDimitry Andric EntryMD MD(*EntryList.front(), T.getEnvironment()); 298bdd1243dSDimitry Andric Entries.emplace_back(MD.emitEntryTuple(MDResources, ShaderFlags)); 299bdd1243dSDimitry Andric } break; 300bdd1243dSDimitry Andric default: 301bdd1243dSDimitry Andric assert(0 && "invalid profile"); 302bdd1243dSDimitry Andric break; 303bdd1243dSDimitry Andric } 304bdd1243dSDimitry Andric 305bdd1243dSDimitry Andric NamedMDNode *EntryPointsNamedMD = 306bdd1243dSDimitry Andric M.getOrInsertNamedMetadata("dx.entryPoints"); 307bdd1243dSDimitry Andric for (auto *Entry : Entries) 308bdd1243dSDimitry Andric EntryPointsNamedMD->addOperand(Entry); 309bdd1243dSDimitry Andric } 310