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/MC/DXContainerRootSignature.h" |
17 | #include "llvm/ObjectYAML/ObjectYAML.h" |
18 | #include "llvm/ObjectYAML/yaml2obj.h" |
19 | #include "llvm/Support/Errc.h" |
20 | #include "llvm/Support/Error.h" |
21 | #include "llvm/Support/raw_ostream.h" |
22 | |
23 | using namespace llvm; |
24 | |
25 | namespace { |
26 | class DXContainerWriter { |
27 | public: |
28 | DXContainerWriter(DXContainerYAML::Object &ObjectFile) |
29 | : ObjectFile(ObjectFile) {} |
30 | |
31 | Error write(raw_ostream &OS); |
32 | |
33 | private: |
34 | DXContainerYAML::Object &ObjectFile; |
35 | |
36 | Error computePartOffsets(); |
37 | Error validatePartOffsets(); |
38 | Error validateSize(uint32_t Computed); |
39 | |
40 | void writeHeader(raw_ostream &OS); |
41 | void writeParts(raw_ostream &OS); |
42 | }; |
43 | } // namespace |
44 | |
45 | Error DXContainerWriter::validateSize(uint32_t Computed) { |
46 | if (!ObjectFile.Header.FileSize) |
47 | ObjectFile.Header.FileSize = Computed; |
48 | else if (*ObjectFile.Header.FileSize < Computed) |
49 | return createStringError(EC: errc::result_out_of_range, |
50 | S: "File size specified is too small." ); |
51 | return Error::success(); |
52 | } |
53 | |
54 | Error DXContainerWriter::validatePartOffsets() { |
55 | if (ObjectFile.Parts.size() != ObjectFile.Header.PartOffsets->size()) |
56 | return createStringError( |
57 | EC: errc::invalid_argument, |
58 | S: "Mismatch between number of parts and part offsets." ); |
59 | uint32_t RollingOffset = |
60 | sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t)); |
61 | for (auto I : llvm::zip(t&: ObjectFile.Parts, u&: *ObjectFile.Header.PartOffsets)) { |
62 | if (RollingOffset > std::get<1>(t&: I)) |
63 | return createStringError(EC: errc::invalid_argument, |
64 | S: "Offset mismatch, not enough space for data." ); |
65 | RollingOffset = |
66 | std::get<1>(t&: I) + sizeof(dxbc::PartHeader) + std::get<0>(t&: I).Size; |
67 | } |
68 | if (Error Err = validateSize(Computed: RollingOffset)) |
69 | return Err; |
70 | |
71 | return Error::success(); |
72 | } |
73 | |
74 | Error DXContainerWriter::computePartOffsets() { |
75 | if (ObjectFile.Header.PartOffsets) |
76 | return validatePartOffsets(); |
77 | uint32_t RollingOffset = |
78 | sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t)); |
79 | ObjectFile.Header.PartOffsets = std::vector<uint32_t>(); |
80 | for (const auto &Part : ObjectFile.Parts) { |
81 | ObjectFile.Header.PartOffsets->push_back(x: RollingOffset); |
82 | RollingOffset += sizeof(dxbc::PartHeader) + Part.Size; |
83 | } |
84 | if (Error Err = validateSize(Computed: RollingOffset)) |
85 | return Err; |
86 | |
87 | return Error::success(); |
88 | } |
89 | |
90 | void DXContainerWriter::(raw_ostream &OS) { |
91 | dxbc::Header ; |
92 | memcpy(dest: Header.Magic, src: "DXBC" , n: 4); |
93 | memcpy(dest: Header.FileHash.Digest, src: ObjectFile.Header.Hash.data(), n: 16); |
94 | Header.Version.Major = ObjectFile.Header.Version.Major; |
95 | Header.Version.Minor = ObjectFile.Header.Version.Minor; |
96 | Header.FileSize = *ObjectFile.Header.FileSize; |
97 | Header.PartCount = ObjectFile.Parts.size(); |
98 | if (sys::IsBigEndianHost) |
99 | Header.swapBytes(); |
100 | OS.write(Ptr: reinterpret_cast<char *>(&Header), Size: sizeof(Header)); |
101 | SmallVector<uint32_t> Offsets(ObjectFile.Header.PartOffsets->begin(), |
102 | ObjectFile.Header.PartOffsets->end()); |
103 | if (sys::IsBigEndianHost) |
104 | for (auto &O : Offsets) |
105 | sys::swapByteOrder(Value&: O); |
106 | OS.write(Ptr: reinterpret_cast<char *>(Offsets.data()), |
107 | Size: Offsets.size() * sizeof(uint32_t)); |
108 | } |
109 | |
110 | void DXContainerWriter::writeParts(raw_ostream &OS) { |
111 | uint32_t RollingOffset = |
112 | sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t)); |
113 | for (auto I : llvm::zip(t&: ObjectFile.Parts, u&: *ObjectFile.Header.PartOffsets)) { |
114 | if (RollingOffset < std::get<1>(t&: I)) { |
115 | uint32_t PadBytes = std::get<1>(t&: I) - RollingOffset; |
116 | OS.write_zeros(NumZeros: PadBytes); |
117 | } |
118 | DXContainerYAML::Part P = std::get<0>(t&: I); |
119 | RollingOffset = std::get<1>(t&: I) + sizeof(dxbc::PartHeader); |
120 | uint32_t PartSize = P.Size; |
121 | |
122 | OS.write(Ptr: P.Name.c_str(), Size: 4); |
123 | if (sys::IsBigEndianHost) |
124 | sys::swapByteOrder(Value&: P.Size); |
125 | OS.write(Ptr: reinterpret_cast<const char *>(&P.Size), Size: sizeof(uint32_t)); |
126 | |
127 | dxbc::PartType PT = dxbc::parsePartType(S: P.Name); |
128 | |
129 | uint64_t DataStart = OS.tell(); |
130 | switch (PT) { |
131 | case dxbc::PartType::DXIL: { |
132 | if (!P.Program) |
133 | continue; |
134 | dxbc::ProgramHeader ; |
135 | Header.Version = dxbc::ProgramHeader::getVersion(Major: P.Program->MajorVersion, |
136 | Minor: P.Program->MinorVersion); |
137 | Header.Unused = 0; |
138 | Header.ShaderKind = P.Program->ShaderKind; |
139 | memcpy(dest: Header.Bitcode.Magic, src: "DXIL" , n: 4); |
140 | Header.Bitcode.MajorVersion = P.Program->DXILMajorVersion; |
141 | Header.Bitcode.MinorVersion = P.Program->DXILMinorVersion; |
142 | Header.Bitcode.Unused = 0; |
143 | |
144 | // Compute the optional fields if needed... |
145 | if (P.Program->DXILOffset) |
146 | Header.Bitcode.Offset = *P.Program->DXILOffset; |
147 | else |
148 | Header.Bitcode.Offset = sizeof(dxbc::BitcodeHeader); |
149 | |
150 | if (P.Program->DXILSize) |
151 | Header.Bitcode.Size = *P.Program->DXILSize; |
152 | else |
153 | Header.Bitcode.Size = P.Program->DXIL ? P.Program->DXIL->size() : 0; |
154 | |
155 | if (P.Program->Size) |
156 | Header.Size = *P.Program->Size; |
157 | else |
158 | Header.Size = sizeof(dxbc::ProgramHeader) + Header.Bitcode.Size; |
159 | |
160 | uint32_t BitcodeOffset = Header.Bitcode.Offset; |
161 | if (sys::IsBigEndianHost) |
162 | Header.swapBytes(); |
163 | OS.write(Ptr: reinterpret_cast<const char *>(&Header), |
164 | Size: sizeof(dxbc::ProgramHeader)); |
165 | if (P.Program->DXIL) { |
166 | if (BitcodeOffset > sizeof(dxbc::BitcodeHeader)) { |
167 | uint32_t PadBytes = BitcodeOffset - sizeof(dxbc::BitcodeHeader); |
168 | OS.write_zeros(NumZeros: PadBytes); |
169 | } |
170 | OS.write(Ptr: reinterpret_cast<char *>(P.Program->DXIL->data()), |
171 | Size: P.Program->DXIL->size()); |
172 | } |
173 | break; |
174 | } |
175 | case dxbc::PartType::SFI0: { |
176 | // If we don't have any flags we can continue here and the data will be |
177 | // zeroed out. |
178 | if (!P.Flags.has_value()) |
179 | continue; |
180 | uint64_t Flags = P.Flags->getEncodedFlags(); |
181 | if (sys::IsBigEndianHost) |
182 | sys::swapByteOrder(Value&: Flags); |
183 | OS.write(Ptr: reinterpret_cast<char *>(&Flags), Size: sizeof(uint64_t)); |
184 | break; |
185 | } |
186 | case dxbc::PartType::HASH: { |
187 | if (!P.Hash.has_value()) |
188 | continue; |
189 | dxbc::ShaderHash Hash = {.Flags: 0, .Digest: {0}}; |
190 | if (P.Hash->IncludesSource) |
191 | Hash.Flags |= static_cast<uint32_t>(dxbc::HashFlags::IncludesSource); |
192 | memcpy(dest: &Hash.Digest[0], src: &P.Hash->Digest[0], n: 16); |
193 | if (sys::IsBigEndianHost) |
194 | Hash.swapBytes(); |
195 | OS.write(Ptr: reinterpret_cast<char *>(&Hash), Size: sizeof(dxbc::ShaderHash)); |
196 | break; |
197 | } |
198 | case dxbc::PartType::PSV0: { |
199 | if (!P.Info.has_value()) |
200 | continue; |
201 | mcdxbc::PSVRuntimeInfo PSV; |
202 | memcpy(dest: &PSV.BaseData, src: &P.Info->Info, n: sizeof(dxbc::PSV::v3::RuntimeInfo)); |
203 | PSV.Resources = P.Info->Resources; |
204 | PSV.EntryName = P.Info->EntryName; |
205 | |
206 | for (auto El : P.Info->SigInputElements) |
207 | PSV.InputElements.push_back(Elt: mcdxbc::PSVSignatureElement{ |
208 | .Name: El.Name, .Indices: El.Indices, .StartRow: El.StartRow, .Cols: El.Cols, .StartCol: El.StartCol, |
209 | .Allocated: El.Allocated, .Kind: El.Kind, .Type: El.Type, .Mode: El.Mode, .DynamicMask: El.DynamicMask, |
210 | .Stream: El.Stream}); |
211 | |
212 | for (auto El : P.Info->SigOutputElements) |
213 | PSV.OutputElements.push_back(Elt: mcdxbc::PSVSignatureElement{ |
214 | .Name: El.Name, .Indices: El.Indices, .StartRow: El.StartRow, .Cols: El.Cols, .StartCol: El.StartCol, |
215 | .Allocated: El.Allocated, .Kind: El.Kind, .Type: El.Type, .Mode: El.Mode, .DynamicMask: El.DynamicMask, |
216 | .Stream: El.Stream}); |
217 | |
218 | for (auto El : P.Info->SigPatchOrPrimElements) |
219 | PSV.PatchOrPrimElements.push_back(Elt: mcdxbc::PSVSignatureElement{ |
220 | .Name: El.Name, .Indices: El.Indices, .StartRow: El.StartRow, .Cols: El.Cols, .StartCol: El.StartCol, |
221 | .Allocated: El.Allocated, .Kind: El.Kind, .Type: El.Type, .Mode: El.Mode, .DynamicMask: El.DynamicMask, |
222 | .Stream: El.Stream}); |
223 | |
224 | static_assert(PSV.OutputVectorMasks.size() == PSV.InputOutputMap.size()); |
225 | for (unsigned I = 0; I < PSV.OutputVectorMasks.size(); ++I) { |
226 | PSV.OutputVectorMasks[I].insert(I: PSV.OutputVectorMasks[I].begin(), |
227 | From: P.Info->OutputVectorMasks[I].begin(), |
228 | To: P.Info->OutputVectorMasks[I].end()); |
229 | PSV.InputOutputMap[I].insert(I: PSV.InputOutputMap[I].begin(), |
230 | From: P.Info->InputOutputMap[I].begin(), |
231 | To: P.Info->InputOutputMap[I].end()); |
232 | } |
233 | |
234 | PSV.PatchOrPrimMasks.insert(I: PSV.PatchOrPrimMasks.begin(), |
235 | From: P.Info->PatchOrPrimMasks.begin(), |
236 | To: P.Info->PatchOrPrimMasks.end()); |
237 | PSV.InputPatchMap.insert(I: PSV.InputPatchMap.begin(), |
238 | From: P.Info->InputPatchMap.begin(), |
239 | To: P.Info->InputPatchMap.end()); |
240 | PSV.PatchOutputMap.insert(I: PSV.PatchOutputMap.begin(), |
241 | From: P.Info->PatchOutputMap.begin(), |
242 | To: P.Info->PatchOutputMap.end()); |
243 | |
244 | PSV.finalize(Stage: static_cast<Triple::EnvironmentType>( |
245 | Triple::Pixel + P.Info->Info.ShaderStage)); |
246 | PSV.write(OS, Version: P.Info->Version); |
247 | break; |
248 | } |
249 | case dxbc::PartType::ISG1: |
250 | case dxbc::PartType::OSG1: |
251 | case dxbc::PartType::PSG1: { |
252 | mcdxbc::Signature Sig; |
253 | if (P.Signature.has_value()) { |
254 | for (const auto &Param : P.Signature->Parameters) { |
255 | Sig.addParam(Stream: Param.Stream, Name: Param.Name, Index: Param.Index, SystemValue: Param.SystemValue, |
256 | CompType: Param.CompType, Register: Param.Register, Mask: Param.Mask, |
257 | ExclusiveMask: Param.ExclusiveMask, MinPrecision: Param.MinPrecision); |
258 | } |
259 | } |
260 | Sig.write(OS); |
261 | break; |
262 | } |
263 | case dxbc::PartType::Unknown: |
264 | break; // Skip any handling for unrecognized parts. |
265 | case dxbc::PartType::RTS0: |
266 | if (!P.RootSignature.has_value()) |
267 | continue; |
268 | |
269 | mcdxbc::RootSignatureDesc RS; |
270 | RS.Flags = P.RootSignature->getEncodedFlags(); |
271 | RS.Version = P.RootSignature->Version; |
272 | RS.RootParameterOffset = P.RootSignature->RootParametersOffset; |
273 | RS.NumStaticSamplers = P.RootSignature->NumStaticSamplers; |
274 | RS.StaticSamplersOffset = P.RootSignature->StaticSamplersOffset; |
275 | |
276 | for (DXContainerYAML::RootParameterLocationYaml &L : |
277 | P.RootSignature->Parameters.Locations) { |
278 | dxbc::RTS0::v1::RootParameterHeader {.ParameterType: L.Header.Type, .ShaderVisibility: L.Header.Visibility, |
279 | .ParameterOffset: L.Header.Offset}; |
280 | |
281 | switch (L.Header.Type) { |
282 | case llvm::to_underlying(E: dxbc::RootParameterType::Constants32Bit): { |
283 | const DXContainerYAML::RootConstantsYaml &ConstantYaml = |
284 | P.RootSignature->Parameters.getOrInsertConstants(ParamDesc&: L); |
285 | dxbc::RTS0::v1::RootConstants Constants; |
286 | Constants.Num32BitValues = ConstantYaml.Num32BitValues; |
287 | Constants.RegisterSpace = ConstantYaml.RegisterSpace; |
288 | Constants.ShaderRegister = ConstantYaml.ShaderRegister; |
289 | RS.ParametersContainer.addParameter(Header, Constant: Constants); |
290 | break; |
291 | } |
292 | case llvm::to_underlying(E: dxbc::RootParameterType::CBV): |
293 | case llvm::to_underlying(E: dxbc::RootParameterType::SRV): |
294 | case llvm::to_underlying(E: dxbc::RootParameterType::UAV): { |
295 | const DXContainerYAML::RootDescriptorYaml &DescriptorYaml = |
296 | P.RootSignature->Parameters.getOrInsertDescriptor(ParamDesc&: L); |
297 | |
298 | dxbc::RTS0::v2::RootDescriptor Descriptor; |
299 | Descriptor.RegisterSpace = DescriptorYaml.RegisterSpace; |
300 | Descriptor.ShaderRegister = DescriptorYaml.ShaderRegister; |
301 | if (RS.Version > 1) |
302 | Descriptor.Flags = DescriptorYaml.getEncodedFlags(); |
303 | RS.ParametersContainer.addParameter(Header, Descriptor); |
304 | break; |
305 | } |
306 | case llvm::to_underlying(E: dxbc::RootParameterType::DescriptorTable): { |
307 | const DXContainerYAML::DescriptorTableYaml &TableYaml = |
308 | P.RootSignature->Parameters.getOrInsertTable(ParamDesc&: L); |
309 | mcdxbc::DescriptorTable Table; |
310 | for (const auto &R : TableYaml.Ranges) { |
311 | |
312 | dxbc::RTS0::v2::DescriptorRange Range; |
313 | Range.RangeType = R.RangeType; |
314 | Range.NumDescriptors = R.NumDescriptors; |
315 | Range.BaseShaderRegister = R.BaseShaderRegister; |
316 | Range.RegisterSpace = R.RegisterSpace; |
317 | Range.OffsetInDescriptorsFromTableStart = |
318 | R.OffsetInDescriptorsFromTableStart; |
319 | if (RS.Version > 1) |
320 | Range.Flags = R.getEncodedFlags(); |
321 | Table.Ranges.push_back(Elt: Range); |
322 | } |
323 | RS.ParametersContainer.addParameter(Header, Table); |
324 | break; |
325 | } |
326 | default: |
327 | // Handling invalid parameter type edge case. We intentionally let |
328 | // obj2yaml/yaml2obj parse and emit invalid dxcontainer data, in order |
329 | // for that to be used as a testing tool more effectively. |
330 | RS.ParametersContainer.addInvalidParameter(Header); |
331 | } |
332 | } |
333 | |
334 | for (const auto &Param : P.RootSignature->samplers()) { |
335 | dxbc::RTS0::v1::StaticSampler NewSampler; |
336 | NewSampler.Filter = Param.Filter; |
337 | NewSampler.AddressU = Param.AddressU; |
338 | NewSampler.AddressV = Param.AddressV; |
339 | NewSampler.AddressW = Param.AddressW; |
340 | NewSampler.MipLODBias = Param.MipLODBias; |
341 | NewSampler.MaxAnisotropy = Param.MaxAnisotropy; |
342 | NewSampler.ComparisonFunc = Param.ComparisonFunc; |
343 | NewSampler.BorderColor = Param.BorderColor; |
344 | NewSampler.MinLOD = Param.MinLOD; |
345 | NewSampler.MaxLOD = Param.MaxLOD; |
346 | NewSampler.ShaderRegister = Param.ShaderRegister; |
347 | NewSampler.RegisterSpace = Param.RegisterSpace; |
348 | NewSampler.ShaderVisibility = Param.ShaderVisibility; |
349 | |
350 | RS.StaticSamplers.push_back(Elt: NewSampler); |
351 | } |
352 | |
353 | RS.write(OS); |
354 | break; |
355 | } |
356 | uint64_t BytesWritten = OS.tell() - DataStart; |
357 | RollingOffset += BytesWritten; |
358 | if (BytesWritten < PartSize) |
359 | OS.write_zeros(NumZeros: PartSize - BytesWritten); |
360 | RollingOffset += PartSize; |
361 | } |
362 | } |
363 | |
364 | Error DXContainerWriter::write(raw_ostream &OS) { |
365 | if (Error Err = computePartOffsets()) |
366 | return Err; |
367 | writeHeader(OS); |
368 | writeParts(OS); |
369 | return Error::success(); |
370 | } |
371 | |
372 | namespace llvm { |
373 | namespace yaml { |
374 | |
375 | bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out, |
376 | ErrorHandler EH) { |
377 | DXContainerWriter Writer(Doc); |
378 | if (Error Err = Writer.write(OS&: Out)) { |
379 | handleAllErrors(E: std::move(Err), |
380 | Handlers: [&](const ErrorInfoBase &Err) { EH(Err.message()); }); |
381 | return false; |
382 | } |
383 | return true; |
384 | } |
385 | |
386 | } // namespace yaml |
387 | } // namespace llvm |
388 | |