LLVM 24.0.0git
DXILPrettyPrinter.cpp
Go to the documentation of this file.
1//===- DXILPrettyPrinter.cpp - Print resources for textual DXIL -----------===//
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 "DXILPrettyPrinter.h"
10#include "DirectX.h"
12#include "llvm/ADT/DenseSet.h"
13#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/StringRef.h"
18#include "llvm/IR/DebugInfo.h"
19#include "llvm/IR/Metadata.h"
20#include "llvm/IR/Module.h"
22#include "llvm/IR/PassManager.h"
24#include "llvm/Pass.h"
29
30using namespace llvm;
31using namespace llvm::dxil;
32
34 switch (RC) {
36 return "texture";
38 return "UAV";
40 return "cbuffer";
42 return "sampler";
43 }
44 llvm_unreachable("covered switch");
45}
46
48 switch (RC) {
50 return "t";
52 return "u";
54 return "cb";
56 return "s";
57 }
58 llvm_unreachable("covered switch");
59}
60
62 if (RI.isTyped()) {
63 switch (RI.getTyped().DXILStorageTy) {
65 return "i1";
67 return "i16";
69 return "u16";
71 return "i32";
73 return "u32";
75 return "i64";
77 return "u64";
79 return "f16";
81 return "f32";
83 return "f64";
85 return "snorm_f16";
87 return "unorm_f16";
89 return "snorm_f32";
91 return "unorm_f32";
93 return "snorm_f64";
95 return "unorm_f64";
97 return "p32i8";
99 return "p32u8";
101 llvm_unreachable("Invalid ElementType");
102 }
103 llvm_unreachable("Unhandled ElementType");
104 } else if (RI.isStruct())
105 return "struct";
106 else if (RI.isCBuffer() || RI.isSampler())
107 return "NA";
108 return "byte";
109}
110
112 switch (RK) {
114 return "1d";
116 return "2d";
118 return "3d";
120 return "cube";
122 return "1darray";
124 return "2darray";
126 return "cubearray";
128 return "tbuffer";
130 return "fbtex2d";
132 return "fbtex2darray";
134 return "2dMS";
136 return "2darrayMS";
145 llvm_unreachable("Invalid ResourceKind for texture");
146 }
147 llvm_unreachable("Unhandled ResourceKind");
148}
149
150namespace {
151struct FormatResourceDimension
152 : public llvm::FormatAdapter<const dxil::ResourceTypeInfo &> {
153 FormatResourceDimension(const dxil::ResourceTypeInfo &RI, bool HasCounter)
154 : llvm::FormatAdapter<const dxil::ResourceTypeInfo &>(RI),
155 HasCounter(HasCounter) {}
156
157 bool HasCounter;
158
159 void format(llvm::raw_ostream &OS, StringRef Style) {
160 dxil::ResourceKind RK = Item.getResourceKind();
161 switch (RK) {
162 default: {
163 OS << getTextureDimName(RK);
164 if (Item.isMultiSample())
165 OS << Item.getMultiSampleCount();
166 break;
167 }
168 case dxil::ResourceKind::RawBuffer:
169 case dxil::ResourceKind::StructuredBuffer:
170 if (!Item.isUAV())
171 OS << "r/o";
172 else if (HasCounter)
173 OS << "r/w+cnt";
174 else
175 OS << "r/w";
176 break;
177 case dxil::ResourceKind::TypedBuffer:
178 OS << "buf";
179 break;
180 case dxil::ResourceKind::CBuffer:
181 OS << "NA";
182 break;
183 case dxil::ResourceKind::RTAccelerationStructure:
184 // TODO: dxc would print "ras" here. Can/should this happen?
185 llvm_unreachable("RTAccelerationStructure printing is not implemented");
186 }
187 }
188};
189
190struct FormatBindingID
191 : public llvm::FormatAdapter<const dxil::ResourceInfo &> {
193
194 explicit FormatBindingID(const dxil::ResourceInfo &RI,
195 const dxil::ResourceTypeInfo &RTI)
196 : llvm::FormatAdapter<const dxil::ResourceInfo &>(RI),
197 RC(RTI.getResourceClass()) {}
198
199 void format(llvm::raw_ostream &OS, StringRef Style) {
200 OS << getRCPrefix(RC).upper() << Item.getBinding().BindingID;
201 }
202};
203
204struct FormatBindingLocation
205 : public llvm::FormatAdapter<const dxil::ResourceInfo &> {
207
208 explicit FormatBindingLocation(const dxil::ResourceInfo &RI,
209 const dxil::ResourceTypeInfo &RTI)
210 : llvm::FormatAdapter<const dxil::ResourceInfo &>(RI),
211 RC(RTI.getResourceClass()) {}
212
213 void format(llvm::raw_ostream &OS, StringRef Style) {
214 const auto &Binding = Item.getBinding();
215 OS << getRCPrefix(RC) << Binding.LowerBound;
216 if (Binding.Space)
217 OS << ",space" << Binding.Space;
218 }
219};
220
221struct FormatBindingSize
222 : public llvm::FormatAdapter<const dxil::ResourceInfo &> {
223 explicit FormatBindingSize(const dxil::ResourceInfo &RI)
224 : llvm::FormatAdapter<const dxil::ResourceInfo &>(RI) {}
225
226 void format(llvm::raw_ostream &OS, StringRef Style) {
227 uint32_t Size = Item.getBinding().Size;
228 if (Size == 0)
229 OS << "unbounded";
230 else
231 OS << Size;
232 }
233};
234
235} // namespace
236
238 DXILResourceTypeMap &DRTM) {
239 // Column widths are arbitrary but match the widths DXC uses.
240 OS << ";\n; Resource Bindings:\n;\n";
241 OS << formatv("; {0,-30} {1,10} {2,7} {3,11} {4,7} {5,14} {6,9}\n", "Name",
242 "Type", "Format", "Dim", "ID", "HLSL Bind", "Count");
243 OS << formatv(
244 "; {0,-+30} {1,-+10} {2,-+7} {3,-+11} {4,-+7} {5,-+14} {6,-+9}\n", "", "",
245 "", "", "", "", "");
246
247 // TODO: Do we want to sort these by binding or something like that?
248 for (const dxil::ResourceInfo &RI : DRM) {
249 if (!RI.hasBinding())
250 continue;
251 const dxil::ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];
252
254 StringRef Name(RI.getName());
257 FormatResourceDimension Dim(RTI, RI.hasCounter());
258 FormatBindingID ID(RI, RTI);
259 FormatBindingLocation Bind(RI, RTI);
260 FormatBindingSize Count(RI);
261 OS << formatv("; {0,-30} {1,10} {2,7} {3,11} {4,7} {5,14} {6,9}\n", Name,
262 Type, Format, Dim, ID, Bind, Count);
263 }
264 OS << ";\n";
265}
266
267namespace {
268class DXILModuleSlotTracker : public ModuleSlotTracker {
269public:
272};
273
274class DXILAssemblyAnnotationWriter : public llvm::AssemblyAnnotationWriter {
275private:
276 ModuleSlotTracker &MST;
277 AbstractSlotTrackerStorage &STS;
278 const DXILDebugInfoMap &DI;
279 DenseSet<const MDNode *> &EmittedMDNodes;
280
281public:
282 DXILAssemblyAnnotationWriter(ModuleSlotTracker &MST,
283 AbstractSlotTrackerStorage &STS,
284 const DXILDebugInfoMap &DI,
285 DenseSet<const MDNode *> &EmittedMDNodes)
286 : MST(MST), STS(STS), DI(DI), EmittedMDNodes(EmittedMDNodes) {}
287
288 void emitInstructionAnnot(const Instruction *OrigI,
289 formatted_raw_ostream &os) override {
290 if (const Instruction *I = &DI.getDXILInstruction(*OrigI); I != OrigI) {
291 os << "; DXIL: to be replaced with: ";
292 I->print(os, MST);
293 os << "\n";
294 }
295 }
296
297 void emitMDNodeAnnot(const MDNode *N, formatted_raw_ostream &os) override {
298 EmittedMDNodes.insert(N);
299
300 if (const Metadata *NewMD = DI.MDReplace.lookup(N)) {
301 if (const auto *NewN = dyn_cast<MDNode>(NewMD))
302 STS.createMetadataSlot(NewN);
303
304 os << "; DXIL: ";
305 N->printAsOperand(os, MST);
306 os << ": to be replaced by: ";
307 NewMD->printAsOperand(os, MST);
308 os << "\n";
309 return;
310 }
311
312 if (const Metadata *ExtraMD = DI.MDExtra.lookup(N)) {
313 if (const auto *ExtraN = dyn_cast<MDNode>(ExtraMD))
314 STS.createMetadataSlot(ExtraN);
315
316 os << "; DXIL: ";
317 N->printAsOperand(os, MST);
318 os << ": additional data: ";
319 ExtraMD->printAsOperand(os, MST);
320 os << "\n";
321 return;
322 }
323 }
324};
325} // namespace
326
329 // Annotation metadata follows module metadata in the order its keys print.
330 // Follow replacement graphs to preserve that order in canonical output.
331 M.renumberMetadataForAssembly();
332
333 ModuleSlotTracker MST(&M);
334 AbstractSlotTrackerStorage *STS = nullptr;
335 MST.setProcessHook(
336 [&](AbstractSlotTrackerStorage *STS_, const Module *) { STS = STS_; });
337 MDNode::get(M.getContext(), {})->print(llvm::nulls(), MST);
338 assert(STS && "Slot tracker storage should have been initialised");
339
340 DenseSet<const Metadata *> ReplacementMetadata;
341 for (auto [_, Replacement] : DI.MDReplace)
342 ReplacementMetadata.insert(Replacement);
343
346 auto AddOriginal = [&](const Metadata *MD) {
347 const auto *N = dyn_cast<MDNode>(MD);
348 if (!N || ReplacementMetadata.contains(N) || !Queued.insert(N).second)
349 return;
350 OriginalNodes.emplace_back(STS->getMetadataSlot(N), N);
351 };
352 for (auto [Original, _] : DI.MDReplace)
353 AddOriginal(Original);
354 for (auto [Original, _] : DI.MDExtra)
355 AddOriginal(Original);
356 llvm::sort(OriginalNodes);
357
359 for (auto [_, N] : OriginalNodes)
360 Worklist.push_back(N);
361
362 SmallVector<const MDNode *> AdditionalMetadata;
363 auto AddAdditional = [&](const Metadata *MD) {
364 const auto *Root = dyn_cast_or_null<MDNode>(MD);
365 if (!Root || Queued.contains(Root))
366 return;
367
368 AdditionalMetadata.push_back(Root);
369 SmallVector<const MDNode *> Nodes = {Root};
370 while (!Nodes.empty()) {
371 const MDNode *N = Nodes.pop_back_val();
372 if (!Queued.insert(N).second)
373 continue;
374 Worklist.push_back(N);
375 for (const MDOperand &Op : llvm::reverse(N->operands()))
376 if (const auto *OpNode = dyn_cast_or_null<MDNode>(Op.get()))
377 Nodes.push_back(OpNode);
378 }
379 };
380
381 for (size_t I = 0; I != Worklist.size(); ++I) {
382 const MDNode *N = Worklist[I];
383 if (const Metadata *Replacement = DI.MDReplace.lookup(N)) {
384 AddAdditional(Replacement);
385 continue;
386 }
387 AddAdditional(DI.MDExtra.lookup(N));
388 }
389 return AdditionalMetadata;
390}
391
392static void prettyPrint(raw_ostream &OS, Module &M, const DXILResourceMap &DRM,
393 DXILResourceTypeMap &DRTM, const DXILDebugInfoMap &DI) {
394 formatted_raw_ostream FOS(OS);
395
396 prettyPrintResources(FOS, DRM, DRTM);
397
398 SmallVector<const MDNode *> AdditionalMetadata =
400 DXILModuleSlotTracker MST(&M);
401 MST.renumberMetadataForAssembly(AdditionalMetadata);
402 AbstractSlotTrackerStorage *STS = nullptr;
403 MST.setProcessHook(
404 [&](AbstractSlotTrackerStorage *STS_, const Module *) { STS = STS_; });
405 // Force initialisation. ModuleSlotTracker does not have a dedicated function
406 // for this so trigger it through a dummy print.
407 MDNode::get(M.getContext(), {})->print(llvm::nulls(), MST);
408 assert(STS && "Slot tracker storage should have been initialised");
409
410 DenseSet<const MDNode *> EmittedMDNodes;
411 DXILAssemblyAnnotationWriter DAAW(MST, *STS, DI, EmittedMDNodes);
412 M.print(FOS, &DAAW);
413
415 MST.collectMDNodes(MDNodes);
416 std::sort(MDNodes.begin(), MDNodes.end(),
417 [](const std::pair<unsigned, const MDNode *> &A,
418 const std::pair<unsigned, const MDNode *> &B) {
419 return A.first < B.first;
420 });
421 for (auto [_, MDNode] : MDNodes) {
422 if (EmittedMDNodes.contains(MDNode))
423 continue;
424 DAAW.emitMDNodeAnnot(MDNode, FOS);
425 MDNode->print(FOS, MST);
426 FOS << "\n";
427 }
428}
429
438
439namespace {
440class DXILPrettyPrinterLegacy : public llvm::ModulePass {
441 raw_ostream &OS; // raw_ostream to print to.
442
443public:
444 static char ID;
445
446 explicit DXILPrettyPrinterLegacy(raw_ostream &O) : ModulePass(ID), OS(O) {}
447
448 StringRef getPassName() const override { return "DXIL Pretty Printer"; }
449
450 bool runOnModule(Module &M) override;
451 void getAnalysisUsage(AnalysisUsage &AU) const override {
452 AU.addRequired<DXILResourceTypeWrapperPass>();
453 AU.addRequired<DXILResourceWrapperPass>();
454 }
455};
456} // namespace
457
458char DXILPrettyPrinterLegacy::ID = 0;
459INITIALIZE_PASS_BEGIN(DXILPrettyPrinterLegacy, "dxil-pretty-printer",
460 "DXIL Pretty Printer", true, false)
463INITIALIZE_PASS_END(DXILPrettyPrinterLegacy, "dxil-pretty-printer",
464 "DXIL Pretty Printer", true, false)
465
466bool DXILPrettyPrinterLegacy::runOnModule(Module &M) {
467 const DXILResourceMap &DRM =
468 getAnalysis<DXILResourceWrapperPass>().getResourceMap();
469 DXILResourceTypeMap &DRTM =
470 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
472 prettyPrint(OS, M, DRM, DRTM, DI);
473 return true;
474}
475
477 return new DXILPrettyPrinterLegacy(OS);
478}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
aarch64 promote const
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static void prettyPrint(raw_ostream &OS, Module &M, const DXILResourceMap &DRM, DXILResourceTypeMap &DRTM, const DXILDebugInfoMap &DI)
static StringRef getTextureDimName(dxil::ResourceKind RK)
static void prettyPrintResources(raw_ostream &OS, const DXILResourceMap &DRM, DXILResourceTypeMap &DRTM)
static StringRef getRCPrefix(dxil::ResourceClass RC)
static StringRef getFormatName(const dxil::ResourceTypeInfo &RI)
static SmallVector< const MDNode * > collectAdditionalMetadata(Module &M, const DXILDebugInfoMap &DI)
static StringRef getRCName(dxil::ResourceClass RC)
DXIL Resource Implicit Binding
dxil translate DXIL Translate Metadata
This file defines the DenseSet and SmallDenseSet classes.
#define _
Module.h This file contains the declarations for the Module class.
This header defines various interfaces for pass management in LLVM.
#define I(x, y, z)
Definition MD5.cpp:57
Machine Check Debug Module
This file contains the declarations for metadata subclasses.
ModuleAnalysisManager MAM
#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 contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
Abstract interface of slot tracker storage.
virtual int getMetadataSlot(const MDNode *)=0
AnalysisUsage & addRequired()
PreservedAnalyses run(Module &M, ModuleAnalysisManager &)
ValueT lookup(const_arg_type_t< KeyT > Val) const
Return the entry for the specified key, or a default constructed value if no such entry exists.
Definition DenseMap.h:250
Implements a dense probed hash-table based set.
Definition DenseSet.h:281
Metadata node.
Definition Metadata.h:1069
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1567
Tracking metadata reference owned by Metadata.
Definition Metadata.h:891
Root of the metadata hierarchy.
Definition Metadata.h:64
LLVM_ABI void print(raw_ostream &OS, const Module *M=nullptr, bool IsForDebug=false) const
Print.
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
Manage lifetime of a slot tracker for printing IR.
ModuleSlotTracker(SlotTracker &Machine, const Module *M, const Function *F=nullptr)
Wrap a preinitialized SlotTracker.
void renumberMetadataForAssembly(ArrayRef< const MDNode * > AdditionalMetadata, MachineMDNodeListType *AdditionalMetadataNodes=nullptr) const
Renumber module metadata and then additional metadata for canonical assembly output.
void setProcessHook(std::function< void(AbstractSlotTrackerStorage *, const Module *)>)
SmallVector< std::pair< unsigned, const MDNode * >, 0 > MachineMDNodeListType
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
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
LLVM_ABI std::string upper() const
Convert the given ASCII string to uppercase.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
std::pair< iterator, bool > insert(const ValueT &V)
Definition DenseSet.h:209
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
Definition DenseSet.h:182
MDMap MDExtra
Enumerate extra metadata when Key is encountered in ValueEnumerator.
MDMap MDReplace
Completely replace one metadata with another in ValueEnumerator.
dxil::ResourceClass getResourceClass() const
LLVM_ABI bool isSampler() const
LLVM_ABI bool isTyped() const
LLVM_ABI bool isCBuffer() const
LLVM_ABI TypedInfo getTyped() const
LLVM_ABI bool isStruct() const
formatted_raw_ostream - A raw_ostream that wraps another one and keeps track of line and column posit...
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
DXILDebugInfoMap run(Module &M)
ResourceKind
The kind of resource for an SRV or UAV resource.
Definition DXILABI.h:44
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1636
LLVM_ABI raw_ostream & nulls()
This returns a reference to a raw_ostream which simply discards output.
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
Definition Format.h:102
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
Definition InstrProf.h:145
DWARFExpression::Operation Op
ModulePass * createDXILPrettyPrinterLegacyPass(raw_ostream &OS)
Pass to pretty print DXIL metadata.
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
#define N