LLVM 24.0.0git
DXContainerGlobals.cpp
Go to the documentation of this file.
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"
17#include "DirectX.h"
21#include "llvm/ADT/StringRef.h"
25#include "llvm/CodeGen/Passes.h"
26#include "llvm/IR/Constants.h"
27#include "llvm/IR/Module.h"
32#include "llvm/Pass.h"
35#include "llvm/Support/MD5.h"
36#include "llvm/Support/Path.h"
39#include <cstdint>
40
41using namespace llvm;
42using namespace llvm::dxil;
43using namespace llvm::mcdxbc;
44
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,
57 StringRef SectionData, StringRef MetadataName,
59 GlobalVariable *getFeatureFlags(Module &M);
60 void computeShaderHashAndDebugName(Module &M,
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,
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) {
97 Globals.push_back(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, 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(M.getContext(), APInt(64, CombinedFeatureFlags));
116 return buildContainerGlobal(M, FeatureFlagsConstant, "dx.sfi0", "SFI0");
117}
118
119void DXContainerGlobals::addSection(Module &M,
121 StringRef SectionData,
122 StringRef MetadataName,
123 StringRef SectionName) {
124 Constant *SectionConstant = ConstantDataArray::getString(
125 M.getContext(), SectionData, /*AddNull*/ false);
126 Globals.emplace_back(
127 buildContainerGlobal(M, SectionConstant, MetadataName, SectionName));
128}
129
130void DXContainerGlobals::computeShaderHashAndDebugName(
131 Module &M, SmallVector<GlobalValue *> &Globals) {
132 ConstantDataArray *DXILConstant;
133 MD5 Digest;
134 dxbc::ShaderHash HashData = {0, {0}};
135
137 if (auto *ILDB = M.getNamedGlobal("dx.ildb")) {
138 DXILConstant = cast<ConstantDataArray>(ILDB->getInitializer());
139 HashData.Flags = static_cast<uint32_t>(dxbc::HashFlags::IncludesSource);
140 } else {
141 reportFatalUsageError("/Zss requires debug info (/Zi or /Zs)");
142 }
143 } else {
144 DXILConstant =
145 cast<ConstantDataArray>(M.getNamedGlobal("dx.dxil")->getInitializer());
146 }
147
148 Digest.update(DXILConstant->getRawDataValues());
149 MD5::MD5Result MD5 = Digest.final();
150
151 memcpy(reinterpret_cast<void *>(&HashData.Digest), MD5.data(), 16);
153 HashData.swapBytes();
154 StringRef Data(reinterpret_cast<char *>(&HashData), sizeof(dxbc::ShaderHash));
155
156 Constant *ModuleConstant =
158 Globals.emplace_back(
159 buildContainerGlobal(M, ModuleConstant, "dx.hash", "HASH"));
160
161 if (M.debug_compile_units().empty())
162 return;
163
164 SmallString<40> DebugNameStr;
165 Digest.stringifyResult(MD5, 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(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(AbsoluteDebugName, 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, AbsoluteDebugName, "dx.pdb.name",
185 }
186
187 // Pass module hash to DXContainerPDBPass.
188 Globals.emplace_back(buildContainerGlobal(
189 M, ConstantDataArray::get(M.getContext(), ArrayRef(HashData.Digest)),
190 "dx.pdb.hash", 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, ILDNData, "dx.ildn", "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 VersionTuple V = getAnalysis<DXILMetadataAnalysisWrapperPass>()
217 .getModuleMetadata()
218 .ValidatorVersion;
219 bool Legacy = !V.empty() && V != VersionTuple(0, 0) && V < VersionTuple(1, 7);
220 Sig.write(OS, /*PadToDword=*/!Legacy);
222 ConstantDataArray::getString(M.getContext(), Data, /*AddNull*/ false);
223 return buildContainerGlobal(M, Constant, Name, SectionName);
224}
225
226void DXContainerGlobals::addSignature(Module &M,
228 const auto &MMI =
229 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
230 const auto &Signatures =
231 getAnalysis<SignatureAnalysisWrapper>().getSignatureInfo();
232 Signature InputSig, OutputSig;
233 if (MMI.ShaderProfile != Triple::Library && MMI.EntryPropertyVec.size() == 1)
234 if (const auto *Sig = Signatures.get(MMI.EntryPropertyVec[0].Entry))
235 Sig->buildSignatures(InputSig, OutputSig, MMI.ValidatorVersion);
236
237 Globals.emplace_back(buildSignature(M, InputSig, "dx.isg1", "ISG1"));
238 Globals.emplace_back(buildSignature(M, OutputSig, "dx.osg1", "OSG1"));
239}
240
241void DXContainerGlobals::addRootSignature(Module &M,
243
244 dxil::ModuleMetadataInfo &MMI =
245 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
246
247 // Root Signature in Library don't compile to DXContainer.
249 return;
250
251 auto &RSA = getAnalysis<RootSignatureAnalysisWrapper>().getRSInfo();
252 const Function *EntryFunction = nullptr;
253
255 assert(MMI.EntryPropertyVec.size() == 1);
256 EntryFunction = MMI.EntryPropertyVec[0].Entry;
257 }
258
259 const mcdxbc::RootSignatureDesc *RS = RSA.getDescForFunction(EntryFunction);
260 if (!RS)
261 return;
262
263 SmallString<256> Data;
264 raw_svector_ostream OS(Data);
265
266 RS->write(OS);
267
268 addSection(M, Globals, Data, "dx.rts0", "RTS0");
269}
270
271void DXContainerGlobals::addResourcesForPSV(Module &M, PSVRuntimeInfo &PSV) {
272 const DXILResourceMap &DRM =
273 getAnalysis<DXILResourceWrapperPass>().getResourceMap();
274 DXILResourceTypeMap &DRTM =
275 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
276
277 auto MakeBinding =
278 [](const dxil::ResourceInfo::ResourceBinding &Binding,
280 const dxbc::PSV::ResourceFlags Flags = dxbc::PSV::ResourceFlags()) {
281 dxbc::PSV::v2::ResourceBindInfo BindInfo;
282 BindInfo.Type = Type;
283 BindInfo.LowerBound = Binding.LowerBound;
284 assert(
285 (Binding.Size == 0 ||
286 (uint64_t)Binding.LowerBound + Binding.Size - 1 <= UINT32_MAX) &&
287 "Resource range is too large");
288 BindInfo.UpperBound = (Binding.Size == 0)
289 ? UINT32_MAX
291 BindInfo.Space = Binding.Space;
292 BindInfo.Kind = static_cast<dxbc::PSV::ResourceKind>(Kind);
293 BindInfo.Flags = Flags;
294 return BindInfo;
295 };
296
297 for (const dxil::ResourceInfo &RI : DRM.cbuffers()) {
298 if (!RI.hasBinding())
299 continue;
300 const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
301 PSV.Resources.push_back(MakeBinding(Binding, dxbc::PSV::ResourceType::CBV,
302 dxil::ResourceKind::CBuffer));
303 }
304 for (const dxil::ResourceInfo &RI : DRM.samplers()) {
305 if (!RI.hasBinding())
306 continue;
307 const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
308 PSV.Resources.push_back(MakeBinding(Binding,
309 dxbc::PSV::ResourceType::Sampler,
310 dxil::ResourceKind::Sampler));
311 }
312 for (const dxil::ResourceInfo &RI : DRM.srvs()) {
313 if (!RI.hasBinding())
314 continue;
315 const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
316
317 dxil::ResourceTypeInfo &TypeInfo = DRTM[RI.getHandleTy()];
319 if (TypeInfo.isStruct())
320 ResType = dxbc::PSV::ResourceType::SRVStructured;
321 else if (TypeInfo.isTyped())
322 ResType = dxbc::PSV::ResourceType::SRVTyped;
323 else
324 ResType = dxbc::PSV::ResourceType::SRVRaw;
325
326 PSV.Resources.push_back(
327 MakeBinding(Binding, ResType, TypeInfo.getResourceKind()));
328 }
329 for (const dxil::ResourceInfo &RI : DRM.uavs()) {
330 if (!RI.hasBinding())
331 continue;
332 const dxil::ResourceInfo::ResourceBinding &Binding = RI.getBinding();
333
334 dxil::ResourceTypeInfo &TypeInfo = DRTM[RI.getHandleTy()];
336 if (RI.hasCounter())
337 ResType = dxbc::PSV::ResourceType::UAVStructuredWithCounter;
338 else if (TypeInfo.isStruct())
339 ResType = dxbc::PSV::ResourceType::UAVStructured;
340 else if (TypeInfo.isTyped())
341 ResType = dxbc::PSV::ResourceType::UAVTyped;
342 else
343 ResType = dxbc::PSV::ResourceType::UAVRaw;
344
345 dxbc::PSV::ResourceFlags Flags;
346 Flags.Bits.UsedByAtomic64 = RI.HasAtomic64Use;
347
348 PSV.Resources.push_back(
349 MakeBinding(Binding, ResType, TypeInfo.getResourceKind(), Flags));
350 }
351}
352
353void DXContainerGlobals::addPipelineStateValidationInfo(
354 Module &M, SmallVector<GlobalValue *> &Globals) {
355 SmallString<256> Data;
356 raw_svector_ostream OS(Data);
357 PSVRuntimeInfo PSV;
359 PSV.BaseData.MaximumWaveLaneCount = std::numeric_limits<uint32_t>::max();
360
361 dxil::ModuleMetadataInfo &MMI =
362 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
363 assert(MMI.EntryPropertyVec.size() == 1 ||
367 static_cast<uint8_t>(MMI.ShaderProfile - Triple::Pixel);
368
369 addResourcesForPSV(M, PSV);
370 unsigned PSVVersion = 3;
371 if (MMI.ShaderProfile != Triple::Library &&
372 MMI.EntryPropertyVec.size() == 1) {
373 const auto &Signatures =
374 getAnalysis<SignatureAnalysisWrapper>().getSignatureInfo();
375 if (const auto *Sig = Signatures.get(MMI.EntryPropertyVec[0].Entry)) {
376 Sig->updatePSV(PSV, MMI.ValidatorVersion);
377 // Preserve the existing empty-signature path, and select an ABI version
378 // compatible with the validator for graphics signature data.
379 VersionTuple V = MMI.ValidatorVersion;
380 if ((!Sig->Inputs.empty() || !Sig->Outputs.empty()) && !V.empty() &&
381 V != VersionTuple(0, 0))
382 PSVVersion = V < VersionTuple(1, 1) ? 0
383 : V < VersionTuple(1, 6) ? 1
384 : V < VersionTuple(1, 8) ? 2
385 : 3;
386 }
387 }
388
389 // Hardcoded values here to unblock loading the shader into D3D.
390 //
391 // TODO: Lots more stuff to do here!
392 //
393 // See issue https://github.com/llvm/llvm-project/issues/96674.
394 switch (MMI.ShaderProfile) {
395 case Triple::Compute:
396 PSV.BaseData.NumThreadsX = MMI.EntryPropertyVec[0].NumThreadsX;
397 PSV.BaseData.NumThreadsY = MMI.EntryPropertyVec[0].NumThreadsY;
398 PSV.BaseData.NumThreadsZ = MMI.EntryPropertyVec[0].NumThreadsZ;
399 if (MMI.EntryPropertyVec[0].WaveSizeMin) {
400 PSV.BaseData.MinimumWaveLaneCount = MMI.EntryPropertyVec[0].WaveSizeMin;
402 MMI.EntryPropertyVec[0].WaveSizeMax
403 ? MMI.EntryPropertyVec[0].WaveSizeMax
404 : MMI.EntryPropertyVec[0].WaveSizeMin;
405 }
406 break;
407 default:
408 break;
409 }
410
411 if (MMI.ShaderProfile != Triple::Library &&
413 PSV.EntryName = MMI.EntryPropertyVec[0].Entry->getName();
414
415 PSV.finalize(MMI.ShaderProfile, PSVVersion);
416 PSV.write(OS, PSVVersion);
417 addSection(M, Globals, Data, "dx.psv0", "PSV0");
418}
419
420void DXContainerGlobals::addCompilerVersion(
421 Module &M, SmallVector<GlobalValue *> &Globals) {
422 if (M.debug_compile_units().empty())
423 return;
424
425 SmallString<256> Data;
426 raw_svector_ostream OS(Data);
427 mcdxbc::CompilerVersion CompilerVersion;
428 CompilerVersion.write(OS);
429 addSection(M, Globals, Data, "dx.vers", "VERS");
430}
431
432void DXContainerGlobals::addSourceInfo(Module &M,
434 dxil::ModuleMetadataInfo &MMI =
435 getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
436
438 return;
439
440 MMI.SourceInfo->computeEntries();
441 MMI.SourceInfo->finalize();
442 SmallString<256> Data;
443 raw_svector_ostream OS(Data);
444 MMI.SourceInfo->write(OS);
445 addSection(M, Globals, Data, "dx.srci", "SRCI");
446}
447
448char DXContainerGlobals::ID = 0;
449INITIALIZE_PASS_BEGIN(DXContainerGlobals, "dxil-globals",
450 "DXContainer Global Emitter", false, true)
456INITIALIZE_PASS_END(DXContainerGlobals, "dxil-globals",
457 "DXContainer Global Emitter", false, true)
458
460 return new DXContainerGlobals();
461}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
This file contains the declarations for the subclasses of Constant, which represent the different fla...
static cl::opt< bool > ShaderHashDependsOnSource("dx-Zss", cl::desc("Compute Shader Hash considering source information"))
DXIL Resource Implicit Binding
static void buildSignature(mcdxbc::Signature &Sig, ArrayRef< SemanticSignatureElement > Elements, bool Input, bool Native16, bool Legacy)
This file provides a bitcode writing pass.
Module.h This file contains the declarations for the Module class.
static Error addSection(const NewSectionInfo &NewSection, Object &Obj)
Machine Check Debug Module
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
This file defines the SmallVector class.
This file contains some functions that are useful when dealing with strings.
AnalysisUsage & addRequired()
void setPreservesAll()
Set by analyses that do not transform their input at all.
static Constant * get(LLVMContext &Context, ArrayRef< ElementTy > Elts)
get() constructor - Return a constant with array type with an element count and element type matching...
Definition Constants.h:878
static LLVM_ABI Constant * getString(LLVMContext &Context, StringRef Initializer, bool AddNull=true, bool ByteString=false)
This method constructs a CDS and initializes it with a text string.
LLVM_ABI StringRef getRawDataValues() const
Return the raw, underlying, bytes of this data.
This is an important base class in LLVM.
Definition Constant.h:44
iterator_range< iterator > samplers()
iterator_range< iterator > srvs()
iterator_range< iterator > cbuffers()
iterator_range< iterator > uavs()
@ PrivateLinkage
Like Internal, but omit from symbol table.
Definition GlobalValue.h:61
LLVM_ABI void update(ArrayRef< uint8_t > Data)
Updates the hash for the byte stream provided.
Definition MD5.cpp:188
static LLVM_ABI void stringifyResult(MD5Result &Result, SmallVectorImpl< char > &Str)
Translates the bytes in Res to a hex string that is deposited into Str.
Definition MD5.cpp:286
LLVM_ABI void final(MD5Result &Result)
Finishes off the hash and puts the result in result.
Definition MD5.cpp:233
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:253
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
char back() const
Get the last character in the string.
Definition StringRef.h:153
@ RootSignature
Definition Triple.h:413
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
LLVM_ABI bool isTyped() const
LLVM_ABI bool isStruct() const
dxil::ResourceKind getResourceKind() const
Wrapper pass for the legacy pass manager.
LLVM_ABI void write(raw_ostream &OS, bool PadToDword=true)
Legacy validator versions through 1.6 require unpadded signature parts.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
ResourceKind
The kind of resource for an SRV or UAV resource.
Definition DXILABI.h:44
cl::opt< std::string > PdbDebugPath
cl::opt< bool > PdbInPrivate
cl::opt< bool > SourceInDebugModule
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition Path.cpp:467
LLVM_ABI bool is_separator(char value, Style style=Style::native)
Check whether the given char is a path separator on the host OS.
Definition Path.cpp:618
constexpr bool IsBigEndianHost
This is an optimization pass for GlobalISel generic memory operations.
ArrayRef< CharT > arrayRefFromStringRef(StringRef Input)
Construct an array ref of bytes from a string ref.
ModulePass * createDXContainerGlobalsPass()
Pass for generating DXContainer part globals.
static constexpr StringLiteral ModuleHashSectionName
Contains module hash.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
LLVM_ABI void appendToCompilerUsed(Module &M, ArrayRef< GlobalValue * > Values)
Adds global values to the llvm.compiler.used list.
ArrayRef(const T &OneElt) -> ArrayRef< T >
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
static constexpr StringLiteral PdbFileNameSectionName
Contains PDB output file name.
LLVM_ABI void reportFatalUsageError(Error Err)
Report a fatal error that does not indicate a bug in LLVM.
Definition Error.cpp:177
std::optional< mcdxbc::SourceInfoBuilder > SourceInfo
Triple::EnvironmentType ShaderProfile
SmallVector< EntryProperties > EntryPropertyVec
LLVM_ABI void write(raw_ostream &OS) const
LLVM_ABI void setFilename(StringRef DebugFilename)
LLVM_ABI void write(raw_ostream &OS) const
dxbc::PSV::v3::RuntimeInfo BaseData
SmallVector< dxbc::PSV::v2::ResourceBindInfo > Resources
LLVM_ABI void finalize(Triple::EnvironmentType Stage, uint32_t Version=std::numeric_limits< uint32_t >::max())
LLVM_ABI void write(raw_ostream &OS, uint32_t Version=std::numeric_limits< uint32_t >::max()) const