xref: /freebsd/contrib/llvm-project/llvm/lib/ObjectYAML/DXContainerEmitter.cpp (revision c989957f28ef5b03f594265612e3437c1e826ed4)
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/ObjectYAML/ObjectYAML.h"
16  #include "llvm/ObjectYAML/yaml2obj.h"
17  #include "llvm/Support/Errc.h"
18  #include "llvm/Support/Error.h"
19  #include "llvm/Support/raw_ostream.h"
20  
21  using namespace llvm;
22  
23  namespace {
24  class DXContainerWriter {
25  public:
26    DXContainerWriter(DXContainerYAML::Object &ObjectFile)
27        : ObjectFile(ObjectFile) {}
28  
29    Error write(raw_ostream &OS);
30  
31  private:
32    DXContainerYAML::Object &ObjectFile;
33  
34    Error computePartOffsets();
35    Error validatePartOffsets();
36    Error validateSize(uint32_t Computed);
37  
38    void writeHeader(raw_ostream &OS);
39    void writeParts(raw_ostream &OS);
40  };
41  } // namespace
42  
43  Error DXContainerWriter::validateSize(uint32_t Computed) {
44    if (!ObjectFile.Header.FileSize)
45      ObjectFile.Header.FileSize = Computed;
46    else if (*ObjectFile.Header.FileSize < Computed)
47      return createStringError(errc::result_out_of_range,
48                               "File size specified is too small.");
49    return Error::success();
50  }
51  
52  Error DXContainerWriter::validatePartOffsets() {
53    if (ObjectFile.Parts.size() != ObjectFile.Header.PartOffsets->size())
54      return createStringError(
55          errc::invalid_argument,
56          "Mismatch between number of parts and part offsets.");
57    uint32_t RollingOffset =
58        sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
59    for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {
60      if (RollingOffset > std::get<1>(I))
61        return createStringError(errc::invalid_argument,
62                                 "Offset mismatch, not enough space for data.");
63      RollingOffset =
64          std::get<1>(I) + sizeof(dxbc::PartHeader) + std::get<0>(I).Size;
65    }
66    if (Error Err = validateSize(RollingOffset))
67      return Err;
68  
69    return Error::success();
70  }
71  
72  Error DXContainerWriter::computePartOffsets() {
73    if (ObjectFile.Header.PartOffsets)
74      return validatePartOffsets();
75    uint32_t RollingOffset =
76        sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
77    ObjectFile.Header.PartOffsets = std::vector<uint32_t>();
78    for (const auto &Part : ObjectFile.Parts) {
79      ObjectFile.Header.PartOffsets->push_back(RollingOffset);
80      RollingOffset += sizeof(dxbc::PartHeader) + Part.Size;
81    }
82    if (Error Err = validateSize(RollingOffset))
83      return Err;
84  
85    return Error::success();
86  }
87  
88  void DXContainerWriter::writeHeader(raw_ostream &OS) {
89    dxbc::Header Header;
90    memcpy(Header.Magic, "DXBC", 4);
91    memcpy(Header.FileHash.Digest, ObjectFile.Header.Hash.data(), 16);
92    Header.Version.Major = ObjectFile.Header.Version.Major;
93    Header.Version.Minor = ObjectFile.Header.Version.Minor;
94    Header.FileSize = *ObjectFile.Header.FileSize;
95    Header.PartCount = ObjectFile.Parts.size();
96    if (sys::IsBigEndianHost)
97      Header.swapBytes();
98    OS.write(reinterpret_cast<char *>(&Header), sizeof(Header));
99    SmallVector<uint32_t> Offsets(ObjectFile.Header.PartOffsets->begin(),
100                                  ObjectFile.Header.PartOffsets->end());
101    if (sys::IsBigEndianHost)
102      for (auto &O : Offsets)
103        sys::swapByteOrder(O);
104    OS.write(reinterpret_cast<char *>(Offsets.data()),
105             Offsets.size() * sizeof(uint32_t));
106  }
107  
108  void DXContainerWriter::writeParts(raw_ostream &OS) {
109    uint32_t RollingOffset =
110        sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
111    for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {
112      if (RollingOffset < std::get<1>(I)) {
113        uint32_t PadBytes = std::get<1>(I) - RollingOffset;
114        OS.write_zeros(PadBytes);
115      }
116      DXContainerYAML::Part P = std::get<0>(I);
117      RollingOffset = std::get<1>(I) + sizeof(dxbc::PartHeader);
118      uint32_t PartSize = P.Size;
119  
120      OS.write(P.Name.c_str(), 4);
121      if (sys::IsBigEndianHost)
122        sys::swapByteOrder(P.Size);
123      OS.write(reinterpret_cast<const char *>(&P.Size), sizeof(uint32_t));
124  
125      dxbc::PartType PT = dxbc::parsePartType(P.Name);
126  
127      uint64_t DataStart = OS.tell();
128      switch (PT) {
129      case dxbc::PartType::DXIL: {
130        if (!P.Program)
131          continue;
132        dxbc::ProgramHeader Header;
133        Header.MajorVersion = P.Program->MajorVersion;
134        Header.MinorVersion = P.Program->MinorVersion;
135        Header.Unused = 0;
136        Header.ShaderKind = P.Program->ShaderKind;
137        memcpy(Header.Bitcode.Magic, "DXIL", 4);
138        Header.Bitcode.MajorVersion = P.Program->DXILMajorVersion;
139        Header.Bitcode.MinorVersion = P.Program->DXILMinorVersion;
140        Header.Bitcode.Unused = 0;
141  
142        // Compute the optional fields if needed...
143        if (P.Program->DXILOffset)
144          Header.Bitcode.Offset = *P.Program->DXILOffset;
145        else
146          Header.Bitcode.Offset = sizeof(dxbc::BitcodeHeader);
147  
148        if (P.Program->DXILSize)
149          Header.Bitcode.Size = *P.Program->DXILSize;
150        else
151          Header.Bitcode.Size = P.Program->DXIL ? P.Program->DXIL->size() : 0;
152  
153        if (P.Program->Size)
154          Header.Size = *P.Program->Size;
155        else
156          Header.Size = sizeof(dxbc::ProgramHeader) + Header.Bitcode.Size;
157  
158        uint32_t BitcodeOffset = Header.Bitcode.Offset;
159        if (sys::IsBigEndianHost)
160          Header.swapBytes();
161        OS.write(reinterpret_cast<const char *>(&Header),
162                 sizeof(dxbc::ProgramHeader));
163        if (P.Program->DXIL) {
164          if (BitcodeOffset > sizeof(dxbc::BitcodeHeader)) {
165            uint32_t PadBytes = BitcodeOffset - sizeof(dxbc::BitcodeHeader);
166            OS.write_zeros(PadBytes);
167          }
168          OS.write(reinterpret_cast<char *>(P.Program->DXIL->data()),
169                   P.Program->DXIL->size());
170        }
171        break;
172      }
173      case dxbc::PartType::SFI0: {
174        // If we don't have any flags we can continue here and the data will be
175        // zeroed out.
176        if (!P.Flags.has_value())
177          continue;
178        uint64_t Flags = P.Flags->getEncodedFlags();
179        if (sys::IsBigEndianHost)
180          sys::swapByteOrder(Flags);
181        OS.write(reinterpret_cast<char *>(&Flags), sizeof(uint64_t));
182        break;
183      }
184      case dxbc::PartType::HASH: {
185        if (!P.Hash.has_value())
186          continue;
187        dxbc::ShaderHash Hash = {0, {0}};
188        if (P.Hash->IncludesSource)
189          Hash.Flags |= static_cast<uint32_t>(dxbc::HashFlags::IncludesSource);
190        memcpy(&Hash.Digest[0], &P.Hash->Digest[0], 16);
191        if (sys::IsBigEndianHost)
192          Hash.swapBytes();
193        OS.write(reinterpret_cast<char *>(&Hash), sizeof(dxbc::ShaderHash));
194        break;
195      }
196      case dxbc::PartType::Unknown:
197        break; // Skip any handling for unrecognized parts.
198      }
199      uint64_t BytesWritten = OS.tell() - DataStart;
200      RollingOffset += BytesWritten;
201      if (BytesWritten < PartSize)
202        OS.write_zeros(PartSize - BytesWritten);
203      RollingOffset += PartSize;
204    }
205  }
206  
207  Error DXContainerWriter::write(raw_ostream &OS) {
208    if (Error Err = computePartOffsets())
209      return Err;
210    writeHeader(OS);
211    writeParts(OS);
212    return Error::success();
213  }
214  
215  namespace llvm {
216  namespace yaml {
217  
218  bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out,
219                        ErrorHandler EH) {
220    DXContainerWriter Writer(Doc);
221    if (Error Err = Writer.write(Out)) {
222      handleAllErrors(std::move(Err),
223                      [&](const ErrorInfoBase &Err) { EH(Err.message()); });
224      return false;
225    }
226    return true;
227  }
228  
229  } // namespace yaml
230  } // namespace llvm
231