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