[llvm-branch-commits] [clang] [llvm] [DirectX][Driver] Add /Qpdb_in_private flag support (PR #204903)

Vladislav Dzhidzhoev via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Jun 26 06:12:32 PDT 2026


https://github.com/dzhidzhoev updated https://github.com/llvm/llvm-project/pull/204903

>From 60faffde5a2d7e49754bf64fbf6f6ed31c7e7a0c Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Fri, 26 Jun 2026 11:23:53 +0200
Subject: [PATCH 1/2] [DirectX][ObjectYAML] Add PRIV part support (#204899)

Add support for DXContainer PRIV in the ObjectYAML pipeline so it can be
represented in structured YAML and round-tripped through
yaml2obj/obj2yaml.

PRIV part can store arbitrary user-provided binary blobs in DXContainer.
Unlike other DXContainer parts, PRIV part does not have to have 4-byte
aligned size. Therefore, if it is present, it is always the last section
in a DXContainer.

llvm-objcopy is already able to extract PRIV section. A test to verify
extraction of binary from PRIV is added.
---
 .../BinaryFormat/DXContainerConstants.def     |  1 +
 llvm/include/llvm/Object/DXContainer.h        |  4 ++
 .../include/llvm/ObjectYAML/DXContainerYAML.h |  1 +
 llvm/lib/Object/DXContainer.cpp               | 11 +++++
 llvm/lib/ObjectYAML/DXContainerEmitter.cpp    |  7 ++++
 llvm/lib/ObjectYAML/DXContainerYAML.cpp       |  9 ++++
 .../DXContainer/dump-section-priv.yaml        | 35 ++++++++++++++++
 .../tools/obj2yaml/DXContainer/PRIVPart.yaml  | 42 +++++++++++++++++++
 llvm/unittests/Object/DXContainerTest.cpp     | 30 +++++++++++++
 .../ObjectYAML/DXContainerYAMLTest.cpp        | 29 +++++++++++++
 10 files changed, 169 insertions(+)
 create mode 100644 llvm/test/tools/llvm-objcopy/DXContainer/dump-section-priv.yaml
 create mode 100644 llvm/test/tools/obj2yaml/DXContainer/PRIVPart.yaml

diff --git a/llvm/include/llvm/BinaryFormat/DXContainerConstants.def b/llvm/include/llvm/BinaryFormat/DXContainerConstants.def
index 419e78c8a87d4..a6f8aaad6487f 100644
--- a/llvm/include/llvm/BinaryFormat/DXContainerConstants.def
+++ b/llvm/include/llvm/BinaryFormat/DXContainerConstants.def
@@ -3,6 +3,7 @@
 CONTAINER_PART(DXIL)
 CONTAINER_PART(ILDB)
 CONTAINER_PART(ILDN)
+CONTAINER_PART(PRIV)
 CONTAINER_PART(SFI0)
 CONTAINER_PART(SRCI)
 CONTAINER_PART(HASH)
diff --git a/llvm/include/llvm/Object/DXContainer.h b/llvm/include/llvm/Object/DXContainer.h
index ce3b9bc94ba97..545e8930f1458 100644
--- a/llvm/include/llvm/Object/DXContainer.h
+++ b/llvm/include/llvm/Object/DXContainer.h
@@ -480,6 +480,7 @@ class DXContainer {
   std::optional<mcdxbc::DebugName> DebugName;
   std::optional<mcdxbc::CompilerVersion> VersionInfo;
   std::optional<mcdxbc::SourceInfo> SourceInfo;
+  std::optional<StringRef> PrivateData;
 
   Error parseHeader();
   Error parsePartOffsets();
@@ -492,6 +493,7 @@ class DXContainer {
   Error parseSignature(StringRef Part, DirectX::Signature &Array);
   Error parseCompilerVersionInfo(StringRef Part);
   Error parseSourceInfo(StringRef Part);
+  Error parsePrivateData(StringRef Part);
   friend class PartIterator;
 
 public:
@@ -613,6 +615,8 @@ class DXContainer {
   const std::optional<mcdxbc::SourceInfo> &getSourceInfo() const {
     return SourceInfo;
   }
+
+  const std::optional<StringRef> &getPrivateData() const { return PrivateData; }
 };
 
 class LLVM_ABI DXContainerObjectFile : public ObjectFile {
diff --git a/llvm/include/llvm/ObjectYAML/DXContainerYAML.h b/llvm/include/llvm/ObjectYAML/DXContainerYAML.h
index 29f6b9bcae000..1d3e044bbb22f 100644
--- a/llvm/include/llvm/ObjectYAML/DXContainerYAML.h
+++ b/llvm/include/llvm/ObjectYAML/DXContainerYAML.h
@@ -401,6 +401,7 @@ struct Part {
   std::optional<DXContainerYAML::DebugName> DebugName;
   std::optional<DXContainerYAML::CompilerVersion> CompilerVersion;
   std::optional<DXContainerYAML::SourceInfo> SourceInfo;
+  std::optional<std::vector<llvm::yaml::Hex8>> PrivateData;
 };
 
 struct Object {
diff --git a/llvm/lib/Object/DXContainer.cpp b/llvm/lib/Object/DXContainer.cpp
index 52d3c3780bf71..e7641077b5029 100644
--- a/llvm/lib/Object/DXContainer.cpp
+++ b/llvm/lib/Object/DXContainer.cpp
@@ -127,6 +127,13 @@ Error DXContainer::parseDebugName(StringRef Part) {
   return Error::success();
 }
 
+Error DXContainer::parsePrivateData(StringRef Part) {
+  if (PrivateData)
+    return parseFailed("more than one PRIV part is present in the file");
+  PrivateData.emplace(Part);
+  return Error::success();
+}
+
 Error DXContainer::parseShaderFeatureFlags(StringRef Part) {
   if (ShaderFeatureFlags)
     return parseFailed("More than one SFI0 part is present in the file");
@@ -577,6 +584,10 @@ Error DXContainer::parsePartOffsets() {
       if (Error Err = parseDebugName(PartData))
         return Err;
       break;
+    case dxbc::PartType::PRIV:
+      if (Error Err = parsePrivateData(PartData))
+        return Err;
+      break;
     case dxbc::PartType::SFI0:
       if (Error Err = parseShaderFeatureFlags(PartData))
         return Err;
diff --git a/llvm/lib/ObjectYAML/DXContainerEmitter.cpp b/llvm/lib/ObjectYAML/DXContainerEmitter.cpp
index ff43eb71d57c7..bcb5376eb68b0 100644
--- a/llvm/lib/ObjectYAML/DXContainerEmitter.cpp
+++ b/llvm/lib/ObjectYAML/DXContainerEmitter.cpp
@@ -205,6 +205,13 @@ Error DXContainerWriter::writeParts(raw_ostream &OS) {
       DebugName.write(OS);
       break;
     }
+    case dxbc::PartType::PRIV: {
+      if (!P.PrivateData)
+        continue;
+      OS.write(reinterpret_cast<char *>(P.PrivateData->data()),
+               P.PrivateData->size());
+      break;
+    }
     case dxbc::PartType::SFI0: {
       // If we don't have any flags we can continue here and the data will be
       // zeroed out.
diff --git a/llvm/lib/ObjectYAML/DXContainerYAML.cpp b/llvm/lib/ObjectYAML/DXContainerYAML.cpp
index ba766dfffd91d..1671ec2afed9b 100644
--- a/llvm/lib/ObjectYAML/DXContainerYAML.cpp
+++ b/llvm/lib/ObjectYAML/DXContainerYAML.cpp
@@ -567,6 +567,7 @@ void MappingTraits<DXContainerYAML::Part>::mapping(IO &IO,
   IO.mapOptional("DebugName", P.DebugName);
   IO.mapOptional("CompilerVersion", P.CompilerVersion);
   IO.mapOptional("SourceInfo", P.SourceInfo);
+  IO.mapOptional("PrivateData", P.PrivateData);
 }
 
 void MappingTraits<DXContainerYAML::Object>::mapping(
@@ -981,6 +982,14 @@ DXContainerYAML::fromDXContainer(object::DXContainer &Container) {
           DebugName->Filename.str()};
       break;
     }
+    case dxbc::PartType::PRIV: {
+      std::optional<StringRef> PrivateData = Container.getPrivateData();
+      assert(PrivateData && "Since we are iterating and found a PRIV part, "
+                            "this should never not have a value");
+      NewPart.PrivateData.emplace(PrivateData->data(),
+                                  PrivateData->data() + PrivateData->size());
+      break;
+    }
     case dxbc::PartType::SFI0: {
       std::optional<uint64_t> Flags = Container.getShaderFeatureFlags();
       // Omit the flags in the YAML if they are missing or zero.
diff --git a/llvm/test/tools/llvm-objcopy/DXContainer/dump-section-priv.yaml b/llvm/test/tools/llvm-objcopy/DXContainer/dump-section-priv.yaml
new file mode 100644
index 0000000000000..a145c45eefc1f
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/DXContainer/dump-section-priv.yaml
@@ -0,0 +1,35 @@
+## Check that --dump-section exports the raw PRIV part data, including when the
+## part size is not a multiple of 4.
+
+# RUN: yaml2obj %s -o %t.dxbc
+# RUN: llvm-objcopy --dump-section=PRIV=%t.priv %t.dxbc
+
+# RUN: wc -c %t.priv | FileCheck %s --check-prefix=PRIV-SIZE
+# PRIV-SIZE: 5
+
+# RUN: od -t x1 %t.priv | FileCheck %s --ignore-case
+# CHECK: de ad be ef 42
+
+--- !dxcontainer
+Header:
+  Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                     0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
+  Version:
+    Major:           1
+    Minor:           0
+  PartCount:       2
+Parts:
+  - Name:            DXIL
+    Size:            24
+    Program:
+      MajorVersion:    6
+      MinorVersion:    0
+      ShaderKind:      5
+      Size:            6
+      DXILMajorVersion: 0
+      DXILMinorVersion: 1
+      DXILSize:        0
+  - Name:            PRIV
+    Size:            5
+    PrivateData:     [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ]
+...
diff --git a/llvm/test/tools/obj2yaml/DXContainer/PRIVPart.yaml b/llvm/test/tools/obj2yaml/DXContainer/PRIVPart.yaml
new file mode 100644
index 0000000000000..0d429a1381413
--- /dev/null
+++ b/llvm/test/tools/obj2yaml/DXContainer/PRIVPart.yaml
@@ -0,0 +1,42 @@
+## Check that the PRIV part round-trips through yaml2obj and obj2yaml when the
+## part size is not a multiple of 4. The pipeline is run twice to ensure obj2yaml
+## output with a non-multiple-of-4 FileSize can be fed back through yaml2obj.
+
+# RUN: yaml2obj %s 2>&1 | obj2yaml 2>&1 | yaml2obj %s 2>&1 | obj2yaml 2>&1 | 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
+  PartCount:       2
+Parts:
+  - Name:            DXIL
+    Size:            24
+    Program:
+      MajorVersion:    6
+      MinorVersion:    0
+      ShaderKind:      5
+      Size:            6
+      DXILMajorVersion: 0
+      DXILMinorVersion: 1
+      DXILSize:        0
+  - Name:            PRIV
+    Size:            5
+    PrivateData:     [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ]
+...
+
+# CHECK:   FileSize:        85
+# CHECK:   PartCount:       2
+# CHECK:   PartOffsets:     [ 40, 72 ]
+# CHECK: Parts:
+# CHECK:   - Name:            DXIL
+# CHECK:     Program:
+# CHECK:       MajorVersion:    6
+# CHECK:       MinorVersion:    0
+# CHECK:       ShaderKind:      5
+# CHECK:   - Name:            PRIV
+# CHECK:     Size:            5
+# CHECK:     PrivateData:     [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ]
diff --git a/llvm/unittests/Object/DXContainerTest.cpp b/llvm/unittests/Object/DXContainerTest.cpp
index 0051839d071e7..9eefc1be296b5 100644
--- a/llvm/unittests/Object/DXContainerTest.cpp
+++ b/llvm/unittests/Object/DXContainerTest.cpp
@@ -320,6 +320,36 @@ TEST(DXCFile, ParseILDNPart) {
   EXPECT_EQ(ILDN->Filename, "abc.pdb");
 }
 
+// This test verifies that PRIV part is correctly parsed.
+// This test is based on the binary output constructed from this yaml.
+// --- !dxcontainer
+// Header:
+//   Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+//                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
+//   Version:
+//     Major:           1
+//     Minor:           0
+//   PartCount:       1
+// Parts:
+//   - Name:            PRIV
+//     Size:            5
+//     PrivateData:     [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ]
+// ...
+TEST(DXCFile, ParsePRIVPart) {
+  uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00,
+                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                      0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00,
+                      0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+                      0x50, 0x52, 0x49, 0x56, 0x05, 0x00, 0x00, 0x00, 0xDE,
+                      0xAD, 0xBE, 0xEF, 0x42};
+  DXContainer C =
+      llvm::cantFail(DXContainer::create(getMemoryBuffer<49>(Buffer)));
+  EXPECT_EQ(C.getHeader().PartCount, 1u);
+  const std::optional<StringRef> &PrivateData = C.getPrivateData();
+  EXPECT_TRUE(PrivateData.has_value());
+  EXPECT_EQ(*PrivateData, "\xDE\xAD\xBE\xEF\x42");
+}
+
 // This test verifies that VERS part is correctly parsed.
 // This test is based on the binary output constructed from this yaml.
 // --- !dxcontainer
diff --git a/llvm/unittests/ObjectYAML/DXContainerYAMLTest.cpp b/llvm/unittests/ObjectYAML/DXContainerYAMLTest.cpp
index 1857f10ffdae1..57515450c59ac 100644
--- a/llvm/unittests/ObjectYAML/DXContainerYAMLTest.cpp
+++ b/llvm/unittests/ObjectYAML/DXContainerYAMLTest.cpp
@@ -614,6 +614,35 @@ TEST(DXCFile, ParseILDNPart) {
   EXPECT_TRUE(memcmp(Buffer, Storage.data(), 56u) == 0);
 }
 
+TEST(DXCFile, ParsePRIVPart) {
+  SmallString<128> Storage;
+
+  ASSERT_TRUE(convert(Storage, R"(--- !dxcontainer
+Header:
+  Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                     0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
+  Version:
+    Major:           1
+    Minor:           0
+  PartCount:       1
+  PartOffsets:     [ 36 ]
+Parts:
+  - Name:            PRIV
+    Size:            5
+    PrivateData:     [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ]
+    )"));
+
+  uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00,
+                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                      0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00,
+                      0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+                      0x50, 0x52, 0x49, 0x56, 0x05, 0x00, 0x00, 0x00, 0xDE,
+                      0xAD, 0xBE, 0xEF, 0x42};
+
+  EXPECT_EQ(Storage.size(), 49u);
+  EXPECT_TRUE(memcmp(Buffer, Storage.data(), 49u) == 0);
+}
+
 TEST(DXCFile, ParseVERSPart) {
   SmallString<128> Storage;
 

>From 5336ab75fb789c9421a4fdf63e7ed5fe0df2b444 Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Sat, 20 Jun 2026 03:59:00 +0200
Subject: [PATCH 2/2] [DirectX][Driver] Add /Qpdb_in_private flag support

In DXC, when `/Qpdb_in_private` flag is specified, debug info PDB is
emitted into PRIV part of the output DXContainer (as well as
into separate PDB file, if its name is specified with `/Fd`).

This patch reimplements similar behavior in llc and Clang.
---
 clang/include/clang/Options/Options.td        |  3 +
 clang/lib/Driver/ToolChains/Clang.cpp         |  5 ++
 clang/test/Driver/dxc_debug.hlsl              |  2 +
 clang/test/Driver/dxc_section_emission.hlsl   | 11 +++
 llvm/lib/MC/MCDXContainerWriter.cpp           | 22 +++--
 .../lib/Target/DirectX/DXContainerGlobals.cpp | 33 ++++----
 llvm/lib/Target/DirectX/DXContainerPDB.cpp    | 46 +++++++++-
 .../ContainerData/PdbInPrivate-no-fd.test     | 17 ++++
 .../ContainerData/PdbInPrivate-no-flag.test   |  5 ++
 .../ContainerData/PdbInPrivate-no-pdbname.ll  | 10 +++
 .../DirectX/ContainerData/PdbInPrivate.ll     | 18 ++++
 .../DirectX/ContainerData/PdbInPrivate.test   | 33 ++++++++
 llvm/unittests/MC/CMakeLists.txt              |  2 +
 llvm/unittests/MC/DXContainerWriterTest.cpp   | 83 +++++++++++++++++++
 14 files changed, 267 insertions(+), 23 deletions(-)
 create mode 100644 llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-fd.test
 create mode 100644 llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-flag.test
 create mode 100644 llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-pdbname.ll
 create mode 100644 llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate.ll
 create mode 100644 llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate.test
 create mode 100644 llvm/unittests/MC/DXContainerWriterTest.cpp

diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 919d46c449750..77b38bff221d1 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -9765,6 +9765,9 @@ def dxc_gis : DXCFlag<"Gis">,
 def : Option<["/", "-"], "Qembed_debug", KIND_FLAG>, Group<dxc_Group>,
   Flags<[Ignored]>, Visibility<[DXCOption]>,
   HelpText<"Embed PDB in shader container (ignored)">;
+def dxc_Qpdb_in_private : DXCFlag<"Qpdb_in_private">,
+                          Flags<[HelpHidden]>,
+                          HelpText<"Store PDB in private user data">;
 def spirv : DXCFlag<"spirv">,
   HelpText<"Generate SPIR-V code">;
 def metal : DXCFlag<"metal">, HelpText<"Generate Metal library">;
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 323417e294d5a..8e785f265553c 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3854,6 +3854,11 @@ static void RenderHLSLOptions(const Driver &D, const ArgList &Args,
   }
   if (Arg *A = Args.getLastArg(options::OPT_dxc_Zsb))
     A->claim(); // /Zsb is the default behavior, no need to forward it to llc.
+
+  if (Args.hasArg(options::OPT_dxc_Qpdb_in_private)) {
+    CmdArgs.push_back("-mllvm");
+    CmdArgs.push_back("--dx-pdb-in-private");
+  }
 }
 
 static void RenderOpenACCOptions(const Driver &D, const ArgList &Args,
diff --git a/clang/test/Driver/dxc_debug.hlsl b/clang/test/Driver/dxc_debug.hlsl
index 1a6c26c80750b..4e86dc45c2f69 100644
--- a/clang/test/Driver/dxc_debug.hlsl
+++ b/clang/test/Driver/dxc_debug.hlsl
@@ -3,6 +3,7 @@
 // RUN: %clang_dxc -Tlib_6_7 -### /Zi /Qembed_debug %s 2>&1 | FileCheck %s
 // RUN: %clang_dxc -Tlib_6_7 -### -Zi %s 2>&1 | FileCheck %s
 // RUN: %clang_dxc -Tlib_6_7 -### -Zi -Qembed_debug %s 2>&1 | FileCheck %s
+// RUN: %clang_dxc -Tlib_6_7 -### /Zi /Qpdb_in_private %s 2>&1 | FileCheck %s -check-prefixes=CHECK,CHECK-PRIV
 // RUN: %clang_dxc -Tlib_6_7 -### -Zi -Zss %s 2>&1 | FileCheck %s --check-prefix=CHECK,CHECK-ZSS
 // RUN: %clang_dxc -Tlib_6_7 -### -Zi -gcodeview %s 2>&1 | FileCheck %s -check-prefixes=CHECK,CHECK-CV
 // RUN: %clang_dxc -Tlib_6_7 -### -Zi -gdwarf %s 2>&1 | FileCheck %s -check-prefixes=CHECK,CHECK-DWARF
@@ -15,6 +16,7 @@
 // Make sure dwarf-version is 4.
 // CHECK-DWARF-SAME: -dwarf-version=4
 // Check that the flags are converted to their llc equivalents.
+// CHECK-PRIV-SAME: --dx-pdb-in-private
 // CHECK-ZSS-SAME: -dx-Zss
 // Make sure dxc command line arguments are passed to clang invocation.
 // CHECK-SAME: -fdx-record-command-line
diff --git a/clang/test/Driver/dxc_section_emission.hlsl b/clang/test/Driver/dxc_section_emission.hlsl
index b27232f6b7d27..be8081e884c78 100644
--- a/clang/test/Driver/dxc_section_emission.hlsl
+++ b/clang/test/Driver/dxc_section_emission.hlsl
@@ -22,4 +22,15 @@
 
 // CHECK: - Name: ILDN
 
+// Check that /Qpdb_in_private emits a PRIV part in the output container.
+// RUN: %clang_dxc -Tlib_6_7 /Fo %t-priv.dxbc /Zi /Qpdb_in_private %s 2>&1
+// RUN: obj2yaml %t-priv.dxbc | FileCheck %s --check-prefix=CHECK-PRIV
+
+// Without /Qpdb_in_private, PRIV is not emitted.
+// RUN: %clang_dxc -Tlib_6_7 /Fo %t-no-priv.dxbc /Zi %s 2>&1
+// RUN: obj2yaml %t-no-priv.dxbc | FileCheck %s --check-prefix=CHECK-NO-PRIV
+
+// CHECK-PRIV: - Name: PRIV
+// CHECK-NO-PRIV-NOT: - Name: PRIV
+
 [numthreads(1, 1, 1)] void main() {}
diff --git a/llvm/lib/MC/MCDXContainerWriter.cpp b/llvm/lib/MC/MCDXContainerWriter.cpp
index 994b90fbcb66b..f5cedf0b542b0 100644
--- a/llvm/lib/MC/MCDXContainerWriter.cpp
+++ b/llvm/lib/MC/MCDXContainerWriter.cpp
@@ -34,14 +34,22 @@ void MCDXContainerBaseWriter::write(raw_ostream &OS, const Triple &TT) {
   // 16 part offsets gives us a little room for growth.
   llvm::SmallVector<uint64_t, 16> PartOffsets;
   uint64_t PartOffset = 0;
+  bool HasPrivate = false;
   for (const MCDXContainerPart &Part : Parts) {
+    if (HasPrivate)
+      reportFatalInternalError(
+          "PRIV must be the last section in a DXContainer");
+    if (Part.Name == "PRIV")
+      HasPrivate = true;
+
     uint64_t SectionSize = Part.Data.size();
     assert(SectionSize < std::numeric_limits<uint32_t>::max() &&
            "Section size too large for DXContainer");
 
     PartOffsets.push_back(PartOffset);
     PartOffset += sizeof(dxbc::PartHeader) + SectionSize;
-    PartOffset = alignTo(PartOffset, Align(4ul));
+    if (!HasPrivate)
+      PartOffset = alignTo(PartOffset, Align(4ul));
     // The DXIL part also writes a program header, so we need to include its
     // size when computing the offset for a part after the DXIL part.
     if (dxbc::isProgramPart(Part.Name))
@@ -81,8 +89,10 @@ void MCDXContainerBaseWriter::write(raw_ostream &OS, const Triple &TT) {
 
     if (dxbc::isProgramPart(Part.Name))
       PartSize += sizeof(dxbc::ProgramHeader);
-    // DXContainer parts should be 4-byte aligned.
-    PartSize = alignTo(PartSize, Align(4));
+    // DXContainer part should be 4-byte aligned, unless it is PRIV part.
+    bool IsPrivate = Part.Name == "PRIV";
+    if (!IsPrivate)
+      PartSize = alignTo(PartSize, Align(4));
     W.write<uint32_t>(static_cast<uint32_t>(PartSize));
     if (dxbc::isProgramPart(Part.Name)) {
       dxbc::ProgramHeader Header;
@@ -112,8 +122,10 @@ void MCDXContainerBaseWriter::write(raw_ostream &OS, const Triple &TT) {
                                    sizeof(dxbc::ProgramHeader)));
     }
     W.write<char>(Part.Data);
-    unsigned Size = W.OS.tell() - Start;
-    W.OS.write_zeros(offsetToAlignment(Size, Align(4)));
+    if (!IsPrivate) {
+      unsigned Size = W.OS.tell() - Start;
+      W.OS.write_zeros(offsetToAlignment(Size, Align(4)));
+    }
   }
 }
 
diff --git a/llvm/lib/Target/DirectX/DXContainerGlobals.cpp b/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
index 16facaf13b96b..fb72e8d96949c 100644
--- a/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
+++ b/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
@@ -46,6 +46,8 @@ cl::opt<std::string> PdbDebugPath(
     cl::desc("Write debug information to the given file, or automatically "
              "named file in directory when ending in '/'"),
     cl::value_desc("filename"));
+cl::opt<bool> PdbInPrivate("dx-pdb-in-private",
+                           cl::desc("Store PDB in private user data"));
 
 namespace {
 class DXContainerGlobals : public llvm::ModulePass {
@@ -162,22 +164,25 @@ void DXContainerGlobals::computeShaderHashAndDebugName(
   SmallString<40> DebugNameStr;
   Digest.stringifyResult(MD5, DebugNameStr);
   DebugNameStr += ".pdb";
-  if (!PdbDebugPath.empty()) {
-    StringRef DebugFile = PdbDebugPath.getValue();
-    SmallString<256> AbsoluteDebugName;
-    if (sys::path::is_separator(DebugFile.back())) {
-      // If /Fd was specified as a directory, put the MD5.pdb file there.
-      AbsoluteDebugName = DebugFile;
-      sys::path::append(AbsoluteDebugName, DebugNameStr);
-    } else {
-      // Otherwise, use /Fd value as a user-provided PDB file name.
-      DebugNameStr = DebugFile;
-      AbsoluteDebugName = DebugNameStr;
+  if (!PdbDebugPath.empty() || PdbInPrivate) {
+    if (!PdbDebugPath.empty()) {
+      StringRef DebugFile = PdbDebugPath.getValue();
+      SmallString<256> AbsoluteDebugName;
+      if (sys::path::is_separator(DebugFile.back())) {
+        // If /Fd was specified as a directory, put the MD5.pdb file there.
+        AbsoluteDebugName = DebugFile;
+        sys::path::append(AbsoluteDebugName, DebugNameStr);
+      } else {
+        // Otherwise, use /Fd value as a user-provided PDB file name.
+        DebugNameStr = DebugFile;
+        AbsoluteDebugName = DebugNameStr;
+      }
+
+      // Pass PDB name to DXContainerPDBPass via PDBNAME section.
+      addSection(M, Globals, AbsoluteDebugName, "dx.pdb.name",
+                 PdbFileNameSectionName);
     }
 
-    // Pass PDB name to DXContainerPDBPass via PDBNAME section.
-    addSection(M, Globals, AbsoluteDebugName, "dx.pdb.name",
-               PdbFileNameSectionName);
     // Pass module hash to DXContainerPDBPass.
     Globals.emplace_back(buildContainerGlobal(
         M, ConstantDataArray::get(M.getContext(), ArrayRef(HashData.Digest)),
diff --git a/llvm/lib/Target/DirectX/DXContainerPDB.cpp b/llvm/lib/Target/DirectX/DXContainerPDB.cpp
index 49fb9f4b290fb..a90be89c54699 100644
--- a/llvm/lib/Target/DirectX/DXContainerPDB.cpp
+++ b/llvm/lib/Target/DirectX/DXContainerPDB.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "DirectX.h"
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/BinaryFormat/DXContainer.h"
 #include "llvm/DebugInfo/CodeView/GUID.h"
@@ -17,9 +18,14 @@
 #include "llvm/IR/Module.h"
 #include "llvm/MC/MCDXContainerWriter.h"
 #include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
 
 using namespace llvm;
 
+extern cl::opt<bool> PdbInPrivate;
+
 namespace {
 
 class DXContainerPDB : public ModulePass, MCDXContainerBaseWriter {
@@ -84,10 +90,22 @@ ArrayRef<MCDXContainerPart> DXContainerPDB::collectParts() {
   return Parts;
 }
 
+static GlobalVariable *createPrivateDataGlobal(Module &M, StringRef Data) {
+  Constant *Content =
+      ConstantDataArray::getString(M.getContext(), Data, /*AddNull*/ false);
+  auto *GV =
+      new GlobalVariable(M, Content->getType(), true,
+                         GlobalValue::PrivateLinkage, Content, "dx.priv");
+  GV->setSection("PRIV");
+  GV->setAlignment(Align(1));
+  return GV;
+}
+
 bool DXContainerPDB::runOnModule(Module &M) {
+  llvm::scope_exit Cleanup([&]() { reset(); });
   this->M = &M;
 
-  StringRef DebugFileName;
+  SmallString<128> DebugFileName;
   ArrayRef<char> ModuleHash;
   for (const GlobalVariable &GV : M.globals()) {
     if (GV.getSection() == PdbFileNameSectionName) {
@@ -101,11 +119,23 @@ bool DXContainerPDB::runOnModule(Module &M) {
   }
 
   // PDB emission was not requested.
-  if (DebugFileName.empty())
+  if (DebugFileName.empty() && !PdbInPrivate)
     return false;
   if (ModuleHash.empty())
     report_fatal_error("Module hash for PDB not found");
 
+  bool DeleteAfterRead = false;
+  if (DebugFileName.empty()) {
+    if (std::error_code EC =
+            sys::fs::createTemporaryFile("dxil", "pdb", DebugFileName))
+      reportFatalInternalError("Failed to create temporary PDB file");
+    DeleteAfterRead = true;
+  }
+  llvm::scope_exit FileCleanup([&]() {
+    if (DeleteAfterRead)
+      sys::fs::remove(DebugFileName);
+  });
+
   BumpPtrAllocator Allocator;
   pdb::PDBFileBuilder Builder(Allocator);
 
@@ -148,9 +178,17 @@ bool DXContainerPDB::runOnModule(Module &M) {
     reportFatalUsageError("Couldn't write to PDB file: " +
                           Twine(toString(std::move(Err))));
 
-  reset();
+  if (!PdbInPrivate)
+    return false;
+
+  ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(
+      DebugFileName, /*IsText=*/false, /*RequiresNullTerminator=*/false);
+  if (!Buf)
+    reportFatalInternalError("Failed to read PDB for PRIV embedding");
+
+  appendToCompilerUsed(M, createPrivateDataGlobal(M, (*Buf)->getBuffer()));
 
-  return false;
+  return true;
 }
 
 char DXContainerPDB::ID = 0;
diff --git a/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-fd.test b/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-fd.test
new file mode 100644
index 0000000000000..dd9efe0c070af
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-fd.test
@@ -0,0 +1,17 @@
+## Check that --dx-pdb-in-private generates a PDB in PRIV without --dx-Fd.
+
+# RUN: llc %S/Inputs/SourceInfo.ll --filetype=obj -o %t.dxbc --dx-pdb-in-private
+# RUN: obj2yaml %t.dxbc | FileCheck %s
+# RUN: llvm-objcopy --dump-section=PRIV=%t.priv %t.dxbc
+# RUN: llvm-pdbutil pdb2yaml --dxcontainer %t.priv | FileCheck %s --check-prefix=PDB
+
+# CHECK:       - Name:            PRIV
+# CHECK-NEXT:    Size:
+
+# PDB:           PartCount:       5
+# PDB:         Parts:
+# PDB-DAG:       - Name:            ILDB
+# PDB-DAG:       - Name:            HASH
+# PDB-DAG:       - Name:            ILDN
+# PDB-DAG:       - Name:            VERS
+# PDB-DAG:       - Name:            SRCI
diff --git a/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-flag.test b/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-flag.test
new file mode 100644
index 0000000000000..4a185ed7842dd
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-flag.test
@@ -0,0 +1,5 @@
+## Check that PRIV is not emitted unless --dx-pdb-in-private is specified.
+
+# RUN: llc %S/Inputs/SourceInfo.ll --filetype=obj -o - | obj2yaml | FileCheck %s --implicit-check-not='PRIV'
+
+# CHECK: - Name: DXIL
diff --git a/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-pdbname.ll b/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-pdbname.ll
new file mode 100644
index 0000000000000..f07d19416b267
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate-no-pdbname.ll
@@ -0,0 +1,10 @@
+;; Check that dxil-pdb can emit PRIV using a temporary PDB when PDBNAME is absent.
+; RUN: opt %s -dxil-pdb --dx-pdb-in-private -S -o - | FileCheck %s
+
+;; CHECK: @dx.priv = private constant {{.*}} section "PRIV"
+
+target triple = "dxilv1.3-pc-shadermodel6.3-library"
+
+ at dx.ildb = private constant [4 x i8] c"BC\C0\DE", section "ILDB", align 4
+ at dx.pdb.hash = private constant [16 x i8] c"dummymodulehash!", section "PDBHASH", align 4
+ at llvm.compiler.used = appending global [2 x ptr] [ptr @dx.ildb, ptr @dx.pdb.hash], section "llvm.metadata"
diff --git a/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate.ll b/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate.ll
new file mode 100644
index 0000000000000..7edc3fd8918d1
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate.ll
@@ -0,0 +1,18 @@
+; RUN: opt %s -dxil-pdb --dx-pdb-in-private -S -o - | FileCheck %s --check-prefix=CHECK-IR
+; RUN: opt %s -dxil-pdb --dx-pdb-in-private -o /dev/null
+; RUN: llvm-pdbutil pdb2yaml --dxcontainer PdbInPrivateTest.pdb | FileCheck %s --check-prefix=CHECK-PDB
+
+;; Check that dxil-pdb pass emits a PRIV global when --dx-pdb-in-private is set.
+; CHECK-IR: @dx.priv = private constant {{.*}} section "PRIV"
+
+; Check that the companion PDB file is still written when PDBNAME is present.
+; CHECK-PDB:   PartCount: 1
+; CHECK-PDB: Parts:
+; CHECK-PDB:   - Name:    ILDB
+
+target triple = "dxilv1.3-pc-shadermodel6.3-library"
+
+ at dx.ildb = private constant [4 x i8] c"BC\C0\DE", section "ILDB", align 4
+ at dx.pdb.name = private constant [20 x i8] c"PdbInPrivateTest.pdb", section "PDBNAME", align 4
+ at dx.pdb.hash = private constant [16 x i8] c"dummymodulehash!", section "PDBHASH", align 4
+ at llvm.compiler.used = appending global [3 x ptr] [ptr @dx.ildb, ptr @dx.pdb.name, ptr @dx.pdb.hash], section "llvm.metadata"
diff --git a/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate.test b/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate.test
new file mode 100644
index 0000000000000..13fecd3eb9069
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ContainerData/PdbInPrivate.test
@@ -0,0 +1,33 @@
+## Check that --dx-pdb-in-private stores the generated PDB in the PRIV part.
+
+## File: --dx-Fd specifies the PDB output path.
+
+# RUN: llc %S/Inputs/SourceInfo.ll --filetype=obj -o %t.dxbc --dx-pdb-in-private --dx-Fd=%t.pdb
+# RUN: obj2yaml %t.dxbc | FileCheck %s
+# RUN: llvm-objcopy --dump-section=PRIV=%t.priv %t.dxbc
+# RUN: diff %t.pdb %t.priv
+# RUN: llvm-pdbutil pdb2yaml --dxcontainer %t.priv | FileCheck %s --check-prefix=PDB
+
+## Directory: --dx-Fd ending in '/' writes <md5>.pdb under that directory.
+
+# RUN: rm -rf %t.dir && mkdir %t.dir
+# RUN: llc %S/Inputs/SourceInfo.ll --filetype=obj -o %t.dir.dxbc --dx-pdb-in-private --dx-Fd=%t.dir/
+# RUN: obj2yaml %t.dir.dxbc | FileCheck %s
+# RUN: llvm-objcopy --dump-section=PRIV=%t.dir.priv %t.dir.dxbc
+# RUN: llvm-objcopy --dump-section=DXIL=%t.dir.bc %t.dir.dxbc
+# RUN: %md5sum %t.dir.bc >%t.dir.bc.md5
+# RUN: %python %S/Inputs/check_pdb_exists.py %t.dir %t.dir.bc.md5
+# RUN: cat %t.dir/*.pdb > %t.dir.pdb
+# RUN: diff %t.dir.pdb %t.dir.priv
+# RUN: llvm-pdbutil pdb2yaml --dxcontainer %t.dir.priv | FileCheck %s --check-prefix=PDB
+
+# CHECK:      - Name: PRIV
+# CHECK-NEXT:   Size:
+
+# PDB:       PartCount:       5
+# PDB:     Parts:
+# PDB-DAG:   - Name: ILDB
+# PDB-DAG:   - Name: HASH
+# PDB-DAG:   - Name: ILDN
+# PDB-DAG:   - Name: VERS
+# PDB-DAG:   - Name: SRCI
diff --git a/llvm/unittests/MC/CMakeLists.txt b/llvm/unittests/MC/CMakeLists.txt
index 4958396c731b8..789fdb1722197 100644
--- a/llvm/unittests/MC/CMakeLists.txt
+++ b/llvm/unittests/MC/CMakeLists.txt
@@ -6,6 +6,7 @@ endforeach()
 
 set(LLVM_LINK_COMPONENTS
   ${LLVM_TARGETS_TO_BUILD}
+  BinaryFormat
   DebugInfoDWARF
   MC
   MCDisassembler
@@ -19,6 +20,7 @@ add_llvm_unittest(MCTests
   DwarfDebugFrameCIE.cpp
   DwarfLineTables.cpp
   DwarfLineTableHeaders.cpp
+  DXContainerWriterTest.cpp
   MCInstPrinter.cpp
   StringTableBuilderTest.cpp
   TargetRegistry.cpp
diff --git a/llvm/unittests/MC/DXContainerWriterTest.cpp b/llvm/unittests/MC/DXContainerWriterTest.cpp
new file mode 100644
index 0000000000000..3641e873dbf4f
--- /dev/null
+++ b/llvm/unittests/MC/DXContainerWriterTest.cpp
@@ -0,0 +1,83 @@
+//===- DXContainerWriterTest.cpp - MCDXContainerWriter tests --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/MC/MCDXContainerWriter.h"
+#include "llvm/Object/DXContainer.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBufferRef.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Triple.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+namespace {
+
+class TestDXContainerWriter : public MCDXContainerBaseWriter {
+  SmallVector<SmallString<8>> PartNames;
+  SmallVector<SmallString<32>> PartData;
+  SmallVector<MCDXContainerPart> Parts;
+
+protected:
+  ArrayRef<MCDXContainerPart> collectParts() override { return Parts; }
+
+public:
+  void addPart(StringRef Name, ArrayRef<uint8_t> Data) {
+    PartNames.emplace_back(Name);
+    PartData.emplace_back(Data.begin(), Data.end());
+    Parts.push_back({PartNames.back(), PartData.back()});
+  }
+};
+
+static Triple getTestTriple() {
+  return Triple("dxilv1.3-pc-shadermodel6.3-library");
+}
+
+static std::string writeContainer(TestDXContainerWriter &Writer) {
+  std::string Buffer;
+  raw_string_ostream OS(Buffer);
+  Writer.write(OS, getTestTriple());
+  return Buffer;
+}
+
+static DXContainer parseContainer(StringRef Buffer) {
+  return llvm::cantFail(DXContainer::create(MemoryBufferRef(Buffer, "")));
+}
+
+TEST(MCDXContainerWriterTest, PrivUnaligned) {
+  TestDXContainerWriter Writer;
+  const uint8_t PrivData[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x42};
+  Writer.addPart("PRIV", PrivData);
+
+  std::string Buffer = writeContainer(Writer);
+  DXContainer C = parseContainer(Buffer);
+
+  EXPECT_EQ(C.getHeader().PartCount, 1u);
+  EXPECT_EQ(C.getHeader().FileSize, 49u);
+  EXPECT_EQ(C.getData().size(), 49u);
+
+  ASSERT_TRUE(C.getPrivateData());
+  EXPECT_EQ(C.getPrivateData()->size(), 5u);
+  EXPECT_EQ(
+      *C.getPrivateData(),
+      StringRef(reinterpret_cast<const char *>(PrivData), sizeof(PrivData)));
+}
+
+TEST(MCDXContainerWriterTest, PrivMustBeLast) {
+  TestDXContainerWriter Writer;
+  const uint8_t PrivData[] = {0x42};
+  const uint8_t DxilData[] = {0xBC, 0xC0, 0xDE, 0x00};
+  Writer.addPart("PRIV", PrivData);
+  Writer.addPart("DXIL", DxilData);
+
+  EXPECT_DEATH(writeContainer(Writer),
+               "PRIV must be the last section in a DXContainer");
+}
+
+} // namespace



More information about the llvm-branch-commits mailing list