blob: 8e98fab2a8a4be8c4c3deedab0e031d9c3aba429 [file] [log] [blame]
//===-- clang-offload-packager/ClangOffloadPackager.cpp - file bundler ---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===---------------------------------------------------------------------===//
//
// This tool takes several device object files and bundles them into a single
// binary image using a custom binary format. This is intended to be used to
// embed many device files into an application to create a fat binary.
//
//===---------------------------------------------------------------------===//
#include "clang/Basic/Version.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/OffloadBinary.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/WithColor.h"
using namespace llvm;
using namespace llvm::object;
static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
static cl::OptionCategory
ClangOffloadPackagerCategory("clang-offload-packager options");
static cl::opt<std::string> OutputFile("o", cl::Required,
cl::desc("Write output to <file>."),
cl::value_desc("file"),
cl::cat(ClangOffloadPackagerCategory));
static cl::list<std::string>
DeviceImages("image",
cl::desc("List of key and value arguments. Required keywords "
"are 'file' and 'triple'."),
cl::value_desc("<key>=<value>,..."),
cl::cat(ClangOffloadPackagerCategory));
static void PrintVersion(raw_ostream &OS) {
OS << clang::getClangToolFullVersion("clang-offload-packager") << '\n';
}
int main(int argc, const char **argv) {
sys::PrintStackTraceOnErrorSignal(argv[0]);
cl::HideUnrelatedOptions(ClangOffloadPackagerCategory);
cl::SetVersionPrinter(PrintVersion);
cl::ParseCommandLineOptions(
argc, argv,
"A utility for bundling several object files into a single binary.\n"
"The output binary can then be embedded into the host section table\n"
"to create a fatbinary containing offloading code.\n");
if (Help) {
cl::PrintHelpMessage();
return EXIT_SUCCESS;
}
auto reportError = [argv](Error E) {
logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
return EXIT_FAILURE;
};
SmallVector<char, 1024> BinaryData;
raw_svector_ostream OS(BinaryData);
for (StringRef Image : DeviceImages) {
BumpPtrAllocator Alloc;
StringSaver Saver(Alloc);
StringMap<StringRef> Args;
for (StringRef Arg : llvm::split(Image, ",")) {
auto KeyAndValue = Arg.split("=");
if (Args.count(KeyAndValue.first))
Args[KeyAndValue.first] =
Saver.save(Args[KeyAndValue.first] + "," + KeyAndValue.second);
else
Args[KeyAndValue.first] = KeyAndValue.second;
}
if (!Args.count("triple") || !Args.count("file"))
return reportError(createStringError(
inconvertibleErrorCode(),
"'file' and 'triple' are required image arguments"));
OffloadBinary::OffloadingImage ImageBinary{};
std::unique_ptr<llvm::MemoryBuffer> DeviceImage;
for (const auto &KeyAndValue : Args) {
StringRef Key = KeyAndValue.getKey();
if (Key == "file") {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ObjectOrErr =
llvm::MemoryBuffer::getFileOrSTDIN(KeyAndValue.getValue());
if (std::error_code EC = ObjectOrErr.getError())
return reportError(errorCodeToError(EC));
// Clang uses the '.o' suffix for LTO bitcode.
if (identify_magic((*ObjectOrErr)->getBuffer()) == file_magic::bitcode)
ImageBinary.TheImageKind = object::IMG_Bitcode;
else
ImageBinary.TheImageKind = getImageKind(
sys::path::extension(KeyAndValue.getValue()).drop_front());
ImageBinary.Image = std::move(*ObjectOrErr);
} else if (Key == "kind") {
ImageBinary.TheOffloadKind = getOffloadKind(KeyAndValue.getValue());
} else {
ImageBinary.StringData[Key] = KeyAndValue.getValue();
}
}
std::unique_ptr<MemoryBuffer> Buffer = OffloadBinary::write(ImageBinary);
if (Buffer->getBufferSize() % OffloadBinary::getAlignment() != 0)
return reportError(
createStringError(inconvertibleErrorCode(),
"Offload binary has invalid size alignment"));
OS << Buffer->getBuffer();
}
Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
FileOutputBuffer::create(OutputFile, BinaryData.size());
if (!OutputOrErr)
return reportError(OutputOrErr.takeError());
std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
std::copy(BinaryData.begin(), BinaryData.end(), Output->getBufferStart());
if (Error E = Output->commit())
return reportError(std::move(E));
}