Skip to content

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

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 2 commits into
base: main
Choose a base branch
from

Conversation

llvm-beanz
Copy link
Collaborator

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 #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.
@llvmbot
Copy link
Member

llvmbot commented Aug 7, 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.

Resolves #152380.


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

3 Files Affected:

  • (modified) llvm/include/llvm/Object/DXContainer.h (+4)
  • (added) llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml (+161)
  • (modified) llvm/tools/llvm-objdump/DXContainerDump.cpp (+128-2)
diff --git a/llvm/include/llvm/Object/DXContainer.h b/llvm/include/llvm/Object/DXContainer.h
index ad1b2361ff064..8a2efe2e928b0 100644
--- a/llvm/include/llvm/Object/DXContainer.h
+++ b/llvm/include/llvm/Object/DXContainer.h
@@ -603,6 +603,10 @@ 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..fbfe17149cfb5
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml
@@ -0,0 +1,161 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objdump -p %t | FileCheck %s
+--- !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..17ae79b133a88 100644
--- a/llvm/tools/llvm-objdump/DXContainerDump.cpp
+++ b/llvm/tools/llvm-objdump/DXContainerDump.cpp
@@ -12,16 +12,142 @@
 //===----------------------------------------------------------------------===//
 
 #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) {
+  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';
+  return Result;
+}
+
+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) {
+  for (auto L : Lengths) {
+    printColumnHeader(OS, L);
+    OS << " ";
+  }
+  OS << "\n";
+}
+
+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)
+       << " ";
+    OS << right_justify(maskToString(El.ExclusiveMask), MaskWidth) << "\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 7, 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.

Resolves #152380.


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

3 Files Affected:

  • (modified) llvm/include/llvm/Object/DXContainer.h (+4)
  • (added) llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml (+161)
  • (modified) llvm/tools/llvm-objdump/DXContainerDump.cpp (+128-2)
diff --git a/llvm/include/llvm/Object/DXContainer.h b/llvm/include/llvm/Object/DXContainer.h
index ad1b2361ff064..8a2efe2e928b0 100644
--- a/llvm/include/llvm/Object/DXContainer.h
+++ b/llvm/include/llvm/Object/DXContainer.h
@@ -603,6 +603,10 @@ 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..fbfe17149cfb5
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/DXContainer/input-output-signatures.yaml
@@ -0,0 +1,161 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objdump -p %t | FileCheck %s
+--- !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..17ae79b133a88 100644
--- a/llvm/tools/llvm-objdump/DXContainerDump.cpp
+++ b/llvm/tools/llvm-objdump/DXContainerDump.cpp
@@ -12,16 +12,142 @@
 //===----------------------------------------------------------------------===//
 
 #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) {
+  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';
+  return Result;
+}
+
+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) {
+  for (auto L : Lengths) {
+    printColumnHeader(OS, L);
+    OS << " ";
+  }
+  OS << "\n";
+}
+
+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)
+       << " ";
+    OS << right_justify(maskToString(El.ExclusiveMask), MaskWidth) << "\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(

Copy link

github-actions bot commented Aug 7, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Collaborator

@jh7370 jh7370 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This'll need someone with more experience with DirectX than me, but I've made some stylistic comments anyway.

@@ -0,0 +1,161 @@
# RUN: yaml2obj %s -o %t
# RUN: llvm-objdump -p %t | FileCheck %s
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few nits/suggestions:

  1. I'd add a blank line between the RUN and YAML lines.
  2. I'd add a brief comment at the start of the test describing the feature being tested.
  3. I'd add --match-full-lines and --strict-whitespace to the FileCheck command, to ensure the formatting matches the expected.

enumToStringRef(El.CompType, dxbc::getSigComponentTypes()).size());
}

// Print Column headers
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Print Column headers
// Print Column headers.

OS << ";\n";
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the namespace starts with no blank lines after the opening brace, but finishes with one before the closing one. I don't think the coding standards have a stated preferred style, but mismatching seems wrong regardless!

Comment on lines +100 to +101
OS << left_justify("Name", LongestName) << " ";
OS << right_justify("Index", LongestIndex) << " ";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the different justify types is the standard way to do this? Just a check to make sure it is intentional.

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
4 participants