| //===- llvm/MC/DXContainerInfo.cpp - DXContainer Info -----*- C++ -------*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/MC/DXContainerInfo.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/BinaryFormat/DXContainer.h" |
| #include "llvm/Config/config.h" |
| #include "llvm/Support/Compression.h" |
| #include "llvm/Support/EndianStream.h" |
| #include "llvm/Support/FormatVariadic.h" |
| #include "llvm/Support/SwapByteOrder.h" |
| #include "llvm/Support/VCSRevision.h" |
| #include <type_traits> |
| |
| using namespace llvm; |
| using namespace llvm::mcdxbc; |
| |
| static uint64_t align(uint64_t Size) { |
| return alignTo(Size, dxbc::DXCONTAINER_STRUCT_ALIGNMENT); |
| } |
| |
| template <typename StructT> |
| static void writeStruct(raw_ostream &OS, StructT S) { |
| static_assert(std::is_class<StructT>() && |
| "This method must be used for writing structure types"); |
| if (sys::IsBigEndianHost) |
| S.swapBytes(); |
| OS.write(reinterpret_cast<const char *>(&S), sizeof(StructT)); |
| } |
| |
| static void writeString(raw_ostream &OS, StringRef S) { |
| OS.write(S.data(), S.size()); |
| // Write null terminator. |
| OS.write_zeros(1); |
| } |
| |
| static void writePadding(raw_ostream &OS, uint64_t Prev) { |
| uint64_t UnpaddedSize = OS.tell() - Prev; |
| uint64_t Padding = align(UnpaddedSize) - UnpaddedSize; |
| if (Padding) |
| OS.write_zeros(Padding); |
| } |
| |
| void DebugName::setFilename(StringRef DebugFilename) { |
| Parameters.NameLength = DebugFilename.size(); |
| Filename = DebugFilename; |
| } |
| |
| void DebugName::write(raw_ostream &OS) const { |
| writeStruct(OS, Parameters); |
| writeString(OS, Filename.substr(0, Parameters.NameLength)); |
| } |
| |
| CompilerVersion::CompilerVersion() { |
| Parameters.Major = LLVM_VERSION_MAJOR; |
| Parameters.Minor = LLVM_VERSION_MINOR; |
| Parameters.Flags = dxbc::CompilerVersionFlags::Default; |
| #ifndef NDEBUG |
| Parameters.Flags |= dxbc::CompilerVersionFlags::Debug; |
| #endif |
| Parameters.CommitCount = 0; |
| Parameters.ContentSizeInBytes = 0; |
| #ifdef LLVM_REVISION |
| CommitSha = LLVM_REVISION; |
| #else |
| CommitSha = ""; |
| #endif |
| CustomVersionString = PACKAGE_VERSION; |
| updateContentSize(); |
| } |
| |
| void CompilerVersion::setCommitSha(StringRef CommitSha) { |
| this->CommitSha = CommitSha; |
| updateContentSize(); |
| } |
| |
| void CompilerVersion::setVersionString(StringRef VersionString) { |
| this->CustomVersionString = VersionString; |
| updateContentSize(); |
| } |
| |
| void CompilerVersion::updateContentSize() { |
| this->Parameters.ContentSizeInBytes = |
| CommitSha.size() + 1 + CustomVersionString.size() + 1; |
| } |
| |
| void CompilerVersion::write(raw_ostream &OS) const { |
| writeStruct(OS, Parameters); |
| SmallString<64> Content; |
| raw_svector_ostream ContentStream(Content); |
| writeString(ContentStream, CommitSha); |
| writeString(ContentStream, CustomVersionString); |
| Content.resize(Parameters.ContentSizeInBytes); |
| OS.write(Content.data(), Parameters.ContentSizeInBytes); |
| } |
| |
| void SourceInfo::Section::computeGenericHeader( |
| uint32_t ContentSize, dxbc::SourceInfo::SectionType SecType) { |
| GenericHeader.AlignedSizeInBytes = align(sizeof(GenericHeader) + ContentSize); |
| GenericHeader.Flags = 0; |
| GenericHeader.Type = SecType; |
| } |
| |
| void SourceInfo::SourceContents::Entry::compute() { |
| Parameters.Flags = 0; |
| Parameters.ContentSizeInBytes = FileContent.size() + 1; |
| Parameters.AlignedSizeInBytes = |
| align(Parameters.ContentSizeInBytes + |
| sizeof(dxbc::SourceInfo::Contents::Entry)); |
| } |
| |
| void SourceInfo::SourceContents::computeUncompressed( |
| dxbc::SourceInfo::Contents::CompressionType CompType) { |
| size_t ContentEntriesSize = 0; |
| for (const SourceContents::Entry &ContentEntry : Entries) |
| ContentEntriesSize += ContentEntry.Parameters.AlignedSizeInBytes; |
| |
| Parameters.Flags = 0; |
| Parameters.Type = CompType; |
| Parameters.Count = Entries.size(); |
| |
| Parameters.UncompressedEntriesSizeInBytes = align(ContentEntriesSize); |
| computeFinalSize(Parameters.UncompressedEntriesSizeInBytes); |
| } |
| |
| void SourceInfo::SourceContents::computeFinalSize(uint32_t CompressedSize) { |
| Parameters.EntriesSizeInBytes = CompressedSize; |
| Parameters.AlignedSizeInBytes = |
| align(Parameters.EntriesSizeInBytes + |
| sizeof(dxbc::SourceInfo::Contents::Header)); |
| computeGenericHeader(Parameters.AlignedSizeInBytes, |
| dxbc::SourceInfo::SectionType::SourceContents); |
| } |
| |
| void SourceInfo::SourceNames::Entry::compute(uint32_t ContentSize) { |
| Parameters.Flags = 0; |
| Parameters.NameSizeInBytes = FileName.size() + 1; |
| Parameters.ContentSizeInBytes = ContentSize; |
| Parameters.AlignedSizeInBytes = align(Parameters.NameSizeInBytes + |
| sizeof(dxbc::SourceInfo::Names::Entry)); |
| } |
| |
| SourceInfo::SourceNames::Header::Header( |
| const dxbc::SourceInfo::Names::HeaderOnDisk &H) { |
| const auto *HPtr = reinterpret_cast<const uint8_t *>(&H); |
| Flags = support::endian::read32le(HPtr); |
| Count = support::endian::read32le(HPtr + 4); |
| EntriesSizeInBytes = support::endian::read16le(HPtr + 8); |
| } |
| |
| void SourceInfo::SourceNames::compute() { |
| size_t NameEntriesSize = 0; |
| for (const SourceNames::Entry &NameEntry : Entries) |
| NameEntriesSize += NameEntry.Parameters.AlignedSizeInBytes; |
| |
| Parameters.Flags = 0; |
| Parameters.Count = Entries.size(); |
| Parameters.EntriesSizeInBytes = align(NameEntriesSize); |
| |
| computeGenericHeader(Parameters.EntriesSizeInBytes + |
| sizeof(dxbc::SourceInfo::Names::HeaderOnDisk), |
| dxbc::SourceInfo::SectionType::SourceNames); |
| } |
| |
| void SourceInfo::ProgramArgs::compute() { |
| size_t ArgEntriesSize = 0; |
| for (auto [ArgName, ArgVal] : Args) { |
| // Null-terminated argument name and empty null-terminated argument value. |
| ArgEntriesSize += ArgName.size() + 1 + ArgVal.size() + 1; |
| } |
| |
| Parameters.Flags = 0; |
| Parameters.SizeInBytes = ArgEntriesSize; |
| Parameters.Count = Args.size(); |
| |
| computeGenericHeader(Parameters.SizeInBytes + |
| sizeof(dxbc::SourceInfo::Args::Header), |
| dxbc::SourceInfo::SectionType::Args); |
| } |
| |
| void SourceInfo::compute() { |
| Parameters.Flags = 0; |
| Parameters.SectionCount = 3; |
| Parameters.AlignedSizeInBytes = |
| align(sizeof(dxbc::SourceInfo::Header) + |
| Names.GenericHeader.AlignedSizeInBytes + |
| Contents.GenericHeader.AlignedSizeInBytes + |
| Args.GenericHeader.AlignedSizeInBytes); |
| } |
| |
| template <typename VecT> static void clearAndReserve(VecT &Vec, size_t N) { |
| Vec.clear(); |
| Vec.reserve(N); |
| } |
| |
| void SourceInfoBuilder::computeEntries() { |
| IsFilled = true; |
| clearAndReserve(BaseData.Names.Entries, FileNamesAndContents.size()); |
| clearAndReserve(BaseData.Contents.Entries, FileNamesAndContents.size()); |
| for (const auto &NameContent : FileNamesAndContents) { |
| SourceInfo::SourceContents::Entry ContentEntry; |
| ContentEntry.FileContent = NameContent.second.str(); |
| ContentEntry.compute(); |
| auto ContentSize = ContentEntry.Parameters.ContentSizeInBytes; |
| BaseData.Contents.Entries.emplace_back(std::move(ContentEntry)); |
| |
| SourceInfo::SourceNames::Entry NameEntry; |
| NameEntry.FileName = NameContent.first; |
| NameEntry.compute(ContentSize); |
| BaseData.Names.Entries.emplace_back(std::move(NameEntry)); |
| } |
| |
| clearAndReserve(BaseData.Args.Args, Args.size()); |
| for (auto [ArgName, ArgValue] : Args) |
| BaseData.Args.Args.emplace_back(ArgName, ArgValue); |
| } |
| |
| void SourceInfoBuilder::recomputeAfterCompression(uint32_t CompressedSize) { |
| BaseData.Contents.computeFinalSize(CompressedSize); |
| BaseData.compute(); |
| } |
| |
| void SourceInfoBuilder::finalize() { |
| assert(IsFilled && "SourceInfo::computeEntries() must be called before " |
| "SourceInfo::computeUncompressed()"); |
| assert(CompressionType && "Compression type must be set."); |
| |
| IsFinalized = true; |
| |
| BaseData.Contents.computeUncompressed(*CompressionType); |
| BaseData.Names.compute(); |
| BaseData.Args.compute(); |
| BaseData.compute(); |
| |
| // Compress Contents right here, to calculate compressed size. |
| CompressedContents.clear(); |
| SmallString<256> Data; |
| { |
| raw_svector_ostream OS(Data); |
| for (auto &E : BaseData.Contents.Entries) { |
| uint64_t EntryOffset = OS.tell(); |
| writeStruct(OS, E.Parameters); |
| writeString(OS, E.FileContent); |
| writePadding(OS, EntryOffset); |
| } |
| writePadding(OS, 0); |
| } |
| uint32_t CompressedSize = 0; |
| switch (BaseData.Contents.Parameters.Type) { |
| case dxbc::SourceInfo::Contents::CompressionType::Zlib: { |
| if (!compression::zlib::isAvailable()) |
| reportFatalUsageError(Twine("DXContainer SRCI Contents should be " |
| "compressed with Zlib, but ") + |
| Twine(compression::getReasonIfUnsupported( |
| compression::Format::Zlib))); |
| |
| SmallVector<uint8_t, 128> CompressedData; |
| compression::zlib::compress( |
| ArrayRef(reinterpret_cast<uint8_t *>(Data.data()), Data.size()), |
| CompressedData, compression::zlib::BestSizeCompression); |
| raw_svector_ostream OS(CompressedContents); |
| OS.write(reinterpret_cast<char *>(CompressedData.data()), |
| CompressedData.size()); |
| CompressedSize = CompressedContents.size(); |
| break; |
| } |
| case dxbc::SourceInfo::Contents::CompressionType::None: { |
| CompressedContents = std::move(Data); |
| CompressedSize = align(CompressedContents.size()); |
| break; |
| } |
| } |
| recomputeAfterCompression(CompressedSize); |
| } |
| |
| void SourceInfoBuilder::write(raw_ostream &OS) const { |
| assert(IsFinalized && |
| "SourceInfo::finalize() must be called before SourceInfo::write()"); |
| |
| writeStruct(OS, BaseData.Parameters); |
| |
| // Write Names section. |
| auto &Names = BaseData.Names; |
| uint64_t NamesOffset = OS.tell(); |
| writeStruct(OS, Names.GenericHeader); |
| support::endian::write(OS, Names.Parameters.Flags, endianness::little); |
| support::endian::write(OS, Names.Parameters.Count, endianness::little); |
| support::endian::write(OS, Names.Parameters.EntriesSizeInBytes, |
| endianness::little); |
| for (auto &E : Names.Entries) { |
| uint64_t EntryOffset = OS.tell(); |
| writeStruct(OS, E.Parameters); |
| writeString(OS, E.FileName); |
| writePadding(OS, EntryOffset); |
| } |
| writePadding(OS, NamesOffset); |
| |
| // Write Contents section. |
| auto &Contents = BaseData.Contents; |
| uint64_t ContentsOffset = OS.tell(); |
| writeStruct(OS, Contents.GenericHeader); |
| writeStruct(OS, Contents.Parameters); |
| |
| if (BaseData.Contents.Parameters.EntriesSizeInBytes != |
| CompressedContents.size()) |
| reportFatalUsageError(formatv( |
| "DXContainer SRCI Contents compressed size in header ({0} bytes) " |
| "doesn't match the actual compressed size ({1} bytes)", |
| BaseData.Contents.Parameters.EntriesSizeInBytes, |
| CompressedContents.size())); |
| |
| OS.write(CompressedContents.data(), CompressedContents.size()); |
| writePadding(OS, ContentsOffset); |
| |
| // Write Args section. |
| auto &Args = BaseData.Args; |
| uint64_t ArgsOffset = OS.tell(); |
| writeStruct(OS, Args.GenericHeader); |
| writeStruct(OS, Args.Parameters); |
| for (auto &E : Args.Args) { |
| writeString(OS, E.first); |
| writeString(OS, E.second); |
| } |
| writePadding(OS, ArgsOffset); |
| } |