diff --git a/llvm/include/llvm/BinaryFormat/DXContainerConstants.def b/llvm/include/llvm/BinaryFormat/DXContainerConstants.def index 419e78c8a87d..a6f8aaad6487 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 ce3b9bc94ba9..545e8930f145 100644 --- a/llvm/include/llvm/Object/DXContainer.h +++ b/llvm/include/llvm/Object/DXContainer.h @@ -480,6 +480,7 @@ class DXContainer { std::optional DebugName; std::optional VersionInfo; std::optional SourceInfo; + std::optional 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 &getSourceInfo() const { return SourceInfo; } + + const std::optional &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 29f6b9bcae00..1d3e044bbb22 100644 --- a/llvm/include/llvm/ObjectYAML/DXContainerYAML.h +++ b/llvm/include/llvm/ObjectYAML/DXContainerYAML.h @@ -401,6 +401,7 @@ struct Part { std::optional DebugName; std::optional CompilerVersion; std::optional SourceInfo; + std::optional> PrivateData; }; struct Object { diff --git a/llvm/lib/Object/DXContainer.cpp b/llvm/lib/Object/DXContainer.cpp index dd4de04f5d38..a13f4f07f5fd 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"); @@ -536,6 +543,9 @@ Error DXContainer::parsePartOffsets() { sizeof(dxbc::Header) + (Header.PartCount * sizeof(uint32_t)); const char *Current = Data.getBuffer().data() + sizeof(dxbc::Header); for (uint32_t Part = 0; Part < Header.PartCount; ++Part) { + if (PrivateData) + return parseFailed("PRIV must be the last section in a DXContainer"); + uint32_t PartOffset; if (Error Err = readInteger(Data.getBuffer(), Current, PartOffset)) return Err; @@ -577,6 +587,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 ff43eb71d57c..5b0639a012e1 100644 --- a/llvm/lib/ObjectYAML/DXContainerEmitter.cpp +++ b/llvm/lib/ObjectYAML/DXContainerEmitter.cpp @@ -125,9 +125,15 @@ assignSectionHeader(dxbc::SourceInfo::SectionHeader &Dst, } Error DXContainerWriter::writeParts(raw_ostream &OS) { + bool HasPrivate = false; uint32_t RollingOffset = sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t)); for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) { + if (HasPrivate) + return createStringError( + errc::invalid_argument, + "PRIV must be the last section in a DXContainer"); + if (RollingOffset < std::get<1>(I)) { uint32_t PadBytes = std::get<1>(I) - RollingOffset; OS.write_zeros(PadBytes); @@ -205,6 +211,15 @@ Error DXContainerWriter::writeParts(raw_ostream &OS) { DebugName.write(OS); break; } + case dxbc::PartType::PRIV: { + if (!P.PrivateData) + continue; + + HasPrivate = true; + OS.write(reinterpret_cast(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 ba766dfffd91..1671ec2afed9 100644 --- a/llvm/lib/ObjectYAML/DXContainerYAML.cpp +++ b/llvm/lib/ObjectYAML/DXContainerYAML.cpp @@ -567,6 +567,7 @@ void MappingTraits::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::mapping( @@ -981,6 +982,14 @@ DXContainerYAML::fromDXContainer(object::DXContainer &Container) { DebugName->Filename.str()}; break; } + case dxbc::PartType::PRIV: { + std::optional 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 Flags = Container.getShaderFeatureFlags(); // Omit the flags in the YAML if they are missing or zero. diff --git a/llvm/test/ObjectYAML/DXContainer/PRIV-not-last.yaml b/llvm/test/ObjectYAML/DXContainer/PRIV-not-last.yaml new file mode 100644 index 000000000000..f573935a731a --- /dev/null +++ b/llvm/test/ObjectYAML/DXContainer/PRIV-not-last.yaml @@ -0,0 +1,29 @@ +## Check that ObjectYAML rejects PRIV parts that are not the last section. + +# RUN: not yaml2obj %s 2>&1 | FileCheck %s + +# CHECK: PRIV must be the last section in a DXContainer + +--- !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: PRIV + Size: 5 + PrivateData: [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ] + - Name: DXIL + Size: 24 + Program: + MajorVersion: 6 + MinorVersion: 0 + ShaderKind: 5 + Size: 6 + DXILMajorVersion: 0 + DXILMinorVersion: 1 + DXILSize: 0 +... 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 000000000000..a145c45eefc1 --- /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 000000000000..0d429a138141 --- /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 0051839d071e..9eefc1be296b 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 &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 2d6d2d21df33..9c342f3ee182 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;