DXContainerEmitter.cpp 7.6 KB

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