1*700637cbSDimitry Andric //===- OffloadBundle.cpp - Utilities for offload bundles---*- C++ -*-===//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------===//
8*700637cbSDimitry Andric
9*700637cbSDimitry Andric #include "llvm/Object/OffloadBundle.h"
10*700637cbSDimitry Andric #include "llvm/BinaryFormat/Magic.h"
11*700637cbSDimitry Andric #include "llvm/IR/Module.h"
12*700637cbSDimitry Andric #include "llvm/IRReader/IRReader.h"
13*700637cbSDimitry Andric #include "llvm/MC/StringTableBuilder.h"
14*700637cbSDimitry Andric #include "llvm/Object/Archive.h"
15*700637cbSDimitry Andric #include "llvm/Object/Binary.h"
16*700637cbSDimitry Andric #include "llvm/Object/COFF.h"
17*700637cbSDimitry Andric #include "llvm/Object/ELFObjectFile.h"
18*700637cbSDimitry Andric #include "llvm/Object/Error.h"
19*700637cbSDimitry Andric #include "llvm/Object/IRObjectFile.h"
20*700637cbSDimitry Andric #include "llvm/Object/ObjectFile.h"
21*700637cbSDimitry Andric #include "llvm/Support/BinaryStreamReader.h"
22*700637cbSDimitry Andric #include "llvm/Support/SourceMgr.h"
23*700637cbSDimitry Andric #include "llvm/Support/Timer.h"
24*700637cbSDimitry Andric
25*700637cbSDimitry Andric using namespace llvm;
26*700637cbSDimitry Andric using namespace llvm::object;
27*700637cbSDimitry Andric
28*700637cbSDimitry Andric static llvm::TimerGroup
29*700637cbSDimitry Andric OffloadBundlerTimerGroup("Offload Bundler Timer Group",
30*700637cbSDimitry Andric "Timer group for offload bundler");
31*700637cbSDimitry Andric
32*700637cbSDimitry Andric // Extract an Offload bundle (usually a Offload Bundle) from a fat_bin
33*700637cbSDimitry Andric // section
extractOffloadBundle(MemoryBufferRef Contents,uint64_t SectionOffset,StringRef FileName,SmallVectorImpl<OffloadBundleFatBin> & Bundles)34*700637cbSDimitry Andric Error extractOffloadBundle(MemoryBufferRef Contents, uint64_t SectionOffset,
35*700637cbSDimitry Andric StringRef FileName,
36*700637cbSDimitry Andric SmallVectorImpl<OffloadBundleFatBin> &Bundles) {
37*700637cbSDimitry Andric
38*700637cbSDimitry Andric size_t Offset = 0;
39*700637cbSDimitry Andric size_t NextbundleStart = 0;
40*700637cbSDimitry Andric
41*700637cbSDimitry Andric // There could be multiple offloading bundles stored at this section.
42*700637cbSDimitry Andric while (NextbundleStart != StringRef::npos) {
43*700637cbSDimitry Andric std::unique_ptr<MemoryBuffer> Buffer =
44*700637cbSDimitry Andric MemoryBuffer::getMemBuffer(Contents.getBuffer().drop_front(Offset), "",
45*700637cbSDimitry Andric /*RequiresNullTerminator=*/false);
46*700637cbSDimitry Andric
47*700637cbSDimitry Andric // Create the FatBinBindle object. This will also create the Bundle Entry
48*700637cbSDimitry Andric // list info.
49*700637cbSDimitry Andric auto FatBundleOrErr =
50*700637cbSDimitry Andric OffloadBundleFatBin::create(*Buffer, SectionOffset + Offset, FileName);
51*700637cbSDimitry Andric if (!FatBundleOrErr)
52*700637cbSDimitry Andric return FatBundleOrErr.takeError();
53*700637cbSDimitry Andric
54*700637cbSDimitry Andric // Add current Bundle to list.
55*700637cbSDimitry Andric Bundles.emplace_back(std::move(**FatBundleOrErr));
56*700637cbSDimitry Andric
57*700637cbSDimitry Andric // Find the next bundle by searching for the magic string
58*700637cbSDimitry Andric StringRef Str = Buffer->getBuffer();
59*700637cbSDimitry Andric NextbundleStart = Str.find(StringRef("__CLANG_OFFLOAD_BUNDLE__"), 24);
60*700637cbSDimitry Andric
61*700637cbSDimitry Andric if (NextbundleStart != StringRef::npos)
62*700637cbSDimitry Andric Offset += NextbundleStart;
63*700637cbSDimitry Andric }
64*700637cbSDimitry Andric
65*700637cbSDimitry Andric return Error::success();
66*700637cbSDimitry Andric }
67*700637cbSDimitry Andric
readEntries(StringRef Buffer,uint64_t SectionOffset)68*700637cbSDimitry Andric Error OffloadBundleFatBin::readEntries(StringRef Buffer,
69*700637cbSDimitry Andric uint64_t SectionOffset) {
70*700637cbSDimitry Andric uint64_t NumOfEntries = 0;
71*700637cbSDimitry Andric
72*700637cbSDimitry Andric BinaryStreamReader Reader(Buffer, llvm::endianness::little);
73*700637cbSDimitry Andric
74*700637cbSDimitry Andric // Read the Magic String first.
75*700637cbSDimitry Andric StringRef Magic;
76*700637cbSDimitry Andric if (auto EC = Reader.readFixedString(Magic, 24))
77*700637cbSDimitry Andric return errorCodeToError(object_error::parse_failed);
78*700637cbSDimitry Andric
79*700637cbSDimitry Andric // Read the number of Code Objects (Entries) in the current Bundle.
80*700637cbSDimitry Andric if (auto EC = Reader.readInteger(NumOfEntries))
81*700637cbSDimitry Andric return errorCodeToError(object_error::parse_failed);
82*700637cbSDimitry Andric
83*700637cbSDimitry Andric NumberOfEntries = NumOfEntries;
84*700637cbSDimitry Andric
85*700637cbSDimitry Andric // For each Bundle Entry (code object)
86*700637cbSDimitry Andric for (uint64_t I = 0; I < NumOfEntries; I++) {
87*700637cbSDimitry Andric uint64_t EntrySize;
88*700637cbSDimitry Andric uint64_t EntryOffset;
89*700637cbSDimitry Andric uint64_t EntryIDSize;
90*700637cbSDimitry Andric StringRef EntryID;
91*700637cbSDimitry Andric
92*700637cbSDimitry Andric if (auto EC = Reader.readInteger(EntryOffset))
93*700637cbSDimitry Andric return errorCodeToError(object_error::parse_failed);
94*700637cbSDimitry Andric
95*700637cbSDimitry Andric if (auto EC = Reader.readInteger(EntrySize))
96*700637cbSDimitry Andric return errorCodeToError(object_error::parse_failed);
97*700637cbSDimitry Andric
98*700637cbSDimitry Andric if (auto EC = Reader.readInteger(EntryIDSize))
99*700637cbSDimitry Andric return errorCodeToError(object_error::parse_failed);
100*700637cbSDimitry Andric
101*700637cbSDimitry Andric if (auto EC = Reader.readFixedString(EntryID, EntryIDSize))
102*700637cbSDimitry Andric return errorCodeToError(object_error::parse_failed);
103*700637cbSDimitry Andric
104*700637cbSDimitry Andric auto Entry = std::make_unique<OffloadBundleEntry>(
105*700637cbSDimitry Andric EntryOffset + SectionOffset, EntrySize, EntryIDSize, EntryID);
106*700637cbSDimitry Andric
107*700637cbSDimitry Andric Entries.push_back(*Entry);
108*700637cbSDimitry Andric }
109*700637cbSDimitry Andric
110*700637cbSDimitry Andric return Error::success();
111*700637cbSDimitry Andric }
112*700637cbSDimitry Andric
113*700637cbSDimitry Andric Expected<std::unique_ptr<OffloadBundleFatBin>>
create(MemoryBufferRef Buf,uint64_t SectionOffset,StringRef FileName)114*700637cbSDimitry Andric OffloadBundleFatBin::create(MemoryBufferRef Buf, uint64_t SectionOffset,
115*700637cbSDimitry Andric StringRef FileName) {
116*700637cbSDimitry Andric if (Buf.getBufferSize() < 24)
117*700637cbSDimitry Andric return errorCodeToError(object_error::parse_failed);
118*700637cbSDimitry Andric
119*700637cbSDimitry Andric // Check for magic bytes.
120*700637cbSDimitry Andric if (identify_magic(Buf.getBuffer()) != file_magic::offload_bundle)
121*700637cbSDimitry Andric return errorCodeToError(object_error::parse_failed);
122*700637cbSDimitry Andric
123*700637cbSDimitry Andric OffloadBundleFatBin *TheBundle = new OffloadBundleFatBin(Buf, FileName);
124*700637cbSDimitry Andric
125*700637cbSDimitry Andric // Read the Bundle Entries
126*700637cbSDimitry Andric Error Err = TheBundle->readEntries(Buf.getBuffer(), SectionOffset);
127*700637cbSDimitry Andric if (Err)
128*700637cbSDimitry Andric return errorCodeToError(object_error::parse_failed);
129*700637cbSDimitry Andric
130*700637cbSDimitry Andric return std::unique_ptr<OffloadBundleFatBin>(TheBundle);
131*700637cbSDimitry Andric }
132*700637cbSDimitry Andric
extractBundle(const ObjectFile & Source)133*700637cbSDimitry Andric Error OffloadBundleFatBin::extractBundle(const ObjectFile &Source) {
134*700637cbSDimitry Andric // This will extract all entries in the Bundle
135*700637cbSDimitry Andric for (OffloadBundleEntry &Entry : Entries) {
136*700637cbSDimitry Andric
137*700637cbSDimitry Andric if (Entry.Size == 0)
138*700637cbSDimitry Andric continue;
139*700637cbSDimitry Andric
140*700637cbSDimitry Andric // create output file name. Which should be
141*700637cbSDimitry Andric // <fileName>-offset<Offset>-size<Size>.co"
142*700637cbSDimitry Andric std::string Str = getFileName().str() + "-offset" + itostr(Entry.Offset) +
143*700637cbSDimitry Andric "-size" + itostr(Entry.Size) + ".co";
144*700637cbSDimitry Andric if (Error Err = object::extractCodeObject(Source, Entry.Offset, Entry.Size,
145*700637cbSDimitry Andric StringRef(Str)))
146*700637cbSDimitry Andric return Err;
147*700637cbSDimitry Andric }
148*700637cbSDimitry Andric
149*700637cbSDimitry Andric return Error::success();
150*700637cbSDimitry Andric }
151*700637cbSDimitry Andric
extractOffloadBundleFatBinary(const ObjectFile & Obj,SmallVectorImpl<OffloadBundleFatBin> & Bundles)152*700637cbSDimitry Andric Error object::extractOffloadBundleFatBinary(
153*700637cbSDimitry Andric const ObjectFile &Obj, SmallVectorImpl<OffloadBundleFatBin> &Bundles) {
154*700637cbSDimitry Andric assert((Obj.isELF() || Obj.isCOFF()) && "Invalid file type");
155*700637cbSDimitry Andric
156*700637cbSDimitry Andric // Iterate through Sections until we find an offload_bundle section.
157*700637cbSDimitry Andric for (SectionRef Sec : Obj.sections()) {
158*700637cbSDimitry Andric Expected<StringRef> Buffer = Sec.getContents();
159*700637cbSDimitry Andric if (!Buffer)
160*700637cbSDimitry Andric return Buffer.takeError();
161*700637cbSDimitry Andric
162*700637cbSDimitry Andric // If it does not start with the reserved suffix, just skip this section.
163*700637cbSDimitry Andric if ((llvm::identify_magic(*Buffer) == llvm::file_magic::offload_bundle) ||
164*700637cbSDimitry Andric (llvm::identify_magic(*Buffer) ==
165*700637cbSDimitry Andric llvm::file_magic::offload_bundle_compressed)) {
166*700637cbSDimitry Andric
167*700637cbSDimitry Andric uint64_t SectionOffset = 0;
168*700637cbSDimitry Andric if (Obj.isELF()) {
169*700637cbSDimitry Andric SectionOffset = ELFSectionRef(Sec).getOffset();
170*700637cbSDimitry Andric } else if (Obj.isCOFF()) // TODO: add COFF Support
171*700637cbSDimitry Andric return createStringError(object_error::parse_failed,
172*700637cbSDimitry Andric "COFF object files not supported.\n");
173*700637cbSDimitry Andric
174*700637cbSDimitry Andric MemoryBufferRef Contents(*Buffer, Obj.getFileName());
175*700637cbSDimitry Andric
176*700637cbSDimitry Andric if (llvm::identify_magic(*Buffer) ==
177*700637cbSDimitry Andric llvm::file_magic::offload_bundle_compressed) {
178*700637cbSDimitry Andric // Decompress the input if necessary.
179*700637cbSDimitry Andric Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
180*700637cbSDimitry Andric CompressedOffloadBundle::decompress(Contents, false);
181*700637cbSDimitry Andric
182*700637cbSDimitry Andric if (!DecompressedBufferOrErr)
183*700637cbSDimitry Andric return createStringError(
184*700637cbSDimitry Andric inconvertibleErrorCode(),
185*700637cbSDimitry Andric "Failed to decompress input: " +
186*700637cbSDimitry Andric llvm::toString(DecompressedBufferOrErr.takeError()));
187*700637cbSDimitry Andric
188*700637cbSDimitry Andric MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
189*700637cbSDimitry Andric if (Error Err = extractOffloadBundle(DecompressedInput, SectionOffset,
190*700637cbSDimitry Andric Obj.getFileName(), Bundles))
191*700637cbSDimitry Andric return Err;
192*700637cbSDimitry Andric } else {
193*700637cbSDimitry Andric if (Error Err = extractOffloadBundle(Contents, SectionOffset,
194*700637cbSDimitry Andric Obj.getFileName(), Bundles))
195*700637cbSDimitry Andric return Err;
196*700637cbSDimitry Andric }
197*700637cbSDimitry Andric }
198*700637cbSDimitry Andric }
199*700637cbSDimitry Andric return Error::success();
200*700637cbSDimitry Andric }
201*700637cbSDimitry Andric
extractCodeObject(const ObjectFile & Source,int64_t Offset,int64_t Size,StringRef OutputFileName)202*700637cbSDimitry Andric Error object::extractCodeObject(const ObjectFile &Source, int64_t Offset,
203*700637cbSDimitry Andric int64_t Size, StringRef OutputFileName) {
204*700637cbSDimitry Andric Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
205*700637cbSDimitry Andric FileOutputBuffer::create(OutputFileName, Size);
206*700637cbSDimitry Andric
207*700637cbSDimitry Andric if (!BufferOrErr)
208*700637cbSDimitry Andric return BufferOrErr.takeError();
209*700637cbSDimitry Andric
210*700637cbSDimitry Andric Expected<MemoryBufferRef> InputBuffOrErr = Source.getMemoryBufferRef();
211*700637cbSDimitry Andric if (Error Err = InputBuffOrErr.takeError())
212*700637cbSDimitry Andric return Err;
213*700637cbSDimitry Andric
214*700637cbSDimitry Andric std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
215*700637cbSDimitry Andric std::copy(InputBuffOrErr->getBufferStart() + Offset,
216*700637cbSDimitry Andric InputBuffOrErr->getBufferStart() + Offset + Size,
217*700637cbSDimitry Andric Buf->getBufferStart());
218*700637cbSDimitry Andric if (Error E = Buf->commit())
219*700637cbSDimitry Andric return E;
220*700637cbSDimitry Andric
221*700637cbSDimitry Andric return Error::success();
222*700637cbSDimitry Andric }
223*700637cbSDimitry Andric
224*700637cbSDimitry Andric // given a file name, offset, and size, extract data into a code object file,
225*700637cbSDimitry Andric // into file <SourceFile>-offset<Offset>-size<Size>.co
extractOffloadBundleByURI(StringRef URIstr)226*700637cbSDimitry Andric Error object::extractOffloadBundleByURI(StringRef URIstr) {
227*700637cbSDimitry Andric // create a URI object
228*700637cbSDimitry Andric Expected<std::unique_ptr<OffloadBundleURI>> UriOrErr(
229*700637cbSDimitry Andric OffloadBundleURI::createOffloadBundleURI(URIstr, FILE_URI));
230*700637cbSDimitry Andric if (!UriOrErr)
231*700637cbSDimitry Andric return UriOrErr.takeError();
232*700637cbSDimitry Andric
233*700637cbSDimitry Andric OffloadBundleURI &Uri = **UriOrErr;
234*700637cbSDimitry Andric std::string OutputFile = Uri.FileName.str();
235*700637cbSDimitry Andric OutputFile +=
236*700637cbSDimitry Andric "-offset" + itostr(Uri.Offset) + "-size" + itostr(Uri.Size) + ".co";
237*700637cbSDimitry Andric
238*700637cbSDimitry Andric // Create an ObjectFile object from uri.file_uri
239*700637cbSDimitry Andric auto ObjOrErr = ObjectFile::createObjectFile(Uri.FileName);
240*700637cbSDimitry Andric if (!ObjOrErr)
241*700637cbSDimitry Andric return ObjOrErr.takeError();
242*700637cbSDimitry Andric
243*700637cbSDimitry Andric auto Obj = ObjOrErr->getBinary();
244*700637cbSDimitry Andric if (Error Err =
245*700637cbSDimitry Andric object::extractCodeObject(*Obj, Uri.Offset, Uri.Size, OutputFile))
246*700637cbSDimitry Andric return Err;
247*700637cbSDimitry Andric
248*700637cbSDimitry Andric return Error::success();
249*700637cbSDimitry Andric }
250*700637cbSDimitry Andric
251*700637cbSDimitry Andric // Utility function to format numbers with commas
formatWithCommas(unsigned long long Value)252*700637cbSDimitry Andric static std::string formatWithCommas(unsigned long long Value) {
253*700637cbSDimitry Andric std::string Num = std::to_string(Value);
254*700637cbSDimitry Andric int InsertPosition = Num.length() - 3;
255*700637cbSDimitry Andric while (InsertPosition > 0) {
256*700637cbSDimitry Andric Num.insert(InsertPosition, ",");
257*700637cbSDimitry Andric InsertPosition -= 3;
258*700637cbSDimitry Andric }
259*700637cbSDimitry Andric return Num;
260*700637cbSDimitry Andric }
261*700637cbSDimitry Andric
262*700637cbSDimitry Andric llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
decompress(llvm::MemoryBufferRef & Input,bool Verbose)263*700637cbSDimitry Andric CompressedOffloadBundle::decompress(llvm::MemoryBufferRef &Input,
264*700637cbSDimitry Andric bool Verbose) {
265*700637cbSDimitry Andric StringRef Blob = Input.getBuffer();
266*700637cbSDimitry Andric
267*700637cbSDimitry Andric if (Blob.size() < V1HeaderSize)
268*700637cbSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy(Blob);
269*700637cbSDimitry Andric
270*700637cbSDimitry Andric if (llvm::identify_magic(Blob) !=
271*700637cbSDimitry Andric llvm::file_magic::offload_bundle_compressed) {
272*700637cbSDimitry Andric if (Verbose)
273*700637cbSDimitry Andric llvm::errs() << "Uncompressed bundle.\n";
274*700637cbSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy(Blob);
275*700637cbSDimitry Andric }
276*700637cbSDimitry Andric
277*700637cbSDimitry Andric size_t CurrentOffset = MagicSize;
278*700637cbSDimitry Andric
279*700637cbSDimitry Andric uint16_t ThisVersion;
280*700637cbSDimitry Andric memcpy(&ThisVersion, Blob.data() + CurrentOffset, sizeof(uint16_t));
281*700637cbSDimitry Andric CurrentOffset += VersionFieldSize;
282*700637cbSDimitry Andric
283*700637cbSDimitry Andric uint16_t CompressionMethod;
284*700637cbSDimitry Andric memcpy(&CompressionMethod, Blob.data() + CurrentOffset, sizeof(uint16_t));
285*700637cbSDimitry Andric CurrentOffset += MethodFieldSize;
286*700637cbSDimitry Andric
287*700637cbSDimitry Andric uint32_t TotalFileSize;
288*700637cbSDimitry Andric if (ThisVersion >= 2) {
289*700637cbSDimitry Andric if (Blob.size() < V2HeaderSize)
290*700637cbSDimitry Andric return createStringError(inconvertibleErrorCode(),
291*700637cbSDimitry Andric "Compressed bundle header size too small");
292*700637cbSDimitry Andric memcpy(&TotalFileSize, Blob.data() + CurrentOffset, sizeof(uint32_t));
293*700637cbSDimitry Andric CurrentOffset += FileSizeFieldSize;
294*700637cbSDimitry Andric }
295*700637cbSDimitry Andric
296*700637cbSDimitry Andric uint32_t UncompressedSize;
297*700637cbSDimitry Andric memcpy(&UncompressedSize, Blob.data() + CurrentOffset, sizeof(uint32_t));
298*700637cbSDimitry Andric CurrentOffset += UncompressedSizeFieldSize;
299*700637cbSDimitry Andric
300*700637cbSDimitry Andric uint64_t StoredHash;
301*700637cbSDimitry Andric memcpy(&StoredHash, Blob.data() + CurrentOffset, sizeof(uint64_t));
302*700637cbSDimitry Andric CurrentOffset += HashFieldSize;
303*700637cbSDimitry Andric
304*700637cbSDimitry Andric llvm::compression::Format CompressionFormat;
305*700637cbSDimitry Andric if (CompressionMethod ==
306*700637cbSDimitry Andric static_cast<uint16_t>(llvm::compression::Format::Zlib))
307*700637cbSDimitry Andric CompressionFormat = llvm::compression::Format::Zlib;
308*700637cbSDimitry Andric else if (CompressionMethod ==
309*700637cbSDimitry Andric static_cast<uint16_t>(llvm::compression::Format::Zstd))
310*700637cbSDimitry Andric CompressionFormat = llvm::compression::Format::Zstd;
311*700637cbSDimitry Andric else
312*700637cbSDimitry Andric return createStringError(inconvertibleErrorCode(),
313*700637cbSDimitry Andric "Unknown compressing method");
314*700637cbSDimitry Andric
315*700637cbSDimitry Andric llvm::Timer DecompressTimer("Decompression Timer", "Decompression time",
316*700637cbSDimitry Andric OffloadBundlerTimerGroup);
317*700637cbSDimitry Andric if (Verbose)
318*700637cbSDimitry Andric DecompressTimer.startTimer();
319*700637cbSDimitry Andric
320*700637cbSDimitry Andric SmallVector<uint8_t, 0> DecompressedData;
321*700637cbSDimitry Andric StringRef CompressedData = Blob.substr(CurrentOffset);
322*700637cbSDimitry Andric if (llvm::Error DecompressionError = llvm::compression::decompress(
323*700637cbSDimitry Andric CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
324*700637cbSDimitry Andric DecompressedData, UncompressedSize))
325*700637cbSDimitry Andric return createStringError(inconvertibleErrorCode(),
326*700637cbSDimitry Andric "Could not decompress embedded file contents: " +
327*700637cbSDimitry Andric llvm::toString(std::move(DecompressionError)));
328*700637cbSDimitry Andric
329*700637cbSDimitry Andric if (Verbose) {
330*700637cbSDimitry Andric DecompressTimer.stopTimer();
331*700637cbSDimitry Andric
332*700637cbSDimitry Andric double DecompressionTimeSeconds =
333*700637cbSDimitry Andric DecompressTimer.getTotalTime().getWallTime();
334*700637cbSDimitry Andric
335*700637cbSDimitry Andric // Recalculate MD5 hash for integrity check.
336*700637cbSDimitry Andric llvm::Timer HashRecalcTimer("Hash Recalculation Timer",
337*700637cbSDimitry Andric "Hash recalculation time",
338*700637cbSDimitry Andric OffloadBundlerTimerGroup);
339*700637cbSDimitry Andric HashRecalcTimer.startTimer();
340*700637cbSDimitry Andric llvm::MD5 Hash;
341*700637cbSDimitry Andric llvm::MD5::MD5Result Result;
342*700637cbSDimitry Andric Hash.update(llvm::ArrayRef<uint8_t>(DecompressedData));
343*700637cbSDimitry Andric Hash.final(Result);
344*700637cbSDimitry Andric uint64_t RecalculatedHash = Result.low();
345*700637cbSDimitry Andric HashRecalcTimer.stopTimer();
346*700637cbSDimitry Andric bool HashMatch = (StoredHash == RecalculatedHash);
347*700637cbSDimitry Andric
348*700637cbSDimitry Andric double CompressionRate =
349*700637cbSDimitry Andric static_cast<double>(UncompressedSize) / CompressedData.size();
350*700637cbSDimitry Andric double DecompressionSpeedMBs =
351*700637cbSDimitry Andric (UncompressedSize / (1024.0 * 1024.0)) / DecompressionTimeSeconds;
352*700637cbSDimitry Andric
353*700637cbSDimitry Andric llvm::errs() << "Compressed bundle format version: " << ThisVersion << "\n";
354*700637cbSDimitry Andric if (ThisVersion >= 2)
355*700637cbSDimitry Andric llvm::errs() << "Total file size (from header): "
356*700637cbSDimitry Andric << formatWithCommas(TotalFileSize) << " bytes\n";
357*700637cbSDimitry Andric llvm::errs() << "Decompression method: "
358*700637cbSDimitry Andric << (CompressionFormat == llvm::compression::Format::Zlib
359*700637cbSDimitry Andric ? "zlib"
360*700637cbSDimitry Andric : "zstd")
361*700637cbSDimitry Andric << "\n"
362*700637cbSDimitry Andric << "Size before decompression: "
363*700637cbSDimitry Andric << formatWithCommas(CompressedData.size()) << " bytes\n"
364*700637cbSDimitry Andric << "Size after decompression: "
365*700637cbSDimitry Andric << formatWithCommas(UncompressedSize) << " bytes\n"
366*700637cbSDimitry Andric << "Compression rate: "
367*700637cbSDimitry Andric << llvm::format("%.2lf", CompressionRate) << "\n"
368*700637cbSDimitry Andric << "Compression ratio: "
369*700637cbSDimitry Andric << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
370*700637cbSDimitry Andric << "Decompression speed: "
371*700637cbSDimitry Andric << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
372*700637cbSDimitry Andric << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
373*700637cbSDimitry Andric << "Recalculated hash: "
374*700637cbSDimitry Andric << llvm::format_hex(RecalculatedHash, 16) << "\n"
375*700637cbSDimitry Andric << "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";
376*700637cbSDimitry Andric }
377*700637cbSDimitry Andric
378*700637cbSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy(
379*700637cbSDimitry Andric llvm::toStringRef(DecompressedData));
380*700637cbSDimitry Andric }
381*700637cbSDimitry Andric
382*700637cbSDimitry Andric llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
compress(llvm::compression::Params P,const llvm::MemoryBuffer & Input,bool Verbose)383*700637cbSDimitry Andric CompressedOffloadBundle::compress(llvm::compression::Params P,
384*700637cbSDimitry Andric const llvm::MemoryBuffer &Input,
385*700637cbSDimitry Andric bool Verbose) {
386*700637cbSDimitry Andric if (!llvm::compression::zstd::isAvailable() &&
387*700637cbSDimitry Andric !llvm::compression::zlib::isAvailable())
388*700637cbSDimitry Andric return createStringError(llvm::inconvertibleErrorCode(),
389*700637cbSDimitry Andric "Compression not supported");
390*700637cbSDimitry Andric
391*700637cbSDimitry Andric llvm::Timer HashTimer("Hash Calculation Timer", "Hash calculation time",
392*700637cbSDimitry Andric OffloadBundlerTimerGroup);
393*700637cbSDimitry Andric if (Verbose)
394*700637cbSDimitry Andric HashTimer.startTimer();
395*700637cbSDimitry Andric llvm::MD5 Hash;
396*700637cbSDimitry Andric llvm::MD5::MD5Result Result;
397*700637cbSDimitry Andric Hash.update(Input.getBuffer());
398*700637cbSDimitry Andric Hash.final(Result);
399*700637cbSDimitry Andric uint64_t TruncatedHash = Result.low();
400*700637cbSDimitry Andric if (Verbose)
401*700637cbSDimitry Andric HashTimer.stopTimer();
402*700637cbSDimitry Andric
403*700637cbSDimitry Andric SmallVector<uint8_t, 0> CompressedBuffer;
404*700637cbSDimitry Andric auto BufferUint8 = llvm::ArrayRef<uint8_t>(
405*700637cbSDimitry Andric reinterpret_cast<const uint8_t *>(Input.getBuffer().data()),
406*700637cbSDimitry Andric Input.getBuffer().size());
407*700637cbSDimitry Andric
408*700637cbSDimitry Andric llvm::Timer CompressTimer("Compression Timer", "Compression time",
409*700637cbSDimitry Andric OffloadBundlerTimerGroup);
410*700637cbSDimitry Andric if (Verbose)
411*700637cbSDimitry Andric CompressTimer.startTimer();
412*700637cbSDimitry Andric llvm::compression::compress(P, BufferUint8, CompressedBuffer);
413*700637cbSDimitry Andric if (Verbose)
414*700637cbSDimitry Andric CompressTimer.stopTimer();
415*700637cbSDimitry Andric
416*700637cbSDimitry Andric uint16_t CompressionMethod = static_cast<uint16_t>(P.format);
417*700637cbSDimitry Andric uint32_t UncompressedSize = Input.getBuffer().size();
418*700637cbSDimitry Andric uint32_t TotalFileSize = MagicNumber.size() + sizeof(TotalFileSize) +
419*700637cbSDimitry Andric sizeof(Version) + sizeof(CompressionMethod) +
420*700637cbSDimitry Andric sizeof(UncompressedSize) + sizeof(TruncatedHash) +
421*700637cbSDimitry Andric CompressedBuffer.size();
422*700637cbSDimitry Andric
423*700637cbSDimitry Andric SmallVector<char, 0> FinalBuffer;
424*700637cbSDimitry Andric llvm::raw_svector_ostream OS(FinalBuffer);
425*700637cbSDimitry Andric OS << MagicNumber;
426*700637cbSDimitry Andric OS.write(reinterpret_cast<const char *>(&Version), sizeof(Version));
427*700637cbSDimitry Andric OS.write(reinterpret_cast<const char *>(&CompressionMethod),
428*700637cbSDimitry Andric sizeof(CompressionMethod));
429*700637cbSDimitry Andric OS.write(reinterpret_cast<const char *>(&TotalFileSize),
430*700637cbSDimitry Andric sizeof(TotalFileSize));
431*700637cbSDimitry Andric OS.write(reinterpret_cast<const char *>(&UncompressedSize),
432*700637cbSDimitry Andric sizeof(UncompressedSize));
433*700637cbSDimitry Andric OS.write(reinterpret_cast<const char *>(&TruncatedHash),
434*700637cbSDimitry Andric sizeof(TruncatedHash));
435*700637cbSDimitry Andric OS.write(reinterpret_cast<const char *>(CompressedBuffer.data()),
436*700637cbSDimitry Andric CompressedBuffer.size());
437*700637cbSDimitry Andric
438*700637cbSDimitry Andric if (Verbose) {
439*700637cbSDimitry Andric auto MethodUsed =
440*700637cbSDimitry Andric P.format == llvm::compression::Format::Zstd ? "zstd" : "zlib";
441*700637cbSDimitry Andric double CompressionRate =
442*700637cbSDimitry Andric static_cast<double>(UncompressedSize) / CompressedBuffer.size();
443*700637cbSDimitry Andric double CompressionTimeSeconds = CompressTimer.getTotalTime().getWallTime();
444*700637cbSDimitry Andric double CompressionSpeedMBs =
445*700637cbSDimitry Andric (UncompressedSize / (1024.0 * 1024.0)) / CompressionTimeSeconds;
446*700637cbSDimitry Andric
447*700637cbSDimitry Andric llvm::errs() << "Compressed bundle format version: " << Version << "\n"
448*700637cbSDimitry Andric << "Total file size (including headers): "
449*700637cbSDimitry Andric << formatWithCommas(TotalFileSize) << " bytes\n"
450*700637cbSDimitry Andric << "Compression method used: " << MethodUsed << "\n"
451*700637cbSDimitry Andric << "Compression level: " << P.level << "\n"
452*700637cbSDimitry Andric << "Binary size before compression: "
453*700637cbSDimitry Andric << formatWithCommas(UncompressedSize) << " bytes\n"
454*700637cbSDimitry Andric << "Binary size after compression: "
455*700637cbSDimitry Andric << formatWithCommas(CompressedBuffer.size()) << " bytes\n"
456*700637cbSDimitry Andric << "Compression rate: "
457*700637cbSDimitry Andric << llvm::format("%.2lf", CompressionRate) << "\n"
458*700637cbSDimitry Andric << "Compression ratio: "
459*700637cbSDimitry Andric << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
460*700637cbSDimitry Andric << "Compression speed: "
461*700637cbSDimitry Andric << llvm::format("%.2lf MB/s", CompressionSpeedMBs) << "\n"
462*700637cbSDimitry Andric << "Truncated MD5 hash: "
463*700637cbSDimitry Andric << llvm::format_hex(TruncatedHash, 16) << "\n";
464*700637cbSDimitry Andric }
465*700637cbSDimitry Andric return llvm::MemoryBuffer::getMemBufferCopy(
466*700637cbSDimitry Andric llvm::StringRef(FinalBuffer.data(), FinalBuffer.size()));
467*700637cbSDimitry Andric }
468