blob: 909207b39ee1733f194ad3295ad19d328e065c7d [file] [edit]
//===- 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);
}