[llvm] [DirectX][ObjectYAML] Add ILDB program part support (PR #189685)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 31 07:50:16 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-directx
Author: Vladislav Dzhidzhoev (dzhidzhoev)
<details>
<summary>Changes</summary>
Add support for DXContainer ILDB parts in the ObjectYAML pipeline so they can be represented in structured YAML and round-tripped through yaml2obj/obj2yaml.
ILDB payloads use the same layout as DXIL, but contain debug IR. This change treats ILDB as a DXIL-shaped program part.
FileSize and DXIL ProgramHeader Size values in DXILPart.yaml were modified to make output binary correct/parseable by dxa utility.
---
Full diff: https://github.com/llvm/llvm-project/pull/189685.diff
9 Files Affected:
- (modified) llvm/include/llvm/BinaryFormat/DXContainer.h (+4)
- (modified) llvm/include/llvm/BinaryFormat/DXContainerConstants.def (+1)
- (modified) llvm/include/llvm/Object/DXContainer.h (+5-2)
- (modified) llvm/lib/BinaryFormat/DXContainer.cpp (+6)
- (modified) llvm/lib/Object/DXContainer.cpp (+9-3)
- (modified) llvm/lib/ObjectYAML/DXContainerEmitter.cpp (+2)
- (modified) llvm/test/tools/obj2yaml/DXContainer/DXILPart.yaml (+27-5)
- (modified) llvm/tools/obj2yaml/dxcontainer2yaml.cpp (+8-5)
- (modified) llvm/unittests/Object/DXContainerTest.cpp (+47-1)
``````````diff
diff --git a/llvm/include/llvm/BinaryFormat/DXContainer.h b/llvm/include/llvm/BinaryFormat/DXContainer.h
index 1756ab0b555ff..3da36f6da525d 100644
--- a/llvm/include/llvm/BinaryFormat/DXContainer.h
+++ b/llvm/include/llvm/BinaryFormat/DXContainer.h
@@ -258,6 +258,10 @@ LLVM_ABI ArrayRef<EnumEntry<StaticBorderColor>> getStaticBorderColors();
LLVM_ABI PartType parsePartType(StringRef S);
+bool isDebugProgramPart(PartType PT);
+
+const char *getProgramPartName(bool IsDebug);
+
struct VertexPSVInfo {
uint8_t OutputPositionPresent;
uint8_t Unused[3];
diff --git a/llvm/include/llvm/BinaryFormat/DXContainerConstants.def b/llvm/include/llvm/BinaryFormat/DXContainerConstants.def
index f576d958037cd..f9f4472b0ebce 100644
--- a/llvm/include/llvm/BinaryFormat/DXContainerConstants.def
+++ b/llvm/include/llvm/BinaryFormat/DXContainerConstants.def
@@ -1,6 +1,7 @@
#ifdef CONTAINER_PART
CONTAINER_PART(DXIL)
+CONTAINER_PART(ILDB)
CONTAINER_PART(SFI0)
CONTAINER_PART(HASH)
CONTAINER_PART(PSV0)
diff --git a/llvm/include/llvm/Object/DXContainer.h b/llvm/include/llvm/Object/DXContainer.h
index c5888b87d6ad7..b70d6a960c130 100644
--- a/llvm/include/llvm/Object/DXContainer.h
+++ b/llvm/include/llvm/Object/DXContainer.h
@@ -468,6 +468,7 @@ class DXContainer {
dxbc::Header Header;
SmallVector<uint32_t, 4> PartOffsets;
std::optional<DXILData> DXIL;
+ std::optional<DXILData> DebugDXIL;
std::optional<uint64_t> ShaderFeatureFlags;
std::optional<dxbc::ShaderHash> Hash;
std::optional<DirectX::PSVRuntimeInfo> PSVInfo;
@@ -478,7 +479,7 @@ class DXContainer {
Error parseHeader();
Error parsePartOffsets();
- Error parseDXILHeader(StringRef Part);
+ Error parseDXILHeader(dxbc::PartType PT, StringRef Part);
Error parseShaderFeatureFlags(StringRef Part);
Error parseHash(StringRef Part);
Error parseRootSignature(StringRef Part);
@@ -561,7 +562,9 @@ class DXContainer {
const dxbc::Header &getHeader() const { return Header; }
- const std::optional<DXILData> &getDXIL() const { return DXIL; }
+ const std::optional<DXILData> &getDXIL(bool Debug) const {
+ return Debug ? DebugDXIL : DXIL;
+ }
std::optional<uint64_t> getShaderFeatureFlags() const {
return ShaderFeatureFlags;
diff --git a/llvm/lib/BinaryFormat/DXContainer.cpp b/llvm/lib/BinaryFormat/DXContainer.cpp
index 22f518067b318..384ef6811346c 100644
--- a/llvm/lib/BinaryFormat/DXContainer.cpp
+++ b/llvm/lib/BinaryFormat/DXContainer.cpp
@@ -110,6 +110,12 @@ dxbc::PartType dxbc::parsePartType(StringRef S) {
.Default(dxbc::PartType::Unknown);
}
+bool dxbc::isDebugProgramPart(PartType PT) { return PT == PartType::ILDB; }
+
+const char *dxbc::getProgramPartName(bool IsDebug) {
+ return IsDebug ? "ILDB" : "DXIL";
+}
+
bool ShaderHash::isPopulated() {
static uint8_t Zeros[16] = {0};
return Flags > 0 || 0 != memcmp(&Digest, &Zeros, 16);
diff --git a/llvm/lib/Object/DXContainer.cpp b/llvm/lib/Object/DXContainer.cpp
index 7b7b8d88c63fc..bcc692f51405a 100644
--- a/llvm/lib/Object/DXContainer.cpp
+++ b/llvm/lib/Object/DXContainer.cpp
@@ -61,9 +61,13 @@ Error DXContainer::parseHeader() {
return readStruct(Data.getBuffer(), Data.getBuffer().data(), Header);
}
-Error DXContainer::parseDXILHeader(StringRef Part) {
+Error DXContainer::parseDXILHeader(dxbc::PartType PT, StringRef Part) {
+ bool IsDebug = dxbc::isDebugProgramPart(PT);
+ std::optional<DXILData> &DXIL = IsDebug ? this->DebugDXIL : this->DXIL;
+
if (DXIL)
- return parseFailed("More than one DXIL part is present in the file");
+ return parseFailed(formatv("More than one {0} part is present in the file",
+ dxbc::getProgramPartName(IsDebug)));
const char *Current = Part.begin();
dxbc::ProgramHeader Header;
if (Error Err = readStruct(Part, Current, Header))
@@ -173,8 +177,10 @@ Error DXContainer::parsePartOffsets() {
StringRef PartData = Data.getBuffer().substr(PartDataStart, PartSize);
LastOffset = PartOffset + PartSize;
switch (PT) {
+ case dxbc::PartType::ILDB:
+ [[fallthrough]];
case dxbc::PartType::DXIL:
- if (Error Err = parseDXILHeader(PartData))
+ if (Error Err = parseDXILHeader(PT, PartData))
return Err;
break;
case dxbc::PartType::SFI0:
diff --git a/llvm/lib/ObjectYAML/DXContainerEmitter.cpp b/llvm/lib/ObjectYAML/DXContainerEmitter.cpp
index b00e45d912be1..225ab0ef39bb4 100644
--- a/llvm/lib/ObjectYAML/DXContainerEmitter.cpp
+++ b/llvm/lib/ObjectYAML/DXContainerEmitter.cpp
@@ -128,6 +128,8 @@ Error DXContainerWriter::writeParts(raw_ostream &OS) {
uint64_t DataStart = OS.tell();
switch (PT) {
+ case dxbc::PartType::ILDB:
+ [[fallthrough]];
case dxbc::PartType::DXIL: {
if (!P.Program)
continue;
diff --git a/llvm/test/tools/obj2yaml/DXContainer/DXILPart.yaml b/llvm/test/tools/obj2yaml/DXContainer/DXILPart.yaml
index 58508f2dcc1e4..1749c7bd44d48 100644
--- a/llvm/test/tools/obj2yaml/DXContainer/DXILPart.yaml
+++ b/llvm/test/tools/obj2yaml/DXContainer/DXILPart.yaml
@@ -11,9 +11,9 @@ Header:
Version:
Major: 1
Minor: 0
- FileSize: 3548
- PartCount: 7
- PartOffsets: [ 60, 76, 92, 108, 236, 1932, 1960 ]
+ FileSize: 2040
+ PartCount: 8
+ PartOffsets: [ 64, 80, 96, 112, 240, 1936, 1964, 2000 ]
Parts:
- Name: FKE0
Size: 8
@@ -33,11 +33,22 @@ Parts:
MajorVersion: 6
MinorVersion: 5
ShaderKind: 5
- Size: 8
+ Size: 7
DXILMajorVersion: 1
DXILMinorVersion: 5
DXILSize: 4
DXIL: [ 0x42, 0x43, 0xC0, 0xDE, ]
+ - Name: ILDB
+ Size: 32
+ Program:
+ MajorVersion: 6
+ MinorVersion: 5
+ ShaderKind: 5
+ Size: 8
+ DXILMajorVersion: 1
+ DXILMinorVersion: 5
+ DXILSize: 8
+ DXIL: [ 0x42, 0x43, 0xC0, 0xDE, 0x21, 0x0C, 0x00, 0x00, ]
...
@@ -49,8 +60,19 @@ Parts:
#CHECK-NEXT: MajorVersion: 6
#CHECK-NEXT: MinorVersion: 5
#CHECK-NEXT: ShaderKind: 5
-#CHECK-NEXT: Size: 8
+#CHECK-NEXT: Size: 7
#CHECK-NEXT: DXILMajorVersion: 1
#CHECK-NEXT: DXILMinorVersion: 5
#CHECK-NEXT: DXILSize: 4
#CHECK-NEXT: DXIL: [ 0x42, 0x43, 0xC0, 0xDE
+#CHECK: - Name: ILDB
+#CHECK-NEXT: Size: 32
+#CHECK-NEXT: Program:
+#CHECK-NEXT: MajorVersion: 6
+#CHECK-NEXT: MinorVersion: 5
+#CHECK-NEXT: ShaderKind: 5
+#CHECK-NEXT: Size: 8
+#CHECK-NEXT: DXILMajorVersion: 1
+#CHECK-NEXT: DXILMinorVersion: 5
+#CHECK-NEXT: DXILSize: 8
+#CHECK-NEXT: DXIL: [ 0x42, 0x43, 0xC0, 0xDE, 0x21, 0xC, 0x0, 0x0
diff --git a/llvm/tools/obj2yaml/dxcontainer2yaml.cpp b/llvm/tools/obj2yaml/dxcontainer2yaml.cpp
index c727595406767..99a4fed92c139 100644
--- a/llvm/tools/obj2yaml/dxcontainer2yaml.cpp
+++ b/llvm/tools/obj2yaml/dxcontainer2yaml.cpp
@@ -53,9 +53,12 @@ dumpDXContainer(MemoryBufferRef Source) {
DXContainerYAML::Part &NewPart = Obj->Parts.back();
dxbc::PartType PT = dxbc::parsePartType(P.Part.getName());
switch (PT) {
+ case dxbc::PartType::ILDB:
+ [[fallthrough]];
case dxbc::PartType::DXIL: {
- std::optional<DXContainer::DXILData> DXIL = Container.getDXIL();
- assert(DXIL && "Since we are iterating and found a DXIL part, "
+ std::optional<DXContainer::DXILData> DXIL =
+ Container.getDXIL(dxbc::isDebugProgramPart(PT));
+ assert(DXIL && "Since we are iterating and found a DXIL/ILDB part, "
"this should never not have a value");
NewPart.Program = DXContainerYAML::DXILProgram{
DXIL->first.getMajorVersion(),
@@ -89,10 +92,10 @@ dumpDXContainer(MemoryBufferRef Source) {
break;
if (const auto *P =
std::get_if<dxbc::PSV::v0::RuntimeInfo>(&PSVInfo->getInfo())) {
- if (!Container.getDXIL())
+ if (!Container.getDXIL(false))
break;
- NewPart.Info =
- DXContainerYAML::PSVInfo(P, Container.getDXIL()->first.ShaderKind);
+ NewPart.Info = DXContainerYAML::PSVInfo(
+ P, Container.getDXIL(false)->first.ShaderKind);
} else if (const auto *P = std::get_if<dxbc::PSV::v1::RuntimeInfo>(
&PSVInfo->getInfo()))
NewPart.Info = DXContainerYAML::PSVInfo(P);
diff --git a/llvm/unittests/Object/DXContainerTest.cpp b/llvm/unittests/Object/DXContainerTest.cpp
index d6f7b26b99cd7..309063631ec99 100644
--- a/llvm/unittests/Object/DXContainerTest.cpp
+++ b/llvm/unittests/Object/DXContainerTest.cpp
@@ -209,7 +209,7 @@ TEST(DXCFile, ParseDXILPart) {
DXContainer C =
llvm::cantFail(DXContainer::create(getMemoryBuffer<116>(Buffer)));
EXPECT_EQ(C.getHeader().PartCount, 1u);
- const std::optional<object::DXContainer::DXILData> &DXIL = C.getDXIL();
+ const std::optional<object::DXContainer::DXILData> &DXIL = C.getDXIL(false);
EXPECT_TRUE(DXIL.has_value());
dxbc::ProgramHeader Header = DXIL->first;
EXPECT_EQ(Header.getMajorVersion(), 6u);
@@ -220,6 +220,52 @@ TEST(DXCFile, ParseDXILPart) {
EXPECT_EQ(Header.Bitcode.MinorVersion, 5u);
}
+// This test verifies that ILDB 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: ILDB
+// Size: 28
+// Program:
+// MajorVersion: 6
+// MinorVersion: 5
+// ShaderKind: 5
+// Size: 8
+// DXILMajorVersion: 1
+// DXILMinorVersion: 5
+// DXILSize: 4
+// DXIL: [ 0x42, 0x43, 0xC0, 0xDE, ]
+// ...
+TEST(DXCFile, ParseILDBPart) {
+ 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,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x49, 0x4c, 0x44, 0x42, 0x1c, 0x00, 0x00, 0x00, 0x65, 0x00, 0x05, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4c, 0x05, 0x01, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x42, 0x43, 0xc0, 0xde};
+ DXContainer C =
+ llvm::cantFail(DXContainer::create(getMemoryBuffer<116>(Buffer)));
+ EXPECT_EQ(C.getHeader().PartCount, 1u);
+ const std::optional<object::DXContainer::DXILData> &DXIL = C.getDXIL(true);
+ EXPECT_TRUE(DXIL.has_value());
+ dxbc::ProgramHeader Header = DXIL->first;
+ EXPECT_EQ(Header.getMajorVersion(), 6u);
+ EXPECT_EQ(Header.getMinorVersion(), 5u);
+ EXPECT_EQ(Header.ShaderKind, 5u);
+ EXPECT_EQ(Header.Size, 8u);
+ EXPECT_EQ(Header.Bitcode.MajorVersion, 1u);
+ EXPECT_EQ(Header.Bitcode.MinorVersion, 5u);
+ EXPECT_TRUE(memcmp(DXIL->second, "\x42\x43\xc0\xde", 4) == 0);
+}
+
static Expected<DXContainer>
generateDXContainer(StringRef Yaml, SmallVectorImpl<char> &BinaryData) {
DXContainerYAML::Object Obj;
``````````
</details>
https://github.com/llvm/llvm-project/pull/189685
More information about the llvm-commits
mailing list