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 <[email protected]> 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 <[email protected]> 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" + [email protected] = private constant [4 x i8] c"BC\C0\DE", section "ILDB", align 4 [email protected] = private constant [16 x i8] c"dummymodulehash!", section "PDBHASH", align 4 [email protected] = 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" + [email protected] = private constant [4 x i8] c"BC\C0\DE", section "ILDB", align 4 [email protected] = private constant [20 x i8] c"PdbInPrivateTest.pdb", section "PDBNAME", align 4 [email protected] = private constant [16 x i8] c"dummymodulehash!", section "PDBHASH", align 4 [email protected] = 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 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
