[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