Skip to content

[DirectX][objdump] Add support for printing signatures #153320

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

llvm-beanz
Copy link
Collaborator

@llvm-beanz llvm-beanz commented Aug 13, 2025

This adds support for printing the signature sections as part of the -p flag for printing private headers.

The formatting aims to roughly match the formatting used by DXC's /dumpbin flag.

The original version's printed output left some trailing whitespace on lines, which caused the tests to fail with the strict whitespace matching.

Re-lands #152531.
Resolves #152380.

This adds support for printing the signature sections as part of the
`-p` flag for printing private headers.

The formatting aims to roughly match the formatting used by DXC's
`/dumpbin` flag.

Resolves llvm#152380.

../llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml
@llvm-beanz llvm-beanz changed the title [DirectX][objdump] Add support for printing signatures (#152531) [DirectX][objdump] Add support for printing signatures Aug 13, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 13, 2025

@llvm/pr-subscribers-backend-directx

Author: Chris B (llvm-beanz)

Changes

This adds support for printing the signature sections as part of the -p flag for printing private headers.

The formatting aims to roughly match the formatting used by DXC's /dumpbin flag.

Re-lands #152531.
Resolves #152380.


Full diff: https://github.com/llvm/llvm-project/pull/153320.diff

3 Files Affected:

  • (modified) llvm/include/llvm/Object/DXContainer.h (+2)
  • (added) llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml (+167)
  • (modified) llvm/tools/llvm-objdump/DXContainerDump.cpp (+136-2)
diff --git a/llvm/include/llvm/Object/DXContainer.h b/llvm/include/llvm/Object/DXContainer.h
index ad1b2361ff064..93d39dabae4b9 100644
--- a/llvm/include/llvm/Object/DXContainer.h
+++ b/llvm/include/llvm/Object/DXContainer.h
@@ -603,6 +603,8 @@ class LLVM_ABI DXContainerObjectFile : public ObjectFile {
   }
 
 public:
+  const DXContainer &getDXContainer() const { return Container; }
+
   static bool classof(const Binary *v) { return v->isDXContainer(); }
 
   Expected<StringRef> getSymbolName(DataRefImpl) const override;
diff --git a/llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml b/llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml
new file mode 100644
index 0000000000000..ad979d2dcb7ea
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml
@@ -0,0 +1,167 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objdump -p %t | FileCheck %s --match-full-lines --strict-whitespace
+
+## This test covers llvm-objdump printing private headers for the ISG1, OSG1,
+## and PSG1 "parts" of the DX container file format. The test uses a few
+## absurdly large values and long string names to ensure that the columns in the
+## printed table widen correctly.
+
+--- !dxcontainer
+Header:
+  Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                     0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
+  Version:
+    Major:           1
+    Minor:           0
+  FileSize:        630
+  PartCount:       3
+  PartOffsets:     [ 64, 124, 184 ]
+Parts:
+  - Name:            ISG1
+    Size:            52
+    Signature:
+      Parameters:
+        - Stream:          0
+          Name:            AAA_HSFoo
+          Index:           4391238    # This value forces the index column to widen
+          SystemValue:     Undefined
+          CompType:        Float32
+          Register:        0
+          Mask:            7
+          ExclusiveMask:   2
+          MinPrecision:    Default
+  - Name:            OSG1
+    Size:            52
+    Signature:
+      Parameters:
+        - Stream:          0
+          Name:            SV_Position
+          Index:           0
+          SystemValue:     Position
+          CompType:        Float32
+          Register:        2147483647 # This value forces the register column to widen
+          Mask:            15
+          ExclusiveMask:   0
+          MinPrecision:    Default
+  - Name:            PSG1
+    Size:            402
+    Signature:
+      Parameters:
+        - Stream:          0
+          Name:            SV_TessFactor
+          Index:           0
+          SystemValue:     FinalQuadEdgeTessfactor  # The tessfactor forces the SysVal column to widen
+          CompType:        Float32
+          Register:        0
+          Mask:            8
+          ExclusiveMask:   8
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            BBB
+          Index:           0
+          SystemValue:     Undefined
+          CompType:        Float32
+          Register:        0
+          Mask:            7
+          ExclusiveMask:   0
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            SV_TessFactor
+          Index:           1
+          SystemValue:     FinalQuadEdgeTessfactor
+          CompType:        Float32
+          Register:        1
+          Mask:            8
+          ExclusiveMask:   8
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            BBB
+          Index:           1
+          SystemValue:     Undefined
+          CompType:        Float32
+          Register:        1
+          Mask:            7
+          ExclusiveMask:   0
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            SV_TessFactor
+          Index:           2
+          SystemValue:     FinalQuadEdgeTessfactor
+          CompType:        Float32
+          Register:        2
+          Mask:            8
+          ExclusiveMask:   8
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            BBB
+          Index:           2
+          SystemValue:     Undefined
+          CompType:        Float32
+          Register:        2
+          Mask:            7
+          ExclusiveMask:   0
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            SV_TessFactor
+          Index:           3
+          SystemValue:     FinalQuadEdgeTessfactor
+          CompType:        Float32
+          Register:        3
+          Mask:            8
+          ExclusiveMask:   8
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            SV_InsideTessFactor
+          Index:           0
+          SystemValue:     FinalQuadInsideTessfactor
+          CompType:        Float32
+          Register:        4
+          Mask:            8
+          ExclusiveMask:   0
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            SV_InsideTessFactor
+          Index:           1
+          SystemValue:     FinalQuadInsideTessfactor
+          CompType:        Float32
+          Register:        5
+          Mask:            8
+          ExclusiveMask:   0
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            AVeryLongStringThatWillForceWidening # This value forces name column to widen
+          Index:           0
+          SystemValue:     Undefined
+          CompType:        Float32
+          Register:        6
+          Mask:            15
+          ExclusiveMask:   4
+          MinPrecision:    Default
+...
+
+# CHECK:; Input signature:
+# CHECK-NEXT:;
+# CHECK-NEXT:; Name                       Index  Mask Register   SysValue  Format  Used
+# CHECK-NEXT:; ------------------------ ------- ----- -------- ---------- ------- -----
+# CHECK-NEXT:; AAA_HSFoo                4391238  xyz         0  Undefined Float32   y
+
+# CHECK:; Output signature:
+# CHECK-NEXT:;
+# CHECK-NEXT:; Name                     Index  Mask   Register   SysValue  Format  Used
+# CHECK-NEXT:; ------------------------ ----- ----- ---------- ---------- ------- -----
+# CHECK-NEXT:; SV_Position                  0  xyzw 2147483647   Position Float32
+
+# CHECK:; Patch Constant signature:
+# CHECK-NEXT:;
+# CHECK-NEXT:; Name                                 Index  Mask Register                  SysValue  Format  Used
+# CHECK-NEXT:; ------------------------------------ ----- ----- -------- ------------------------- ------- -----
+# CHECK-NEXT:; SV_TessFactor                            0     w        0   FinalQuadEdgeTessfactor Float32     w
+# CHECK-NEXT:; BBB                                      0  xyz         0                 Undefined Float32
+# CHECK-NEXT:; SV_TessFactor                            1     w        1   FinalQuadEdgeTessfactor Float32     w
+# CHECK-NEXT:; BBB                                      1  xyz         1                 Undefined Float32
+# CHECK-NEXT:; SV_TessFactor                            2     w        2   FinalQuadEdgeTessfactor Float32     w
+# CHECK-NEXT:; BBB                                      2  xyz         2                 Undefined Float32
+# CHECK-NEXT:; SV_TessFactor                            3     w        3   FinalQuadEdgeTessfactor Float32     w
+# CHECK-NEXT:; SV_InsideTessFactor                      0     w        4 FinalQuadInsideTessfactor Float32
+# CHECK-NEXT:; SV_InsideTessFactor                      1     w        5 FinalQuadInsideTessfactor Float32
+# CHECK-NEXT:; AVeryLongStringThatWillForceWidening     0  xyzw        6                 Undefined Float32    z
diff --git a/llvm/tools/llvm-objdump/DXContainerDump.cpp b/llvm/tools/llvm-objdump/DXContainerDump.cpp
index 2fb073473de53..3c70399a7a9b4 100644
--- a/llvm/tools/llvm-objdump/DXContainerDump.cpp
+++ b/llvm/tools/llvm-objdump/DXContainerDump.cpp
@@ -12,16 +12,150 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm-objdump.h"
+#include "llvm/BinaryFormat/DXContainer.h"
 #include "llvm/Object/DXContainer.h"
+#include "llvm/Support/ScopedPrinter.h"
 
 using namespace llvm;
+using namespace llvm::object;
+
+static llvm::SmallString<4> maskToString(uint8_t Mask,
+                                         bool StripTrailing = false) {
+  llvm::SmallString<4> Result("    ");
+  if (Mask & 1)
+    Result[0] = 'x';
+  if (Mask & 2)
+    Result[1] = 'y';
+  if (Mask & 4)
+    Result[2] = 'z';
+  if (Mask & 8)
+    Result[3] = 'w';
+  if (!StripTrailing)
+    return Result;
+  int Size = 8 - countl_zero(Mask);
+  return Result.slice(0, Size);
+}
+
+static void printColumnHeader(raw_ostream &OS, size_t Length) {
+  for (size_t I = 0; I < Length; ++I)
+    OS << "-";
+}
+
+static void printColumnHeaders(raw_ostream &OS, ArrayRef<size_t> Lengths) {
+  // Generate the header in a temporary to avoid trailing whitespace.
+  SmallString<256> Str;
+  raw_svector_ostream Tmp(Str);
+  for (auto L : Lengths) {
+    printColumnHeader(Tmp, L);
+    Tmp << " ";
+  }
+  Str.back() = '\n';
+  OS << Str;
+}
+
+static size_t digitsForNumber(size_t N) {
+  return static_cast<size_t>(log10(static_cast<double>(N))) + 1;
+}
 
 namespace {
 class DXContainerDumper : public objdump::Dumper {
+  const DXContainerObjectFile &Obj;
+
 public:
-  DXContainerDumper(const object::DXContainerObjectFile &Obj)
-      : objdump::Dumper(Obj) {}
+  DXContainerDumper(const DXContainerObjectFile &O)
+      : objdump::Dumper(O), Obj(O) {}
+
+  void printPrivateHeaders() override;
+  void printSignature(const DirectX::Signature &S);
 };
+
+void DXContainerDumper::printSignature(const DirectX::Signature &S) {
+  // DXC prints a table like this as part of the shader disassembly:
+  //; Name                 Index   Mask Register SysValue  Format   Used
+  //; -------------------- ----- ------ -------- -------- ------- ------
+  //; NORMAL                   0   xyz         0     NONE   float   xyz
+  //; TEXCOORD                 0   xy          1     NONE   float   xy
+
+  // DXC's implementation doesn't scale columns entirely completely for the
+  // provided input, so this implementation is a bit more complicated in
+  // formatting logic to scale with the size of the printed text.
+
+  // DXC gives names 21 characters for some unknown reason, I arbitrarily chose
+  // to start at 24 so that we're not going shorter but are using a round
+  // number.
+  size_t LongestName = 24;
+  size_t LongestSV = 10;
+  size_t LongestIndex = strlen("Index");
+  size_t LongestRegister = strlen("Register");
+  size_t LongestFormat = strlen("Format");
+  const size_t MaskWidth = 5;
+  // Compute the column widths. Skip calculating the "Mask" and "Used" columns
+  // since they both have widths of 4.
+  for (auto El : S) {
+    LongestName = std::max(LongestName, S.getName(El.NameOffset).size());
+    LongestSV = std::max(
+        LongestSV,
+        enumToStringRef(El.SystemValue, dxbc::getD3DSystemValues()).size());
+    LongestIndex = std::max(LongestIndex, digitsForNumber(El.Index));
+    LongestRegister = std::max(LongestRegister, digitsForNumber(El.Register));
+    LongestFormat = std::max(
+        LongestFormat,
+        enumToStringRef(El.CompType, dxbc::getSigComponentTypes()).size());
+  }
+
+  // Print Column headers.
+  OS << "; ";
+  OS << left_justify("Name", LongestName) << " ";
+  OS << right_justify("Index", LongestIndex) << " ";
+  OS << right_justify("Mask", MaskWidth) << " ";
+  OS << right_justify("Register", LongestRegister) << " ";
+  OS << right_justify("SysValue", LongestSV) << " ";
+  OS << right_justify("Format", LongestFormat) << " ";
+  OS << right_justify("Used", MaskWidth) << "\n";
+  OS << "; ";
+  printColumnHeaders(OS, {LongestName, LongestIndex, MaskWidth, LongestRegister,
+                          LongestSV, LongestFormat, MaskWidth});
+
+  for (auto El : S) {
+    OS << "; " << left_justify(S.getName(El.NameOffset), LongestName) << " ";
+    OS << right_justify(std::to_string(El.Index), LongestIndex) << " ";
+    OS << right_justify(maskToString(El.Mask), MaskWidth) << " ";
+    OS << right_justify(std::to_string(El.Register), LongestRegister) << " ";
+    OS << right_justify(
+              enumToStringRef(El.SystemValue, dxbc::getD3DSystemValues()),
+              LongestSV)
+       << " ";
+    OS << right_justify(
+        enumToStringRef(El.CompType, dxbc::getSigComponentTypes()),
+        LongestFormat);
+    if (El.ExclusiveMask)
+      OS << "  " << maskToString(El.ExclusiveMask, true);
+    OS << "\n";
+  }
+}
+
+void DXContainerDumper::printPrivateHeaders() {
+  const DXContainer &C =
+      cast<object::DXContainerObjectFile>(Obj).getDXContainer();
+
+  if (!C.getInputSignature().isEmpty()) {
+    OS << "; Input signature:\n;\n";
+    printSignature(C.getInputSignature());
+    OS << ";\n";
+  }
+
+  if (!C.getOutputSignature().isEmpty()) {
+    OS << "; Output signature:\n;\n";
+    printSignature(C.getOutputSignature());
+    OS << ";\n";
+  }
+
+  if (!C.getPatchConstantSignature().isEmpty()) {
+    OS << "; Patch Constant signature:\n;\n";
+    printSignature(C.getPatchConstantSignature());
+    OS << ";\n";
+  }
+}
 } // namespace
 
 std::unique_ptr<objdump::Dumper> llvm::objdump::createDXContainerDumper(

@llvmbot
Copy link
Member

llvmbot commented Aug 13, 2025

@llvm/pr-subscribers-llvm-binary-utilities

Author: Chris B (llvm-beanz)

Changes

This adds support for printing the signature sections as part of the -p flag for printing private headers.

The formatting aims to roughly match the formatting used by DXC's /dumpbin flag.

Re-lands #152531.
Resolves #152380.


Full diff: https://github.com/llvm/llvm-project/pull/153320.diff

3 Files Affected:

  • (modified) llvm/include/llvm/Object/DXContainer.h (+2)
  • (added) llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml (+167)
  • (modified) llvm/tools/llvm-objdump/DXContainerDump.cpp (+136-2)
diff --git a/llvm/include/llvm/Object/DXContainer.h b/llvm/include/llvm/Object/DXContainer.h
index ad1b2361ff064..93d39dabae4b9 100644
--- a/llvm/include/llvm/Object/DXContainer.h
+++ b/llvm/include/llvm/Object/DXContainer.h
@@ -603,6 +603,8 @@ class LLVM_ABI DXContainerObjectFile : public ObjectFile {
   }
 
 public:
+  const DXContainer &getDXContainer() const { return Container; }
+
   static bool classof(const Binary *v) { return v->isDXContainer(); }
 
   Expected<StringRef> getSymbolName(DataRefImpl) const override;
diff --git a/llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml b/llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml
new file mode 100644
index 0000000000000..ad979d2dcb7ea
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml
@@ -0,0 +1,167 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objdump -p %t | FileCheck %s --match-full-lines --strict-whitespace
+
+## This test covers llvm-objdump printing private headers for the ISG1, OSG1,
+## and PSG1 "parts" of the DX container file format. The test uses a few
+## absurdly large values and long string names to ensure that the columns in the
+## printed table widen correctly.
+
+--- !dxcontainer
+Header:
+  Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                     0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
+  Version:
+    Major:           1
+    Minor:           0
+  FileSize:        630
+  PartCount:       3
+  PartOffsets:     [ 64, 124, 184 ]
+Parts:
+  - Name:            ISG1
+    Size:            52
+    Signature:
+      Parameters:
+        - Stream:          0
+          Name:            AAA_HSFoo
+          Index:           4391238    # This value forces the index column to widen
+          SystemValue:     Undefined
+          CompType:        Float32
+          Register:        0
+          Mask:            7
+          ExclusiveMask:   2
+          MinPrecision:    Default
+  - Name:            OSG1
+    Size:            52
+    Signature:
+      Parameters:
+        - Stream:          0
+          Name:            SV_Position
+          Index:           0
+          SystemValue:     Position
+          CompType:        Float32
+          Register:        2147483647 # This value forces the register column to widen
+          Mask:            15
+          ExclusiveMask:   0
+          MinPrecision:    Default
+  - Name:            PSG1
+    Size:            402
+    Signature:
+      Parameters:
+        - Stream:          0
+          Name:            SV_TessFactor
+          Index:           0
+          SystemValue:     FinalQuadEdgeTessfactor  # The tessfactor forces the SysVal column to widen
+          CompType:        Float32
+          Register:        0
+          Mask:            8
+          ExclusiveMask:   8
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            BBB
+          Index:           0
+          SystemValue:     Undefined
+          CompType:        Float32
+          Register:        0
+          Mask:            7
+          ExclusiveMask:   0
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            SV_TessFactor
+          Index:           1
+          SystemValue:     FinalQuadEdgeTessfactor
+          CompType:        Float32
+          Register:        1
+          Mask:            8
+          ExclusiveMask:   8
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            BBB
+          Index:           1
+          SystemValue:     Undefined
+          CompType:        Float32
+          Register:        1
+          Mask:            7
+          ExclusiveMask:   0
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            SV_TessFactor
+          Index:           2
+          SystemValue:     FinalQuadEdgeTessfactor
+          CompType:        Float32
+          Register:        2
+          Mask:            8
+          ExclusiveMask:   8
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            BBB
+          Index:           2
+          SystemValue:     Undefined
+          CompType:        Float32
+          Register:        2
+          Mask:            7
+          ExclusiveMask:   0
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            SV_TessFactor
+          Index:           3
+          SystemValue:     FinalQuadEdgeTessfactor
+          CompType:        Float32
+          Register:        3
+          Mask:            8
+          ExclusiveMask:   8
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            SV_InsideTessFactor
+          Index:           0
+          SystemValue:     FinalQuadInsideTessfactor
+          CompType:        Float32
+          Register:        4
+          Mask:            8
+          ExclusiveMask:   0
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            SV_InsideTessFactor
+          Index:           1
+          SystemValue:     FinalQuadInsideTessfactor
+          CompType:        Float32
+          Register:        5
+          Mask:            8
+          ExclusiveMask:   0
+          MinPrecision:    Default
+        - Stream:          0
+          Name:            AVeryLongStringThatWillForceWidening # This value forces name column to widen
+          Index:           0
+          SystemValue:     Undefined
+          CompType:        Float32
+          Register:        6
+          Mask:            15
+          ExclusiveMask:   4
+          MinPrecision:    Default
+...
+
+# CHECK:; Input signature:
+# CHECK-NEXT:;
+# CHECK-NEXT:; Name                       Index  Mask Register   SysValue  Format  Used
+# CHECK-NEXT:; ------------------------ ------- ----- -------- ---------- ------- -----
+# CHECK-NEXT:; AAA_HSFoo                4391238  xyz         0  Undefined Float32   y
+
+# CHECK:; Output signature:
+# CHECK-NEXT:;
+# CHECK-NEXT:; Name                     Index  Mask   Register   SysValue  Format  Used
+# CHECK-NEXT:; ------------------------ ----- ----- ---------- ---------- ------- -----
+# CHECK-NEXT:; SV_Position                  0  xyzw 2147483647   Position Float32
+
+# CHECK:; Patch Constant signature:
+# CHECK-NEXT:;
+# CHECK-NEXT:; Name                                 Index  Mask Register                  SysValue  Format  Used
+# CHECK-NEXT:; ------------------------------------ ----- ----- -------- ------------------------- ------- -----
+# CHECK-NEXT:; SV_TessFactor                            0     w        0   FinalQuadEdgeTessfactor Float32     w
+# CHECK-NEXT:; BBB                                      0  xyz         0                 Undefined Float32
+# CHECK-NEXT:; SV_TessFactor                            1     w        1   FinalQuadEdgeTessfactor Float32     w
+# CHECK-NEXT:; BBB                                      1  xyz         1                 Undefined Float32
+# CHECK-NEXT:; SV_TessFactor                            2     w        2   FinalQuadEdgeTessfactor Float32     w
+# CHECK-NEXT:; BBB                                      2  xyz         2                 Undefined Float32
+# CHECK-NEXT:; SV_TessFactor                            3     w        3   FinalQuadEdgeTessfactor Float32     w
+# CHECK-NEXT:; SV_InsideTessFactor                      0     w        4 FinalQuadInsideTessfactor Float32
+# CHECK-NEXT:; SV_InsideTessFactor                      1     w        5 FinalQuadInsideTessfactor Float32
+# CHECK-NEXT:; AVeryLongStringThatWillForceWidening     0  xyzw        6                 Undefined Float32    z
diff --git a/llvm/tools/llvm-objdump/DXContainerDump.cpp b/llvm/tools/llvm-objdump/DXContainerDump.cpp
index 2fb073473de53..3c70399a7a9b4 100644
--- a/llvm/tools/llvm-objdump/DXContainerDump.cpp
+++ b/llvm/tools/llvm-objdump/DXContainerDump.cpp
@@ -12,16 +12,150 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm-objdump.h"
+#include "llvm/BinaryFormat/DXContainer.h"
 #include "llvm/Object/DXContainer.h"
+#include "llvm/Support/ScopedPrinter.h"
 
 using namespace llvm;
+using namespace llvm::object;
+
+static llvm::SmallString<4> maskToString(uint8_t Mask,
+                                         bool StripTrailing = false) {
+  llvm::SmallString<4> Result("    ");
+  if (Mask & 1)
+    Result[0] = 'x';
+  if (Mask & 2)
+    Result[1] = 'y';
+  if (Mask & 4)
+    Result[2] = 'z';
+  if (Mask & 8)
+    Result[3] = 'w';
+  if (!StripTrailing)
+    return Result;
+  int Size = 8 - countl_zero(Mask);
+  return Result.slice(0, Size);
+}
+
+static void printColumnHeader(raw_ostream &OS, size_t Length) {
+  for (size_t I = 0; I < Length; ++I)
+    OS << "-";
+}
+
+static void printColumnHeaders(raw_ostream &OS, ArrayRef<size_t> Lengths) {
+  // Generate the header in a temporary to avoid trailing whitespace.
+  SmallString<256> Str;
+  raw_svector_ostream Tmp(Str);
+  for (auto L : Lengths) {
+    printColumnHeader(Tmp, L);
+    Tmp << " ";
+  }
+  Str.back() = '\n';
+  OS << Str;
+}
+
+static size_t digitsForNumber(size_t N) {
+  return static_cast<size_t>(log10(static_cast<double>(N))) + 1;
+}
 
 namespace {
 class DXContainerDumper : public objdump::Dumper {
+  const DXContainerObjectFile &Obj;
+
 public:
-  DXContainerDumper(const object::DXContainerObjectFile &Obj)
-      : objdump::Dumper(Obj) {}
+  DXContainerDumper(const DXContainerObjectFile &O)
+      : objdump::Dumper(O), Obj(O) {}
+
+  void printPrivateHeaders() override;
+  void printSignature(const DirectX::Signature &S);
 };
+
+void DXContainerDumper::printSignature(const DirectX::Signature &S) {
+  // DXC prints a table like this as part of the shader disassembly:
+  //; Name                 Index   Mask Register SysValue  Format   Used
+  //; -------------------- ----- ------ -------- -------- ------- ------
+  //; NORMAL                   0   xyz         0     NONE   float   xyz
+  //; TEXCOORD                 0   xy          1     NONE   float   xy
+
+  // DXC's implementation doesn't scale columns entirely completely for the
+  // provided input, so this implementation is a bit more complicated in
+  // formatting logic to scale with the size of the printed text.
+
+  // DXC gives names 21 characters for some unknown reason, I arbitrarily chose
+  // to start at 24 so that we're not going shorter but are using a round
+  // number.
+  size_t LongestName = 24;
+  size_t LongestSV = 10;
+  size_t LongestIndex = strlen("Index");
+  size_t LongestRegister = strlen("Register");
+  size_t LongestFormat = strlen("Format");
+  const size_t MaskWidth = 5;
+  // Compute the column widths. Skip calculating the "Mask" and "Used" columns
+  // since they both have widths of 4.
+  for (auto El : S) {
+    LongestName = std::max(LongestName, S.getName(El.NameOffset).size());
+    LongestSV = std::max(
+        LongestSV,
+        enumToStringRef(El.SystemValue, dxbc::getD3DSystemValues()).size());
+    LongestIndex = std::max(LongestIndex, digitsForNumber(El.Index));
+    LongestRegister = std::max(LongestRegister, digitsForNumber(El.Register));
+    LongestFormat = std::max(
+        LongestFormat,
+        enumToStringRef(El.CompType, dxbc::getSigComponentTypes()).size());
+  }
+
+  // Print Column headers.
+  OS << "; ";
+  OS << left_justify("Name", LongestName) << " ";
+  OS << right_justify("Index", LongestIndex) << " ";
+  OS << right_justify("Mask", MaskWidth) << " ";
+  OS << right_justify("Register", LongestRegister) << " ";
+  OS << right_justify("SysValue", LongestSV) << " ";
+  OS << right_justify("Format", LongestFormat) << " ";
+  OS << right_justify("Used", MaskWidth) << "\n";
+  OS << "; ";
+  printColumnHeaders(OS, {LongestName, LongestIndex, MaskWidth, LongestRegister,
+                          LongestSV, LongestFormat, MaskWidth});
+
+  for (auto El : S) {
+    OS << "; " << left_justify(S.getName(El.NameOffset), LongestName) << " ";
+    OS << right_justify(std::to_string(El.Index), LongestIndex) << " ";
+    OS << right_justify(maskToString(El.Mask), MaskWidth) << " ";
+    OS << right_justify(std::to_string(El.Register), LongestRegister) << " ";
+    OS << right_justify(
+              enumToStringRef(El.SystemValue, dxbc::getD3DSystemValues()),
+              LongestSV)
+       << " ";
+    OS << right_justify(
+        enumToStringRef(El.CompType, dxbc::getSigComponentTypes()),
+        LongestFormat);
+    if (El.ExclusiveMask)
+      OS << "  " << maskToString(El.ExclusiveMask, true);
+    OS << "\n";
+  }
+}
+
+void DXContainerDumper::printPrivateHeaders() {
+  const DXContainer &C =
+      cast<object::DXContainerObjectFile>(Obj).getDXContainer();
+
+  if (!C.getInputSignature().isEmpty()) {
+    OS << "; Input signature:\n;\n";
+    printSignature(C.getInputSignature());
+    OS << ";\n";
+  }
+
+  if (!C.getOutputSignature().isEmpty()) {
+    OS << "; Output signature:\n;\n";
+    printSignature(C.getOutputSignature());
+    OS << ";\n";
+  }
+
+  if (!C.getPatchConstantSignature().isEmpty()) {
+    OS << "; Patch Constant signature:\n;\n";
+    printSignature(C.getPatchConstantSignature());
+    OS << ";\n";
+  }
+}
 } // namespace
 
 std::unique_ptr<objdump::Dumper> llvm::objdump::createDXContainerDumper(

@jh7370
Copy link
Collaborator

jh7370 commented Aug 13, 2025

Could you please clarify what was wrong with the original version, in the PR description?

@llvm-beanz
Copy link
Collaborator Author

Could you please clarify what was wrong with the original version, in the PR description?

Done. Sorry about the churn. My formatting scripts removed trailing whitespace from the tests which caused the failures. I've updated the printing logic to not print trailing whitespaces.

@jh7370
Copy link
Collaborator

jh7370 commented Aug 13, 2025

No idea why, but the pre-merge checks are failing on both Windows and Linux due to the new test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[DirectX][objdump] Support for dumping shader signature parts
3 participants