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