blob: e18da35e79b63e02f0b4fb0d58fa0cd4895e26a5 [file] [log] [blame]
//===-- clang-offload-wrapper/ClangOffloadWrapper.cpp -----------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Implementation of the offload wrapper tool. It takes offload target binaries
/// as input and creates wrapper bitcode file containing target binaries
/// packaged as data.
///
//===----------------------------------------------------------------------===//
#include "clang/Basic/Version.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstdint>
using namespace llvm;
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
ClangOffloadWrapperCategory("clang-offload-wrapper options");
static cl::opt<std::string> Output("o", cl::Required,
cl::desc("Output filename"),
cl::value_desc("filename"),
cl::cat(ClangOffloadWrapperCategory));
static cl::list<std::string> Inputs(cl::Positional, cl::OneOrMore,
cl::desc("<input files>"),
cl::cat(ClangOffloadWrapperCategory));
static cl::opt<std::string>
Target("target", cl::Required,
cl::desc("Target triple for the output module"),
cl::value_desc("triple"), cl::cat(ClangOffloadWrapperCategory));
static cl::list<std::string>
OffloadTargets("offload-targets", cl::CommaSeparated, cl::OneOrMore,
cl::desc("Comma-separated list of device target triples"),
cl::value_desc("triples"),
cl::cat(ClangOffloadWrapperCategory));
namespace {
class BinaryWrapper {
public:
// Binary descriptor. The first field is the a reference to the binary bits,
// and the second is the target triple the binary was built for.
using BinaryDesc = std::pair<ArrayRef<char>, StringRef>;
private:
LLVMContext C;
Module M;
// Saver for generated strings.
BumpPtrAllocator Alloc;
UniqueStringSaver SS;
private:
void createImages(ArrayRef<BinaryDesc> Binaries) {
for (const BinaryDesc &Bin : Binaries) {
StringRef SectionName = SS.save(".omp_offloading." + Bin.second);
auto *DataC = ConstantDataArray::get(C, Bin.first);
auto *ImageB =
new GlobalVariable(M, DataC->getType(), /*isConstant=*/true,
GlobalVariable::ExternalLinkage, DataC,
".omp_offloading.img_start." + Bin.second);
ImageB->setSection(SectionName);
ImageB->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
ImageB->setVisibility(llvm::GlobalValue::HiddenVisibility);
auto *EmptyC =
ConstantAggregateZero::get(ArrayType::get(Type::getInt8Ty(C), 0u));
auto *ImageE =
new GlobalVariable(M, EmptyC->getType(), /*isConstant=*/true,
GlobalVariable::ExternalLinkage, EmptyC,
".omp_offloading.img_end." + Bin.second);
ImageE->setSection(SectionName);
ImageE->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
ImageE->setVisibility(GlobalValue::HiddenVisibility);
}
}
public:
BinaryWrapper(StringRef Target) : M("offload.wrapper.object", C), SS(Alloc) {
M.setTargetTriple(Target);
}
const Module &wrapBinaries(ArrayRef<BinaryDesc> Binaries) {
createImages(Binaries);
return M;
}
};
} // anonymous namespace
int main(int argc, const char **argv) {
sys::PrintStackTraceOnErrorSignal(argv[0]);
cl::HideUnrelatedOptions(ClangOffloadWrapperCategory);
cl::SetVersionPrinter([](raw_ostream &OS) {
OS << clang::getClangToolFullVersion("clang-offload-wrapper") << '\n';
});
cl::ParseCommandLineOptions(
argc, argv,
"A tool to create a wrapper bitcode for offload target binaries. Takes "
"offload\ntarget binaries as input and produces bitcode file containing "
"target binaries packaged\nas data.\n");
if (Help) {
cl::PrintHelpMessage();
return 0;
}
auto reportError = [argv](Error E) {
logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
};
if (Triple(Target).getArch() == Triple::UnknownArch) {
reportError(createStringError(
errc::invalid_argument, "'" + Target + "': unsupported target triple"));
return 1;
}
if (Inputs.size() != OffloadTargets.size()) {
reportError(createStringError(
errc::invalid_argument,
"number of input files and offload targets should match"));
return 1;
}
// Read device binaries.
SmallVector<std::unique_ptr<MemoryBuffer>, 4u> Buffers;
SmallVector<BinaryWrapper::BinaryDesc, 4u> Images;
Buffers.reserve(Inputs.size());
Images.reserve(Inputs.size());
for (unsigned I = 0; I < Inputs.size(); ++I) {
const std::string &File = Inputs[I];
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
MemoryBuffer::getFileOrSTDIN(File);
if (!BufOrErr) {
reportError(createFileError(File, BufOrErr.getError()));
return 1;
}
const std::unique_ptr<MemoryBuffer> &Buf =
Buffers.emplace_back(std::move(*BufOrErr));
Images.emplace_back(
makeArrayRef(Buf->getBufferStart(), Buf->getBufferSize()),
OffloadTargets[I]);
}
// Create the output file to write the resulting bitcode to.
std::error_code EC;
ToolOutputFile Out(Output, EC, sys::fs::OF_None);
if (EC) {
reportError(createFileError(Output, EC));
return 1;
}
// Create a wrapper for device binaries and write its bitcode to the file.
WriteBitcodeToFile(BinaryWrapper(Target).wrapBinaries(
makeArrayRef(Images.data(), Images.size())),
Out.os());
if (Out.os().has_error()) {
reportError(createFileError(Output, Out.os().error()));
return 1;
}
// Success.
Out.keep();
return 0;
}