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