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