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 for (auto El : P.Info->SigInputElements) 205 PSV.InputElements.push_back(mcdxbc::PSVSignatureElement{ 206 El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol, 207 El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask, 208 El.Stream}); 209 210 for (auto El : P.Info->SigOutputElements) 211 PSV.OutputElements.push_back(mcdxbc::PSVSignatureElement{ 212 El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol, 213 El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask, 214 El.Stream}); 215 216 for (auto El : P.Info->SigPatchOrPrimElements) 217 PSV.PatchOrPrimElements.push_back(mcdxbc::PSVSignatureElement{ 218 El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol, 219 El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask, 220 El.Stream}); 221 222 static_assert(PSV.OutputVectorMasks.size() == PSV.InputOutputMap.size()); 223 for (unsigned I = 0; I < PSV.OutputVectorMasks.size(); ++I) { 224 PSV.OutputVectorMasks[I].insert(PSV.OutputVectorMasks[I].begin(), 225 P.Info->OutputVectorMasks[I].begin(), 226 P.Info->OutputVectorMasks[I].end()); 227 PSV.InputOutputMap[I].insert(PSV.InputOutputMap[I].begin(), 228 P.Info->InputOutputMap[I].begin(), 229 P.Info->InputOutputMap[I].end()); 230 } 231 232 PSV.PatchOrPrimMasks.insert(PSV.PatchOrPrimMasks.begin(), 233 P.Info->PatchOrPrimMasks.begin(), 234 P.Info->PatchOrPrimMasks.end()); 235 PSV.InputPatchMap.insert(PSV.InputPatchMap.begin(), 236 P.Info->InputPatchMap.begin(), 237 P.Info->InputPatchMap.end()); 238 PSV.PatchOutputMap.insert(PSV.PatchOutputMap.begin(), 239 P.Info->PatchOutputMap.begin(), 240 P.Info->PatchOutputMap.end()); 241 242 PSV.finalize(static_cast<Triple::EnvironmentType>( 243 Triple::Pixel + P.Info->Info.ShaderStage)); 244 PSV.write(OS, P.Info->Version); 245 break; 246 } 247 case dxbc::PartType::ISG1: 248 case dxbc::PartType::OSG1: 249 case dxbc::PartType::PSG1: { 250 mcdxbc::Signature Sig; 251 if (P.Signature.has_value()) { 252 for (const auto &Param : P.Signature->Parameters) { 253 Sig.addParam(Param.Stream, Param.Name, Param.Index, Param.SystemValue, 254 Param.CompType, Param.Register, Param.Mask, 255 Param.ExclusiveMask, Param.MinPrecision); 256 } 257 } 258 Sig.write(OS); 259 break; 260 } 261 case dxbc::PartType::Unknown: 262 break; // Skip any handling for unrecognized parts. 263 } 264 uint64_t BytesWritten = OS.tell() - DataStart; 265 RollingOffset += BytesWritten; 266 if (BytesWritten < PartSize) 267 OS.write_zeros(PartSize - BytesWritten); 268 RollingOffset += PartSize; 269 } 270 } 271 272 Error DXContainerWriter::write(raw_ostream &OS) { 273 if (Error Err = computePartOffsets()) 274 return Err; 275 writeHeader(OS); 276 writeParts(OS); 277 return Error::success(); 278 } 279 280 namespace llvm { 281 namespace yaml { 282 283 bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out, 284 ErrorHandler EH) { 285 DXContainerWriter Writer(Doc); 286 if (Error Err = Writer.write(Out)) { 287 handleAllErrors(std::move(Err), 288 [&](const ErrorInfoBase &Err) { EH(Err.message()); }); 289 return false; 290 } 291 return true; 292 } 293 294 } // namespace yaml 295 } // namespace llvm 296