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:
DXContainerWriter(DXContainerYAML::Object & ObjectFile)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 
validateSize(uint32_t Computed)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();
51 }
52 
validatePartOffsets()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.");
64     RollingOffset =
65         std::get<1>(I) + sizeof(dxbc::PartHeader) + std::get<0>(I).Size;
66   }
67   if (Error Err = validateSize(RollingOffset))
68     return Err;
69 
70   return Error::success();
71 }
72 
computePartOffsets()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;
82   }
83   if (Error Err = validateSize(RollingOffset))
84     return Err;
85 
86   return Error::success();
87 }
88 
writeHeader(raw_ostream & OS)89 void DXContainerWriter::writeHeader(raw_ostream &OS) {
90   dxbc::Header Header;
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)
98     Header.swapBytes();
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));
107 }
108 
writeParts(raw_ostream & OS)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);
116     }
117     DXContainerYAML::Part P = std::get<0>(I);
118     RollingOffset = std::get<1>(I) + sizeof(dxbc::PartHeader);
119     uint32_t PartSize = P.Size;
120 
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));
125 
126     dxbc::PartType PT = dxbc::parsePartType(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 Header;
134       Header.MajorVersion = P.Program->MajorVersion;
135       Header.MinorVersion = P.Program->MinorVersion;
136       Header.Unused = 0;
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;
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(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);
168         }
169         OS.write(reinterpret_cast<char *>(P.Program->DXIL->data()),
170                  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(Flags);
182       OS.write(reinterpret_cast<char *>(&Flags), sizeof(uint64_t));
183       break;
184     }
185     case dxbc::PartType::HASH: {
186       if (!P.Hash.has_value())
187         continue;
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)
193         Hash.swapBytes();
194       OS.write(reinterpret_cast<char *>(&Hash), 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(&PSV.BaseData, &P.Info->Info, sizeof(dxbc::PSV::v2::RuntimeInfo));
202       PSV.Resources = P.Info->Resources;
203 
204       for (auto El : P.Info->SigInputElements)
205         PSV.InputElements.push_back(mcdxbc::PSVSignatureElement{
206             El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol,
207             El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask,
208             El.Stream});
209 
210       for (auto El : P.Info->SigOutputElements)
211         PSV.OutputElements.push_back(mcdxbc::PSVSignatureElement{
212             El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol,
213             El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask,
214             El.Stream});
215 
216       for (auto El : P.Info->SigPatchOrPrimElements)
217         PSV.PatchOrPrimElements.push_back(mcdxbc::PSVSignatureElement{
218             El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol,
219             El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask,
220             El.Stream});
221 
222       static_assert(PSV.OutputVectorMasks.size() == PSV.InputOutputMap.size());
223       for (unsigned I = 0; I < PSV.OutputVectorMasks.size(); ++I) {
224         PSV.OutputVectorMasks[I].insert(PSV.OutputVectorMasks[I].begin(),
225                                         P.Info->OutputVectorMasks[I].begin(),
226                                         P.Info->OutputVectorMasks[I].end());
227         PSV.InputOutputMap[I].insert(PSV.InputOutputMap[I].begin(),
228                                      P.Info->InputOutputMap[I].begin(),
229                                      P.Info->InputOutputMap[I].end());
230       }
231 
232       PSV.PatchOrPrimMasks.insert(PSV.PatchOrPrimMasks.begin(),
233                                   P.Info->PatchOrPrimMasks.begin(),
234                                   P.Info->PatchOrPrimMasks.end());
235       PSV.InputPatchMap.insert(PSV.InputPatchMap.begin(),
236                                P.Info->InputPatchMap.begin(),
237                                P.Info->InputPatchMap.end());
238       PSV.PatchOutputMap.insert(PSV.PatchOutputMap.begin(),
239                                 P.Info->PatchOutputMap.begin(),
240                                 P.Info->PatchOutputMap.end());
241 
242       PSV.finalize(static_cast<Triple::EnvironmentType>(
243           Triple::Pixel + P.Info->Info.ShaderStage));
244       PSV.write(OS, P.Info->Version);
245       break;
246     }
247     case dxbc::PartType::ISG1:
248     case dxbc::PartType::OSG1:
249     case dxbc::PartType::PSG1: {
250       mcdxbc::Signature Sig;
251       if (P.Signature.has_value()) {
252         for (const auto &Param : P.Signature->Parameters) {
253           Sig.addParam(Param.Stream, Param.Name, Param.Index, Param.SystemValue,
254                        Param.CompType, Param.Register, Param.Mask,
255                        Param.ExclusiveMask, Param.MinPrecision);
256         }
257       }
258       Sig.write(OS);
259       break;
260     }
261     case dxbc::PartType::Unknown:
262       break; // Skip any handling for unrecognized parts.
263     }
264     uint64_t BytesWritten = OS.tell() - DataStart;
265     RollingOffset += BytesWritten;
266     if (BytesWritten < PartSize)
267       OS.write_zeros(PartSize - BytesWritten);
268     RollingOffset += PartSize;
269   }
270 }
271 
write(raw_ostream & OS)272 Error DXContainerWriter::write(raw_ostream &OS) {
273   if (Error Err = computePartOffsets())
274     return Err;
275   writeHeader(OS);
276   writeParts(OS);
277   return Error::success();
278 }
279 
280 namespace llvm {
281 namespace yaml {
282 
yaml2dxcontainer(DXContainerYAML::Object & Doc,raw_ostream & Out,ErrorHandler EH)283 bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out,
284                       ErrorHandler EH) {
285   DXContainerWriter Writer(Doc);
286   if (Error Err = Writer.write(Out)) {
287     handleAllErrors(std::move(Err),
288                     [&](const ErrorInfoBase &Err) { EH(Err.message()); });
289     return false;
290   }
291   return true;
292 }
293 
294 } // namespace yaml
295 } // namespace llvm
296