1 //===- DXContainerEmitter.cpp - Convert YAML to a DXContainer -------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 /// Binary emitter for yaml to DXContainer binary
12 //===----------------------------------------------------------------------===//
14 #include "llvm/BinaryFormat/DXContainer.h"
15 #include "llvm/MC/DXContainerPSVInfo.h"
16 #include "llvm/ObjectYAML/ObjectYAML.h"
17 #include "llvm/ObjectYAML/yaml2obj.h"
18 #include "llvm/Support/Errc.h"
19 #include "llvm/Support/Error.h"
20 #include "llvm/Support/raw_ostream.h"
25 class DXContainerWriter
{
27 DXContainerWriter(DXContainerYAML::Object
&ObjectFile
)
28 : ObjectFile(ObjectFile
) {}
30 Error
write(raw_ostream
&OS
);
33 DXContainerYAML::Object
&ObjectFile
;
35 Error
computePartOffsets();
36 Error
validatePartOffsets();
37 Error
validateSize(uint32_t Computed
);
39 void writeHeader(raw_ostream
&OS
);
40 void writeParts(raw_ostream
&OS
);
44 Error
DXContainerWriter::validateSize(uint32_t Computed
) {
45 if (!ObjectFile
.Header
.FileSize
)
46 ObjectFile
.Header
.FileSize
= Computed
;
47 else if (*ObjectFile
.Header
.FileSize
< Computed
)
48 return createStringError(errc::result_out_of_range
,
49 "File size specified is too small.");
50 return Error::success();
53 Error
DXContainerWriter::validatePartOffsets() {
54 if (ObjectFile
.Parts
.size() != ObjectFile
.Header
.PartOffsets
->size())
55 return createStringError(
56 errc::invalid_argument
,
57 "Mismatch between number of parts and part offsets.");
58 uint32_t RollingOffset
=
59 sizeof(dxbc::Header
) + (ObjectFile
.Header
.PartCount
* sizeof(uint32_t));
60 for (auto I
: llvm::zip(ObjectFile
.Parts
, *ObjectFile
.Header
.PartOffsets
)) {
61 if (RollingOffset
> std::get
<1>(I
))
62 return createStringError(errc::invalid_argument
,
63 "Offset mismatch, not enough space for data.");
65 std::get
<1>(I
) + sizeof(dxbc::PartHeader
) + std::get
<0>(I
).Size
;
67 if (Error Err
= validateSize(RollingOffset
))
70 return Error::success();
73 Error
DXContainerWriter::computePartOffsets() {
74 if (ObjectFile
.Header
.PartOffsets
)
75 return validatePartOffsets();
76 uint32_t RollingOffset
=
77 sizeof(dxbc::Header
) + (ObjectFile
.Header
.PartCount
* sizeof(uint32_t));
78 ObjectFile
.Header
.PartOffsets
= std::vector
<uint32_t>();
79 for (const auto &Part
: ObjectFile
.Parts
) {
80 ObjectFile
.Header
.PartOffsets
->push_back(RollingOffset
);
81 RollingOffset
+= sizeof(dxbc::PartHeader
) + Part
.Size
;
83 if (Error Err
= validateSize(RollingOffset
))
86 return Error::success();
89 void DXContainerWriter::writeHeader(raw_ostream
&OS
) {
91 memcpy(Header
.Magic
, "DXBC", 4);
92 memcpy(Header
.FileHash
.Digest
, ObjectFile
.Header
.Hash
.data(), 16);
93 Header
.Version
.Major
= ObjectFile
.Header
.Version
.Major
;
94 Header
.Version
.Minor
= ObjectFile
.Header
.Version
.Minor
;
95 Header
.FileSize
= *ObjectFile
.Header
.FileSize
;
96 Header
.PartCount
= ObjectFile
.Parts
.size();
97 if (sys::IsBigEndianHost
)
99 OS
.write(reinterpret_cast<char *>(&Header
), sizeof(Header
));
100 SmallVector
<uint32_t> Offsets(ObjectFile
.Header
.PartOffsets
->begin(),
101 ObjectFile
.Header
.PartOffsets
->end());
102 if (sys::IsBigEndianHost
)
103 for (auto &O
: Offsets
)
104 sys::swapByteOrder(O
);
105 OS
.write(reinterpret_cast<char *>(Offsets
.data()),
106 Offsets
.size() * sizeof(uint32_t));
109 void DXContainerWriter::writeParts(raw_ostream
&OS
) {
110 uint32_t RollingOffset
=
111 sizeof(dxbc::Header
) + (ObjectFile
.Header
.PartCount
* sizeof(uint32_t));
112 for (auto I
: llvm::zip(ObjectFile
.Parts
, *ObjectFile
.Header
.PartOffsets
)) {
113 if (RollingOffset
< std::get
<1>(I
)) {
114 uint32_t PadBytes
= std::get
<1>(I
) - RollingOffset
;
115 OS
.write_zeros(PadBytes
);
117 DXContainerYAML::Part P
= std::get
<0>(I
);
118 RollingOffset
= std::get
<1>(I
) + sizeof(dxbc::PartHeader
);
119 uint32_t PartSize
= P
.Size
;
121 OS
.write(P
.Name
.c_str(), 4);
122 if (sys::IsBigEndianHost
)
123 sys::swapByteOrder(P
.Size
);
124 OS
.write(reinterpret_cast<const char *>(&P
.Size
), sizeof(uint32_t));
126 dxbc::PartType PT
= dxbc::parsePartType(P
.Name
);
128 uint64_t DataStart
= OS
.tell();
130 case dxbc::PartType::DXIL
: {
133 dxbc::ProgramHeader Header
;
134 Header
.Version
= dxbc::ProgramHeader::getVersion(P
.Program
->MajorVersion
,
135 P
.Program
->MinorVersion
);
137 Header
.ShaderKind
= P
.Program
->ShaderKind
;
138 memcpy(Header
.Bitcode
.Magic
, "DXIL", 4);
139 Header
.Bitcode
.MajorVersion
= P
.Program
->DXILMajorVersion
;
140 Header
.Bitcode
.MinorVersion
= P
.Program
->DXILMinorVersion
;
141 Header
.Bitcode
.Unused
= 0;
143 // Compute the optional fields if needed...
144 if (P
.Program
->DXILOffset
)
145 Header
.Bitcode
.Offset
= *P
.Program
->DXILOffset
;
147 Header
.Bitcode
.Offset
= sizeof(dxbc::BitcodeHeader
);
149 if (P
.Program
->DXILSize
)
150 Header
.Bitcode
.Size
= *P
.Program
->DXILSize
;
152 Header
.Bitcode
.Size
= P
.Program
->DXIL
? P
.Program
->DXIL
->size() : 0;
155 Header
.Size
= *P
.Program
->Size
;
157 Header
.Size
= sizeof(dxbc::ProgramHeader
) + Header
.Bitcode
.Size
;
159 uint32_t BitcodeOffset
= Header
.Bitcode
.Offset
;
160 if (sys::IsBigEndianHost
)
162 OS
.write(reinterpret_cast<const char *>(&Header
),
163 sizeof(dxbc::ProgramHeader
));
164 if (P
.Program
->DXIL
) {
165 if (BitcodeOffset
> sizeof(dxbc::BitcodeHeader
)) {
166 uint32_t PadBytes
= BitcodeOffset
- sizeof(dxbc::BitcodeHeader
);
167 OS
.write_zeros(PadBytes
);
169 OS
.write(reinterpret_cast<char *>(P
.Program
->DXIL
->data()),
170 P
.Program
->DXIL
->size());
174 case dxbc::PartType::SFI0
: {
175 // If we don't have any flags we can continue here and the data will be
177 if (!P
.Flags
.has_value())
179 uint64_t Flags
= P
.Flags
->getEncodedFlags();
180 if (sys::IsBigEndianHost
)
181 sys::swapByteOrder(Flags
);
182 OS
.write(reinterpret_cast<char *>(&Flags
), sizeof(uint64_t));
185 case dxbc::PartType::HASH
: {
186 if (!P
.Hash
.has_value())
188 dxbc::ShaderHash Hash
= {0, {0}};
189 if (P
.Hash
->IncludesSource
)
190 Hash
.Flags
|= static_cast<uint32_t>(dxbc::HashFlags::IncludesSource
);
191 memcpy(&Hash
.Digest
[0], &P
.Hash
->Digest
[0], 16);
192 if (sys::IsBigEndianHost
)
194 OS
.write(reinterpret_cast<char *>(&Hash
), sizeof(dxbc::ShaderHash
));
197 case dxbc::PartType::PSV0
: {
198 if (!P
.Info
.has_value())
200 mcdxbc::PSVRuntimeInfo PSV
;
201 memcpy(&PSV
.BaseData
, &P
.Info
->Info
, sizeof(dxbc::PSV::v3::RuntimeInfo
));
202 PSV
.Resources
= P
.Info
->Resources
;
203 PSV
.EntryName
= P
.Info
->EntryName
;
205 for (auto El
: P
.Info
->SigInputElements
)
206 PSV
.InputElements
.push_back(mcdxbc::PSVSignatureElement
{
207 El
.Name
, El
.Indices
, El
.StartRow
, El
.Cols
, El
.StartCol
,
208 El
.Allocated
, El
.Kind
, El
.Type
, El
.Mode
, El
.DynamicMask
,
211 for (auto El
: P
.Info
->SigOutputElements
)
212 PSV
.OutputElements
.push_back(mcdxbc::PSVSignatureElement
{
213 El
.Name
, El
.Indices
, El
.StartRow
, El
.Cols
, El
.StartCol
,
214 El
.Allocated
, El
.Kind
, El
.Type
, El
.Mode
, El
.DynamicMask
,
217 for (auto El
: P
.Info
->SigPatchOrPrimElements
)
218 PSV
.PatchOrPrimElements
.push_back(mcdxbc::PSVSignatureElement
{
219 El
.Name
, El
.Indices
, El
.StartRow
, El
.Cols
, El
.StartCol
,
220 El
.Allocated
, El
.Kind
, El
.Type
, El
.Mode
, El
.DynamicMask
,
223 static_assert(PSV
.OutputVectorMasks
.size() == PSV
.InputOutputMap
.size());
224 for (unsigned I
= 0; I
< PSV
.OutputVectorMasks
.size(); ++I
) {
225 PSV
.OutputVectorMasks
[I
].insert(PSV
.OutputVectorMasks
[I
].begin(),
226 P
.Info
->OutputVectorMasks
[I
].begin(),
227 P
.Info
->OutputVectorMasks
[I
].end());
228 PSV
.InputOutputMap
[I
].insert(PSV
.InputOutputMap
[I
].begin(),
229 P
.Info
->InputOutputMap
[I
].begin(),
230 P
.Info
->InputOutputMap
[I
].end());
233 PSV
.PatchOrPrimMasks
.insert(PSV
.PatchOrPrimMasks
.begin(),
234 P
.Info
->PatchOrPrimMasks
.begin(),
235 P
.Info
->PatchOrPrimMasks
.end());
236 PSV
.InputPatchMap
.insert(PSV
.InputPatchMap
.begin(),
237 P
.Info
->InputPatchMap
.begin(),
238 P
.Info
->InputPatchMap
.end());
239 PSV
.PatchOutputMap
.insert(PSV
.PatchOutputMap
.begin(),
240 P
.Info
->PatchOutputMap
.begin(),
241 P
.Info
->PatchOutputMap
.end());
243 PSV
.finalize(static_cast<Triple::EnvironmentType
>(
244 Triple::Pixel
+ P
.Info
->Info
.ShaderStage
));
245 PSV
.write(OS
, P
.Info
->Version
);
248 case dxbc::PartType::ISG1
:
249 case dxbc::PartType::OSG1
:
250 case dxbc::PartType::PSG1
: {
251 mcdxbc::Signature Sig
;
252 if (P
.Signature
.has_value()) {
253 for (const auto &Param
: P
.Signature
->Parameters
) {
254 Sig
.addParam(Param
.Stream
, Param
.Name
, Param
.Index
, Param
.SystemValue
,
255 Param
.CompType
, Param
.Register
, Param
.Mask
,
256 Param
.ExclusiveMask
, Param
.MinPrecision
);
262 case dxbc::PartType::Unknown
:
263 break; // Skip any handling for unrecognized parts.
265 uint64_t BytesWritten
= OS
.tell() - DataStart
;
266 RollingOffset
+= BytesWritten
;
267 if (BytesWritten
< PartSize
)
268 OS
.write_zeros(PartSize
- BytesWritten
);
269 RollingOffset
+= PartSize
;
273 Error
DXContainerWriter::write(raw_ostream
&OS
) {
274 if (Error Err
= computePartOffsets())
278 return Error::success();
284 bool yaml2dxcontainer(DXContainerYAML::Object
&Doc
, raw_ostream
&Out
,
286 DXContainerWriter
Writer(Doc
);
287 if (Error Err
= Writer
.write(Out
)) {
288 handleAllErrors(std::move(Err
),
289 [&](const ErrorInfoBase
&Err
) { EH(Err
.message()); });