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 OS.write(P.Name.c_str(), 4); 118 if (sys::IsBigEndianHost) 119 sys::swapByteOrder(P.Size); 120 OS.write(reinterpret_cast<const char *>(&P.Size), sizeof(uint32_t)); 121 RollingOffset = std::get<1>(I) + sizeof(dxbc::PartHeader); 122 123 if (P.Name == "DXIL" && P.Program) { 124 dxbc::ProgramHeader Header; 125 Header.MajorVersion = P.Program->MajorVersion; 126 Header.MinorVersion = P.Program->MinorVersion; 127 Header.ShaderKind = P.Program->ShaderKind; 128 memcpy(Header.Bitcode.Magic, "DXIL", 4); 129 Header.Bitcode.MajorVersion = P.Program->DXILMajorVersion; 130 Header.Bitcode.MinorVersion = P.Program->DXILMinorVersion; 131 132 // Compute the optional fields if needed... 133 if (P.Program->DXILOffset) 134 Header.Bitcode.Offset = P.Program->DXILOffset.getValue(); 135 else 136 Header.Bitcode.Offset = sizeof(dxbc::BitcodeHeader); 137 138 if (P.Program->DXILSize) 139 Header.Bitcode.Size = P.Program->DXILSize.getValue(); 140 else 141 Header.Bitcode.Size = P.Program->DXIL ? P.Program->DXIL->size() : 0; 142 143 if (P.Program->Size) 144 Header.Size = P.Program->Size.getValue(); 145 else 146 Header.Size = sizeof(dxbc::ProgramHeader) + Header.Bitcode.Size; 147 148 uint32_t BitcodeOffset = Header.Bitcode.Offset; 149 if (sys::IsBigEndianHost) 150 Header.swapBytes(); 151 OS.write(reinterpret_cast<const char *>(&Header), 152 sizeof(dxbc::ProgramHeader)); 153 if (P.Program->DXIL) { 154 if (BitcodeOffset > sizeof(dxbc::BitcodeHeader)) { 155 uint32_t PadBytes = BitcodeOffset - sizeof(dxbc::BitcodeHeader); 156 OS.write_zeros(PadBytes); 157 } 158 OS.write(reinterpret_cast<char *>(P.Program->DXIL->data()), 159 P.Program->DXIL->size()); 160 } 161 } 162 } 163 } 164 165 Error DXContainerWriter::write(raw_ostream &OS) { 166 if (Error Err = computePartOffsets()) 167 return Err; 168 writeHeader(OS); 169 writeParts(OS); 170 return Error::success(); 171 } 172 173 namespace llvm { 174 namespace yaml { 175 176 bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out, 177 ErrorHandler EH) { 178 DXContainerWriter Writer(Doc); 179 if (Error Err = Writer.write(Out)) { 180 handleAllErrors(std::move(Err), 181 [&](const ErrorInfoBase &Err) { EH(Err.message()); }); 182 return false; 183 } 184 return true; 185 } 186 187 } // namespace yaml 188 } // namespace llvm 189