xref: /freebsd/contrib/llvm-project/llvm/lib/ObjectYAML/DXContainerEmitter.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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.Version = dxbc::ProgramHeader::getVersion(P.Program->MajorVersion,
135                                                         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::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(mcdxbc::PSVSignatureElement{
207              El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol,
208              El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask,
209              El.Stream});
210  
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,
215              El.Stream});
216  
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,
221              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(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());
231        }
232  
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());
242  
243        PSV.finalize(static_cast<Triple::EnvironmentType>(
244            Triple::Pixel + P.Info->Info.ShaderStage));
245        PSV.write(OS, 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(Param.Stream, Param.Name, Param.Index, Param.SystemValue,
255                         Param.CompType, Param.Register, Param.Mask,
256                         Param.ExclusiveMask, 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(PartSize - BytesWritten);
269      RollingOffset += PartSize;
270    }
271  }
272  
write(raw_ostream & OS)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  
yaml2dxcontainer(DXContainerYAML::Object & Doc,raw_ostream & Out,ErrorHandler EH)284  bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out,
285                        ErrorHandler EH) {
286    DXContainerWriter Writer(Doc);
287    if (Error Err = Writer.write(Out)) {
288      handleAllErrors(std::move(Err),
289                      [&](const ErrorInfoBase &Err) { EH(Err.message()); });
290      return false;
291    }
292    return true;
293  }
294  
295  } // namespace yaml
296  } // namespace llvm
297