xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/DirectX/DXILMetadata.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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