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