1 | //===- DXContainerEmitter.cpp - Convert YAML to a DXContainer -------------===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | /// |
9 | /// \file |
10 | /// Binary emitter for yaml to DXContainer binary |
11 | /// |
12 | //===----------------------------------------------------------------------===// |
13 | |
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" |
21 | |
22 | using namespace llvm; |
23 | |
24 | namespace { |
25 | class DXContainerWriter { |
26 | public: |
27 | DXContainerWriter(DXContainerYAML::Object &ObjectFile) |
28 | : ObjectFile(ObjectFile) {} |
29 | |
30 | Error write(raw_ostream &OS); |
31 | |
32 | private: |
33 | DXContainerYAML::Object &ObjectFile; |
34 | |
35 | Error computePartOffsets(); |
36 | Error validatePartOffsets(); |
37 | Error validateSize(uint32_t Computed); |
38 | |
39 | void writeHeader(raw_ostream &OS); |
40 | void writeParts(raw_ostream &OS); |
41 | }; |
42 | } // namespace |
43 | |
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(EC: errc::result_out_of_range, |
49 | S: "File size specified is too small." ); |
50 | return Error::success(); |
51 | } |
52 | |
53 | Error DXContainerWriter::validatePartOffsets() { |
54 | if (ObjectFile.Parts.size() != ObjectFile.Header.PartOffsets->size()) |
55 | return createStringError( |
56 | EC: errc::invalid_argument, |
57 | S: "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(t&: ObjectFile.Parts, u&: *ObjectFile.Header.PartOffsets)) { |
61 | if (RollingOffset > std::get<1>(t&: I)) |
62 | return createStringError(EC: errc::invalid_argument, |
63 | S: "Offset mismatch, not enough space for data." ); |
64 | RollingOffset = |
65 | std::get<1>(t&: I) + sizeof(dxbc::PartHeader) + std::get<0>(t&: I).Size; |
66 | } |
67 | if (Error Err = validateSize(Computed: RollingOffset)) |
68 | return Err; |
69 | |
70 | return Error::success(); |
71 | } |
72 | |
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(x: RollingOffset); |
81 | RollingOffset += sizeof(dxbc::PartHeader) + Part.Size; |
82 | } |
83 | if (Error Err = validateSize(Computed: RollingOffset)) |
84 | return Err; |
85 | |
86 | return Error::success(); |
87 | } |
88 | |
89 | void DXContainerWriter::(raw_ostream &OS) { |
90 | dxbc::Header ; |
91 | memcpy(dest: Header.Magic, src: "DXBC" , n: 4); |
92 | memcpy(dest: Header.FileHash.Digest, src: ObjectFile.Header.Hash.data(), n: 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) |
98 | Header.swapBytes(); |
99 | OS.write(Ptr: reinterpret_cast<char *>(&Header), Size: 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(Value&: O); |
105 | OS.write(Ptr: reinterpret_cast<char *>(Offsets.data()), |
106 | Size: Offsets.size() * sizeof(uint32_t)); |
107 | } |
108 | |
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(t&: ObjectFile.Parts, u&: *ObjectFile.Header.PartOffsets)) { |
113 | if (RollingOffset < std::get<1>(t&: I)) { |
114 | uint32_t PadBytes = std::get<1>(t&: I) - RollingOffset; |
115 | OS.write_zeros(NumZeros: PadBytes); |
116 | } |
117 | DXContainerYAML::Part P = std::get<0>(t&: I); |
118 | RollingOffset = std::get<1>(t&: I) + sizeof(dxbc::PartHeader); |
119 | uint32_t PartSize = P.Size; |
120 | |
121 | OS.write(Ptr: P.Name.c_str(), Size: 4); |
122 | if (sys::IsBigEndianHost) |
123 | sys::swapByteOrder(Value&: P.Size); |
124 | OS.write(Ptr: reinterpret_cast<const char *>(&P.Size), Size: sizeof(uint32_t)); |
125 | |
126 | dxbc::PartType PT = dxbc::parsePartType(S: P.Name); |
127 | |
128 | uint64_t DataStart = OS.tell(); |
129 | switch (PT) { |
130 | case dxbc::PartType::DXIL: { |
131 | if (!P.Program) |
132 | continue; |
133 | dxbc::ProgramHeader ; |
134 | Header.Version = dxbc::ProgramHeader::getVersion(Major: P.Program->MajorVersion, |
135 | Minor: P.Program->MinorVersion); |
136 | Header.Unused = 0; |
137 | Header.ShaderKind = P.Program->ShaderKind; |
138 | memcpy(dest: Header.Bitcode.Magic, src: "DXIL" , n: 4); |
139 | Header.Bitcode.MajorVersion = P.Program->DXILMajorVersion; |
140 | Header.Bitcode.MinorVersion = P.Program->DXILMinorVersion; |
141 | Header.Bitcode.Unused = 0; |
142 | |
143 | // Compute the optional fields if needed... |
144 | if (P.Program->DXILOffset) |
145 | Header.Bitcode.Offset = *P.Program->DXILOffset; |
146 | else |
147 | Header.Bitcode.Offset = sizeof(dxbc::BitcodeHeader); |
148 | |
149 | if (P.Program->DXILSize) |
150 | Header.Bitcode.Size = *P.Program->DXILSize; |
151 | else |
152 | Header.Bitcode.Size = P.Program->DXIL ? P.Program->DXIL->size() : 0; |
153 | |
154 | if (P.Program->Size) |
155 | Header.Size = *P.Program->Size; |
156 | else |
157 | Header.Size = sizeof(dxbc::ProgramHeader) + Header.Bitcode.Size; |
158 | |
159 | uint32_t BitcodeOffset = Header.Bitcode.Offset; |
160 | if (sys::IsBigEndianHost) |
161 | Header.swapBytes(); |
162 | OS.write(Ptr: reinterpret_cast<const char *>(&Header), |
163 | Size: 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(NumZeros: PadBytes); |
168 | } |
169 | OS.write(Ptr: reinterpret_cast<char *>(P.Program->DXIL->data()), |
170 | Size: P.Program->DXIL->size()); |
171 | } |
172 | break; |
173 | } |
174 | case dxbc::PartType::SFI0: { |
175 | // If we don't have any flags we can continue here and the data will be |
176 | // zeroed out. |
177 | if (!P.Flags.has_value()) |
178 | continue; |
179 | uint64_t Flags = P.Flags->getEncodedFlags(); |
180 | if (sys::IsBigEndianHost) |
181 | sys::swapByteOrder(Value&: Flags); |
182 | OS.write(Ptr: reinterpret_cast<char *>(&Flags), Size: sizeof(uint64_t)); |
183 | break; |
184 | } |
185 | case dxbc::PartType::HASH: { |
186 | if (!P.Hash.has_value()) |
187 | continue; |
188 | dxbc::ShaderHash Hash = {.Flags: 0, .Digest: {0}}; |
189 | if (P.Hash->IncludesSource) |
190 | Hash.Flags |= static_cast<uint32_t>(dxbc::HashFlags::IncludesSource); |
191 | memcpy(dest: &Hash.Digest[0], src: &P.Hash->Digest[0], n: 16); |
192 | if (sys::IsBigEndianHost) |
193 | Hash.swapBytes(); |
194 | OS.write(Ptr: reinterpret_cast<char *>(&Hash), Size: sizeof(dxbc::ShaderHash)); |
195 | break; |
196 | } |
197 | case dxbc::PartType::PSV0: { |
198 | if (!P.Info.has_value()) |
199 | continue; |
200 | mcdxbc::PSVRuntimeInfo PSV; |
201 | memcpy(dest: &PSV.BaseData, src: &P.Info->Info, n: sizeof(dxbc::PSV::v3::RuntimeInfo)); |
202 | PSV.Resources = P.Info->Resources; |
203 | PSV.EntryName = P.Info->EntryName; |
204 | |
205 | for (auto El : P.Info->SigInputElements) |
206 | PSV.InputElements.push_back(Elt: mcdxbc::PSVSignatureElement{ |
207 | .Name: El.Name, .Indices: El.Indices, .StartRow: El.StartRow, .Cols: El.Cols, .StartCol: El.StartCol, |
208 | .Allocated: El.Allocated, .Kind: El.Kind, .Type: El.Type, .Mode: El.Mode, .DynamicMask: El.DynamicMask, |
209 | .Stream: El.Stream}); |
210 | |
211 | for (auto El : P.Info->SigOutputElements) |
212 | PSV.OutputElements.push_back(Elt: mcdxbc::PSVSignatureElement{ |
213 | .Name: El.Name, .Indices: El.Indices, .StartRow: El.StartRow, .Cols: El.Cols, .StartCol: El.StartCol, |
214 | .Allocated: El.Allocated, .Kind: El.Kind, .Type: El.Type, .Mode: El.Mode, .DynamicMask: El.DynamicMask, |
215 | .Stream: El.Stream}); |
216 | |
217 | for (auto El : P.Info->SigPatchOrPrimElements) |
218 | PSV.PatchOrPrimElements.push_back(Elt: mcdxbc::PSVSignatureElement{ |
219 | .Name: El.Name, .Indices: El.Indices, .StartRow: El.StartRow, .Cols: El.Cols, .StartCol: El.StartCol, |
220 | .Allocated: El.Allocated, .Kind: El.Kind, .Type: El.Type, .Mode: El.Mode, .DynamicMask: El.DynamicMask, |
221 | .Stream: El.Stream}); |
222 | |
223 | static_assert(PSV.OutputVectorMasks.size() == PSV.InputOutputMap.size()); |
224 | for (unsigned I = 0; I < PSV.OutputVectorMasks.size(); ++I) { |
225 | PSV.OutputVectorMasks[I].insert(I: PSV.OutputVectorMasks[I].begin(), |
226 | From: P.Info->OutputVectorMasks[I].begin(), |
227 | To: P.Info->OutputVectorMasks[I].end()); |
228 | PSV.InputOutputMap[I].insert(I: PSV.InputOutputMap[I].begin(), |
229 | From: P.Info->InputOutputMap[I].begin(), |
230 | To: P.Info->InputOutputMap[I].end()); |
231 | } |
232 | |
233 | PSV.PatchOrPrimMasks.insert(I: PSV.PatchOrPrimMasks.begin(), |
234 | From: P.Info->PatchOrPrimMasks.begin(), |
235 | To: P.Info->PatchOrPrimMasks.end()); |
236 | PSV.InputPatchMap.insert(I: PSV.InputPatchMap.begin(), |
237 | From: P.Info->InputPatchMap.begin(), |
238 | To: P.Info->InputPatchMap.end()); |
239 | PSV.PatchOutputMap.insert(I: PSV.PatchOutputMap.begin(), |
240 | From: P.Info->PatchOutputMap.begin(), |
241 | To: P.Info->PatchOutputMap.end()); |
242 | |
243 | PSV.finalize(Stage: static_cast<Triple::EnvironmentType>( |
244 | Triple::Pixel + P.Info->Info.ShaderStage)); |
245 | PSV.write(OS, Version: P.Info->Version); |
246 | break; |
247 | } |
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(Stream: Param.Stream, Name: Param.Name, Index: Param.Index, SystemValue: Param.SystemValue, |
255 | CompType: Param.CompType, Register: Param.Register, Mask: Param.Mask, |
256 | ExclusiveMask: Param.ExclusiveMask, MinPrecision: Param.MinPrecision); |
257 | } |
258 | } |
259 | Sig.write(OS); |
260 | break; |
261 | } |
262 | case dxbc::PartType::Unknown: |
263 | break; // Skip any handling for unrecognized parts. |
264 | } |
265 | uint64_t BytesWritten = OS.tell() - DataStart; |
266 | RollingOffset += BytesWritten; |
267 | if (BytesWritten < PartSize) |
268 | OS.write_zeros(NumZeros: PartSize - BytesWritten); |
269 | RollingOffset += PartSize; |
270 | } |
271 | } |
272 | |
273 | Error DXContainerWriter::write(raw_ostream &OS) { |
274 | if (Error Err = computePartOffsets()) |
275 | return Err; |
276 | writeHeader(OS); |
277 | writeParts(OS); |
278 | return Error::success(); |
279 | } |
280 | |
281 | namespace llvm { |
282 | namespace yaml { |
283 | |
284 | bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out, |
285 | ErrorHandler EH) { |
286 | DXContainerWriter Writer(Doc); |
287 | if (Error Err = Writer.write(OS&: Out)) { |
288 | handleAllErrors(E: std::move(Err), |
289 | Handlers: [&](const ErrorInfoBase &Err) { EH(Err.message()); }); |
290 | return false; |
291 | } |
292 | return true; |
293 | } |
294 | |
295 | } // namespace yaml |
296 | } // namespace llvm |
297 | |