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