Skip to content

Commit 1cdbcce

Browse files
authored
[DirectX][ObjectYAML] Add PRIV part support (#174)
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 be 4-byte aligned. 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.
1 parent ee0354f commit 1cdbcce

11 files changed

Lines changed: 209 additions & 0 deletions

File tree

llvm/include/llvm/BinaryFormat/DXContainerConstants.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
CONTAINER_PART(DXIL)
44
CONTAINER_PART(ILDB)
55
CONTAINER_PART(ILDN)
6+
CONTAINER_PART(PRIV)
67
CONTAINER_PART(SFI0)
78
CONTAINER_PART(SRCI)
89
CONTAINER_PART(HASH)

llvm/include/llvm/Object/DXContainer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ class DXContainer {
480480
std::optional<mcdxbc::DebugName> DebugName;
481481
std::optional<mcdxbc::CompilerVersion> VersionInfo;
482482
std::optional<mcdxbc::SourceInfo> SourceInfo;
483+
std::optional<StringRef> PrivateData;
483484

484485
Error parseHeader();
485486
Error parsePartOffsets();
@@ -492,6 +493,7 @@ class DXContainer {
492493
Error parseSignature(StringRef Part, DirectX::Signature &Array);
493494
Error parseCompilerVersionInfo(StringRef Part);
494495
Error parseSourceInfo(StringRef Part);
496+
Error parsePrivateData(StringRef Part);
495497
friend class PartIterator;
496498

497499
public:
@@ -613,6 +615,8 @@ class DXContainer {
613615
const std::optional<mcdxbc::SourceInfo> &getSourceInfo() const {
614616
return SourceInfo;
615617
}
618+
619+
const std::optional<StringRef> &getPrivateData() const { return PrivateData; }
616620
};
617621

618622
class LLVM_ABI DXContainerObjectFile : public ObjectFile {

llvm/include/llvm/ObjectYAML/DXContainerYAML.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ struct Part {
401401
std::optional<DXContainerYAML::DebugName> DebugName;
402402
std::optional<DXContainerYAML::CompilerVersion> CompilerVersion;
403403
std::optional<DXContainerYAML::SourceInfo> SourceInfo;
404+
std::optional<std::vector<llvm::yaml::Hex8>> PrivateData;
404405
};
405406

406407
struct Object {

llvm/lib/Object/DXContainer.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ Error DXContainer::parseDebugName(StringRef Part) {
127127
return Error::success();
128128
}
129129

130+
Error DXContainer::parsePrivateData(StringRef Part) {
131+
if (PrivateData)
132+
return parseFailed("more than one PRIV part is present in the file");
133+
PrivateData.emplace(Part);
134+
return Error::success();
135+
}
136+
130137
Error DXContainer::parseShaderFeatureFlags(StringRef Part) {
131138
if (ShaderFeatureFlags)
132139
return parseFailed("More than one SFI0 part is present in the file");
@@ -536,6 +543,9 @@ Error DXContainer::parsePartOffsets() {
536543
sizeof(dxbc::Header) + (Header.PartCount * sizeof(uint32_t));
537544
const char *Current = Data.getBuffer().data() + sizeof(dxbc::Header);
538545
for (uint32_t Part = 0; Part < Header.PartCount; ++Part) {
546+
if (PrivateData)
547+
return parseFailed("PRIV must be the last section in a DXContainer");
548+
539549
uint32_t PartOffset;
540550
if (Error Err = readInteger(Data.getBuffer(), Current, PartOffset))
541551
return Err;
@@ -577,6 +587,10 @@ Error DXContainer::parsePartOffsets() {
577587
if (Error Err = parseDebugName(PartData))
578588
return Err;
579589
break;
590+
case dxbc::PartType::PRIV:
591+
if (Error Err = parsePrivateData(PartData))
592+
return Err;
593+
break;
580594
case dxbc::PartType::SFI0:
581595
if (Error Err = parseShaderFeatureFlags(PartData))
582596
return Err;

llvm/lib/ObjectYAML/DXContainerEmitter.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,15 @@ assignSectionHeader(dxbc::SourceInfo::SectionHeader &Dst,
125125
}
126126

127127
Error DXContainerWriter::writeParts(raw_ostream &OS) {
128+
bool HasPrivate = false;
128129
uint32_t RollingOffset =
129130
sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
130131
for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {
132+
if (HasPrivate)
133+
return createStringError(
134+
errc::invalid_argument,
135+
"PRIV must be the last section in a DXContainer");
136+
131137
if (RollingOffset < std::get<1>(I)) {
132138
uint32_t PadBytes = std::get<1>(I) - RollingOffset;
133139
OS.write_zeros(PadBytes);
@@ -205,6 +211,15 @@ Error DXContainerWriter::writeParts(raw_ostream &OS) {
205211
DebugName.write(OS);
206212
break;
207213
}
214+
case dxbc::PartType::PRIV: {
215+
if (!P.PrivateData)
216+
continue;
217+
218+
HasPrivate = true;
219+
OS.write(reinterpret_cast<char *>(P.PrivateData->data()),
220+
P.PrivateData->size());
221+
break;
222+
}
208223
case dxbc::PartType::SFI0: {
209224
// If we don't have any flags we can continue here and the data will be
210225
// zeroed out.

llvm/lib/ObjectYAML/DXContainerYAML.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ void MappingTraits<DXContainerYAML::Part>::mapping(IO &IO,
567567
IO.mapOptional("DebugName", P.DebugName);
568568
IO.mapOptional("CompilerVersion", P.CompilerVersion);
569569
IO.mapOptional("SourceInfo", P.SourceInfo);
570+
IO.mapOptional("PrivateData", P.PrivateData);
570571
}
571572

572573
void MappingTraits<DXContainerYAML::Object>::mapping(
@@ -981,6 +982,14 @@ DXContainerYAML::fromDXContainer(object::DXContainer &Container) {
981982
DebugName->Filename.str()};
982983
break;
983984
}
985+
case dxbc::PartType::PRIV: {
986+
std::optional<StringRef> PrivateData = Container.getPrivateData();
987+
assert(PrivateData && "Since we are iterating and found a PRIV part, "
988+
"this should never not have a value");
989+
NewPart.PrivateData.emplace(PrivateData->data(),
990+
PrivateData->data() + PrivateData->size());
991+
break;
992+
}
984993
case dxbc::PartType::SFI0: {
985994
std::optional<uint64_t> Flags = Container.getShaderFeatureFlags();
986995
// Omit the flags in the YAML if they are missing or zero.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Check that ObjectYAML rejects PRIV parts that are not the last section.
2+
3+
# RUN: not yaml2obj %s 2>&1 | FileCheck %s
4+
5+
# CHECK: PRIV must be the last section in a DXContainer
6+
7+
--- !dxcontainer
8+
Header:
9+
Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
10+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
11+
Version:
12+
Major: 1
13+
Minor: 0
14+
PartCount: 2
15+
Parts:
16+
- Name: PRIV
17+
Size: 5
18+
PrivateData: [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ]
19+
- Name: DXIL
20+
Size: 24
21+
Program:
22+
MajorVersion: 6
23+
MinorVersion: 0
24+
ShaderKind: 5
25+
Size: 6
26+
DXILMajorVersion: 0
27+
DXILMinorVersion: 1
28+
DXILSize: 0
29+
...
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
## Check that --dump-section exports the raw PRIV part data, including when the
2+
## part size is not a multiple of 4.
3+
4+
# RUN: yaml2obj %s -o %t.dxbc
5+
# RUN: llvm-objcopy --dump-section=PRIV=%t.priv %t.dxbc
6+
7+
# RUN: wc -c %t.priv | FileCheck %s --check-prefix=PRIV-SIZE
8+
# PRIV-SIZE: 5
9+
10+
# RUN: od -t x1 %t.priv | FileCheck %s --ignore-case
11+
# CHECK: de ad be ef 42
12+
13+
--- !dxcontainer
14+
Header:
15+
Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
16+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
17+
Version:
18+
Major: 1
19+
Minor: 0
20+
PartCount: 2
21+
Parts:
22+
- Name: DXIL
23+
Size: 24
24+
Program:
25+
MajorVersion: 6
26+
MinorVersion: 0
27+
ShaderKind: 5
28+
Size: 6
29+
DXILMajorVersion: 0
30+
DXILMinorVersion: 1
31+
DXILSize: 0
32+
- Name: PRIV
33+
Size: 5
34+
PrivateData: [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ]
35+
...
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## Check that the PRIV part round-trips through yaml2obj and obj2yaml when the
2+
## part size is not a multiple of 4. The pipeline is run twice to ensure obj2yaml
3+
## output with a non-multiple-of-4 FileSize can be fed back through yaml2obj.
4+
5+
# RUN: yaml2obj %s 2>&1 | obj2yaml 2>&1 | yaml2obj %s 2>&1 | obj2yaml 2>&1 | FileCheck %s
6+
7+
--- !dxcontainer
8+
Header:
9+
Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
10+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
11+
Version:
12+
Major: 1
13+
Minor: 0
14+
PartCount: 2
15+
Parts:
16+
- Name: DXIL
17+
Size: 24
18+
Program:
19+
MajorVersion: 6
20+
MinorVersion: 0
21+
ShaderKind: 5
22+
Size: 6
23+
DXILMajorVersion: 0
24+
DXILMinorVersion: 1
25+
DXILSize: 0
26+
- Name: PRIV
27+
Size: 5
28+
PrivateData: [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ]
29+
...
30+
31+
# CHECK: FileSize: 85
32+
# CHECK: PartCount: 2
33+
# CHECK: PartOffsets: [ 40, 72 ]
34+
# CHECK: Parts:
35+
# CHECK: - Name: DXIL
36+
# CHECK: Program:
37+
# CHECK: MajorVersion: 6
38+
# CHECK: MinorVersion: 0
39+
# CHECK: ShaderKind: 5
40+
# CHECK: - Name: PRIV
41+
# CHECK: Size: 5
42+
# CHECK: PrivateData: [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ]

llvm/unittests/Object/DXContainerTest.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,36 @@ TEST(DXCFile, ParseILDNPart) {
320320
EXPECT_EQ(ILDN->Filename, "abc.pdb");
321321
}
322322

323+
// This test verifies that PRIV part is correctly parsed.
324+
// This test is based on the binary output constructed from this yaml.
325+
// --- !dxcontainer
326+
// Header:
327+
// Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
328+
// 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
329+
// Version:
330+
// Major: 1
331+
// Minor: 0
332+
// PartCount: 1
333+
// Parts:
334+
// - Name: PRIV
335+
// Size: 5
336+
// PrivateData: [ 0xDE, 0xAD, 0xBE, 0xEF, 0x42 ]
337+
// ...
338+
TEST(DXCFile, ParsePRIVPart) {
339+
uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00,
340+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
341+
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00,
342+
0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
343+
0x50, 0x52, 0x49, 0x56, 0x05, 0x00, 0x00, 0x00, 0xDE,
344+
0xAD, 0xBE, 0xEF, 0x42};
345+
DXContainer C =
346+
llvm::cantFail(DXContainer::create(getMemoryBuffer<49>(Buffer)));
347+
EXPECT_EQ(C.getHeader().PartCount, 1u);
348+
const std::optional<StringRef> &PrivateData = C.getPrivateData();
349+
EXPECT_TRUE(PrivateData.has_value());
350+
EXPECT_EQ(*PrivateData, "\xDE\xAD\xBE\xEF\x42");
351+
}
352+
323353
// This test verifies that VERS part is correctly parsed.
324354
// This test is based on the binary output constructed from this yaml.
325355
// --- !dxcontainer

0 commit comments

Comments
 (0)