1//===- DXILTranslateMetadata.cpp - Pass to emit DXIL metadata -------------===//
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#include "DXILTranslateMetadata.h"
10#include "DXILRootSignature.h"
11#include "DXILShaderFlags.h"
12#include "DXILSignatureAnalysis.h"
13#include "DirectX.h"
14#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/SmallVector.h"
16#include "llvm/ADT/Twine.h"
17#include "llvm/Analysis/DXILMetadataAnalysis.h"
18#include "llvm/Analysis/DXILResource.h"
19#include "llvm/IR/BasicBlock.h"
20#include "llvm/IR/Constants.h"
21#include "llvm/IR/DiagnosticInfo.h"
22#include "llvm/IR/DiagnosticPrinter.h"
23#include "llvm/IR/Function.h"
24#include "llvm/IR/IRBuilder.h"
25#include "llvm/IR/LLVMContext.h"
26#include "llvm/IR/MDBuilder.h"
27#include "llvm/IR/Metadata.h"
28#include "llvm/IR/Module.h"
29#include "llvm/InitializePasses.h"
30#include "llvm/Pass.h"
31#include "llvm/Support/ErrorHandling.h"
32#include "llvm/Support/VersionTuple.h"
33#include "llvm/TargetParser/Triple.h"
34#include <cstdint>
35
36using namespace llvm;
37using namespace llvm::dxil;
38
39namespace {
40
41/// A simple wrapper of DiagnosticInfo that generates module-level diagnostic
42/// for the DXILValidateMetadata pass
43class DiagnosticInfoValidateMD : public DiagnosticInfo {
44private:
45 const Twine &Msg;
46 const Module &Mod;
47
48public:
49 /// \p M is the module for which the diagnostic is being emitted. \p Msg is
50 /// the message to show. Note that this class does not copy this message, so
51 /// this reference must be valid for the whole life time of the diagnostic.
52 DiagnosticInfoValidateMD(const Module &M,
53 const Twine &Msg LLVM_LIFETIME_BOUND,
54 DiagnosticSeverity Severity = DS_Error)
55 : DiagnosticInfo(DK_Unsupported, Severity), Msg(Msg), Mod(M) {}
56
57 void print(DiagnosticPrinter &DP) const override {
58 DP << Mod.getName() << ": " << Msg << '\n';
59 }
60};
61
62static void reportError(Module &M, Twine Message,
63 DiagnosticSeverity Severity = DS_Error) {
64 M.getContext().diagnose(DI: DiagnosticInfoValidateMD(M, Message, Severity));
65}
66
67static void reportLoopError(Module &M, Twine Message,
68 DiagnosticSeverity Severity = DS_Error) {
69 reportError(M, Message: Twine("Invalid \"llvm.loop\" metadata: ") + Message, Severity);
70}
71
72enum class EntryPropsTag {
73 ShaderFlags = 0,
74 GSState,
75 DSState,
76 HSState,
77 NumThreads,
78 AutoBindingSpace,
79 RayPayloadSize,
80 RayAttribSize,
81 ShaderKind,
82 MSState,
83 ASStateTag,
84 WaveSize,
85 EntryRootSig,
86 WaveRange = 23,
87};
88
89} // namespace
90
91static NamedMDNode *emitResourceMetadata(Module &M, DXILResourceMap &DRM,
92 DXILResourceTypeMap &DRTM) {
93 LLVMContext &Context = M.getContext();
94
95 for (ResourceInfo &RI : DRM)
96 if (RI.hasBinding() && !RI.hasSymbol())
97 RI.createSymbol(M,
98 Ty: DRTM[RI.getHandleTy()].createElementStruct(CBufferName: RI.getName()));
99
100 SmallVector<Metadata *> SRVs, UAVs, CBufs, Smps;
101 for (const ResourceInfo &RI : DRM.srvs())
102 if (RI.hasBinding())
103 SRVs.push_back(Elt: RI.getAsMetadata(M, RTI&: DRTM[RI.getHandleTy()]));
104 for (const ResourceInfo &RI : DRM.uavs())
105 if (RI.hasBinding())
106 UAVs.push_back(Elt: RI.getAsMetadata(M, RTI&: DRTM[RI.getHandleTy()]));
107 for (const ResourceInfo &RI : DRM.cbuffers())
108 if (RI.hasBinding())
109 CBufs.push_back(Elt: RI.getAsMetadata(M, RTI&: DRTM[RI.getHandleTy()]));
110 for (const ResourceInfo &RI : DRM.samplers())
111 if (RI.hasBinding())
112 Smps.push_back(Elt: RI.getAsMetadata(M, RTI&: DRTM[RI.getHandleTy()]));
113
114 Metadata *SRVMD = SRVs.empty() ? nullptr : MDNode::get(Context, MDs: SRVs);
115 Metadata *UAVMD = UAVs.empty() ? nullptr : MDNode::get(Context, MDs: UAVs);
116 Metadata *CBufMD = CBufs.empty() ? nullptr : MDNode::get(Context, MDs: CBufs);
117 Metadata *SmpMD = Smps.empty() ? nullptr : MDNode::get(Context, MDs: Smps);
118
119 if (DRM.empty())
120 return nullptr;
121
122 NamedMDNode *ResourceMD = M.getOrInsertNamedMetadata(Name: "dx.resources");
123 ResourceMD->addOperand(
124 M: MDNode::get(Context&: M.getContext(), MDs: {SRVMD, UAVMD, CBufMD, SmpMD}));
125
126 return ResourceMD;
127}
128
129static StringRef getShortShaderStage(Triple::EnvironmentType Env) {
130 switch (Env) {
131 case Triple::Pixel:
132 return "ps";
133 case Triple::Vertex:
134 return "vs";
135 case Triple::Geometry:
136 return "gs";
137 case Triple::Hull:
138 return "hs";
139 case Triple::Domain:
140 return "ds";
141 case Triple::Compute:
142 return "cs";
143 case Triple::Library:
144 return "lib";
145 case Triple::Mesh:
146 return "ms";
147 case Triple::Amplification:
148 return "as";
149 case Triple::RootSignature:
150 return "rootsig";
151 default:
152 break;
153 }
154 llvm_unreachable("Unsupported environment for DXIL generation.");
155}
156
157static uint32_t getShaderStage(Triple::EnvironmentType Env) {
158 return (uint32_t)Env - (uint32_t)llvm::Triple::Pixel;
159}
160
161static SmallVector<Metadata *>
162getTagValueAsMetadata(EntryPropsTag Tag, uint64_t Value, LLVMContext &Ctx) {
163 SmallVector<Metadata *> MDVals;
164 MDVals.emplace_back(Args: ConstantAsMetadata::get(
165 C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: static_cast<int>(Tag))));
166 switch (Tag) {
167 case EntryPropsTag::ShaderFlags:
168 MDVals.emplace_back(Args: ConstantAsMetadata::get(
169 C: ConstantInt::get(Ty: Type::getInt64Ty(C&: Ctx), V: Value)));
170 break;
171 case EntryPropsTag::ShaderKind:
172 MDVals.emplace_back(Args: ConstantAsMetadata::get(
173 C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: Value)));
174 break;
175 case EntryPropsTag::GSState:
176 case EntryPropsTag::DSState:
177 case EntryPropsTag::HSState:
178 case EntryPropsTag::NumThreads:
179 case EntryPropsTag::AutoBindingSpace:
180 case EntryPropsTag::RayPayloadSize:
181 case EntryPropsTag::RayAttribSize:
182 case EntryPropsTag::MSState:
183 case EntryPropsTag::ASStateTag:
184 case EntryPropsTag::WaveSize:
185 case EntryPropsTag::EntryRootSig:
186 case EntryPropsTag::WaveRange:
187 llvm_unreachable("NYI: Unhandled entry property tag");
188 }
189 return MDVals;
190}
191
192static MDTuple *getEntryPropAsMetadata(Module &M, const EntryProperties &EP,
193 uint64_t EntryShaderFlags,
194 const ModuleMetadataInfo &MMDI) {
195 SmallVector<Metadata *> MDVals;
196 LLVMContext &Ctx = EP.Entry->getContext();
197 if (EntryShaderFlags != 0)
198 MDVals.append(RHS: getTagValueAsMetadata(Tag: EntryPropsTag::ShaderFlags,
199 Value: EntryShaderFlags, Ctx));
200
201 if (EP.Entry != nullptr) {
202 // FIXME: support more props.
203 // See https://github.com/llvm/llvm-project/issues/57948.
204 // Add shader kind for lib entries.
205 if (MMDI.ShaderProfile == Triple::EnvironmentType::Library &&
206 EP.ShaderStage != Triple::EnvironmentType::Library)
207 MDVals.append(RHS: getTagValueAsMetadata(Tag: EntryPropsTag::ShaderKind,
208 Value: getShaderStage(Env: EP.ShaderStage), Ctx));
209
210 if (EP.ShaderStage == Triple::EnvironmentType::Compute) {
211 // Handle mandatory "hlsl.numthreads"
212 MDVals.emplace_back(Args: ConstantAsMetadata::get(C: ConstantInt::get(
213 Ty: Type::getInt32Ty(C&: Ctx), V: static_cast<int>(EntryPropsTag::NumThreads))));
214 Metadata *NumThreadVals[] = {ConstantAsMetadata::get(C: ConstantInt::get(
215 Ty: Type::getInt32Ty(C&: Ctx), V: EP.NumThreadsX)),
216 ConstantAsMetadata::get(C: ConstantInt::get(
217 Ty: Type::getInt32Ty(C&: Ctx), V: EP.NumThreadsY)),
218 ConstantAsMetadata::get(C: ConstantInt::get(
219 Ty: Type::getInt32Ty(C&: Ctx), V: EP.NumThreadsZ))};
220 MDVals.emplace_back(Args: MDNode::get(Context&: Ctx, MDs: NumThreadVals));
221
222 // Handle optional "hlsl.wavesize". The fields are optionally represented
223 // if they are non-zero.
224 if (EP.WaveSizeMin != 0) {
225 bool IsWaveRange = VersionTuple(6, 8) <= MMDI.ShaderModelVersion;
226 bool IsWaveSize =
227 !IsWaveRange && VersionTuple(6, 6) <= MMDI.ShaderModelVersion;
228
229 if (!IsWaveRange && !IsWaveSize) {
230 reportError(M, Message: "Shader model 6.6 or greater is required to specify "
231 "the \"hlsl.wavesize\" function attribute");
232 return nullptr;
233 }
234
235 // A range is being specified if EP.WaveSizeMax != 0
236 if (EP.WaveSizeMax && !IsWaveRange) {
237 reportError(
238 M, Message: "Shader model 6.8 or greater is required to specify "
239 "wave size range values of the \"hlsl.wavesize\" function "
240 "attribute");
241 return nullptr;
242 }
243
244 EntryPropsTag Tag =
245 IsWaveSize ? EntryPropsTag::WaveSize : EntryPropsTag::WaveRange;
246 MDVals.emplace_back(Args: ConstantAsMetadata::get(
247 C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: static_cast<int>(Tag))));
248
249 SmallVector<Metadata *> WaveSizeVals = {ConstantAsMetadata::get(
250 C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: EP.WaveSizeMin))};
251 if (IsWaveRange) {
252 WaveSizeVals.push_back(Elt: ConstantAsMetadata::get(
253 C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: EP.WaveSizeMax)));
254 WaveSizeVals.push_back(Elt: ConstantAsMetadata::get(
255 C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: EP.WaveSizePref)));
256 }
257
258 MDVals.emplace_back(Args: MDNode::get(Context&: Ctx, MDs: WaveSizeVals));
259 }
260 }
261 }
262
263 if (MDVals.empty())
264 return nullptr;
265 return MDNode::get(Context&: Ctx, MDs: MDVals);
266}
267
268static MDTuple *constructEntryMetadata(const Function *EntryFn,
269 MDTuple *Signatures, MDNode *Resources,
270 MDTuple *Properties, LLVMContext &Ctx) {
271 // Each entry point metadata record specifies:
272 // * reference to the entry point function global symbol
273 // * unmangled name
274 // * list of signatures
275 // * list of resources
276 // * list of tag-value pairs of shader capabilities and other properties
277 Metadata *MDVals[5];
278 MDVals[0] =
279 EntryFn ? ValueAsMetadata::get(V: const_cast<Function *>(EntryFn)) : nullptr;
280 MDVals[1] = MDString::get(Context&: Ctx, Str: EntryFn ? EntryFn->getName() : "");
281 MDVals[2] = Signatures;
282 MDVals[3] = Resources;
283 MDVals[4] = Properties;
284 return MDNode::get(Context&: Ctx, MDs: MDVals);
285}
286
287static MDTuple *emitEntryMD(Module &M, const EntryProperties &EP,
288 MDTuple *Signatures, MDNode *MDResources,
289 const uint64_t EntryShaderFlags,
290 const ModuleMetadataInfo &MMDI) {
291 MDTuple *Properties = getEntryPropAsMetadata(M, EP, EntryShaderFlags, MMDI);
292 return constructEntryMetadata(EntryFn: EP.Entry, Signatures, Resources: MDResources, Properties,
293 Ctx&: EP.Entry->getContext());
294}
295
296static void emitValidatorVersionMD(Module &M, const ModuleMetadataInfo &MMDI) {
297 if (MMDI.ValidatorVersion.empty())
298 return;
299
300 LLVMContext &Ctx = M.getContext();
301 IRBuilder<> IRB(M);
302 Metadata *MDVals[2];
303 MDVals[0] =
304 ConstantAsMetadata::get(C: IRB.getInt32(C: MMDI.ValidatorVersion.getMajor()));
305 MDVals[1] = ConstantAsMetadata::get(
306 C: IRB.getInt32(C: MMDI.ValidatorVersion.getMinor().value_or(u: 0)));
307 NamedMDNode *ValVerNode = M.getOrInsertNamedMetadata(Name: "dx.valver");
308 // Set validator version obtained from DXIL Metadata Analysis pass
309 ValVerNode->clearOperands();
310 ValVerNode->addOperand(M: MDNode::get(Context&: Ctx, MDs: MDVals));
311}
312
313static void emitShaderModelVersionMD(Module &M,
314 const ModuleMetadataInfo &MMDI) {
315 LLVMContext &Ctx = M.getContext();
316 IRBuilder<> IRB(M);
317 Metadata *SMVals[3];
318 VersionTuple SM = MMDI.ShaderModelVersion;
319 SMVals[0] = MDString::get(Context&: Ctx, Str: getShortShaderStage(Env: MMDI.ShaderProfile));
320 SMVals[1] = ConstantAsMetadata::get(C: IRB.getInt32(C: SM.getMajor()));
321 SMVals[2] = ConstantAsMetadata::get(C: IRB.getInt32(C: SM.getMinor().value_or(u: 0)));
322 NamedMDNode *SMMDNode = M.getOrInsertNamedMetadata(Name: "dx.shaderModel");
323 SMMDNode->addOperand(M: MDNode::get(Context&: Ctx, MDs: SMVals));
324}
325
326static void emitDXILVersionTupleMD(Module &M, const ModuleMetadataInfo &MMDI) {
327 LLVMContext &Ctx = M.getContext();
328 IRBuilder<> IRB(M);
329 VersionTuple DXILVer = MMDI.DXILVersion;
330 Metadata *DXILVals[2];
331 DXILVals[0] = ConstantAsMetadata::get(C: IRB.getInt32(C: DXILVer.getMajor()));
332 DXILVals[1] =
333 ConstantAsMetadata::get(C: IRB.getInt32(C: DXILVer.getMinor().value_or(u: 0)));
334 NamedMDNode *DXILVerMDNode = M.getOrInsertNamedMetadata(Name: "dx.version");
335 DXILVerMDNode->addOperand(M: MDNode::get(Context&: Ctx, MDs: DXILVals));
336}
337
338static MDTuple *emitTopLevelLibraryNode(Module &M, MDNode *RMD,
339 uint64_t ShaderFlags) {
340 LLVMContext &Ctx = M.getContext();
341 MDTuple *Properties = nullptr;
342 if (ShaderFlags != 0) {
343 SmallVector<Metadata *> MDVals;
344 MDVals.append(
345 RHS: getTagValueAsMetadata(Tag: EntryPropsTag::ShaderFlags, Value: ShaderFlags, Ctx));
346 Properties = MDNode::get(Context&: Ctx, MDs: MDVals);
347 }
348 // Library has an entry metadata with resource table metadata and all other
349 // MDNodes as null.
350 return constructEntryMetadata(EntryFn: nullptr, Signatures: nullptr, Resources: RMD, Properties, Ctx);
351}
352
353static void translateBranchMetadata(Module &M, Instruction *BBTerminatorInst) {
354 MDNode *HlslControlFlowMD =
355 BBTerminatorInst->getMetadata(Kind: "hlsl.controlflow.hint");
356
357 if (!HlslControlFlowMD)
358 return;
359
360 assert(HlslControlFlowMD->getNumOperands() == 2 &&
361 "invalid operands for hlsl.controlflow.hint");
362
363 MDBuilder MDHelper(M.getContext());
364
365 llvm::Metadata *HintsStr = MDHelper.createString(Str: "dx.controlflow.hints");
366 llvm::Metadata *HintsValue = MDHelper.createConstant(
367 C: mdconst::extract<ConstantInt>(MD: HlslControlFlowMD->getOperand(I: 1)));
368
369 MDNode *MDNode = llvm::MDNode::get(Context&: M.getContext(), MDs: {HintsStr, HintsValue});
370
371 BBTerminatorInst->setMetadata(Kind: "dx.controlflow.hints", Node: MDNode);
372 BBTerminatorInst->setMetadata(Kind: "hlsl.controlflow.hint", Node: nullptr);
373}
374
375// Determines if the metadata node will be compatible with DXIL's loop metadata
376// representation.
377//
378// Reports an error for compatible metadata that is ill-formed.
379static bool isLoopMDCompatible(Module &M, Metadata *MD) {
380 // DXIL only accepts the following loop hints:
381 std::array<StringLiteral, 3> ValidHintNames = {"llvm.loop.unroll.count",
382 "llvm.loop.unroll.disable",
383 "llvm.loop.unroll.full"};
384
385 MDNode *HintMD = dyn_cast<MDNode>(Val: MD);
386 if (!HintMD || HintMD->getNumOperands() == 0)
387 return false;
388
389 auto *HintStr = dyn_cast<MDString>(Val: HintMD->getOperand(I: 0));
390 if (!HintStr)
391 return false;
392
393 if (!llvm::is_contained(Range&: ValidHintNames, Element: HintStr->getString()))
394 return false;
395
396 auto ValidCountNode = [](MDNode *CountMD) -> bool {
397 if (CountMD->getNumOperands() == 2)
398 if (auto *Count = dyn_cast<ConstantAsMetadata>(Val: CountMD->getOperand(I: 1)))
399 if (isa<ConstantInt>(Val: Count->getValue()))
400 return true;
401 return false;
402 };
403
404 if (HintStr->getString() == "llvm.loop.unroll.count") {
405 if (!ValidCountNode(HintMD)) {
406 reportLoopError(M, Message: "\"llvm.loop.unroll.count\" must have 2 operands and "
407 "the second must be a constant integer");
408 return false;
409 }
410 } else if (HintMD->getNumOperands() != 1) {
411 reportLoopError(
412 M, Message: "\"llvm.loop.unroll.disable\" and \"llvm.loop.unroll.full\" "
413 "must be provided as a single operand");
414 return false;
415 }
416
417 return true;
418}
419
420static void translateLoopMetadata(Module &M, Instruction *I, MDNode *BaseMD) {
421 // A distinct node has the self-referential form: !0 = !{ !0, ... }
422 auto IsDistinctNode = [](MDNode *Node) -> bool {
423 return Node && Node->getNumOperands() != 0 && Node == Node->getOperand(I: 0);
424 };
425
426 // Set metadata to null to remove empty/ill-formed metadata from instruction
427 if (BaseMD->getNumOperands() == 0 || !IsDistinctNode(BaseMD))
428 return I->setMetadata(Kind: "llvm.loop", Node: nullptr);
429
430 // It is valid to have a chain of self-refential loop metadata nodes, as
431 // below. We will collapse these into just one when we reconstruct the
432 // metadata.
433 //
434 // Eg:
435 // !0 = !{!0, !1}
436 // !1 = !{!1, !2}
437 // !2 = !{!"llvm.loop.unroll.disable"}
438 //
439 // So, traverse down a potential self-referential chain
440 while (1 < BaseMD->getNumOperands() &&
441 IsDistinctNode(dyn_cast<MDNode>(Val: BaseMD->getOperand(I: 1))))
442 BaseMD = dyn_cast<MDNode>(Val: BaseMD->getOperand(I: 1));
443
444 // To reconstruct a distinct node we create a temporary node that we will
445 // then update to create a self-reference.
446 llvm::TempMDTuple TempNode = llvm::MDNode::getTemporary(Context&: M.getContext(), MDs: {});
447 SmallVector<Metadata *> CompatibleOperands = {TempNode.get()};
448
449 // Iterate and reconstruct the metadata nodes that contains any hints,
450 // stripping any unrecognized metadata.
451 ArrayRef<MDOperand> Operands = BaseMD->operands();
452 for (auto &Op : Operands.drop_front())
453 if (isLoopMDCompatible(M, MD: Op.get()))
454 CompatibleOperands.push_back(Elt: Op.get());
455
456 if (2 < CompatibleOperands.size())
457 reportLoopError(M, Message: "Provided conflicting hints");
458
459 MDNode *CompatibleLoopMD = MDNode::get(Context&: M.getContext(), MDs: CompatibleOperands);
460 TempNode->replaceAllUsesWith(MD: CompatibleLoopMD);
461
462 I->setMetadata(Kind: "llvm.loop", Node: CompatibleLoopMD);
463}
464
465using InstructionMDList = std::array<unsigned, 7>;
466
467static InstructionMDList getCompatibleInstructionMDs(llvm::Module &M) {
468 return {
469 M.getMDKindID(Name: "dx.nonuniform"), M.getMDKindID(Name: "dx.controlflow.hints"),
470 M.getMDKindID(Name: "dx.precise"), llvm::LLVMContext::MD_range,
471 llvm::LLVMContext::MD_alias_scope, llvm::LLVMContext::MD_noalias,
472 M.getMDKindID(Name: "llvm.loop")};
473}
474
475static void translateInstructionMetadata(Module &M) {
476 // construct allowlist of valid metadata node kinds
477 InstructionMDList DXILCompatibleMDs = getCompatibleInstructionMDs(M);
478 unsigned char MDLoopKind = M.getContext().getMDKindID(Name: "llvm.loop");
479
480 for (Function &F : M) {
481 for (BasicBlock &BB : F) {
482 // This needs to be done first so that "hlsl.controlflow.hints" isn't
483 // removed in the allow-list below
484 translateBranchMetadata(M, BBTerminatorInst: BB.getTerminator());
485
486 for (auto &I : make_early_inc_range(Range&: BB)) {
487 if (isa<UncondBrInst, CondBrInst>(Val: I))
488 if (MDNode *LoopMD = I.getMetadata(KindID: MDLoopKind))
489 translateLoopMetadata(M, I: &I, BaseMD: LoopMD);
490 I.dropUnknownNonDebugMetadata(KnownIDs: DXILCompatibleMDs);
491 }
492 }
493 }
494}
495
496static void cleanModuleFlags(Module &M) {
497 NamedMDNode *MDFlags = M.getModuleFlagsMetadata();
498 if (!MDFlags)
499 return;
500
501 SmallVector<llvm::Module::ModuleFlagEntry> FlagEntries;
502 M.getModuleFlagsMetadata(Flags&: FlagEntries);
503 bool Updated = false;
504 for (auto &Flag : FlagEntries) {
505 // llvm 3.7 only supports behavior up to AppendUnique.
506 if (Flag.Behavior <= Module::ModFlagBehavior::AppendUnique)
507 continue;
508 Flag.Behavior = Module::ModFlagBehavior::Warning;
509 Updated = true;
510 }
511
512 if (!Updated)
513 return;
514
515 MDFlags->eraseFromParent();
516
517 for (auto &Flag : FlagEntries)
518 M.addModuleFlag(Behavior: Flag.Behavior, Key: Flag.Key->getString(), Val: Flag.Val);
519}
520
521using GlobalMDList = std::array<StringLiteral, 12>;
522
523// The following are compatible with DXIL but not emit with clang, they can
524// be added when applicable:
525// dx.typeAnnotations, dx.dxrPayloadAnnotations
526static GlobalMDList CompatibleNamedModuleMDs = {
527 "llvm.ident", "llvm.module.flags",
528 "dx.resources", "dx.valver",
529 "dx.shaderModel", "dx.version",
530 "dx.entryPoints", "dx.source.contents",
531 "dx.source.defines", "dx.source.mainFileName",
532 "dx.source.args", "dx.viewIdState"};
533
534static void translateGlobalMetadata(Module &M, DXILResourceMap &DRM,
535 DXILResourceTypeMap &DRTM,
536 const ModuleShaderFlags &ShaderFlags,
537 const ModuleMetadataInfo &MMDI,
538 const ModuleSignatureInfo &SignatureInfo) {
539 LLVMContext &Ctx = M.getContext();
540 IRBuilder<> IRB(M);
541 SmallVector<MDNode *> EntryFnMDNodes;
542
543 emitValidatorVersionMD(M, MMDI);
544 emitShaderModelVersionMD(M, MMDI);
545 emitDXILVersionTupleMD(M, MMDI);
546 NamedMDNode *NamedResourceMD = emitResourceMetadata(M, DRM, DRTM);
547 auto *ResourceMD =
548 (NamedResourceMD != nullptr) ? NamedResourceMD->getOperand(i: 0) : nullptr;
549
550 if (MMDI.ShaderProfile == Triple::EnvironmentType::Library) {
551 // Get the combined shader flag mask of all functions in the library to be
552 // used as shader flags mask value associated with top-level library entry
553 // metadata.
554 uint64_t CombinedMask = ShaderFlags.getCombinedFlags();
555 EntryFnMDNodes.emplace_back(
556 Args: emitTopLevelLibraryNode(M, RMD: ResourceMD, ShaderFlags: CombinedMask));
557 } else if (1 < MMDI.EntryPropertyVec.size())
558 reportError(M, Message: "Non-library shader: One and only one entry expected");
559
560 for (const EntryProperties &EntryProp : MMDI.EntryPropertyVec) {
561 uint64_t EntryShaderFlags = 0;
562 if (MMDI.ShaderProfile != Triple::EnvironmentType::Library) {
563 EntryShaderFlags = ShaderFlags.getFunctionFlags(EntryProp.Entry);
564 if (EntryProp.ShaderStage != MMDI.ShaderProfile)
565 reportError(
566 M, Message: "Shader stage '" +
567 Twine(getShortShaderStage(Env: EntryProp.ShaderStage)) +
568 "' for entry '" + Twine(EntryProp.Entry->getName()) +
569 "' different from specified target profile '" +
570 Twine(Triple::getEnvironmentTypeName(Kind: MMDI.ShaderProfile) +
571 "'"));
572 }
573 MDTuple *Signatures = nullptr;
574 if (const EntrySignature *Sig = SignatureInfo.get(F: EntryProp.Entry)) {
575 Signatures = Sig->getAsMetadata(Ctx, ValidatorVersion: MMDI.ValidatorVersion);
576 // This module-level dependency state describes a single shader, not a
577 // concatenation of the independent signatures in a library.
578 if (MMDI.ShaderProfile != Triple::Library &&
579 (MMDI.ValidatorVersion.empty() ||
580 MMDI.ValidatorVersion == VersionTuple(0, 0) ||
581 MMDI.ValidatorVersion >= VersionTuple(1, 1)) &&
582 (Sig->InputVectors || Sig->OutputVectors)) {
583 auto State = Sig->getDependencyState();
584 auto *StateMD = M.getOrInsertNamedMetadata(Name: "dx.viewIdState");
585 StateMD->clearOperands();
586 StateMD->addOperand(M: MDNode::get(
587 Context&: Ctx, MDs: ConstantAsMetadata::get(C: ConstantDataArray::get(Context&: Ctx, Elts&: State))));
588 }
589 }
590 EntryFnMDNodes.emplace_back(Args: emitEntryMD(
591 M, EP: EntryProp, Signatures, MDResources: ResourceMD, EntryShaderFlags, MMDI));
592 }
593
594 NamedMDNode *EntryPointsNamedMD =
595 M.getOrInsertNamedMetadata(Name: "dx.entryPoints");
596 for (auto *Entry : EntryFnMDNodes)
597 EntryPointsNamedMD->addOperand(M: Entry);
598
599 cleanModuleFlags(M);
600
601 // Finally, strip all module metadata that is not explicitly specified in the
602 // allow-list
603 SmallVector<NamedMDNode *> ToStrip;
604
605 for (NamedMDNode &NamedMD : M.named_metadata())
606 if (!NamedMD.getName().starts_with(Prefix: "llvm.dbg.") &&
607 !llvm::is_contained(Range&: CompatibleNamedModuleMDs, Element: NamedMD.getName()))
608 ToStrip.push_back(Elt: &NamedMD);
609
610 for (NamedMDNode *NamedMD : ToStrip)
611 NamedMD->eraseFromParent();
612}
613
614PreservedAnalyses DXILTranslateMetadata::run(Module &M,
615 ModuleAnalysisManager &MAM) {
616 DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(IR&: M);
617 DXILResourceTypeMap &DRTM = MAM.getResult<DXILResourceTypeAnalysis>(IR&: M);
618 const ModuleShaderFlags &ShaderFlags = MAM.getResult<ShaderFlagsAnalysis>(IR&: M);
619 const dxil::ModuleMetadataInfo MMDI = MAM.getResult<DXILMetadataAnalysis>(IR&: M);
620
621 const auto &SignatureInfo = MAM.getResult<SignatureAnalysis>(IR&: M);
622 translateGlobalMetadata(M, DRM, DRTM, ShaderFlags, MMDI, SignatureInfo);
623 translateInstructionMetadata(M);
624
625 return PreservedAnalyses::all();
626}
627
628void DXILTranslateMetadataLegacy::getAnalysisUsage(AnalysisUsage &AU) const {
629 AU.addRequired<DXILResourceTypeWrapperPass>();
630 AU.addRequired<DXILResourceWrapperPass>();
631 AU.addRequired<ShaderFlagsAnalysisWrapper>();
632 AU.addRequired<DXILMetadataAnalysisWrapperPass>();
633 AU.addRequired<RootSignatureAnalysisWrapper>();
634 AU.addRequired<SignatureAnalysisWrapper>();
635
636 AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
637 AU.addPreserved<DXILResourceBindingWrapperPass>();
638 AU.addPreserved<DXILResourceWrapperPass>();
639 AU.addPreserved<RootSignatureAnalysisWrapper>();
640 AU.addPreserved<SignatureAnalysisWrapper>();
641 AU.addPreserved<ShaderFlagsAnalysisWrapper>();
642}
643
644bool DXILTranslateMetadataLegacy::runOnModule(Module &M) {
645 DXILResourceMap &DRM =
646 getAnalysis<DXILResourceWrapperPass>().getResourceMap();
647 DXILResourceTypeMap &DRTM =
648 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
649 const ModuleShaderFlags &ShaderFlags =
650 getAnalysis<ShaderFlagsAnalysisWrapper>().getShaderFlags();
651 dxil::ModuleMetadataInfo MMDI =
652 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
653
654 const auto &SignatureInfo =
655 getAnalysis<SignatureAnalysisWrapper>().getSignatureInfo();
656 translateGlobalMetadata(M, DRM, DRTM, ShaderFlags, MMDI, SignatureInfo);
657 translateInstructionMetadata(M);
658 return true;
659}
660
661char DXILTranslateMetadataLegacy::ID = 0;
662
663ModulePass *llvm::createDXILTranslateMetadataLegacyPass() {
664 return new DXILTranslateMetadataLegacy();
665}
666
667INITIALIZE_PASS_BEGIN(DXILTranslateMetadataLegacy, "dxil-translate-metadata",
668 "DXIL Translate Metadata", false, false)
669INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass)
670INITIALIZE_PASS_DEPENDENCY(ShaderFlagsAnalysisWrapper)
671INITIALIZE_PASS_DEPENDENCY(RootSignatureAnalysisWrapper)
672INITIALIZE_PASS_DEPENDENCY(SignatureAnalysisWrapper)
673INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass)
674INITIALIZE_PASS_END(DXILTranslateMetadataLegacy, "dxil-translate-metadata",
675 "DXIL Translate Metadata", false, false)
676