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
20using namespace llvm;
21using namespace llvm::mcdxbc;
22
23static uint64_t align(uint64_t Size) {
24 return alignTo(Value: Size, Align: dxbc::DXCONTAINER_STRUCT_ALIGNMENT);
25}
26
27template <typename StructT>
28static 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
36static 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
42static 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
49void DebugName::setFilename(StringRef DebugFilename) {
50 Parameters.NameLength = DebugFilename.size();
51 Filename = DebugFilename;
52}
53
54void DebugName::write(raw_ostream &OS) const {
55 writeStruct(OS, S: Parameters);
56 writeString(OS, S: Filename.substr(Start: 0, N: Parameters.NameLength));
57}
58
59CompilerVersion::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
77void CompilerVersion::setCommitSha(StringRef CommitSha) {
78 this->CommitSha = CommitSha;
79 updateContentSize();
80}
81
82void CompilerVersion::setVersionString(StringRef VersionString) {
83 this->CustomVersionString = VersionString;
84 updateContentSize();
85}
86
87void CompilerVersion::updateContentSize() {
88 this->Parameters.ContentSizeInBytes =
89 CommitSha.size() + 1 + CustomVersionString.size() + 1;
90}
91
92void 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
102void SourceInfo::Section::computeGenericHeader(
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
109void 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
117void 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
131void 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
140void 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
148SourceInfo::SourceNames::Header::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
156void 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
170void 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
186void 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
196template <typename VecT> static void clearAndReserve(VecT &Vec, size_t N) {
197 Vec.clear();
198 Vec.reserve(N);
199}
200
201void 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
222void SourceInfoBuilder::recomputeAfterCompression(uint32_t CompressedSize) {
223 BaseData.Contents.computeFinalSize(CompressedSize);
224 BaseData.compute();
225}
226
227void 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
280void 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