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