| 1 | //===- llvm/MC/DXContainerInfo.cpp - DXContainer Info -----*- C++ -------*-===// |
| 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 | #include "llvm/MC/DXContainerInfo.h" |
| 10 | #include "llvm/ADT/SmallString.h" |
| 11 | #include "llvm/BinaryFormat/DXContainer.h" |
| 12 | #include "llvm/Config/config.h" |
| 13 | #include "llvm/Support/Compression.h" |
| 14 | #include "llvm/Support/EndianStream.h" |
| 15 | #include "llvm/Support/FormatVariadic.h" |
| 16 | #include "llvm/Support/SwapByteOrder.h" |
| 17 | #include "llvm/Support/VCSRevision.h" |
| 18 | #include <type_traits> |
| 19 | |
| 20 | using namespace llvm; |
| 21 | using namespace llvm::mcdxbc; |
| 22 | |
| 23 | static uint64_t align(uint64_t Size) { |
| 24 | return alignTo(Value: Size, Align: dxbc::DXCONTAINER_STRUCT_ALIGNMENT); |
| 25 | } |
| 26 | |
| 27 | template <typename StructT> |
| 28 | static void writeStruct(raw_ostream &OS, StructT S) { |
| 29 | static_assert(std::is_class<StructT>() && |
| 30 | "This method must be used for writing structure types" ); |
| 31 | if (sys::IsBigEndianHost) |
| 32 | S.swapBytes(); |
| 33 | OS.write(Ptr: reinterpret_cast<const char *>(&S), Size: sizeof(StructT)); |
| 34 | } |
| 35 | |
| 36 | static void writeString(raw_ostream &OS, StringRef S) { |
| 37 | OS.write(Ptr: S.data(), Size: S.size()); |
| 38 | // Write null terminator. |
| 39 | OS.write_zeros(NumZeros: 1); |
| 40 | } |
| 41 | |
| 42 | static void writePadding(raw_ostream &OS, uint64_t Prev) { |
| 43 | uint64_t UnpaddedSize = OS.tell() - Prev; |
| 44 | uint64_t Padding = align(Size: UnpaddedSize) - UnpaddedSize; |
| 45 | if (Padding) |
| 46 | OS.write_zeros(NumZeros: Padding); |
| 47 | } |
| 48 | |
| 49 | void DebugName::setFilename(StringRef DebugFilename) { |
| 50 | Parameters.NameLength = DebugFilename.size(); |
| 51 | Filename = DebugFilename; |
| 52 | } |
| 53 | |
| 54 | void DebugName::write(raw_ostream &OS) const { |
| 55 | writeStruct(OS, S: Parameters); |
| 56 | writeString(OS, S: Filename.substr(Start: 0, N: Parameters.NameLength)); |
| 57 | } |
| 58 | |
| 59 | CompilerVersion::CompilerVersion() { |
| 60 | Parameters.Major = LLVM_VERSION_MAJOR; |
| 61 | Parameters.Minor = LLVM_VERSION_MINOR; |
| 62 | Parameters.Flags = dxbc::CompilerVersionFlags::Default; |
| 63 | #ifndef NDEBUG |
| 64 | Parameters.Flags |= dxbc::CompilerVersionFlags::Debug; |
| 65 | #endif |
| 66 | Parameters.CommitCount = 0; |
| 67 | Parameters.ContentSizeInBytes = 0; |
| 68 | #ifdef LLVM_REVISION |
| 69 | CommitSha = LLVM_REVISION; |
| 70 | #else |
| 71 | CommitSha = "" ; |
| 72 | #endif |
| 73 | CustomVersionString = PACKAGE_VERSION; |
| 74 | updateContentSize(); |
| 75 | } |
| 76 | |
| 77 | void CompilerVersion::setCommitSha(StringRef CommitSha) { |
| 78 | this->CommitSha = CommitSha; |
| 79 | updateContentSize(); |
| 80 | } |
| 81 | |
| 82 | void CompilerVersion::setVersionString(StringRef VersionString) { |
| 83 | this->CustomVersionString = VersionString; |
| 84 | updateContentSize(); |
| 85 | } |
| 86 | |
| 87 | void CompilerVersion::updateContentSize() { |
| 88 | this->Parameters.ContentSizeInBytes = |
| 89 | CommitSha.size() + 1 + CustomVersionString.size() + 1; |
| 90 | } |
| 91 | |
| 92 | void CompilerVersion::write(raw_ostream &OS) const { |
| 93 | writeStruct(OS, S: Parameters); |
| 94 | SmallString<64> Content; |
| 95 | raw_svector_ostream ContentStream(Content); |
| 96 | writeString(OS&: ContentStream, S: CommitSha); |
| 97 | writeString(OS&: ContentStream, S: CustomVersionString); |
| 98 | Content.resize(N: Parameters.ContentSizeInBytes); |
| 99 | OS.write(Ptr: Content.data(), Size: Parameters.ContentSizeInBytes); |
| 100 | } |
| 101 | |
| 102 | void SourceInfo::Section::( |
| 103 | uint32_t ContentSize, dxbc::SourceInfo::SectionType SecType) { |
| 104 | GenericHeader.AlignedSizeInBytes = align(Size: sizeof(GenericHeader) + ContentSize); |
| 105 | GenericHeader.Flags = 0; |
| 106 | GenericHeader.Type = SecType; |
| 107 | } |
| 108 | |
| 109 | void SourceInfo::SourceContents::Entry::compute() { |
| 110 | Parameters.Flags = 0; |
| 111 | Parameters.ContentSizeInBytes = FileContent.size() + 1; |
| 112 | Parameters.AlignedSizeInBytes = |
| 113 | align(Size: Parameters.ContentSizeInBytes + |
| 114 | sizeof(dxbc::SourceInfo::Contents::Entry)); |
| 115 | } |
| 116 | |
| 117 | void SourceInfo::SourceContents::computeUncompressed( |
| 118 | dxbc::SourceInfo::Contents::CompressionType CompType) { |
| 119 | size_t ContentEntriesSize = 0; |
| 120 | for (const SourceContents::Entry &ContentEntry : Entries) |
| 121 | ContentEntriesSize += ContentEntry.Parameters.AlignedSizeInBytes; |
| 122 | |
| 123 | Parameters.Flags = 0; |
| 124 | Parameters.Type = CompType; |
| 125 | Parameters.Count = Entries.size(); |
| 126 | |
| 127 | Parameters.UncompressedEntriesSizeInBytes = align(Size: ContentEntriesSize); |
| 128 | computeFinalSize(CompressedSize: Parameters.UncompressedEntriesSizeInBytes); |
| 129 | } |
| 130 | |
| 131 | void SourceInfo::SourceContents::computeFinalSize(uint32_t CompressedSize) { |
| 132 | Parameters.EntriesSizeInBytes = CompressedSize; |
| 133 | Parameters.AlignedSizeInBytes = |
| 134 | align(Size: Parameters.EntriesSizeInBytes + |
| 135 | sizeof(dxbc::SourceInfo::Contents::Header)); |
| 136 | computeGenericHeader(ContentSize: Parameters.AlignedSizeInBytes, |
| 137 | SecType: dxbc::SourceInfo::SectionType::SourceContents); |
| 138 | } |
| 139 | |
| 140 | void SourceInfo::SourceNames::Entry::compute(uint32_t ContentSize) { |
| 141 | Parameters.Flags = 0; |
| 142 | Parameters.NameSizeInBytes = FileName.size() + 1; |
| 143 | Parameters.ContentSizeInBytes = ContentSize; |
| 144 | Parameters.AlignedSizeInBytes = align(Size: Parameters.NameSizeInBytes + |
| 145 | sizeof(dxbc::SourceInfo::Names::Entry)); |
| 146 | } |
| 147 | |
| 148 | SourceInfo::SourceNames::Header::( |
| 149 | const dxbc::SourceInfo::Names::HeaderOnDisk &H) { |
| 150 | const auto *HPtr = reinterpret_cast<const uint8_t *>(&H); |
| 151 | Flags = support::endian::read32le(P: HPtr); |
| 152 | Count = support::endian::read32le(P: HPtr + 4); |
| 153 | EntriesSizeInBytes = support::endian::read16le(P: HPtr + 8); |
| 154 | } |
| 155 | |
| 156 | void SourceInfo::SourceNames::compute() { |
| 157 | size_t NameEntriesSize = 0; |
| 158 | for (const SourceNames::Entry &NameEntry : Entries) |
| 159 | NameEntriesSize += NameEntry.Parameters.AlignedSizeInBytes; |
| 160 | |
| 161 | Parameters.Flags = 0; |
| 162 | Parameters.Count = Entries.size(); |
| 163 | Parameters.EntriesSizeInBytes = align(Size: NameEntriesSize); |
| 164 | |
| 165 | computeGenericHeader(ContentSize: Parameters.EntriesSizeInBytes + |
| 166 | sizeof(dxbc::SourceInfo::Names::HeaderOnDisk), |
| 167 | SecType: dxbc::SourceInfo::SectionType::SourceNames); |
| 168 | } |
| 169 | |
| 170 | void SourceInfo::ProgramArgs::compute() { |
| 171 | size_t ArgEntriesSize = 0; |
| 172 | for (auto [ArgName, ArgVal] : Args) { |
| 173 | // Null-terminated argument name and empty null-terminated argument value. |
| 174 | ArgEntriesSize += ArgName.size() + 1 + ArgVal.size() + 1; |
| 175 | } |
| 176 | |
| 177 | Parameters.Flags = 0; |
| 178 | Parameters.SizeInBytes = ArgEntriesSize; |
| 179 | Parameters.Count = Args.size(); |
| 180 | |
| 181 | computeGenericHeader(ContentSize: Parameters.SizeInBytes + |
| 182 | sizeof(dxbc::SourceInfo::Args::Header), |
| 183 | SecType: dxbc::SourceInfo::SectionType::Args); |
| 184 | } |
| 185 | |
| 186 | void SourceInfo::compute() { |
| 187 | Parameters.Flags = 0; |
| 188 | Parameters.SectionCount = 3; |
| 189 | Parameters.AlignedSizeInBytes = |
| 190 | align(Size: sizeof(dxbc::SourceInfo::Header) + |
| 191 | Names.GenericHeader.AlignedSizeInBytes + |
| 192 | Contents.GenericHeader.AlignedSizeInBytes + |
| 193 | Args.GenericHeader.AlignedSizeInBytes); |
| 194 | } |
| 195 | |
| 196 | template <typename VecT> static void clearAndReserve(VecT &Vec, size_t N) { |
| 197 | Vec.clear(); |
| 198 | Vec.reserve(N); |
| 199 | } |
| 200 | |
| 201 | void SourceInfoBuilder::computeEntries() { |
| 202 | IsFilled = true; |
| 203 | clearAndReserve(Vec&: BaseData.Names.Entries, N: FileNamesAndContents.size()); |
| 204 | clearAndReserve(Vec&: BaseData.Contents.Entries, N: FileNamesAndContents.size()); |
| 205 | for (const auto &NameContent : FileNamesAndContents) { |
| 206 | SourceInfo::SourceContents::Entry ContentEntry; |
| 207 | ContentEntry.FileContent = NameContent.second.str(); |
| 208 | ContentEntry.compute(); |
| 209 | BaseData.Contents.Entries.emplace_back(Args: std::move(ContentEntry)); |
| 210 | |
| 211 | SourceInfo::SourceNames::Entry NameEntry; |
| 212 | NameEntry.FileName = NameContent.first; |
| 213 | NameEntry.compute(ContentSize: ContentEntry.Parameters.ContentSizeInBytes); |
| 214 | BaseData.Names.Entries.emplace_back(Args: std::move(NameEntry)); |
| 215 | } |
| 216 | |
| 217 | clearAndReserve(Vec&: BaseData.Args.Args, N: Args.size()); |
| 218 | for (auto [ArgName, ArgValue] : Args) |
| 219 | BaseData.Args.Args.emplace_back(Args&: ArgName, Args&: ArgValue); |
| 220 | } |
| 221 | |
| 222 | void SourceInfoBuilder::recomputeAfterCompression(uint32_t CompressedSize) { |
| 223 | BaseData.Contents.computeFinalSize(CompressedSize); |
| 224 | BaseData.compute(); |
| 225 | } |
| 226 | |
| 227 | void SourceInfoBuilder::finalize() { |
| 228 | assert(IsFilled && "SourceInfo::computeEntries() must be called before " |
| 229 | "SourceInfo::computeUncompressed()" ); |
| 230 | assert(CompressionType && "Compression type must be set." ); |
| 231 | |
| 232 | IsFinalized = true; |
| 233 | |
| 234 | BaseData.Contents.computeUncompressed(CompType: *CompressionType); |
| 235 | BaseData.Names.compute(); |
| 236 | BaseData.Args.compute(); |
| 237 | BaseData.compute(); |
| 238 | |
| 239 | // Compress Contents right here, to calculate compressed size. |
| 240 | CompressedContents.clear(); |
| 241 | SmallString<256> Data; |
| 242 | { |
| 243 | raw_svector_ostream OS(Data); |
| 244 | for (auto &E : BaseData.Contents.Entries) { |
| 245 | uint64_t EntryOffset = OS.tell(); |
| 246 | writeStruct(OS, S: E.Parameters); |
| 247 | writeString(OS, S: E.FileContent); |
| 248 | writePadding(OS, Prev: EntryOffset); |
| 249 | } |
| 250 | writePadding(OS, Prev: 0); |
| 251 | } |
| 252 | uint32_t CompressedSize = 0; |
| 253 | switch (BaseData.Contents.Parameters.Type) { |
| 254 | case dxbc::SourceInfo::Contents::CompressionType::Zlib: { |
| 255 | if (!compression::zlib::isAvailable()) |
| 256 | reportFatalUsageError(reason: Twine("DXContainer SRCI Contents should be " |
| 257 | "compressed with Zlib, but " ) + |
| 258 | Twine(compression::getReasonIfUnsupported( |
| 259 | F: compression::Format::Zlib))); |
| 260 | |
| 261 | SmallVector<uint8_t, 128> CompressedData; |
| 262 | compression::zlib::compress( |
| 263 | Input: ArrayRef(reinterpret_cast<uint8_t *>(Data.data()), Data.size()), |
| 264 | CompressedBuffer&: CompressedData, Level: compression::zlib::BestSizeCompression); |
| 265 | raw_svector_ostream OS(CompressedContents); |
| 266 | OS.write(Ptr: reinterpret_cast<char *>(CompressedData.data()), |
| 267 | Size: CompressedData.size()); |
| 268 | CompressedSize = CompressedContents.size(); |
| 269 | break; |
| 270 | } |
| 271 | case dxbc::SourceInfo::Contents::CompressionType::None: { |
| 272 | CompressedContents = std::move(Data); |
| 273 | CompressedSize = align(Size: CompressedContents.size()); |
| 274 | break; |
| 275 | } |
| 276 | } |
| 277 | recomputeAfterCompression(CompressedSize); |
| 278 | } |
| 279 | |
| 280 | void SourceInfoBuilder::write(raw_ostream &OS) const { |
| 281 | assert(IsFinalized && |
| 282 | "SourceInfo::finalize() must be called before SourceInfo::write()" ); |
| 283 | |
| 284 | writeStruct(OS, S: BaseData.Parameters); |
| 285 | |
| 286 | // Write Names section. |
| 287 | auto &Names = BaseData.Names; |
| 288 | uint64_t NamesOffset = OS.tell(); |
| 289 | writeStruct(OS, S: Names.GenericHeader); |
| 290 | support::endian::write(os&: OS, value: Names.Parameters.Flags, endian: endianness::little); |
| 291 | support::endian::write(os&: OS, value: Names.Parameters.Count, endian: endianness::little); |
| 292 | support::endian::write(os&: OS, value: Names.Parameters.EntriesSizeInBytes, |
| 293 | endian: endianness::little); |
| 294 | for (auto &E : Names.Entries) { |
| 295 | uint64_t EntryOffset = OS.tell(); |
| 296 | writeStruct(OS, S: E.Parameters); |
| 297 | writeString(OS, S: E.FileName); |
| 298 | writePadding(OS, Prev: EntryOffset); |
| 299 | } |
| 300 | writePadding(OS, Prev: NamesOffset); |
| 301 | |
| 302 | // Write Contents section. |
| 303 | auto &Contents = BaseData.Contents; |
| 304 | uint64_t ContentsOffset = OS.tell(); |
| 305 | writeStruct(OS, S: Contents.GenericHeader); |
| 306 | writeStruct(OS, S: Contents.Parameters); |
| 307 | |
| 308 | if (BaseData.Contents.Parameters.EntriesSizeInBytes != |
| 309 | CompressedContents.size()) |
| 310 | reportFatalUsageError(reason: formatv( |
| 311 | Fmt: "DXContainer SRCI Contents compressed size in header ({0} bytes) " |
| 312 | "doesn't match the actual compressed size ({1} bytes)" , |
| 313 | Vals: BaseData.Contents.Parameters.EntriesSizeInBytes, |
| 314 | Vals: CompressedContents.size())); |
| 315 | |
| 316 | OS.write(Ptr: CompressedContents.data(), Size: CompressedContents.size()); |
| 317 | writePadding(OS, Prev: ContentsOffset); |
| 318 | |
| 319 | // Write Args section. |
| 320 | auto &Args = BaseData.Args; |
| 321 | uint64_t ArgsOffset = OS.tell(); |
| 322 | writeStruct(OS, S: Args.GenericHeader); |
| 323 | writeStruct(OS, S: Args.Parameters); |
| 324 | for (auto &E : Args.Args) { |
| 325 | writeString(OS, S: E.first); |
| 326 | writeString(OS, S: E.second); |
| 327 | } |
| 328 | writePadding(OS, Prev: ArgsOffset); |
| 329 | } |
| 330 | |