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