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