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
23using namespace llvm;
24
25namespace {
26class DXContainerWriter {
27public:
28 DXContainerWriter(DXContainerYAML::Object &ObjectFile)
29 : ObjectFile(ObjectFile) {}
30
31 Error write(raw_ostream &OS);
32
33private:
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
45Error 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
54Error 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
74Error 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
90void DXContainerWriter::writeHeader(raw_ostream &OS) {
91 dxbc::Header 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
110void 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 Header;
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 Header{.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
364Error 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
372namespace llvm {
373namespace yaml {
374
375bool 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