1//===- DXContainerGlobals.cpp - DXContainer global generator pass ---------===//
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// DXContainerGlobalsPass implementation.
10//
11//===----------------------------------------------------------------------===//
12
13#include "DXILRootSignature.h"
14#include "DXILShaderFlags.h"
15#include "DXILSignatureAnalysis.h"
16#include "DXILWriter/DXILWriterPass.h"
17#include "DirectX.h"
18#include "MCTargetDesc/DirectXContainerObjectWriter.h"
19#include "llvm/ADT/SmallVector.h"
20#include "llvm/ADT/StringExtras.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/Analysis/DXILMetadataAnalysis.h"
23#include "llvm/Analysis/DXILResource.h"
24#include "llvm/BinaryFormat/DXContainer.h"
25#include "llvm/CodeGen/Passes.h"
26#include "llvm/IR/Constants.h"
27#include "llvm/IR/Module.h"
28#include "llvm/InitializePasses.h"
29#include "llvm/MC/DXContainerInfo.h"
30#include "llvm/MC/DXContainerPSVInfo.h"
31#include "llvm/MC/MCDXContainerWriter.h"
32#include "llvm/Pass.h"
33#include "llvm/Support/CommandLine.h"
34#include "llvm/Support/Compression.h"
35#include "llvm/Support/MD5.h"
36#include "llvm/Support/Path.h"
37#include "llvm/TargetParser/Triple.h"
38#include "llvm/Transforms/Utils/ModuleUtils.h"
39#include <cstdint>
40
41using namespace llvm;
42using namespace llvm::dxil;
43using namespace llvm::mcdxbc;
44
45static cl::opt<bool> ShaderHashDependsOnSource(
46 "dx-Zss", cl::desc("Compute Shader Hash considering source information"));
47
48cl::opt<bool> dxil::PdbInPrivate("dx-pdb-in-private",
49 cl::desc("Store PDB in private user data"));
50
51namespace {
52class DXContainerGlobals : public llvm::ModulePass {
53
54 GlobalVariable *buildContainerGlobal(Module &M, Constant *Content,
55 StringRef Name, StringRef SectionName);
56 void addSection(Module &M, SmallVector<GlobalValue *> &Globals,
57 StringRef SectionData, StringRef MetadataName,
58 StringRef SectionName);
59 GlobalVariable *getFeatureFlags(Module &M);
60 void computeShaderHashAndDebugName(Module &M,
61 SmallVector<GlobalValue *> &Globals);
62 GlobalVariable *buildSignature(Module &M, Signature &Sig, StringRef Name,
63 StringRef SectionName);
64 void addSignature(Module &M, SmallVector<GlobalValue *> &Globals);
65 void addRootSignature(Module &M, SmallVector<GlobalValue *> &Globals);
66 void addResourcesForPSV(Module &M, PSVRuntimeInfo &PSV);
67 void addPipelineStateValidationInfo(Module &M,
68 SmallVector<GlobalValue *> &Globals);
69 void addCompilerVersion(Module &M, SmallVector<GlobalValue *> &Globals);
70 void addSourceInfo(Module &M, SmallVector<GlobalValue *> &Globals);
71
72public:
73 static char ID; // Pass identification, replacement for typeid
74 DXContainerGlobals() : ModulePass(ID) {}
75
76 StringRef getPassName() const override {
77 return "DXContainer Global Emitter";
78 }
79
80 bool runOnModule(Module &M) override;
81
82 void getAnalysisUsage(AnalysisUsage &AU) const override {
83 AU.setPreservesAll();
84 AU.addRequired<ShaderFlagsAnalysisWrapper>();
85 AU.addRequired<RootSignatureAnalysisWrapper>();
86 AU.addRequired<SignatureAnalysisWrapper>();
87 AU.addRequired<DXILMetadataAnalysisWrapperPass>();
88 AU.addRequired<DXILResourceTypeWrapperPass>();
89 AU.addRequired<DXILResourceWrapperPass>();
90 }
91};
92
93} // namespace
94
95bool DXContainerGlobals::runOnModule(Module &M) {
96 llvm::SmallVector<GlobalValue *> Globals;
97 Globals.push_back(Elt: getFeatureFlags(M));
98 computeShaderHashAndDebugName(M, Globals);
99 addSignature(M, Globals);
100 addRootSignature(M, Globals);
101 addPipelineStateValidationInfo(M, Globals);
102 addCompilerVersion(M, Globals);
103 addSourceInfo(M, Globals);
104 appendToCompilerUsed(M, Values: Globals);
105 return true;
106}
107
108GlobalVariable *DXContainerGlobals::getFeatureFlags(Module &M) {
109 uint64_t CombinedFeatureFlags = getAnalysis<ShaderFlagsAnalysisWrapper>()
110 .getShaderFlags()
111 .getCombinedFlags()
112 .getFeatureFlags();
113
114 Constant *FeatureFlagsConstant =
115 ConstantInt::get(Context&: M.getContext(), V: APInt(64, CombinedFeatureFlags));
116 return buildContainerGlobal(M, Content: FeatureFlagsConstant, Name: "dx.sfi0", SectionName: "SFI0");
117}
118
119void DXContainerGlobals::addSection(Module &M,
120 SmallVector<GlobalValue *> &Globals,
121 StringRef SectionData,
122 StringRef MetadataName,
123 StringRef SectionName) {
124 Constant *SectionConstant = ConstantDataArray::getString(
125 Context&: M.getContext(), Initializer: SectionData, /*AddNull*/ false);
126 Globals.emplace_back(
127 Args: buildContainerGlobal(M, Content: SectionConstant, Name: MetadataName, SectionName));
128}
129
130void DXContainerGlobals::computeShaderHashAndDebugName(
131 Module &M, SmallVector<GlobalValue *> &Globals) {
132 ConstantDataArray *DXILConstant;
133 MD5 Digest;
134 dxbc::ShaderHash HashData = {.Flags: 0, .Digest: {0}};
135
136 if (ShaderHashDependsOnSource) {
137 if (auto *ILDB = M.getNamedGlobal(Name: "dx.ildb")) {
138 DXILConstant = cast<ConstantDataArray>(Val: ILDB->getInitializer());
139 HashData.Flags = static_cast<uint32_t>(dxbc::HashFlags::IncludesSource);
140 } else {
141 reportFatalUsageError(reason: "/Zss requires debug info (/Zi or /Zs)");
142 }
143 } else {
144 DXILConstant =
145 cast<ConstantDataArray>(Val: M.getNamedGlobal(Name: "dx.dxil")->getInitializer());
146 }
147
148 Digest.update(Str: DXILConstant->getRawDataValues());
149 MD5::MD5Result MD5 = Digest.final();
150
151 memcpy(dest: reinterpret_cast<void *>(&HashData.Digest), src: MD5.data(), n: 16);
152 if (sys::IsBigEndianHost)
153 HashData.swapBytes();
154 StringRef Data(reinterpret_cast<char *>(&HashData), sizeof(dxbc::ShaderHash));
155
156 Constant *ModuleConstant =
157 ConstantDataArray::get(Context&: M.getContext(), Elts: arrayRefFromStringRef(Input: Data));
158 Globals.emplace_back(
159 Args: buildContainerGlobal(M, Content: ModuleConstant, Name: "dx.hash", SectionName: "HASH"));
160
161 if (M.debug_compile_units().empty())
162 return;
163
164 SmallString<40> DebugNameStr;
165 Digest.stringifyResult(Result&: MD5, Str&: DebugNameStr);
166 DebugNameStr += ".pdb";
167 if (!PdbDebugPath.empty() || PdbInPrivate) {
168 if (!PdbDebugPath.empty()) {
169 StringRef DebugFile = PdbDebugPath.getValue();
170 SmallString<256> AbsoluteDebugName;
171 if (sys::path::is_separator(value: DebugFile.back())) {
172 // If PDB output path was specified as a directory, put the MD5.pdb file
173 // there.
174 AbsoluteDebugName = DebugFile;
175 sys::path::append(path&: AbsoluteDebugName, a: DebugNameStr);
176 } else {
177 // Otherwise, use PDB output path as a user-provided PDB file name.
178 DebugNameStr = DebugFile;
179 AbsoluteDebugName = DebugNameStr;
180 }
181
182 // Pass PDB name to DXContainerPDBPass via PDBNAME section.
183 addSection(M, Globals, SectionData: AbsoluteDebugName, MetadataName: "dx.pdb.name",
184 SectionName: PdbFileNameSectionName);
185 }
186
187 // Pass module hash to DXContainerPDBPass.
188 Globals.emplace_back(Args: buildContainerGlobal(
189 M, Content: ConstantDataArray::get(Context&: M.getContext(), Elts: ArrayRef(HashData.Digest)),
190 Name: "dx.pdb.hash", SectionName: ModuleHashSectionName));
191 }
192
193 // Emit ILDN part in debug info mode.
194 mcdxbc::DebugName DebugName;
195 DebugName.setFilename(DebugNameStr);
196 SmallString<64> ILDNData;
197 raw_svector_ostream OS(ILDNData);
198 DebugName.write(OS);
199 addSection(M, Globals, SectionData: ILDNData, MetadataName: "dx.ildn", SectionName: "ILDN");
200}
201
202GlobalVariable *DXContainerGlobals::buildContainerGlobal(
203 Module &M, Constant *Content, StringRef Name, StringRef SectionName) {
204 auto *GV = new llvm::GlobalVariable(
205 M, Content->getType(), true, GlobalValue::PrivateLinkage, Content, Name);
206 GV->setSection(SectionName);
207 GV->setAlignment(Align(4));
208 return GV;
209}
210
211GlobalVariable *DXContainerGlobals::buildSignature(Module &M, Signature &Sig,
212 StringRef Name,
213 StringRef SectionName) {
214 SmallString<256> Data;
215 raw_svector_ostream OS(Data);
216 Sig.write(OS);
217 Constant *Constant =
218 ConstantDataArray::getString(Context&: M.getContext(), Initializer: Data, /*AddNull*/ false);
219 return buildContainerGlobal(M, Content: Constant, Name, SectionName);
220}
221
222void DXContainerGlobals::addSignature(Module &M,
223 SmallVector<GlobalValue *> &Globals) {
224 const auto &MMI =
225 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
226 const auto &Signatures =
227 getAnalysis<SignatureAnalysisWrapper>().getSignatureInfo();
228 Signature InputSig, OutputSig;
229 if (MMI.ShaderProfile != Triple::Library && MMI.EntryPropertyVec.size() == 1)
230 if (const auto *Sig = Signatures.get(F: MMI.EntryPropertyVec[0].Entry))
231 Sig->buildSignatures(Input&: InputSig, Output&: OutputSig, ValidatorVersion: MMI.ValidatorVersion);
232
233 Globals.emplace_back(Args: buildSignature(M, Sig&: InputSig, Name: "dx.isg1", SectionName: "ISG1"));
234 Globals.emplace_back(Args: buildSignature(M, Sig&: OutputSig, Name: "dx.osg1", SectionName: "OSG1"));
235}
236
237void DXContainerGlobals::addRootSignature(Module &M,
238 SmallVector<GlobalValue *> &Globals) {
239
240 dxil::ModuleMetadataInfo &MMI =
241 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
242
243 // Root Signature in Library don't compile to DXContainer.
244 if (MMI.ShaderProfile == llvm::Triple::Library)
245 return;
246
247 auto &RSA = getAnalysis<RootSignatureAnalysisWrapper>().getRSInfo();
248 const Function *EntryFunction = nullptr;
249
250 if (MMI.ShaderProfile != llvm::Triple::RootSignature) {
251 assert(MMI.EntryPropertyVec.size() == 1);
252 EntryFunction = MMI.EntryPropertyVec[0].Entry;
253 }
254
255 const mcdxbc::RootSignatureDesc *RS = RSA.getDescForFunction(F: EntryFunction);
256 if (!RS)
257 return;
258
259 SmallString<256> Data;
260 raw_svector_ostream OS(Data);
261
262 RS->write(OS);
263
264 addSection(M, Globals, SectionData: Data, MetadataName: "dx.rts0", SectionName: "RTS0");
265}
266
267void DXContainerGlobals::addResourcesForPSV(Module &M, PSVRuntimeInfo &PSV) {
268 const DXILResourceMap &DRM =
269 getAnalysis<DXILResourceWrapperPass>().getResourceMap();
270 DXILResourceTypeMap &DRTM =
271 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
272
273 auto MakeBinding =
274 [](const dxil::ResourceInfo::ResourceBinding &Binding,
275 const dxbc::PSV::ResourceType Type, const dxil::ResourceKind Kind,
276 const dxbc::PSV::ResourceFlags Flags = dxbc::PSV::ResourceFlags()) {
277 dxbc::PSV::v2::ResourceBindInfo BindInfo;
278 BindInfo.Type = Type;
279 BindInfo.LowerBound = Binding.LowerBound;
280 assert(
281 (Binding.Size == 0 ||
282 (uint64_t)Binding.LowerBound + Binding.Size - 1 <= UINT32_MAX) &&
283 "Resource range is too large");
284 BindInfo.UpperBound = (Binding.Size == 0)
285 ? UINT32_MAX
286 : Binding.LowerBound + Binding.Size - 1;
287 BindInfo.Space = Binding.Space;
288 BindInfo.Kind = static_cast<dxbc::PSV::ResourceKind>(Kind);
289 BindInfo.Flags = Flags;
290 return BindInfo;
291 };
292
293 for (const dxil::ResourceInfo &RI : DRM.cbuffers()) {
294 if (!RI.hasBinding())
295 continue;
296 const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
297 PSV.Resources.push_back(Elt: MakeBinding(Binding, dxbc::PSV::ResourceType::CBV,
298 dxil::ResourceKind::CBuffer));
299 }
300 for (const dxil::ResourceInfo &RI : DRM.samplers()) {
301 if (!RI.hasBinding())
302 continue;
303 const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
304 PSV.Resources.push_back(Elt: MakeBinding(Binding,
305 dxbc::PSV::ResourceType::Sampler,
306 dxil::ResourceKind::Sampler));
307 }
308 for (const dxil::ResourceInfo &RI : DRM.srvs()) {
309 if (!RI.hasBinding())
310 continue;
311 const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
312
313 dxil::ResourceTypeInfo &TypeInfo = DRTM[RI.getHandleTy()];
314 dxbc::PSV::ResourceType ResType;
315 if (TypeInfo.isStruct())
316 ResType = dxbc::PSV::ResourceType::SRVStructured;
317 else if (TypeInfo.isTyped())
318 ResType = dxbc::PSV::ResourceType::SRVTyped;
319 else
320 ResType = dxbc::PSV::ResourceType::SRVRaw;
321
322 PSV.Resources.push_back(
323 Elt: MakeBinding(Binding, ResType, TypeInfo.getResourceKind()));
324 }
325 for (const dxil::ResourceInfo &RI : DRM.uavs()) {
326 if (!RI.hasBinding())
327 continue;
328 const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
329
330 dxil::ResourceTypeInfo &TypeInfo = DRTM[RI.getHandleTy()];
331 dxbc::PSV::ResourceType ResType;
332 if (RI.hasCounter())
333 ResType = dxbc::PSV::ResourceType::UAVStructuredWithCounter;
334 else if (TypeInfo.isStruct())
335 ResType = dxbc::PSV::ResourceType::UAVStructured;
336 else if (TypeInfo.isTyped())
337 ResType = dxbc::PSV::ResourceType::UAVTyped;
338 else
339 ResType = dxbc::PSV::ResourceType::UAVRaw;
340
341 dxbc::PSV::ResourceFlags Flags;
342 Flags.Bits.UsedByAtomic64 = RI.HasAtomic64Use;
343
344 PSV.Resources.push_back(
345 Elt: MakeBinding(Binding, ResType, TypeInfo.getResourceKind(), Flags));
346 }
347}
348
349void DXContainerGlobals::addPipelineStateValidationInfo(
350 Module &M, SmallVector<GlobalValue *> &Globals) {
351 SmallString<256> Data;
352 raw_svector_ostream OS(Data);
353 PSVRuntimeInfo PSV;
354 PSV.BaseData.MinimumWaveLaneCount = 0;
355 PSV.BaseData.MaximumWaveLaneCount = std::numeric_limits<uint32_t>::max();
356
357 dxil::ModuleMetadataInfo &MMI =
358 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
359 assert(MMI.EntryPropertyVec.size() == 1 ||
360 MMI.ShaderProfile == Triple::Library ||
361 MMI.ShaderProfile == Triple::RootSignature);
362 PSV.BaseData.ShaderStage =
363 static_cast<uint8_t>(MMI.ShaderProfile - Triple::Pixel);
364
365 addResourcesForPSV(M, PSV);
366 unsigned PSVVersion = 3;
367 if (MMI.ShaderProfile != Triple::Library &&
368 MMI.EntryPropertyVec.size() == 1) {
369 const auto &Signatures =
370 getAnalysis<SignatureAnalysisWrapper>().getSignatureInfo();
371 if (const auto *Sig = Signatures.get(F: MMI.EntryPropertyVec[0].Entry)) {
372 Sig->updatePSV(PSV, ValidatorVersion: MMI.ValidatorVersion);
373 // Preserve the existing empty-signature path, and select an ABI version
374 // compatible with the validator for graphics signature data.
375 VersionTuple V = MMI.ValidatorVersion;
376 if ((!Sig->Inputs.empty() || !Sig->Outputs.empty()) && !V.empty() &&
377 V != VersionTuple(0, 0))
378 PSVVersion = V < VersionTuple(1, 1) ? 0
379 : V < VersionTuple(1, 6) ? 1
380 : V < VersionTuple(1, 8) ? 2
381 : 3;
382 }
383 }
384
385 // Hardcoded values here to unblock loading the shader into D3D.
386 //
387 // TODO: Lots more stuff to do here!
388 //
389 // See issue https://github.com/llvm/llvm-project/issues/96674.
390 switch (MMI.ShaderProfile) {
391 case Triple::Compute:
392 PSV.BaseData.NumThreadsX = MMI.EntryPropertyVec[0].NumThreadsX;
393 PSV.BaseData.NumThreadsY = MMI.EntryPropertyVec[0].NumThreadsY;
394 PSV.BaseData.NumThreadsZ = MMI.EntryPropertyVec[0].NumThreadsZ;
395 if (MMI.EntryPropertyVec[0].WaveSizeMin) {
396 PSV.BaseData.MinimumWaveLaneCount = MMI.EntryPropertyVec[0].WaveSizeMin;
397 PSV.BaseData.MaximumWaveLaneCount =
398 MMI.EntryPropertyVec[0].WaveSizeMax
399 ? MMI.EntryPropertyVec[0].WaveSizeMax
400 : MMI.EntryPropertyVec[0].WaveSizeMin;
401 }
402 break;
403 default:
404 break;
405 }
406
407 if (MMI.ShaderProfile != Triple::Library &&
408 MMI.ShaderProfile != Triple::RootSignature)
409 PSV.EntryName = MMI.EntryPropertyVec[0].Entry->getName();
410
411 PSV.finalize(Stage: MMI.ShaderProfile, Version: PSVVersion);
412 PSV.write(OS, Version: PSVVersion);
413 addSection(M, Globals, SectionData: Data, MetadataName: "dx.psv0", SectionName: "PSV0");
414}
415
416void DXContainerGlobals::addCompilerVersion(
417 Module &M, SmallVector<GlobalValue *> &Globals) {
418 if (M.debug_compile_units().empty())
419 return;
420
421 SmallString<256> Data;
422 raw_svector_ostream OS(Data);
423 mcdxbc::CompilerVersion CompilerVersion;
424 CompilerVersion.write(OS);
425 addSection(M, Globals, SectionData: Data, MetadataName: "dx.vers", SectionName: "VERS");
426}
427
428void DXContainerGlobals::addSourceInfo(Module &M,
429 SmallVector<GlobalValue *> &Globals) {
430 dxil::ModuleMetadataInfo &MMI =
431 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
432
433 if (!MMI.SourceInfo || SourceInDebugModule)
434 return;
435
436 MMI.SourceInfo->computeEntries();
437 MMI.SourceInfo->finalize();
438 SmallString<256> Data;
439 raw_svector_ostream OS(Data);
440 MMI.SourceInfo->write(OS);
441 addSection(M, Globals, SectionData: Data, MetadataName: "dx.srci", SectionName: "SRCI");
442}
443
444char DXContainerGlobals::ID = 0;
445INITIALIZE_PASS_BEGIN(DXContainerGlobals, "dxil-globals",
446 "DXContainer Global Emitter", false, true)
447INITIALIZE_PASS_DEPENDENCY(ShaderFlagsAnalysisWrapper)
448INITIALIZE_PASS_DEPENDENCY(SignatureAnalysisWrapper)
449INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass)
450INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
451INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass)
452INITIALIZE_PASS_END(DXContainerGlobals, "dxil-globals",
453 "DXContainer Global Emitter", false, true)
454
455ModulePass *llvm::createDXContainerGlobalsPass() {
456 return new DXContainerGlobals();
457}
458