Newer
Older

Eugene Zelenko
committed
//===-- clang-offload-bundler/ClangOffloadBundler.cpp ---------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file implements a clang-offload-bundler that bundles different
/// files that relate with the same source code but different targets into a
/// single one. Also the implements the opposite functionality, i.e. unbundle
/// files previous created by this tool.
///
//===----------------------------------------------------------------------===//
#include "clang/Basic/Version.h"

Eugene Zelenko
committed
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"

Eugene Zelenko
committed
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"

Eugene Zelenko
committed
#include "llvm/ADT/Triple.h"
#include "llvm/Bitcode/ReaderWriter.h"

Eugene Zelenko
committed
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"

Eugene Zelenko
committed
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/ObjectFile.h"

Eugene Zelenko
committed
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"

Eugene Zelenko
committed
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"

Eugene Zelenko
committed
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"

Eugene Zelenko
committed
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Signals.h"

Eugene Zelenko
committed
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <system_error>
#include <vector>
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
using namespace llvm;
using namespace llvm::object;
static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
// Mark all our options with this category, everything else (except for -version
// and -help) will be hidden.
static cl::OptionCategory
ClangOffloadBundlerCategory("clang-offload-bundler options");
static cl::list<std::string>
InputFileNames("inputs", cl::CommaSeparated, cl::OneOrMore,
cl::desc("[<input file>,...]"),
cl::cat(ClangOffloadBundlerCategory));
static cl::list<std::string>
OutputFileNames("outputs", cl::CommaSeparated, cl::OneOrMore,
cl::desc("[<output file>,...]"),
cl::cat(ClangOffloadBundlerCategory));
static cl::list<std::string>
TargetNames("targets", cl::CommaSeparated, cl::OneOrMore,
cl::desc("[<offload kind>-<target triple>,...]"),
cl::cat(ClangOffloadBundlerCategory));
static cl::opt<std::string>
FilesType("type", cl::Required,
cl::desc("Type of the files to be bundled/unbundled.\n"
"Current supported types are:\n"
" i - cpp-output\n"
" ii - c++-cpp-output\n"
" ll - llvm\n"
" bc - llvm-bc\n"
" s - assembler\n"
" o - object\n"
" gch - precompiled-header\n"
" ast - clang AST file"),
cl::cat(ClangOffloadBundlerCategory));
static cl::opt<bool>
Unbundle("unbundle",
cl::desc("Unbundle bundled file into several output files.\n"),
cl::init(false), cl::cat(ClangOffloadBundlerCategory));

Samuel Antao
committed
static cl::opt<bool> PrintExternalCommands(
"###",
cl::desc("Print any external commands that are to be executed "
"instead of actually executing them - for testing purposes.\n"),
cl::init(false), cl::cat(ClangOffloadBundlerCategory));
static cl::opt<bool> DumpTemporaryFiles(
"dump-temporary-files",
cl::desc("Dumps any temporary files created - for testing purposes.\n"),
cl::init(false), cl::cat(ClangOffloadBundlerCategory));
/// Magic string that marks the existence of offloading data.
#define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
/// The index of the host input in the list of inputs.
static unsigned HostInputIndex = ~0u;

Samuel Antao
committed
/// Path to the current binary.
static std::string BundlerExecutable;
/// Obtain the offload kind and real machine triple out of the target
/// information specified by the user.
static void getOffloadKindAndTriple(StringRef Target, StringRef &OffloadKind,
StringRef &Triple) {
auto KindTriplePair = Target.split('-');
OffloadKind = KindTriplePair.first;
Triple = KindTriplePair.second;
}

Samuel Antao
committed
static StringRef getTriple(StringRef Target) {
StringRef OffloadKind;
StringRef Triple;
getOffloadKindAndTriple(Target, OffloadKind, Triple);
return Triple;
}
static bool hasHostKind(StringRef Target) {
StringRef OffloadKind;
StringRef Triple;
getOffloadKindAndTriple(Target, OffloadKind, Triple);
return OffloadKind == "host";
}
/// Generic file handler interface.
class FileHandler {
public:

Eugene Zelenko
committed
FileHandler() {}
virtual ~FileHandler() {}
/// Update the file handler with information from the header of the bundled
/// file
virtual void ReadHeader(MemoryBuffer &Input) = 0;

Eugene Zelenko
committed
/// Read the marker of the next bundled to be read in the file. The triple of
/// the target associated with that bundle is returned. An empty string is
/// returned if there are no more bundles to be read.
virtual StringRef ReadBundleStart(MemoryBuffer &Input) = 0;

Eugene Zelenko
committed
/// Read the marker that closes the current bundle.
virtual void ReadBundleEnd(MemoryBuffer &Input) = 0;

Eugene Zelenko
committed
/// Read the current bundle and write the result into the stream \a OS.
virtual void ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
/// Write the header of the bundled file to \a OS based on the information
/// gathered from \a Inputs.
virtual void WriteHeader(raw_fd_ostream &OS,
ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0;

Eugene Zelenko
committed
/// Write the marker that initiates a bundle for the triple \a TargetTriple to
/// \a OS.
virtual void WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) = 0;

Eugene Zelenko
committed
/// Write the marker that closes a bundle for the triple \a TargetTriple to \a

Samuel Antao
committed
/// OS. Return true if any error was found.

Eugene Zelenko
committed

Samuel Antao
committed
virtual bool WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) = 0;

Eugene Zelenko
committed
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/// Write the bundle from \a Input into \a OS.
virtual void WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
};
/// Handler for binary files. The bundled file will have the following format
/// (all integers are stored in little-endian format):
///
/// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
///
/// NumberOfOffloadBundles (8-byte integer)
///
/// OffsetOfBundle1 (8-byte integer)
/// SizeOfBundle1 (8-byte integer)
/// NumberOfBytesInTripleOfBundle1 (8-byte integer)
/// TripleOfBundle1 (byte length defined before)
///
/// ...
///
/// OffsetOfBundleN (8-byte integer)
/// SizeOfBundleN (8-byte integer)
/// NumberOfBytesInTripleOfBundleN (8-byte integer)
/// TripleOfBundleN (byte length defined before)
///
/// Bundle1
/// ...
/// BundleN
/// Read 8-byte integers from a buffer in little-endian format.
static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) {
uint64_t Res = 0;
const char *Data = Buffer.data();
for (unsigned i = 0; i < 8; ++i) {
Res <<= 8;
uint64_t Char = (uint64_t)Data[pos + 7 - i];
Res |= 0xffu & Char;
}
return Res;
}
/// Write 8-byte integers to a buffer in little-endian format.
static void Write8byteIntegerToBuffer(raw_fd_ostream &OS, uint64_t Val) {
for (unsigned i = 0; i < 8; ++i) {
char Char = (char)(Val & 0xffu);
OS.write(&Char, 1);
Val >>= 8;
}
}
class BinaryFileHandler final : public FileHandler {
/// Information about the bundles extracted from the header.
struct BundleInfo final {
/// Size of the bundle.
uint64_t Size = 0u;
/// Offset at which the bundle starts in the bundled file.
uint64_t Offset = 0u;

Eugene Zelenko
committed
BundleInfo() {}
BundleInfo(uint64_t Size, uint64_t Offset) : Size(Size), Offset(Offset) {}
};

Eugene Zelenko
committed
/// Map between a triple and the corresponding bundle information.
StringMap<BundleInfo> BundlesInfo;
/// Iterator for the bundle information that is being read.
StringMap<BundleInfo>::iterator CurBundleInfo;
public:

Eugene Zelenko
committed
BinaryFileHandler() : FileHandler() {}
~BinaryFileHandler() final {}
void ReadHeader(MemoryBuffer &Input) final {
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
StringRef FC = Input.getBuffer();
// Initialize the current bundle with the end of the container.
CurBundleInfo = BundlesInfo.end();
// Check if buffer is smaller than magic string.
size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
if (ReadChars > FC.size())
return;
// Check if no magic was found.
StringRef Magic(FC.data(), sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
if (!Magic.equals(OFFLOAD_BUNDLER_MAGIC_STR))
return;
// Read number of bundles.
if (ReadChars + 8 > FC.size())
return;
uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
ReadChars += 8;
// Read bundle offsets, sizes and triples.
for (uint64_t i = 0; i < NumberOfBundles; ++i) {
// Read offset.
if (ReadChars + 8 > FC.size())
return;
uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
ReadChars += 8;
// Read size.
if (ReadChars + 8 > FC.size())
return;
uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
ReadChars += 8;
// Read triple size.
if (ReadChars + 8 > FC.size())
return;
uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
ReadChars += 8;
// Read triple.
if (ReadChars + TripleSize > FC.size())
return;
StringRef Triple(&FC.data()[ReadChars], TripleSize);
ReadChars += TripleSize;
// Check if the offset and size make sense.
if (!Size || !Offset || Offset + Size > FC.size())
return;
assert(BundlesInfo.find(Triple) == BundlesInfo.end() &&
"Triple is duplicated??");
BundlesInfo[Triple] = BundleInfo(Size, Offset);
}
// Set the iterator to where we will start to read.
CurBundleInfo = BundlesInfo.begin();
}

Eugene Zelenko
committed
StringRef ReadBundleStart(MemoryBuffer &Input) final {
if (CurBundleInfo == BundlesInfo.end())
return StringRef();
return CurBundleInfo->first();
}

Eugene Zelenko
committed
void ReadBundleEnd(MemoryBuffer &Input) final {
assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
++CurBundleInfo;
}

Eugene Zelenko
committed
void ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
StringRef FC = Input.getBuffer();
OS.write(FC.data() + CurBundleInfo->second.Offset,
CurBundleInfo->second.Size);
}
void WriteHeader(raw_fd_ostream &OS,

Eugene Zelenko
committed
ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
// Compute size of the header.
uint64_t HeaderSize = 0;
HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
HeaderSize += 8; // Number of Bundles
for (auto &T : TargetNames) {
HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple.
HeaderSize += T.size(); // The triple.
}
// Write to the buffer the header.
OS << OFFLOAD_BUNDLER_MAGIC_STR;
Write8byteIntegerToBuffer(OS, TargetNames.size());
unsigned Idx = 0;
for (auto &T : TargetNames) {
MemoryBuffer &MB = *Inputs[Idx++].get();
// Bundle offset.
Write8byteIntegerToBuffer(OS, HeaderSize);
// Size of the bundle (adds to the next bundle's offset)
Write8byteIntegerToBuffer(OS, MB.getBufferSize());
HeaderSize += MB.getBufferSize();
// Size of the triple
Write8byteIntegerToBuffer(OS, T.size());
// Triple
OS << T;
}
}

Eugene Zelenko
committed
void WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {}
bool WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {

Samuel Antao
committed
return false;
}

Eugene Zelenko
committed
void WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
OS.write(Input.getBufferStart(), Input.getBufferSize());
}
};

Samuel Antao
committed
/// Handler for object files. The bundles are organized by sections with a
/// designated name.
///
/// In order to bundle we create an IR file with the content of each section and
/// use incremental linking to produce the resulting object. We also add section
/// with a single byte to state the name of the component the main object file
/// (the one we are bundling into) refers to.
///
/// To unbundle, we use just copy the contents of the designated section. If the
/// requested bundle refer to the main object file, we just copy it with no
/// changes.
class ObjectFileHandler final : public FileHandler {
/// The object file we are currently dealing with.
std::unique_ptr<ObjectFile> Obj;

Samuel Antao
committed
/// Return the input file contents.
StringRef getInputFileContents() const { return Obj->getData(); }

Samuel Antao
committed
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
/// Return true if the provided section is an offload section and return the
/// triple by reference.
static bool IsOffloadSection(SectionRef CurSection,
StringRef &OffloadTriple) {
StringRef SectionName;
CurSection.getName(SectionName);
if (SectionName.empty())
return false;
// If it does not start with the reserved suffix, just skip this section.
if (!SectionName.startswith(OFFLOAD_BUNDLER_MAGIC_STR))
return false;
// Return the triple that is right after the reserved prefix.
OffloadTriple = SectionName.substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
return true;
}
/// Total number of inputs.
unsigned NumberOfInputs = 0;
/// Total number of processed inputs, i.e, inputs that were already
/// read from the buffers.
unsigned NumberOfProcessedInputs = 0;
/// LLVM context used to to create the auxiliar modules.
LLVMContext VMContext;
/// LLVM module used to create an object with all the bundle
/// components.
std::unique_ptr<Module> AuxModule;
/// The current triple we are working with.
StringRef CurrentTriple;
/// The name of the main input file.
StringRef MainInputFileName;
/// Iterator of the current and next section.
section_iterator CurrentSection;
section_iterator NextSection;
public:

Eugene Zelenko
committed
ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn)
: FileHandler(), Obj(std::move(ObjIn)),
CurrentSection(Obj->section_begin()),
NextSection(Obj->section_begin()) {}

Samuel Antao
committed

Eugene Zelenko
committed
~ObjectFileHandler() final {}
void ReadHeader(MemoryBuffer &Input) final {}
StringRef ReadBundleStart(MemoryBuffer &Input) final {
while (NextSection != Obj->section_end()) {

Samuel Antao
committed
CurrentSection = NextSection;
++NextSection;
StringRef OffloadTriple;
// Check if the current section name starts with the reserved prefix. If
// so, return the triple.
if (IsOffloadSection(*CurrentSection, OffloadTriple))
return OffloadTriple;
}
return StringRef();
}

Eugene Zelenko
committed
void ReadBundleEnd(MemoryBuffer &Input) final {}
void ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {

Samuel Antao
committed
// If the current section has size one, that means that the content we are
// interested in is the file itself. Otherwise it is the content of the
// section.
//
// TODO: Instead of copying the input file as is, deactivate the section
// that is no longer needed.
StringRef Content;
CurrentSection->getContents(Content);
if (Content.size() < 2)
OS.write(Input.getBufferStart(), Input.getBufferSize());
else
OS.write(Content.data(), Content.size());
}
void WriteHeader(raw_fd_ostream &OS,

Eugene Zelenko
committed
ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {

Samuel Antao
committed
assert(HostInputIndex != ~0u && "Host input index not defined.");
// Record number of inputs.
NumberOfInputs = Inputs.size();
// Create an LLVM module to have the content we need to bundle.
auto *M = new Module("clang-offload-bundle", VMContext);
M->setTargetTriple(getTriple(TargetNames[HostInputIndex]));
AuxModule.reset(M);
}

Eugene Zelenko
committed
void WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {

Samuel Antao
committed
++NumberOfProcessedInputs;
// Record the triple we are using, that will be used to name the section we
// will create.
CurrentTriple = TargetTriple;
}

Eugene Zelenko
committed
bool WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {

Samuel Antao
committed
assert(NumberOfProcessedInputs <= NumberOfInputs &&
"Processing more inputs that actually exist!");
assert(HostInputIndex != ~0u && "Host input index not defined.");
// If this is not the last output, we don't have to do anything.
if (NumberOfProcessedInputs != NumberOfInputs)
return false;
// Create the bitcode file name to write the resulting code to. Keep it if
// save-temps is active.
SmallString<128> BitcodeFileName;
if (sys::fs::createTemporaryFile("clang-offload-bundler", "bc",
BitcodeFileName)) {

Eugene Zelenko
committed
errs() << "error: unable to create temporary file.\n";

Samuel Antao
committed
return true;
}
// Dump the contents of the temporary file if that was requested.
if (DumpTemporaryFiles) {

Eugene Zelenko
committed
errs() << ";\n; Object file bundler IR file.\n;\n";

Samuel Antao
committed
AuxModule.get()->dump();
}
// Find clang in order to create the bundle binary.

Eugene Zelenko
committed
StringRef Dir = sys::path::parent_path(BundlerExecutable);

Samuel Antao
committed
auto ClangBinary = sys::findProgramByName("clang", Dir);
if (ClangBinary.getError()) {
// Remove bitcode file.
sys::fs::remove(BitcodeFileName);

Eugene Zelenko
committed
errs() << "error: unable to find 'clang' in path.\n";

Samuel Antao
committed
return true;
}
// Do the incremental linking. We write to the output file directly. So, we
// close it and use the name to pass down to clang.
OS.close();
SmallString<128> TargetName = getTriple(TargetNames[HostInputIndex]);
const char *ClangArgs[] = {"clang",
"-r",
"-target",
TargetName.c_str(),
"-o",
OutputFileNames.front().c_str(),
InputFileNames[HostInputIndex].c_str(),
BitcodeFileName.c_str(),
"-nostdlib",
nullptr};
// If the user asked for the commands to be printed out, we do that instead
// of executing it.
if (PrintExternalCommands) {

Eugene Zelenko
committed
errs() << "\"" << ClangBinary.get() << "\"";

Samuel Antao
committed
for (unsigned I = 1; ClangArgs[I]; ++I)

Eugene Zelenko
committed
errs() << " \"" << ClangArgs[I] << "\"";
errs() << "\n";

Samuel Antao
committed
} else {
// Write the bitcode contents to the temporary file.
{
std::error_code EC;
raw_fd_ostream BitcodeFile(BitcodeFileName, EC, sys::fs::F_None);
if (EC) {

Eugene Zelenko
committed
errs() << "error: unable to open temporary file.\n";

Samuel Antao
committed
return true;
}
WriteBitcodeToFile(AuxModule.get(), BitcodeFile);
}
bool Failed = sys::ExecuteAndWait(ClangBinary.get(), ClangArgs);
// Remove bitcode file.
sys::fs::remove(BitcodeFileName);
if (Failed) {

Eugene Zelenko
committed
errs() << "error: incremental linking by external tool failed.\n";

Samuel Antao
committed
return true;
}
}
return false;
}

Eugene Zelenko
committed
void WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {

Samuel Antao
committed
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
Module *M = AuxModule.get();
// Create the new section name, it will consist of the reserved prefix
// concatenated with the triple.
std::string SectionName = OFFLOAD_BUNDLER_MAGIC_STR;
SectionName += CurrentTriple;
// Create the constant with the content of the section. For the input we are
// bundling into (the host input), this is just a place-holder, so a single
// byte is sufficient.
assert(HostInputIndex != ~0u && "Host input index undefined??");
Constant *Content;
if (NumberOfProcessedInputs == HostInputIndex + 1) {
uint8_t Byte[] = {0};
Content = ConstantDataArray::get(VMContext, Byte);
} else
Content = ConstantDataArray::get(
VMContext, ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(
Input.getBufferStart()),
Input.getBufferSize()));
// Create the global in the desired section. We don't want these globals in
// the symbol table, so we mark them private.
auto *GV = new GlobalVariable(*M, Content->getType(), /*IsConstant=*/true,
GlobalVariable::PrivateLinkage, Content);
GV->setSection(SectionName);
}
};
/// Handler for text files. The bundled file will have the following format.
///
/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
/// Bundle 1
/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
/// ...
/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
/// Bundle N
/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
class TextFileHandler final : public FileHandler {
/// String that begins a line comment.
StringRef Comment;
/// String that initiates a bundle.
std::string BundleStartString;
/// String that closes a bundle.
std::string BundleEndString;
/// Number of chars read from input.
size_t ReadChars = 0u;
protected:

Eugene Zelenko
committed
void ReadHeader(MemoryBuffer &Input) final {}
StringRef ReadBundleStart(MemoryBuffer &Input) final {
StringRef FC = Input.getBuffer();
// Find start of the bundle.
ReadChars = FC.find(BundleStartString, ReadChars);
if (ReadChars == FC.npos)
return StringRef();
// Get position of the triple.
size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
// Get position that closes the triple.
size_t TripleEnd = ReadChars = FC.find("\n", ReadChars);
if (TripleEnd == FC.npos)
return StringRef();
// Next time we read after the new line.
++ReadChars;
return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
}

Eugene Zelenko
committed
void ReadBundleEnd(MemoryBuffer &Input) final {
StringRef FC = Input.getBuffer();
// Read up to the next new line.
assert(FC[ReadChars] == '\n' && "The bundle should end with a new line.");
size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1);
if (TripleEnd == FC.npos)
return;
// Next time we read after the new line.
++ReadChars;
}

Eugene Zelenko
committed
void ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
StringRef FC = Input.getBuffer();
size_t BundleStart = ReadChars;
// Find end of the bundle.
size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
OS << Bundle;
}
void WriteHeader(raw_fd_ostream &OS,

Eugene Zelenko
committed
ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {}
void WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
OS << BundleStartString << TargetTriple << "\n";
}

Eugene Zelenko
committed
bool WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
OS << BundleEndString << TargetTriple << "\n";

Samuel Antao
committed
return false;

Eugene Zelenko
committed
void WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
OS << Input.getBuffer();
}
public:
TextFileHandler(StringRef Comment)
: FileHandler(), Comment(Comment), ReadChars(0) {
BundleStartString =
"\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ ";
BundleEndString =
"\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ ";
}
};

Samuel Antao
committed
/// Return an appropriate object file handler. We use the specific object
/// handler if we know how to deal with that format, otherwise we use a default
/// binary file handler.
static FileHandler *CreateObjectFileHandler(MemoryBuffer &FirstInput) {
// Check if the input file format is one that we know how to deal with.
Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput);
// Failed to open the input as a known binary. Use the default binary handler.
if (!BinaryOrErr) {
// We don't really care about the error (we just consume it), if we could
// not get a valid device binary object we use the default binary handler.
consumeError(BinaryOrErr.takeError());
return new BinaryFileHandler();
}
// We only support regular object files. If this is not an object file,
// default to the binary handler. The handler will be owned by the client of
// this function.
std::unique_ptr<ObjectFile> Obj(
dyn_cast<ObjectFile>(BinaryOrErr.get().release()));

Samuel Antao
committed
if (!Obj)
return new BinaryFileHandler();
return new ObjectFileHandler(std::move(Obj));

Samuel Antao
committed
}
/// Return an appropriate handler given the input files and options.
static FileHandler *CreateFileHandler(MemoryBuffer &FirstInput) {
if (FilesType == "i")
return new TextFileHandler(/*Comment=*/"//");
if (FilesType == "ii")
return new TextFileHandler(/*Comment=*/"//");
if (FilesType == "ll")
return new TextFileHandler(/*Comment=*/";");
if (FilesType == "bc")
return new BinaryFileHandler();
if (FilesType == "s")
return new TextFileHandler(/*Comment=*/"#");
if (FilesType == "o")

Samuel Antao
committed
return CreateObjectFileHandler(FirstInput);
if (FilesType == "gch")
return new BinaryFileHandler();
if (FilesType == "ast")
return new BinaryFileHandler();

Eugene Zelenko
committed
errs() << "error: invalid file type specified.\n";
return nullptr;
}
/// Bundle the files. Return true if an error was found.
static bool BundleFiles() {
std::error_code EC;
// Create output file.
raw_fd_ostream OutputFile(OutputFileNames.front(), EC, sys::fs::F_None);
if (EC) {

Eugene Zelenko
committed
errs() << "error: Can't open file " << OutputFileNames.front() << ".\n";
return true;
}
// Open input files.
std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers(
InputFileNames.size());
unsigned Idx = 0;
for (auto &I : InputFileNames) {
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
MemoryBuffer::getFileOrSTDIN(I);
if (std::error_code EC = CodeOrErr.getError()) {

Eugene Zelenko
committed
errs() << "error: Can't open file " << I << ": " << EC.message() << "\n";
return true;
}
InputBuffers[Idx++] = std::move(CodeOrErr.get());
}
// Get the file handler. We use the host buffer as reference.
assert(HostInputIndex != ~0u && "Host input index undefined??");
std::unique_ptr<FileHandler> FH;
FH.reset(CreateFileHandler(*InputBuffers[HostInputIndex].get()));
// Quit if we don't have a handler.
if (!FH.get())
return true;
// Write header.
FH.get()->WriteHeader(OutputFile, InputBuffers);

Samuel Antao
committed
// Write all bundles along with the start/end markers. If an error was found
// writing the end of the bundle component, abort the bundle writing.
auto Input = InputBuffers.begin();
for (auto &Triple : TargetNames) {
FH.get()->WriteBundleStart(OutputFile, Triple);
FH.get()->WriteBundle(OutputFile, *Input->get());

Samuel Antao
committed
if (FH.get()->WriteBundleEnd(OutputFile, Triple))
return true;
++Input;
}
return false;
}
// Unbundle the files. Return true if an error was found.
static bool UnbundleFiles() {
// Open Input file.
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
MemoryBuffer::getFileOrSTDIN(InputFileNames.front());
if (std::error_code EC = CodeOrErr.getError()) {

Eugene Zelenko
committed
errs() << "error: Can't open file " << InputFileNames.front() << ": "
<< EC.message() << "\n";
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
return true;
}
MemoryBuffer &Input = *CodeOrErr.get();
// Select the right files handler.
std::unique_ptr<FileHandler> FH;
FH.reset(CreateFileHandler(Input));
// Quit if we don't have a handler.
if (!FH.get())
return true;
// Read the header of the bundled file.
FH.get()->ReadHeader(Input);
// Create a work list that consist of the map triple/output file.
StringMap<StringRef> Worklist;
auto Output = OutputFileNames.begin();
for (auto &Triple : TargetNames) {
Worklist[Triple] = *Output;
++Output;
}
// Read all the bundles that are in the work list. If we find no bundles we
// assume the file is meant for the host target.
bool FoundHostBundle = false;
while (!Worklist.empty()) {
StringRef CurTriple = FH.get()->ReadBundleStart(Input);
// We don't have more bundles.
if (CurTriple.empty())
break;
auto Output = Worklist.find(CurTriple);
// The file may have more bundles for other targets, that we don't care
// about. Therefore, move on to the next triple
if (Output == Worklist.end()) {
continue;
}
// Check if the output file can be opened and copy the bundle to it.
std::error_code EC;
raw_fd_ostream OutputFile(Output->second, EC, sys::fs::F_None);
if (EC) {

Eugene Zelenko
committed
errs() << "error: Can't open file " << Output->second << ": "
<< EC.message() << "\n";
return true;
}
FH.get()->ReadBundle(OutputFile, Input);
FH.get()->ReadBundleEnd(Input);
// Record if we found the host bundle.
if (hasHostKind(CurTriple))
FoundHostBundle = true;
}
// If no bundles were found, assume the input file is the host bundle and
// create empty files for the remaining targets.
if (Worklist.size() == TargetNames.size()) {
for (auto &E : Worklist) {
std::error_code EC;
raw_fd_ostream OutputFile(E.second, EC, sys::fs::F_None);
if (EC) {

Eugene Zelenko
committed
errs() << "error: Can't open file " << E.second << ": " << EC.message()
<< "\n";
return true;
}
// If this entry has a host kind, copy the input file to the output file.
if (hasHostKind(E.first()))
OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
}
return false;
}
// If we found elements, we emit an error if none of those were for the host.
if (!FoundHostBundle) {

Eugene Zelenko
committed
errs() << "error: Can't find bundle for the host target\n";
return true;
}
// If we still have any elements in the worklist, create empty files for them.
for (auto &E : Worklist) {
std::error_code EC;
raw_fd_ostream OutputFile(E.second, EC, sys::fs::F_None);
if (EC) {

Eugene Zelenko
committed
errs() << "error: Can't open file " << E.second << ": " << EC.message()
<< "\n";
return true;
}
}
return false;
}
static void PrintVersion() {
raw_ostream &OS = outs();
OS << clang::getClangToolFullVersion("clang-offload-bundler") << '\n';
}
int main(int argc, const char **argv) {

Eugene Zelenko
committed
sys::PrintStackTraceOnErrorSignal(argv[0]);
cl::HideUnrelatedOptions(ClangOffloadBundlerCategory);
cl::SetVersionPrinter(PrintVersion);
cl::ParseCommandLineOptions(
argc, argv,
"A tool to bundle several input files of the specified type <type> \n"
"referring to the same source file but different targets into a single \n"
"one. The resulting file can also be unbundled into different files by \n"
"this tool if -unbundle is provided.\n");
if (Help)
cl::PrintHelpMessage();
bool Error = false;
if (Unbundle) {
if (InputFileNames.size() != 1) {
Error = true;

Eugene Zelenko
committed
errs() << "error: only one input file supported in unbundling mode.\n";
}
if (OutputFileNames.size() != TargetNames.size()) {
Error = true;

Eugene Zelenko
committed
errs() << "error: number of output files and targets should match in "
"unbundling mode.\n";
}
} else {
if (OutputFileNames.size() != 1) {
Error = true;

Eugene Zelenko
committed
errs() << "error: only one output file supported in bundling mode.\n";
}
if (InputFileNames.size() != TargetNames.size()) {
Error = true;

Eugene Zelenko
committed
errs() << "error: number of input files and targets should match in "
"bundling mode.\n";
}
}
// Verify that the offload kinds and triples are known. We also check that we
// have exactly one host target.
unsigned Index = 0u;
unsigned HostTargetNum = 0u;
for (StringRef Target : TargetNames) {
StringRef Kind;
StringRef Triple;
getOffloadKindAndTriple(Target, Kind, Triple);
bool KindIsValid = !Kind.empty();
KindIsValid = KindIsValid &&
StringSwitch<bool>(Kind)
.Case("host", true)
.Case("openmp", true)
.Default(false);
bool TripleIsValid = !Triple.empty();
llvm::Triple T(Triple);
TripleIsValid &= T.getArch() != Triple::UnknownArch;
if (!KindIsValid || !TripleIsValid) {
Error = true;

Eugene Zelenko
committed
errs() << "error: invalid target '" << Target << "'";
if (!KindIsValid)

Eugene Zelenko
committed
errs() << ", unknown offloading kind '" << Kind << "'";
if (!TripleIsValid)

Eugene Zelenko
committed
errs() << ", unknown target triple '" << Triple << "'";
errs() << ".\n";
}
if (KindIsValid && Kind == "host") {
++HostTargetNum;
// Save the index of the input that refers to the host.
HostInputIndex = Index;
}
++Index;
}
if (HostTargetNum != 1) {
Error = true;

Eugene Zelenko
committed
errs() << "error: expecting exactly one host target but got "