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 for (auto &O : *ObjectFile.Header.PartOffsets) 100 if (sys::IsBigEndianHost) 101 sys::swapByteOrder(O); 102 OS.write(reinterpret_cast<char *>(ObjectFile.Header.PartOffsets->data()), 103 ObjectFile.Header.PartOffsets->size() * sizeof(uint32_t)); 104 } 105 void DXContainerWriter::writeParts(raw_ostream &OS) { 106 uint32_t RollingOffset = 107 sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t)); 108 for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) { 109 if (RollingOffset < std::get<1>(I)) { 110 uint32_t PadBytes = std::get<1>(I) - RollingOffset; 111 std::vector<uint8_t> FillData(PadBytes, 0); 112 OS.write(reinterpret_cast<char *>(FillData.data()), PadBytes); 113 } 114 DXContainerYAML::Part P = std::get<0>(I); 115 OS.write(P.Name.c_str(), 4); 116 if (sys::IsBigEndianHost) 117 sys::swapByteOrder(P.Size); 118 OS.write(reinterpret_cast<const char *>(&P.Size), sizeof(uint32_t)); 119 RollingOffset = std::get<1>(I) + sizeof(dxbc::PartHeader); 120 121 // TODO: Write Part data 122 } 123 } 124 125 Error DXContainerWriter::write(raw_ostream &OS) { 126 if (Error Err = computePartOffsets()) 127 return Err; 128 writeHeader(OS); 129 writeParts(OS); 130 return Error::success(); 131 } 132 133 namespace llvm { 134 namespace yaml { 135 136 bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out, 137 ErrorHandler EH) { 138 DXContainerWriter Writer(Doc); 139 if (Error Err = Writer.write(Out)) { 140 handleAllErrors(std::move(Err), 141 [&](const ErrorInfoBase &Err) { EH(Err.message()); }); 142 return false; 143 } 144 return true; 145 } 146 147 } // namespace yaml 148 } // namespace llvm 149