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