xref: /freebsd/contrib/llvm-project/llvm/lib/Object/OffloadBundle.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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