1//===----------------------------------------------------------------------===//
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/// \file
10/// This file implements the DXContainer-specific dumper for llvm-objdump.
11///
12//===----------------------------------------------------------------------===//
13
14#include "llvm-objdump.h"
15#include "llvm/BinaryFormat/DXContainer.h"
16#include "llvm/Object/DXContainer.h"
17#include "llvm/Support/ScopedPrinter.h"
18
19using namespace llvm;
20using namespace llvm::object;
21
22static llvm::SmallString<4> maskToString(uint8_t Mask,
23 bool StripTrailing = false) {
24 llvm::SmallString<4> Result(" ");
25 if (Mask & 1)
26 Result[0] = 'x';
27 if (Mask & 2)
28 Result[1] = 'y';
29 if (Mask & 4)
30 Result[2] = 'z';
31 if (Mask & 8)
32 Result[3] = 'w';
33 if (!StripTrailing)
34 return Result;
35 int Size = 8 - countl_zero(Val: Mask);
36 return Result.slice(Start: 0, End: Size);
37}
38
39static void printColumnHeader(raw_ostream &OS, size_t Length) {
40 for (size_t I = 0; I < Length; ++I)
41 OS << "-";
42}
43
44static void printColumnHeaders(raw_ostream &OS, ArrayRef<size_t> Lengths) {
45 // Generate the header in a temporary to avoid trailing whitespace.
46 SmallString<256> Str;
47 raw_svector_ostream Tmp(Str);
48 for (auto L : Lengths) {
49 printColumnHeader(OS&: Tmp, Length: L);
50 Tmp << " ";
51 }
52 Str.back() = '\n';
53 OS << Str;
54}
55
56static size_t digitsForNumber(size_t N) {
57 if (N == 0)
58 return 1;
59 return static_cast<size_t>(log10(x: static_cast<double>(N))) + 1;
60}
61
62namespace {
63class DXContainerDumper : public objdump::Dumper {
64 const DXContainerObjectFile &Obj;
65
66public:
67 DXContainerDumper(const DXContainerObjectFile &O)
68 : objdump::Dumper(O), Obj(O) {}
69
70 void printPrivateHeaders() override;
71 void printSignature(const DirectX::Signature &S);
72};
73
74void DXContainerDumper::printSignature(const DirectX::Signature &S) {
75 // DXC prints a table like this as part of the shader disassembly:
76 //; Name Index Mask Register SysValue Format Used
77 //; -------------------- ----- ------ -------- -------- ------- ------
78 //; NORMAL 0 xyz 0 NONE float xyz
79 //; TEXCOORD 0 xy 1 NONE float xy
80
81 // DXC's implementation doesn't scale columns entirely completely for the
82 // provided input, so this implementation is a bit more complicated in
83 // formatting logic to scale with the size of the printed text.
84
85 // DXC gives names 21 characters for some unknown reason, I arbitrarily chose
86 // to start at 24 so that we're not going shorter but are using a round
87 // number.
88 size_t LongestName = 24;
89 size_t LongestSV = 10;
90 size_t LongestIndex = strlen(s: "Index");
91 size_t LongestRegister = strlen(s: "Register");
92 size_t LongestFormat = strlen(s: "Format");
93 const size_t MaskWidth = 5;
94 // Compute the column widths. Skip calculating the "Mask" and "Used" columns
95 // since they both have widths of 4.
96 for (auto El : S) {
97 LongestName = std::max(a: LongestName, b: S.getName(Offset: El.NameOffset).size());
98 LongestSV = std::max(
99 a: LongestSV,
100 b: enumToStringRef(Value: El.SystemValue, EnumValues: dxbc::getD3DSystemValues()).size());
101 LongestIndex = std::max(a: LongestIndex, b: digitsForNumber(N: El.Index));
102 LongestRegister = std::max(a: LongestRegister, b: digitsForNumber(N: El.Register));
103 LongestFormat = std::max(
104 a: LongestFormat,
105 b: enumToStringRef(Value: El.CompType, EnumValues: dxbc::getSigComponentTypes()).size());
106 }
107
108 // Print Column headers.
109 OS << "; ";
110 OS << left_justify(Str: "Name", Width: LongestName) << " ";
111 OS << right_justify(Str: "Index", Width: LongestIndex) << " ";
112 OS << right_justify(Str: "Mask", Width: MaskWidth) << " ";
113 OS << right_justify(Str: "Register", Width: LongestRegister) << " ";
114 OS << right_justify(Str: "SysValue", Width: LongestSV) << " ";
115 OS << right_justify(Str: "Format", Width: LongestFormat) << " ";
116 OS << right_justify(Str: "Used", Width: MaskWidth) << "\n";
117 OS << "; ";
118 printColumnHeaders(OS, Lengths: {LongestName, LongestIndex, MaskWidth, LongestRegister,
119 LongestSV, LongestFormat, MaskWidth});
120
121 for (auto El : S) {
122 OS << "; " << left_justify(Str: S.getName(Offset: El.NameOffset), Width: LongestName) << " ";
123 OS << right_justify(Str: std::to_string(val: El.Index), Width: LongestIndex) << " ";
124 OS << right_justify(Str: maskToString(Mask: El.Mask), Width: MaskWidth) << " ";
125 OS << right_justify(Str: std::to_string(val: El.Register), Width: LongestRegister) << " ";
126 OS << right_justify(
127 Str: enumToStringRef(Value: El.SystemValue, EnumValues: dxbc::getD3DSystemValues()),
128 Width: LongestSV)
129 << " ";
130 OS << right_justify(
131 Str: enumToStringRef(Value: El.CompType, EnumValues: dxbc::getSigComponentTypes()),
132 Width: LongestFormat);
133 if (El.ExclusiveMask)
134 OS << " " << maskToString(Mask: El.ExclusiveMask, StripTrailing: true);
135 OS << "\n";
136 }
137}
138
139void DXContainerDumper::printPrivateHeaders() {
140 const DXContainer &C =
141 cast<object::DXContainerObjectFile>(Val: Obj).getDXContainer();
142
143 if (!C.getInputSignature().isEmpty()) {
144 OS << "; Input signature:\n;\n";
145 printSignature(S: C.getInputSignature());
146 OS << ";\n";
147 }
148
149 if (!C.getOutputSignature().isEmpty()) {
150 OS << "; Output signature:\n;\n";
151 printSignature(S: C.getOutputSignature());
152 OS << ";\n";
153 }
154
155 if (!C.getPatchConstantSignature().isEmpty()) {
156 OS << "; Patch Constant signature:\n;\n";
157 printSignature(S: C.getPatchConstantSignature());
158 OS << ";\n";
159 }
160}
161} // namespace
162
163std::unique_ptr<objdump::Dumper> llvm::objdump::createDXContainerDumper(
164 const object::DXContainerObjectFile &Obj) {
165 return std::make_unique<DXContainerDumper>(args: Obj);
166}
167