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
10#include "DirectX.h"
11#include "llvm/ADT/DenseSet.h"
12#include "llvm/ADT/STLExtras.h"
14#include "llvm/ADT/StringRef.h"
17#include "llvm/IR/DebugInfo.h"
18#include "llvm/IR/Metadata.h"
19#include "llvm/IR/Module.h"
21#include "llvm/IR/PassManager.h"
23#include "llvm/Pass.h"
28
29using namespace llvm;
30using namespace llvm::dxil;
31
33 switch (RC) {
35 return "texture";
37 return "UAV";
39 return "cbuffer";
41 return "sampler";
42 }
43 llvm_unreachable("covered switch");
44}
45
47 switch (RC) {
49 return "t";
51 return "u";
53 return "cb";
55 return "s";
56 }
57 llvm_unreachable("covered switch");
58}
59
61 if (RI.isTyped()) {
62 switch (RI.getTyped().DXILStorageTy) {
64 return "i1";
66 return "i16";
68 return "u16";
70 return "i32";
72 return "u32";
74 return "i64";
76 return "u64";
78 return "f16";
80 return "f32";
82 return "f64";
84 return "snorm_f16";
86 return "unorm_f16";
88 return "snorm_f32";
90 return "unorm_f32";
92 return "snorm_f64";
94 return "unorm_f64";
96 return "p32i8";
98 return "p32u8";
100 llvm_unreachable("Invalid ElementType");
101 }
102 llvm_unreachable("Unhandled ElementType");
103 } else if (RI.isStruct())
104 return "struct";
105 else if (RI.isCBuffer() || RI.isSampler())
106 return "NA";
107 return "byte";
108}
109
111 switch (RK) {
113 return "1d";
115 return "2d";
117 return "3d";
119 return "cube";
121 return "1darray";
123 return "2darray";
125 return "cubearray";
127 return "tbuffer";
129 return "fbtex2d";
131 return "fbtex2darray";
133 return "2dMS";
135 return "2darrayMS";
144 llvm_unreachable("Invalid ResourceKind for texture");
145 }
146 llvm_unreachable("Unhandled ResourceKind");
147}
148
149namespace {
150struct FormatResourceDimension
151 : public llvm::FormatAdapter<const dxil::ResourceTypeInfo &> {
152 FormatResourceDimension(const dxil::ResourceTypeInfo &RI, bool HasCounter)
153 : llvm::FormatAdapter<const dxil::ResourceTypeInfo &>(RI),
154 HasCounter(HasCounter) {}
155
156 bool HasCounter;
157
158 void format(llvm::raw_ostream &OS, StringRef Style) {
159 dxil::ResourceKind RK = Item.getResourceKind();
160 switch (RK) {
161 default: {
162 OS << getTextureDimName(RK);
163 if (Item.isMultiSample())
164 OS << Item.getMultiSampleCount();
165 break;
166 }
167 case dxil::ResourceKind::RawBuffer:
168 case dxil::ResourceKind::StructuredBuffer:
169 if (!Item.isUAV())
170 OS << "r/o";
171 else if (HasCounter)
172 OS << "r/w+cnt";
173 else
174 OS << "r/w";
175 break;
176 case dxil::ResourceKind::TypedBuffer:
177 OS << "buf";
178 break;
179 case dxil::ResourceKind::CBuffer:
180 OS << "NA";
181 break;
182 case dxil::ResourceKind::RTAccelerationStructure:
183 // TODO: dxc would print "ras" here. Can/should this happen?
184 llvm_unreachable("RTAccelerationStructure printing is not implemented");
185 }
186 }
187};
188
189struct FormatBindingID
190 : public llvm::FormatAdapter<const dxil::ResourceInfo &> {
192
193 explicit FormatBindingID(const dxil::ResourceInfo &RI,
194 const dxil::ResourceTypeInfo &RTI)
195 : llvm::FormatAdapter<const dxil::ResourceInfo &>(RI),
196 RC(RTI.getResourceClass()) {}
197
198 void format(llvm::raw_ostream &OS, StringRef Style) {
199 OS << getRCPrefix(RC).upper() << Item.getBinding().BindingID;
200 }
201};
202
203struct FormatBindingLocation
204 : public llvm::FormatAdapter<const dxil::ResourceInfo &> {
206
207 explicit FormatBindingLocation(const dxil::ResourceInfo &RI,
208 const dxil::ResourceTypeInfo &RTI)
209 : llvm::FormatAdapter<const dxil::ResourceInfo &>(RI),
210 RC(RTI.getResourceClass()) {}
211
212 void format(llvm::raw_ostream &OS, StringRef Style) {
213 const auto &Binding = Item.getBinding();
214 OS << getRCPrefix(RC) << Binding.LowerBound;
215 if (Binding.Space)
216 OS << ",space" << Binding.Space;
217 }
218};
219
220struct FormatBindingSize
221 : public llvm::FormatAdapter<const dxil::ResourceInfo &> {
222 explicit FormatBindingSize(const dxil::ResourceInfo &RI)
223 : llvm::FormatAdapter<const dxil::ResourceInfo &>(RI) {}
224
225 void format(llvm::raw_ostream &OS, StringRef Style) {
226 uint32_t Size = Item.getBinding().Size;
227 if (Size == 0)
228 OS << "unbounded";
229 else
230 OS << Size;
231 }
232};
233
234} // namespace
235
237 DXILResourceTypeMap &DRTM) {
238 // Column widths are arbitrary but match the widths DXC uses.
239 OS << ";\n; Resource Bindings:\n;\n";
240 OS << formatv("; {0,-30} {1,10} {2,7} {3,11} {4,7} {5,14} {6,9}\n", "Name",
241 "Type", "Format", "Dim", "ID", "HLSL Bind", "Count");
242 OS << formatv(
243 "; {0,-+30} {1,-+10} {2,-+7} {3,-+11} {4,-+7} {5,-+14} {6,-+9}\n", "", "",
244 "", "", "", "", "");
245
246 // TODO: Do we want to sort these by binding or something like that?
247 for (const dxil::ResourceInfo &RI : DRM) {
248 if (!RI.hasBinding())
249 continue;
250 const dxil::ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];
251
253 StringRef Name(RI.getName());
256 FormatResourceDimension Dim(RTI, RI.hasCounter());
257 FormatBindingID ID(RI, RTI);
258 FormatBindingLocation Bind(RI, RTI);
259 FormatBindingSize Count(RI);
260 OS << formatv("; {0,-30} {1,10} {2,7} {3,11} {4,7} {5,14} {6,9}\n", Name,
261 Type, Format, Dim, ID, Bind, Count);
262 }
263 OS << ";\n";
264}
265
266namespace {
267class DXILModuleSlotTracker : public ModuleSlotTracker {
268public:
271};
272
273class DXILAssemblyAnnotationWriter : public llvm::AssemblyAnnotationWriter {
274private:
275 ModuleSlotTracker &MST;
276 AbstractSlotTrackerStorage &STS;
277 const DXILDebugInfoMap &DI;
278 DenseSet<const MDNode *> &EmittedMDNodes;
279
280public:
281 DXILAssemblyAnnotationWriter(ModuleSlotTracker &MST,
282 AbstractSlotTrackerStorage &STS,
283 const DXILDebugInfoMap &DI,
284 DenseSet<const MDNode *> &EmittedMDNodes)
285 : MST(MST), STS(STS), DI(DI), EmittedMDNodes(EmittedMDNodes) {}
286
287 void emitInstructionAnnot(const Instruction *OrigI,
288 formatted_raw_ostream &os) override {
289 if (const Instruction *I = &DI.getDXILInstruction(*OrigI); I != OrigI) {
290 os << "; DXIL: to be replaced with: ";
291 I->print(os, MST);
292 os << "\n";
293 }
294 }
295
296 void emitMDNodeAnnot(const MDNode *N, formatted_raw_ostream &os) override {
297 EmittedMDNodes.insert(N);
298
299 if (const Metadata *NewMD = DI.MDReplace.lookup(N)) {
300 if (const auto *NewN = dyn_cast<MDNode>(NewMD))
301 STS.createMetadataSlot(NewN);
302
303 os << "; DXIL: ";
304 N->printAsOperand(os, MST);
305 os << ": to be replaced by: ";
306 NewMD->printAsOperand(os, MST);
307 os << "\n";
308 return;
309 }
310
311 if (const Metadata *ExtraMD = DI.MDExtra.lookup(N)) {
312 if (const auto *ExtraN = dyn_cast<MDNode>(ExtraMD))
313 STS.createMetadataSlot(ExtraN);
314
315 os << "; DXIL: ";
316 N->printAsOperand(os, MST);
317 os << ": additional data: ";
318 ExtraMD->printAsOperand(os, MST);
319 os << "\n";
320 return;
321 }
322 }
323};
324} // namespace
325
328 // Annotation metadata follows module metadata in the order its keys print.
329 // Follow replacement graphs to preserve that order in canonical output.
330 M.renumberMetadataForAssembly();
331
332 ModuleSlotTracker MST(&M);
333 AbstractSlotTrackerStorage *STS = nullptr;
334 MST.setProcessHook(
335 [&](AbstractSlotTrackerStorage *STS_, const Module *) { STS = STS_; });
336 MDNode::get(M.getContext(), {})->print(llvm::nulls(), MST);
337 assert(STS && "Slot tracker storage should have been initialised");
338
339 DenseSet<const Metadata *> ReplacementMetadata;
340 for (auto [_, Replacement] : DI.MDReplace)
341 ReplacementMetadata.insert(Replacement);
342
345 auto AddOriginal = [&](const Metadata *MD) {
346 const auto *N = dyn_cast<MDNode>(MD);
347 if (!N || ReplacementMetadata.contains(N) || !Queued.insert(N).second)
348 return;
349 OriginalNodes.emplace_back(STS->getMetadataSlot(N), N);
350 };
351 for (auto [Original, _] : DI.MDReplace)
352 AddOriginal(Original);
353 for (auto [Original, _] : DI.MDExtra)
354 AddOriginal(Original);
355 llvm::sort(OriginalNodes);
356
358 for (auto [_, N] : OriginalNodes)
359 Worklist.push_back(N);
360
361 SmallVector<const MDNode *> AdditionalMetadata;
362 auto AddAdditional = [&](const Metadata *MD) {
363 const auto *Root = dyn_cast_or_null<MDNode>(MD);
364 if (!Root || Queued.contains(Root))
365 return;
366
367 AdditionalMetadata.push_back(Root);
368 SmallVector<const MDNode *> Nodes = {Root};
369 while (!Nodes.empty()) {
370 const MDNode *N = Nodes.pop_back_val();
371 if (!Queued.insert(N).second)
372 continue;
373 Worklist.push_back(N);
374 for (const MDOperand &Op : llvm::reverse(N->operands()))
375 if (const auto *OpNode = dyn_cast_or_null<MDNode>(Op.get()))
376 Nodes.push_back(OpNode);
377 }
378 };
379
380 for (size_t I = 0; I != Worklist.size(); ++I) {
381 const MDNode *N = Worklist[I];
382 if (const Metadata *Replacement = DI.MDReplace.lookup(N)) {
383 AddAdditional(Replacement);
384 continue;
385 }
386 AddAdditional(DI.MDExtra.lookup(N));
387 }
388 return AdditionalMetadata;
389}
390
391static void prettyPrint(raw_ostream &OS, Module &M, const DXILResourceMap &DRM,
392 DXILResourceTypeMap &DRTM) {
393 formatted_raw_ostream FOS(OS);
394
395 prettyPrintResources(FOS, DRM, DRTM);
396
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
437
438namespace {
439class DXILPrettyPrinterLegacy : public llvm::ModulePass {
440 raw_ostream &OS; // raw_ostream to print to.
441
442public:
443 static char ID;
444
445 explicit DXILPrettyPrinterLegacy(raw_ostream &O) : ModulePass(ID), OS(O) {}
446
447 StringRef getPassName() const override { return "DXIL Pretty Printer"; }
448
449 bool runOnModule(Module &M) override;
450 void getAnalysisUsage(AnalysisUsage &AU) const override {
451 AU.addRequired<DXILResourceTypeWrapperPass>();
452 AU.addRequired<DXILResourceWrapperPass>();
453 }
454};
455} // namespace
456
457char DXILPrettyPrinterLegacy::ID = 0;
458INITIALIZE_PASS_BEGIN(DXILPrettyPrinterLegacy, "dxil-pretty-printer",
459 "DXIL Pretty Printer", true, false)
462INITIALIZE_PASS_END(DXILPrettyPrinterLegacy, "dxil-pretty-printer",
463 "DXIL Pretty Printer", true, false)
464
465bool DXILPrettyPrinterLegacy::runOnModule(Module &M) {
466 const DXILResourceMap &DRM =
467 getAnalysis<DXILResourceWrapperPass>().getResourceMap();
468 DXILResourceTypeMap &DRTM =
469 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
470 prettyPrint(OS, M, DRM, DRTM);
471 return false;
472}
473
475 return new DXILPrettyPrinterLegacy(OS);
476}
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 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 void prettyPrint(raw_ostream &OS, Module &M, const DXILResourceMap &DRM, DXILResourceTypeMap &DRTM)
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:793
Implements a dense probed hash-table based set.
Definition DenseSet.h:281
Metadata node.
Definition Metadata.h:1081
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1579
Tracking metadata reference owned by Metadata.
Definition Metadata.h:902
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:253
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 all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
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.
ResourceKind
The kind of resource for an SRV or UAV resource.
Definition DXILABI.h:44
DXILDebugInfoMap collectDXILDebugInfo(Module &M)
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:408
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1652
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)
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
#define N