| //===- DXContainerEmitter.cpp - Convert YAML to a DXContainer -------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// Binary emitter for yaml to DXContainer binary | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/BinaryFormat/DXContainer.h" | |
| #include "llvm/ObjectYAML/ObjectYAML.h" | |
| #include "llvm/ObjectYAML/yaml2obj.h" | |
| #include "llvm/Support/Errc.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| using namespace llvm; | |
| namespace { | |
| class DXContainerWriter { | |
| public: | |
| DXContainerWriter(DXContainerYAML::Object &ObjectFile) | |
| : ObjectFile(ObjectFile) {} | |
| Error write(raw_ostream &OS); | |
| private: | |
| DXContainerYAML::Object &ObjectFile; | |
| Error computePartOffsets(); | |
| Error validatePartOffsets(); | |
| Error validateSize(uint32_t Computed); | |
| void writeHeader(raw_ostream &OS); | |
| void writeParts(raw_ostream &OS); | |
| }; | |
| } // namespace | |
| Error DXContainerWriter::validateSize(uint32_t Computed) { | |
| if (!ObjectFile.Header.FileSize) | |
| ObjectFile.Header.FileSize = Computed; | |
| else if (*ObjectFile.Header.FileSize < Computed) | |
| return createStringError(errc::result_out_of_range, | |
| "File size specified is too small."); | |
| return Error::success(); | |
| } | |
| Error DXContainerWriter::validatePartOffsets() { | |
| if (ObjectFile.Parts.size() != ObjectFile.Header.PartOffsets->size()) | |
| return createStringError( | |
| errc::invalid_argument, | |
| "Mismatch between number of parts and part offsets."); | |
| uint32_t RollingOffset = | |
| sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t)); | |
| for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) { | |
| if (RollingOffset > std::get<1>(I)) | |
| return createStringError(errc::invalid_argument, | |
| "Offset mismatch, not enough space for data."); | |
| RollingOffset = | |
| std::get<1>(I) + sizeof(dxbc::PartHeader) + std::get<0>(I).Size; | |
| } | |
| if (Error Err = validateSize(RollingOffset)) | |
| return Err; | |
| return Error::success(); | |
| } | |
| Error DXContainerWriter::computePartOffsets() { | |
| if (ObjectFile.Header.PartOffsets) | |
| return validatePartOffsets(); | |
| uint32_t RollingOffset = | |
| sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t)); | |
| ObjectFile.Header.PartOffsets = std::vector<uint32_t>(); | |
| for (const auto &Part : ObjectFile.Parts) { | |
| ObjectFile.Header.PartOffsets->push_back(RollingOffset); | |
| RollingOffset += sizeof(dxbc::PartHeader) + Part.Size; | |
| } | |
| if (Error Err = validateSize(RollingOffset)) | |
| return Err; | |
| return Error::success(); | |
| } | |
| void DXContainerWriter::writeHeader(raw_ostream &OS) { | |
| dxbc::Header Header; | |
| memcpy(Header.Magic, "DXBC", 4); | |
| memcpy(Header.FileHash.Digest, ObjectFile.Header.Hash.data(), 16); | |
| Header.Version.Major = ObjectFile.Header.Version.Major; | |
| Header.Version.Minor = ObjectFile.Header.Version.Minor; | |
| Header.FileSize = *ObjectFile.Header.FileSize; | |
| Header.PartCount = ObjectFile.Parts.size(); | |
| if (sys::IsBigEndianHost) | |
| Header.swapBytes(); | |
| OS.write(reinterpret_cast<char *>(&Header), sizeof(Header)); | |
| SmallVector<uint32_t> Offsets(ObjectFile.Header.PartOffsets->begin(), | |
| ObjectFile.Header.PartOffsets->end()); | |
| if (sys::IsBigEndianHost) | |
| for (auto &O : Offsets) | |
| sys::swapByteOrder(O); | |
| OS.write(reinterpret_cast<char *>(Offsets.data()), | |
| Offsets.size() * sizeof(uint32_t)); | |
| } | |
| void DXContainerWriter::writeParts(raw_ostream &OS) { | |
| uint32_t RollingOffset = | |
| sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t)); | |
| for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) { | |
| if (RollingOffset < std::get<1>(I)) { | |
| uint32_t PadBytes = std::get<1>(I) - RollingOffset; | |
| OS.write_zeros(PadBytes); | |
| } | |
| DXContainerYAML::Part P = std::get<0>(I); | |
| RollingOffset = std::get<1>(I) + sizeof(dxbc::PartHeader); | |
| uint32_t PartSize = P.Size; | |
| OS.write(P.Name.c_str(), 4); | |
| if (sys::IsBigEndianHost) | |
| sys::swapByteOrder(P.Size); | |
| OS.write(reinterpret_cast<const char *>(&P.Size), sizeof(uint32_t)); | |
| dxbc::PartType PT = dxbc::parsePartType(P.Name); | |
| uint64_t DataStart = OS.tell(); | |
| switch (PT) { | |
| case dxbc::PartType::DXIL: { | |
| if (!P.Program) | |
| continue; | |
| dxbc::ProgramHeader Header; | |
| Header.MajorVersion = P.Program->MajorVersion; | |
| Header.MinorVersion = P.Program->MinorVersion; | |
| Header.Unused = 0; | |
| Header.ShaderKind = P.Program->ShaderKind; | |
| memcpy(Header.Bitcode.Magic, "DXIL", 4); | |
| Header.Bitcode.MajorVersion = P.Program->DXILMajorVersion; | |
| Header.Bitcode.MinorVersion = P.Program->DXILMinorVersion; | |
| Header.Bitcode.Unused = 0; | |
| // Compute the optional fields if needed... | |
| if (P.Program->DXILOffset) | |
| Header.Bitcode.Offset = *P.Program->DXILOffset; | |
| else | |
| Header.Bitcode.Offset = sizeof(dxbc::BitcodeHeader); | |
| if (P.Program->DXILSize) | |
| Header.Bitcode.Size = *P.Program->DXILSize; | |
| else | |
| Header.Bitcode.Size = P.Program->DXIL ? P.Program->DXIL->size() : 0; | |
| if (P.Program->Size) | |
| Header.Size = *P.Program->Size; | |
| else | |
| Header.Size = sizeof(dxbc::ProgramHeader) + Header.Bitcode.Size; | |
| uint32_t BitcodeOffset = Header.Bitcode.Offset; | |
| if (sys::IsBigEndianHost) | |
| Header.swapBytes(); | |
| OS.write(reinterpret_cast<const char *>(&Header), | |
| sizeof(dxbc::ProgramHeader)); | |
| if (P.Program->DXIL) { | |
| if (BitcodeOffset > sizeof(dxbc::BitcodeHeader)) { | |
| uint32_t PadBytes = BitcodeOffset - sizeof(dxbc::BitcodeHeader); | |
| OS.write_zeros(PadBytes); | |
| } | |
| OS.write(reinterpret_cast<char *>(P.Program->DXIL->data()), | |
| P.Program->DXIL->size()); | |
| } | |
| break; | |
| } | |
| case dxbc::PartType::SFI0: { | |
| // If we don't have any flags we can continue here and the data will be | |
| // zeroed out. | |
| if (!P.Flags.has_value()) | |
| continue; | |
| uint64_t Flags = P.Flags->getEncodedFlags(); | |
| if (sys::IsBigEndianHost) | |
| sys::swapByteOrder(Flags); | |
| OS.write(reinterpret_cast<char *>(&Flags), sizeof(uint64_t)); | |
| break; | |
| } | |
| case dxbc::PartType::HASH: { | |
| if (!P.Hash.has_value()) | |
| continue; | |
| dxbc::ShaderHash Hash = {0, {0}}; | |
| if (P.Hash->IncludesSource) | |
| Hash.Flags |= static_cast<uint32_t>(dxbc::HashFlags::IncludesSource); | |
| memcpy(&Hash.Digest[0], &P.Hash->Digest[0], 16); | |
| if (sys::IsBigEndianHost) | |
| Hash.swapBytes(); | |
| OS.write(reinterpret_cast<char *>(&Hash), sizeof(dxbc::ShaderHash)); | |
| break; | |
| } | |
| case dxbc::PartType::Unknown: | |
| break; // Skip any handling for unrecognized parts. | |
| } | |
| uint64_t BytesWritten = OS.tell() - DataStart; | |
| RollingOffset += BytesWritten; | |
| if (BytesWritten < PartSize) | |
| OS.write_zeros(PartSize - BytesWritten); | |
| RollingOffset += PartSize; | |
| } | |
| } | |
| Error DXContainerWriter::write(raw_ostream &OS) { | |
| if (Error Err = computePartOffsets()) | |
| return Err; | |
| writeHeader(OS); | |
| writeParts(OS); | |
| return Error::success(); | |
| } | |
| namespace llvm { | |
| namespace yaml { | |
| bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out, | |
| ErrorHandler EH) { | |
| DXContainerWriter Writer(Doc); | |
| if (Error Err = Writer.write(Out)) { | |
| handleAllErrors(std::move(Err), | |
| [&](const ErrorInfoBase &Err) { EH(Err.message()); }); | |
| return false; | |
| } | |
| return true; | |
| } | |
| } // namespace yaml | |
| } // namespace llvm |