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