2021-12-30 16:41:36 -05:00
|
|
|
//===-- clang-linker-wrapper/ClangLinkerWrapper.cpp - wrapper over linker-===//
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
//===---------------------------------------------------------------------===//
|
2022-01-03 12:31:52 -05:00
|
|
|
//
|
|
|
|
// This tool works as a wrapper over a linking job. This tool is used to create
|
|
|
|
// linked device images for offloading. It scans the linker's input for embedded
|
|
|
|
// device offloading data stored in sections `.llvm.offloading.<triple>.<arch>`
|
|
|
|
// and extracts it as a temporary file. The extracted device files will then be
|
|
|
|
// passed to a device linking job to create a final device image.
|
|
|
|
//
|
2021-12-30 16:41:36 -05:00
|
|
|
//===---------------------------------------------------------------------===//
|
|
|
|
|
2022-01-25 17:46:01 -05:00
|
|
|
#include "OffloadWrapper.h"
|
2021-12-30 16:41:36 -05:00
|
|
|
#include "clang/Basic/Version.h"
|
2022-01-03 12:31:52 -05:00
|
|
|
#include "llvm/BinaryFormat/Magic.h"
|
|
|
|
#include "llvm/Bitcode/BitcodeWriter.h"
|
2022-01-07 17:12:51 -05:00
|
|
|
#include "llvm/CodeGen/CommandFlags.h"
|
2022-01-03 12:31:52 -05:00
|
|
|
#include "llvm/IR/Constants.h"
|
2022-01-07 17:12:51 -05:00
|
|
|
#include "llvm/IR/DiagnosticPrinter.h"
|
2022-01-03 12:31:52 -05:00
|
|
|
#include "llvm/IR/Module.h"
|
|
|
|
#include "llvm/IRReader/IRReader.h"
|
2022-01-07 17:12:51 -05:00
|
|
|
#include "llvm/LTO/LTO.h"
|
2022-01-25 14:25:39 -05:00
|
|
|
#include "llvm/MC/TargetRegistry.h"
|
2021-12-30 16:41:36 -05:00
|
|
|
#include "llvm/Object/Archive.h"
|
2022-01-03 12:31:52 -05:00
|
|
|
#include "llvm/Object/ArchiveWriter.h"
|
|
|
|
#include "llvm/Object/Binary.h"
|
|
|
|
#include "llvm/Object/ObjectFile.h"
|
2022-03-25 11:03:02 -04:00
|
|
|
#include "llvm/Object/OffloadBinary.h"
|
2021-12-30 16:41:36 -05:00
|
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
#include "llvm/Support/Errc.h"
|
2022-01-03 12:31:52 -05:00
|
|
|
#include "llvm/Support/FileOutputBuffer.h"
|
2021-12-30 16:41:36 -05:00
|
|
|
#include "llvm/Support/FileSystem.h"
|
2022-01-04 17:20:04 -05:00
|
|
|
#include "llvm/Support/Host.h"
|
2022-01-03 12:31:52 -05:00
|
|
|
#include "llvm/Support/InitLLVM.h"
|
2021-12-30 16:41:36 -05:00
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
|
|
#include "llvm/Support/Path.h"
|
|
|
|
#include "llvm/Support/Program.h"
|
|
|
|
#include "llvm/Support/Signals.h"
|
2022-01-03 12:31:52 -05:00
|
|
|
#include "llvm/Support/SourceMgr.h"
|
2021-12-30 16:41:36 -05:00
|
|
|
#include "llvm/Support/StringSaver.h"
|
2022-01-07 17:12:51 -05:00
|
|
|
#include "llvm/Support/TargetSelect.h"
|
2021-12-30 16:41:36 -05:00
|
|
|
#include "llvm/Support/WithColor.h"
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2022-01-25 14:25:39 -05:00
|
|
|
#include "llvm/Target/TargetMachine.h"
|
2021-12-30 16:41:36 -05:00
|
|
|
|
|
|
|
using namespace llvm;
|
2022-01-03 12:31:52 -05:00
|
|
|
using namespace llvm::object;
|
2021-12-30 16:41:36 -05:00
|
|
|
|
|
|
|
static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
|
|
|
|
|
2022-01-12 16:14:52 -05:00
|
|
|
enum DebugKind {
|
|
|
|
NoDebugInfo,
|
|
|
|
DirectivesOnly,
|
|
|
|
FullDebugInfo,
|
|
|
|
};
|
|
|
|
|
2021-12-30 16:41:36 -05:00
|
|
|
// Mark all our options with this category, everything else (except for -help)
|
|
|
|
// will be hidden.
|
|
|
|
static cl::OptionCategory
|
|
|
|
ClangLinkerWrapperCategory("clang-linker-wrapper options");
|
|
|
|
|
2022-01-12 16:14:52 -05:00
|
|
|
static cl::opt<std::string> LinkerUserPath("linker-path", cl::Required,
|
2021-12-30 16:41:36 -05:00
|
|
|
cl::desc("Path of linker binary"),
|
|
|
|
cl::cat(ClangLinkerWrapperCategory));
|
|
|
|
|
2022-01-07 17:12:51 -05:00
|
|
|
static cl::opt<std::string>
|
2022-01-12 16:14:52 -05:00
|
|
|
TargetFeatures("target-feature", cl::ZeroOrMore,
|
|
|
|
cl::desc("Target features for triple"),
|
2022-01-07 17:12:51 -05:00
|
|
|
cl::cat(ClangLinkerWrapperCategory));
|
|
|
|
|
2022-01-12 16:14:52 -05:00
|
|
|
static cl::opt<std::string> OptLevel("opt-level", cl::ZeroOrMore,
|
2022-01-07 17:12:51 -05:00
|
|
|
cl::desc("Optimization level for LTO"),
|
2022-01-11 10:53:59 -05:00
|
|
|
cl::init("O2"),
|
2022-01-07 17:12:51 -05:00
|
|
|
cl::cat(ClangLinkerWrapperCategory));
|
|
|
|
|
2022-01-25 11:23:27 -05:00
|
|
|
static cl::list<std::string>
|
|
|
|
BitcodeLibraries("target-library", cl::ZeroOrMore,
|
|
|
|
cl::desc("Path for the target bitcode library"),
|
|
|
|
cl::cat(ClangLinkerWrapperCategory));
|
2022-01-11 10:53:59 -05:00
|
|
|
|
2022-01-15 23:10:52 -05:00
|
|
|
static cl::opt<bool> EmbedBitcode(
|
2022-01-11 15:50:39 -05:00
|
|
|
"target-embed-bc", cl::ZeroOrMore,
|
2022-01-12 16:14:52 -05:00
|
|
|
cl::desc("Embed linked bitcode instead of an executable device image"),
|
2022-01-11 15:50:39 -05:00
|
|
|
cl::init(false), cl::cat(ClangLinkerWrapperCategory));
|
|
|
|
|
2022-04-19 14:14:16 -04:00
|
|
|
static cl::opt<bool> DryRun(
|
|
|
|
"dry-run", cl::ZeroOrMore,
|
|
|
|
cl::desc("List the linker commands to be run without executing them"),
|
|
|
|
cl::init(false), cl::cat(ClangLinkerWrapperCategory));
|
|
|
|
|
|
|
|
static cl::opt<bool>
|
|
|
|
PrintWrappedModule("print-wrapped-module", cl::ZeroOrMore,
|
|
|
|
cl::desc("Print the wrapped module's IR for testing"),
|
|
|
|
cl::init(false), cl::cat(ClangLinkerWrapperCategory));
|
|
|
|
|
2022-01-12 16:14:52 -05:00
|
|
|
static cl::opt<std::string>
|
|
|
|
HostTriple("host-triple", cl::ZeroOrMore,
|
|
|
|
cl::desc("Triple to use for the host compilation"),
|
|
|
|
cl::init(sys::getDefaultTargetTriple()),
|
|
|
|
cl::cat(ClangLinkerWrapperCategory));
|
|
|
|
|
2022-01-25 11:23:27 -05:00
|
|
|
static cl::list<std::string>
|
|
|
|
PtxasArgs("ptxas-args", cl::ZeroOrMore,
|
2022-02-03 16:41:47 -05:00
|
|
|
cl::desc("Argument to pass to the ptxas invocation"),
|
|
|
|
cl::cat(ClangLinkerWrapperCategory));
|
2022-01-12 16:14:52 -05:00
|
|
|
|
|
|
|
static cl::opt<bool> Verbose("v", cl::ZeroOrMore,
|
|
|
|
cl::desc("Verbose output from tools"),
|
|
|
|
cl::init(false),
|
|
|
|
cl::cat(ClangLinkerWrapperCategory));
|
|
|
|
|
|
|
|
static cl::opt<DebugKind> DebugInfo(
|
|
|
|
cl::desc("Choose debugging level:"), cl::init(NoDebugInfo),
|
|
|
|
cl::values(clEnumValN(NoDebugInfo, "g0", "No debug information"),
|
|
|
|
clEnumValN(DirectivesOnly, "gline-directives-only",
|
|
|
|
"Direction information"),
|
|
|
|
clEnumValN(FullDebugInfo, "g", "Full debugging support")));
|
|
|
|
|
2022-01-16 16:06:59 -05:00
|
|
|
static cl::opt<bool> SaveTemps("save-temps", cl::ZeroOrMore,
|
|
|
|
cl::desc("Save intermediary results."),
|
|
|
|
cl::cat(ClangLinkerWrapperCategory));
|
|
|
|
|
2022-02-03 16:41:47 -05:00
|
|
|
static cl::opt<std::string> CudaPath("cuda-path", cl::ZeroOrMore,
|
|
|
|
cl::desc("Save intermediary results."),
|
|
|
|
cl::cat(ClangLinkerWrapperCategory));
|
|
|
|
|
2022-01-03 12:31:52 -05:00
|
|
|
// Do not parse linker options.
|
2021-12-30 16:41:36 -05:00
|
|
|
static cl::list<std::string>
|
2022-01-11 15:50:39 -05:00
|
|
|
HostLinkerArgs(cl::Positional,
|
|
|
|
cl::desc("<options to be passed to linker>..."));
|
2021-12-30 16:41:36 -05:00
|
|
|
|
2022-01-03 12:31:52 -05:00
|
|
|
/// Path of the current binary.
|
2022-01-11 15:50:39 -05:00
|
|
|
static const char *LinkerExecutable;
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2022-01-18 10:56:12 -05:00
|
|
|
/// Filename of the executable being created.
|
|
|
|
static StringRef ExecutableName;
|
|
|
|
|
2022-03-02 12:38:29 -05:00
|
|
|
/// System root if passed in to the linker via. '--sysroot='.
|
|
|
|
static StringRef Sysroot = "";
|
|
|
|
|
2022-02-03 16:41:47 -05:00
|
|
|
/// Binary path for the CUDA installation.
|
|
|
|
static std::string CudaBinaryPath;
|
|
|
|
|
2022-01-05 13:21:03 -05:00
|
|
|
/// Temporary files created by the linker wrapper.
|
2022-01-04 17:20:04 -05:00
|
|
|
static SmallVector<std::string, 16> TempFiles;
|
2022-01-05 13:21:03 -05:00
|
|
|
|
2022-01-07 17:12:51 -05:00
|
|
|
/// Codegen flags for LTO backend.
|
|
|
|
static codegen::RegisterCodeGenFlags CodeGenFlags;
|
|
|
|
|
2022-01-03 12:31:52 -05:00
|
|
|
/// Magic section string that marks the existence of offloading data. The
|
2022-03-25 11:03:02 -04:00
|
|
|
/// section will contain one or more offloading binaries stored contiguously.
|
|
|
|
#define OFFLOAD_SECTION_MAGIC_STR ".llvm.offloading"
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2022-02-21 14:39:58 -05:00
|
|
|
/// The magic offset for the first object inside CUDA's fatbinary. This can be
|
|
|
|
/// different but it should work for what is passed here.
|
|
|
|
static constexpr unsigned FatbinaryOffset = 0x50;
|
|
|
|
|
2022-01-04 17:20:04 -05:00
|
|
|
/// Information for a device offloading file extracted from the host.
|
2022-01-03 12:31:52 -05:00
|
|
|
struct DeviceFile {
|
2022-04-12 11:21:36 -04:00
|
|
|
DeviceFile(OffloadKind Kind, StringRef TheTriple, StringRef Arch,
|
2022-05-06 09:37:08 -04:00
|
|
|
StringRef Filename, bool IsLibrary = false)
|
|
|
|
: Kind(Kind), TheTriple(TheTriple), Arch(Arch), Filename(Filename),
|
|
|
|
IsLibrary(IsLibrary) {}
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2022-04-12 11:21:36 -04:00
|
|
|
OffloadKind Kind;
|
2022-02-21 10:08:06 -05:00
|
|
|
std::string TheTriple;
|
|
|
|
std::string Arch;
|
|
|
|
std::string Filename;
|
2022-05-06 09:37:08 -04:00
|
|
|
bool IsLibrary;
|
2022-02-21 10:08:06 -05:00
|
|
|
};
|
2022-01-04 17:20:04 -05:00
|
|
|
|
2022-02-21 10:08:06 -05:00
|
|
|
namespace llvm {
|
2022-02-21 14:39:58 -05:00
|
|
|
/// Helper that allows DeviceFile to be used as a key in a DenseMap. For now we
|
|
|
|
/// assume device files with matching architectures and triples but different
|
|
|
|
/// offloading kinds should be handlded together, this may not be true in the
|
|
|
|
/// future.
|
2022-04-12 11:21:36 -04:00
|
|
|
|
|
|
|
// Provide DenseMapInfo for OffloadKind.
|
|
|
|
template <> struct DenseMapInfo<OffloadKind> {
|
|
|
|
static inline OffloadKind getEmptyKey() { return OFK_LAST; }
|
|
|
|
static inline OffloadKind getTombstoneKey() {
|
|
|
|
return static_cast<OffloadKind>(OFK_LAST + 1);
|
|
|
|
}
|
|
|
|
static unsigned getHashValue(const OffloadKind &Val) { return Val * 37U; }
|
|
|
|
|
|
|
|
static bool isEqual(const OffloadKind &LHS, const OffloadKind &RHS) {
|
|
|
|
return LHS == RHS;
|
|
|
|
}
|
|
|
|
};
|
2022-02-21 10:08:06 -05:00
|
|
|
template <> struct DenseMapInfo<DeviceFile> {
|
|
|
|
static DeviceFile getEmptyKey() {
|
2022-04-12 11:21:36 -04:00
|
|
|
return {DenseMapInfo<OffloadKind>::getEmptyKey(),
|
2022-02-21 18:55:51 -05:00
|
|
|
DenseMapInfo<StringRef>::getEmptyKey(),
|
2022-02-21 10:08:06 -05:00
|
|
|
DenseMapInfo<StringRef>::getEmptyKey(),
|
|
|
|
DenseMapInfo<StringRef>::getEmptyKey()};
|
|
|
|
}
|
|
|
|
static DeviceFile getTombstoneKey() {
|
2022-04-12 11:21:36 -04:00
|
|
|
return {DenseMapInfo<OffloadKind>::getTombstoneKey(),
|
2022-02-21 18:55:51 -05:00
|
|
|
DenseMapInfo<StringRef>::getTombstoneKey(),
|
2022-02-21 10:08:06 -05:00
|
|
|
DenseMapInfo<StringRef>::getTombstoneKey(),
|
|
|
|
DenseMapInfo<StringRef>::getTombstoneKey()};
|
|
|
|
}
|
|
|
|
static unsigned getHashValue(const DeviceFile &I) {
|
|
|
|
return DenseMapInfo<StringRef>::getHashValue(I.TheTriple) ^
|
|
|
|
DenseMapInfo<StringRef>::getHashValue(I.Arch);
|
|
|
|
}
|
|
|
|
static bool isEqual(const DeviceFile &LHS, const DeviceFile &RHS) {
|
|
|
|
return LHS.TheTriple == RHS.TheTriple && LHS.Arch == RHS.Arch;
|
|
|
|
}
|
2022-01-03 12:31:52 -05:00
|
|
|
};
|
2022-02-21 10:08:06 -05:00
|
|
|
} // namespace llvm
|
2022-01-03 12:31:52 -05:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2022-05-12 12:39:15 -04:00
|
|
|
Error extractFromBuffer(std::unique_ptr<MemoryBuffer> Buffer,
|
|
|
|
SmallVectorImpl<DeviceFile> &DeviceFiles,
|
|
|
|
bool IsLibrary = false);
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2022-02-28 13:14:54 -05:00
|
|
|
void printCommands(ArrayRef<StringRef> CmdArgs) {
|
|
|
|
if (CmdArgs.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
llvm::errs() << " \"" << CmdArgs.front() << "\" ";
|
2022-03-02 12:38:29 -05:00
|
|
|
for (auto IC = std::next(CmdArgs.begin()), IE = CmdArgs.end(); IC != IE; ++IC)
|
|
|
|
llvm::errs() << *IC << (std::next(IC) != IE ? " " : "\n");
|
2022-02-28 13:14:54 -05:00
|
|
|
}
|
|
|
|
|
2022-02-07 14:59:03 -05:00
|
|
|
std::string getMainExecutable(const char *Name) {
|
|
|
|
void *Ptr = (void *)(intptr_t)&getMainExecutable;
|
|
|
|
auto COWPath = sys::fs::getMainExecutable(Name, Ptr);
|
|
|
|
return sys::path::parent_path(COWPath).str();
|
|
|
|
}
|
|
|
|
|
2022-02-21 18:55:51 -05:00
|
|
|
/// Extract the device file from the string '<kind>-<triple>-<arch>=<library>'.
|
2022-01-25 11:23:27 -05:00
|
|
|
DeviceFile getBitcodeLibrary(StringRef LibraryStr) {
|
|
|
|
auto DeviceAndPath = StringRef(LibraryStr).split('=');
|
2022-02-21 18:55:51 -05:00
|
|
|
auto StringAndArch = DeviceAndPath.first.rsplit('-');
|
|
|
|
auto KindAndTriple = StringAndArch.first.split('-');
|
2022-04-12 11:21:36 -04:00
|
|
|
return DeviceFile(getOffloadKind(KindAndTriple.first), KindAndTriple.second,
|
2022-02-21 18:55:51 -05:00
|
|
|
StringAndArch.second, DeviceAndPath.second);
|
2022-01-25 11:23:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a temporary filename suitable for output.
|
2022-01-16 16:06:59 -05:00
|
|
|
Error createOutputFile(const Twine &Prefix, StringRef Extension,
|
|
|
|
SmallString<128> &NewFilename) {
|
|
|
|
if (!SaveTemps) {
|
|
|
|
if (std::error_code EC =
|
|
|
|
sys::fs::createTemporaryFile(Prefix, Extension, NewFilename))
|
|
|
|
return createFileError(NewFilename, EC);
|
|
|
|
TempFiles.push_back(static_cast<std::string>(NewFilename));
|
|
|
|
} else {
|
|
|
|
const Twine &Filename = Prefix + "." + Extension;
|
|
|
|
Filename.toNullTerminatedStringRef(NewFilename);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2022-04-19 14:14:16 -04:00
|
|
|
/// Execute the command \p ExecutablePath with the arguments \p Args.
|
|
|
|
Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) {
|
|
|
|
if (Verbose || DryRun)
|
|
|
|
printCommands(Args);
|
|
|
|
|
|
|
|
if (!DryRun)
|
|
|
|
if (sys::ExecuteAndWait(ExecutablePath, Args))
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
"'" + sys::path::filename(ExecutablePath) + "'" +
|
|
|
|
" failed");
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
Expected<std::string> findProgram(StringRef Name, ArrayRef<StringRef> Paths) {
|
|
|
|
|
|
|
|
ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths);
|
|
|
|
if (!Path)
|
|
|
|
Path = sys::findProgramByName(Name);
|
|
|
|
if (!Path && DryRun)
|
|
|
|
return Name.str();
|
|
|
|
if (!Path)
|
|
|
|
return createStringError(Path.getError(),
|
|
|
|
"Unable to find '" + Name + "' in path");
|
|
|
|
return *Path;
|
|
|
|
}
|
|
|
|
|
2022-01-03 12:31:52 -05:00
|
|
|
Error runLinker(std::string &LinkerPath, SmallVectorImpl<std::string> &Args) {
|
2021-12-30 16:41:36 -05:00
|
|
|
std::vector<StringRef> LinkerArgs;
|
|
|
|
LinkerArgs.push_back(LinkerPath);
|
|
|
|
for (auto &Arg : Args)
|
|
|
|
LinkerArgs.push_back(Arg);
|
|
|
|
|
2022-04-19 14:14:16 -04:00
|
|
|
if (Error Err = executeCommands(LinkerPath, LinkerArgs))
|
|
|
|
return Err;
|
2021-12-30 16:41:36 -05:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2022-01-03 12:31:52 -05:00
|
|
|
void PrintVersion(raw_ostream &OS) {
|
2021-12-30 16:41:36 -05:00
|
|
|
OS << clang::getClangToolFullVersion("clang-linker-wrapper") << '\n';
|
|
|
|
}
|
|
|
|
|
2022-03-25 11:03:02 -04:00
|
|
|
/// Attempts to extract all the embedded device images contained inside the
|
|
|
|
/// buffer \p Contents. The buffer is expected to contain a valid offloading
|
|
|
|
/// binary format.
|
|
|
|
Error extractOffloadFiles(StringRef Contents, StringRef Prefix,
|
2022-05-06 09:37:08 -04:00
|
|
|
SmallVectorImpl<DeviceFile> &DeviceFiles,
|
|
|
|
bool IsLibrary = false) {
|
2022-03-25 11:03:02 -04:00
|
|
|
uint64_t Offset = 0;
|
|
|
|
// There could be multiple offloading binaries stored at this section.
|
|
|
|
while (Offset < Contents.size()) {
|
|
|
|
std::unique_ptr<MemoryBuffer> Buffer =
|
|
|
|
MemoryBuffer::getMemBuffer(Contents.drop_front(Offset), "",
|
|
|
|
/*RequiresNullTerminator*/ false);
|
|
|
|
auto BinaryOrErr = OffloadBinary::create(*Buffer);
|
|
|
|
if (!BinaryOrErr)
|
|
|
|
return BinaryOrErr.takeError();
|
|
|
|
OffloadBinary &Binary = **BinaryOrErr;
|
|
|
|
|
|
|
|
if (Binary.getVersion() != 1)
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
"Incompatible device image version");
|
|
|
|
|
|
|
|
StringRef Kind = getOffloadKindName(Binary.getOffloadKind());
|
|
|
|
StringRef Suffix = getImageKindName(Binary.getImageKind());
|
|
|
|
|
|
|
|
SmallString<128> TempFile;
|
|
|
|
if (Error Err =
|
|
|
|
createOutputFile(Prefix + "-" + Kind + "-" + Binary.getTriple() +
|
|
|
|
"-" + Binary.getArch(),
|
|
|
|
Suffix, TempFile))
|
2022-04-15 21:18:56 -04:00
|
|
|
return Err;
|
2022-03-25 11:03:02 -04:00
|
|
|
|
|
|
|
Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
|
|
|
|
FileOutputBuffer::create(TempFile, Binary.getImage().size());
|
|
|
|
if (!OutputOrErr)
|
|
|
|
return OutputOrErr.takeError();
|
|
|
|
std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
|
|
|
|
std::copy(Binary.getImage().bytes_begin(), Binary.getImage().bytes_end(),
|
|
|
|
Output->getBufferStart());
|
|
|
|
if (Error E = Output->commit())
|
2022-04-15 21:18:56 -04:00
|
|
|
return E;
|
2022-03-25 11:03:02 -04:00
|
|
|
|
2022-04-12 11:21:36 -04:00
|
|
|
DeviceFiles.emplace_back(Binary.getOffloadKind(), Binary.getTriple(),
|
|
|
|
Binary.getArch(), TempFile, IsLibrary);
|
2022-03-25 11:03:02 -04:00
|
|
|
|
|
|
|
Offset += Binary.getSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2022-05-12 12:39:15 -04:00
|
|
|
Error extractFromBinary(const ObjectFile &Obj,
|
|
|
|
SmallVectorImpl<DeviceFile> &DeviceFiles,
|
|
|
|
bool IsLibrary = false) {
|
2022-01-16 16:06:59 -05:00
|
|
|
StringRef Prefix = sys::path::stem(Obj.getFileName());
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2022-03-25 11:03:02 -04:00
|
|
|
// Extract offloading binaries from sections with the name `.llvm.offloading`.
|
2022-01-03 12:31:52 -05:00
|
|
|
for (const SectionRef &Sec : Obj.sections()) {
|
|
|
|
Expected<StringRef> Name = Sec.getName();
|
2022-03-25 11:03:02 -04:00
|
|
|
if (!Name || !Name->equals(OFFLOAD_SECTION_MAGIC_STR))
|
2022-01-03 12:31:52 -05:00
|
|
|
continue;
|
|
|
|
|
2022-03-25 11:03:02 -04:00
|
|
|
Expected<StringRef> Contents = Sec.getContents();
|
|
|
|
if (!Contents)
|
|
|
|
return Contents.takeError();
|
|
|
|
|
2022-05-06 09:37:08 -04:00
|
|
|
if (Error Err =
|
|
|
|
extractOffloadFiles(*Contents, Prefix, DeviceFiles, IsLibrary))
|
2022-05-12 12:39:15 -04:00
|
|
|
return Err;
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
|
2022-05-12 12:39:15 -04:00
|
|
|
return Error::success();
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
|
2022-05-12 12:39:15 -04:00
|
|
|
Error extractFromBitcode(std::unique_ptr<MemoryBuffer> Buffer,
|
|
|
|
SmallVectorImpl<DeviceFile> &DeviceFiles,
|
|
|
|
bool IsLibrary = false) {
|
2022-01-03 12:31:52 -05:00
|
|
|
LLVMContext Context;
|
|
|
|
SMDiagnostic Err;
|
|
|
|
std::unique_ptr<Module> M = getLazyIRModule(std::move(Buffer), Err, Context);
|
|
|
|
if (!M)
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
"Failed to create module");
|
|
|
|
|
|
|
|
StringRef Prefix =
|
|
|
|
sys::path::stem(M->getName()).take_until([](char C) { return C == '-'; });
|
|
|
|
|
2022-03-25 11:03:02 -04:00
|
|
|
// Extract offloading data from globals with the `.llvm.offloading` section
|
|
|
|
// name.
|
2022-01-03 12:31:52 -05:00
|
|
|
for (GlobalVariable &GV : M->globals()) {
|
2022-03-25 11:03:02 -04:00
|
|
|
if (!GV.hasSection() || !GV.getSection().equals(OFFLOAD_SECTION_MAGIC_STR))
|
2022-01-03 12:31:52 -05:00
|
|
|
continue;
|
|
|
|
|
|
|
|
auto *CDS = dyn_cast<ConstantDataSequential>(GV.getInitializer());
|
|
|
|
if (!CDS)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
StringRef Contents = CDS->getAsString();
|
|
|
|
|
2022-05-06 09:37:08 -04:00
|
|
|
if (Error Err =
|
|
|
|
extractOffloadFiles(Contents, Prefix, DeviceFiles, IsLibrary))
|
2022-05-12 12:39:15 -04:00
|
|
|
return Err;
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
|
2022-05-12 12:39:15 -04:00
|
|
|
return Error::success();
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
|
2022-05-12 12:39:15 -04:00
|
|
|
Error extractFromArchive(const Archive &Library,
|
|
|
|
SmallVectorImpl<DeviceFile> &DeviceFiles) {
|
2022-01-03 12:31:52 -05:00
|
|
|
// Try to extract device code from each file stored in the static archive.
|
|
|
|
Error Err = Error::success();
|
|
|
|
for (auto Child : Library.children(Err)) {
|
2022-05-11 16:53:36 -04:00
|
|
|
auto ChildBufferOrErr = Child.getMemoryBufferRef();
|
|
|
|
if (!ChildBufferOrErr)
|
|
|
|
return ChildBufferOrErr.takeError();
|
2022-01-03 12:31:52 -05:00
|
|
|
std::unique_ptr<MemoryBuffer> ChildBuffer =
|
2022-05-11 16:53:36 -04:00
|
|
|
MemoryBuffer::getMemBuffer(*ChildBufferOrErr, false);
|
|
|
|
|
|
|
|
// Check if the buffer has the required alignment.
|
|
|
|
if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
|
|
|
|
ChildBuffer->getBufferStart()))
|
|
|
|
ChildBuffer = MemoryBuffer::getMemBufferCopy(
|
|
|
|
ChildBufferOrErr->getBuffer(),
|
|
|
|
ChildBufferOrErr->getBufferIdentifier());
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2022-05-12 12:39:15 -04:00
|
|
|
if (Error Err = extractFromBuffer(std::move(ChildBuffer), DeviceFiles,
|
|
|
|
/*IsLibrary*/ true))
|
|
|
|
return Err;
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Err)
|
2022-05-12 12:39:15 -04:00
|
|
|
return Err;
|
|
|
|
return Error::success();
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Extracts embedded device offloading code from a memory \p Buffer to a list
|
2022-05-12 12:39:15 -04:00
|
|
|
/// of \p DeviceFiles.
|
|
|
|
Error extractFromBuffer(std::unique_ptr<MemoryBuffer> Buffer,
|
|
|
|
SmallVectorImpl<DeviceFile> &DeviceFiles,
|
|
|
|
bool IsLibrary) {
|
2022-01-03 12:31:52 -05:00
|
|
|
file_magic Type = identify_magic(Buffer->getBuffer());
|
|
|
|
switch (Type) {
|
|
|
|
case file_magic::bitcode:
|
2022-05-06 09:37:08 -04:00
|
|
|
return extractFromBitcode(std::move(Buffer), DeviceFiles, IsLibrary);
|
2022-01-03 12:31:52 -05:00
|
|
|
case file_magic::elf_relocatable:
|
|
|
|
case file_magic::macho_object:
|
|
|
|
case file_magic::coff_object: {
|
|
|
|
Expected<std::unique_ptr<ObjectFile>> ObjFile =
|
|
|
|
ObjectFile::createObjectFile(*Buffer, Type);
|
|
|
|
if (!ObjFile)
|
|
|
|
return ObjFile.takeError();
|
2022-05-06 09:37:08 -04:00
|
|
|
return extractFromBinary(*ObjFile->get(), DeviceFiles, IsLibrary);
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
case file_magic::archive: {
|
|
|
|
Expected<std::unique_ptr<llvm::object::Archive>> LibFile =
|
|
|
|
object::Archive::create(*Buffer);
|
|
|
|
if (!LibFile)
|
|
|
|
return LibFile.takeError();
|
|
|
|
return extractFromArchive(*LibFile->get(), DeviceFiles);
|
|
|
|
}
|
|
|
|
default:
|
2022-05-12 12:39:15 -04:00
|
|
|
return Error::success();
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
2022-01-04 17:20:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Move these to a separate file.
|
|
|
|
namespace nvptx {
|
2022-01-07 17:12:51 -05:00
|
|
|
Expected<std::string> assemble(StringRef InputFile, Triple TheTriple,
|
2022-04-22 13:19:16 -04:00
|
|
|
StringRef Arch, bool RDC = true) {
|
2022-02-03 16:41:47 -05:00
|
|
|
// NVPTX uses the ptxas binary to create device object files.
|
2022-04-19 14:14:16 -04:00
|
|
|
Expected<std::string> PtxasPath = findProgram("ptxas", {CudaBinaryPath});
|
2022-01-07 17:12:51 -05:00
|
|
|
if (!PtxasPath)
|
2022-04-19 14:14:16 -04:00
|
|
|
return PtxasPath.takeError();
|
2022-01-07 17:12:51 -05:00
|
|
|
|
|
|
|
// Create a new file to write the linked device image to.
|
|
|
|
SmallString<128> TempFile;
|
2022-01-18 10:56:12 -05:00
|
|
|
if (Error Err =
|
|
|
|
createOutputFile(sys::path::filename(ExecutableName) + "-device-" +
|
|
|
|
TheTriple.getArchName() + "-" + Arch,
|
|
|
|
"cubin", TempFile))
|
2022-01-16 16:06:59 -05:00
|
|
|
return std::move(Err);
|
2022-01-07 17:12:51 -05:00
|
|
|
|
|
|
|
SmallVector<StringRef, 16> CmdArgs;
|
|
|
|
std::string Opt = "-" + OptLevel;
|
|
|
|
CmdArgs.push_back(*PtxasPath);
|
|
|
|
CmdArgs.push_back(TheTriple.isArch64Bit() ? "-m64" : "-m32");
|
2022-01-12 16:14:52 -05:00
|
|
|
if (Verbose)
|
|
|
|
CmdArgs.push_back("-v");
|
|
|
|
if (DebugInfo == DirectivesOnly && OptLevel[1] == '0')
|
|
|
|
CmdArgs.push_back("-lineinfo");
|
|
|
|
else if (DebugInfo == FullDebugInfo && OptLevel[1] == '0')
|
|
|
|
CmdArgs.push_back("-g");
|
2022-01-25 11:23:27 -05:00
|
|
|
for (auto &Arg : PtxasArgs)
|
|
|
|
CmdArgs.push_back(Arg);
|
2022-01-07 17:12:51 -05:00
|
|
|
CmdArgs.push_back("-o");
|
|
|
|
CmdArgs.push_back(TempFile);
|
|
|
|
CmdArgs.push_back(Opt);
|
|
|
|
CmdArgs.push_back("--gpu-name");
|
|
|
|
CmdArgs.push_back(Arch);
|
2022-04-22 13:19:16 -04:00
|
|
|
if (RDC)
|
|
|
|
CmdArgs.push_back("-c");
|
2022-01-07 17:12:51 -05:00
|
|
|
|
|
|
|
CmdArgs.push_back(InputFile);
|
|
|
|
|
2022-04-19 14:14:16 -04:00
|
|
|
if (Error Err = executeCommands(*PtxasPath, CmdArgs))
|
2022-04-19 18:40:15 -04:00
|
|
|
return std::move(Err);
|
2022-01-07 17:12:51 -05:00
|
|
|
|
|
|
|
return static_cast<std::string>(TempFile);
|
|
|
|
}
|
|
|
|
|
2022-01-25 11:23:27 -05:00
|
|
|
Expected<std::string> link(ArrayRef<std::string> InputFiles, Triple TheTriple,
|
2022-01-04 17:20:04 -05:00
|
|
|
StringRef Arch) {
|
|
|
|
// NVPTX uses the nvlink binary to link device object files.
|
2022-04-19 14:14:16 -04:00
|
|
|
Expected<std::string> NvlinkPath = findProgram("nvlink", {CudaBinaryPath});
|
2022-02-03 16:41:47 -05:00
|
|
|
if (!NvlinkPath)
|
2022-04-19 14:14:16 -04:00
|
|
|
return NvlinkPath.takeError();
|
2022-01-04 17:20:04 -05:00
|
|
|
|
|
|
|
// Create a new file to write the linked device image to.
|
|
|
|
SmallString<128> TempFile;
|
2022-01-18 10:56:12 -05:00
|
|
|
if (Error Err =
|
|
|
|
createOutputFile(sys::path::filename(ExecutableName) + "-device-" +
|
|
|
|
TheTriple.getArchName() + "-" + Arch,
|
|
|
|
"out", TempFile))
|
2022-01-16 16:06:59 -05:00
|
|
|
return std::move(Err);
|
2022-01-04 17:20:04 -05:00
|
|
|
|
|
|
|
SmallVector<StringRef, 16> CmdArgs;
|
|
|
|
CmdArgs.push_back(*NvlinkPath);
|
|
|
|
CmdArgs.push_back(TheTriple.isArch64Bit() ? "-m64" : "-m32");
|
2022-01-12 16:14:52 -05:00
|
|
|
if (Verbose)
|
|
|
|
CmdArgs.push_back("-v");
|
|
|
|
if (DebugInfo != NoDebugInfo)
|
|
|
|
CmdArgs.push_back("-g");
|
2022-01-04 17:20:04 -05:00
|
|
|
CmdArgs.push_back("-o");
|
|
|
|
CmdArgs.push_back(TempFile);
|
|
|
|
CmdArgs.push_back("-arch");
|
|
|
|
CmdArgs.push_back(Arch);
|
|
|
|
|
|
|
|
// Add extracted input files.
|
2022-01-11 15:50:39 -05:00
|
|
|
for (StringRef Input : InputFiles)
|
2022-01-04 17:20:04 -05:00
|
|
|
CmdArgs.push_back(Input);
|
|
|
|
|
2022-04-19 14:14:16 -04:00
|
|
|
if (Error Err = executeCommands(*NvlinkPath, CmdArgs))
|
2022-04-19 18:40:15 -04:00
|
|
|
return std::move(Err);
|
2022-01-04 17:20:04 -05:00
|
|
|
|
|
|
|
return static_cast<std::string>(TempFile);
|
|
|
|
}
|
2022-04-12 11:21:36 -04:00
|
|
|
|
|
|
|
Expected<std::string> fatbinary(ArrayRef<StringRef> InputFiles,
|
|
|
|
Triple TheTriple, ArrayRef<StringRef> Archs) {
|
|
|
|
// NVPTX uses the fatbinary program to bundle the linked images.
|
|
|
|
Expected<std::string> FatBinaryPath =
|
|
|
|
findProgram("fatbinary", {CudaBinaryPath});
|
|
|
|
if (!FatBinaryPath)
|
|
|
|
return FatBinaryPath.takeError();
|
|
|
|
|
|
|
|
// Create a new file to write the linked device image to.
|
|
|
|
SmallString<128> TempFile;
|
|
|
|
if (Error Err = createOutputFile(sys::path::filename(ExecutableName) +
|
|
|
|
"-device-" + TheTriple.getArchName(),
|
|
|
|
"fatbin", TempFile))
|
|
|
|
return std::move(Err);
|
|
|
|
|
|
|
|
BumpPtrAllocator Alloc;
|
|
|
|
StringSaver Saver(Alloc);
|
|
|
|
|
|
|
|
SmallVector<StringRef, 16> CmdArgs;
|
|
|
|
CmdArgs.push_back(*FatBinaryPath);
|
|
|
|
CmdArgs.push_back(TheTriple.isArch64Bit() ? "-64" : "-32");
|
|
|
|
CmdArgs.push_back("--create");
|
|
|
|
CmdArgs.push_back(TempFile);
|
|
|
|
for (const auto &FileAndArch : llvm::zip(InputFiles, Archs))
|
|
|
|
CmdArgs.push_back(Saver.save("--image=profile=" + std::get<1>(FileAndArch) +
|
|
|
|
",file=" + std::get<0>(FileAndArch)));
|
|
|
|
|
|
|
|
if (Error Err = executeCommands(*FatBinaryPath, CmdArgs))
|
|
|
|
return std::move(Err);
|
|
|
|
|
|
|
|
return static_cast<std::string>(TempFile);
|
|
|
|
}
|
2022-01-04 17:20:04 -05:00
|
|
|
} // namespace nvptx
|
2022-01-13 12:42:02 -05:00
|
|
|
namespace amdgcn {
|
2022-01-25 11:23:27 -05:00
|
|
|
Expected<std::string> link(ArrayRef<std::string> InputFiles, Triple TheTriple,
|
2022-01-13 12:42:02 -05:00
|
|
|
StringRef Arch) {
|
2022-02-07 14:59:03 -05:00
|
|
|
// AMDGPU uses lld to link device object files.
|
2022-04-26 10:49:31 -04:00
|
|
|
Expected<std::string> LLDPath =
|
|
|
|
findProgram("lld", {getMainExecutable("lld")});
|
2022-01-13 12:42:02 -05:00
|
|
|
if (!LLDPath)
|
2022-04-19 14:14:16 -04:00
|
|
|
return LLDPath.takeError();
|
2022-01-13 12:42:02 -05:00
|
|
|
|
|
|
|
// Create a new file to write the linked device image to.
|
|
|
|
SmallString<128> TempFile;
|
2022-01-18 10:56:12 -05:00
|
|
|
if (Error Err = createOutputFile(sys::path::filename(ExecutableName) + "-" +
|
|
|
|
TheTriple.getArchName() + "-" + Arch,
|
|
|
|
"out", TempFile))
|
2022-01-16 16:06:59 -05:00
|
|
|
return std::move(Err);
|
2022-01-13 12:42:02 -05:00
|
|
|
|
|
|
|
SmallVector<StringRef, 16> CmdArgs;
|
|
|
|
CmdArgs.push_back(*LLDPath);
|
|
|
|
CmdArgs.push_back("-flavor");
|
|
|
|
CmdArgs.push_back("gnu");
|
|
|
|
CmdArgs.push_back("--no-undefined");
|
|
|
|
CmdArgs.push_back("-shared");
|
|
|
|
CmdArgs.push_back("-o");
|
|
|
|
CmdArgs.push_back(TempFile);
|
|
|
|
|
|
|
|
// Add extracted input files.
|
|
|
|
for (StringRef Input : InputFiles)
|
|
|
|
CmdArgs.push_back(Input);
|
|
|
|
|
2022-04-19 14:14:16 -04:00
|
|
|
if (Error Err = executeCommands(*LLDPath, CmdArgs))
|
2022-04-19 18:40:15 -04:00
|
|
|
return std::move(Err);
|
2022-01-13 12:42:02 -05:00
|
|
|
|
|
|
|
return static_cast<std::string>(TempFile);
|
|
|
|
}
|
|
|
|
} // namespace amdgcn
|
2022-01-04 17:20:04 -05:00
|
|
|
|
2022-02-11 21:58:15 -05:00
|
|
|
namespace generic {
|
|
|
|
|
|
|
|
const char *getLDMOption(const llvm::Triple &T) {
|
|
|
|
switch (T.getArch()) {
|
|
|
|
case llvm::Triple::x86:
|
|
|
|
if (T.isOSIAMCU())
|
|
|
|
return "elf_iamcu";
|
|
|
|
return "elf_i386";
|
|
|
|
case llvm::Triple::aarch64:
|
|
|
|
return "aarch64linux";
|
|
|
|
case llvm::Triple::aarch64_be:
|
|
|
|
return "aarch64linuxb";
|
|
|
|
case llvm::Triple::ppc64:
|
|
|
|
return "elf64ppc";
|
|
|
|
case llvm::Triple::ppc64le:
|
|
|
|
return "elf64lppc";
|
|
|
|
case llvm::Triple::x86_64:
|
|
|
|
if (T.isX32())
|
|
|
|
return "elf32_x86_64";
|
|
|
|
return "elf_x86_64";
|
|
|
|
case llvm::Triple::ve:
|
|
|
|
return "elf64ve";
|
|
|
|
default:
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Expected<std::string> link(ArrayRef<std::string> InputFiles, Triple TheTriple,
|
|
|
|
StringRef Arch) {
|
|
|
|
// Create a new file to write the linked device image to.
|
|
|
|
SmallString<128> TempFile;
|
|
|
|
if (Error Err = createOutputFile(sys::path::filename(ExecutableName) + "-" +
|
|
|
|
TheTriple.getArchName() + "-" + Arch,
|
|
|
|
"out", TempFile))
|
|
|
|
return std::move(Err);
|
|
|
|
|
|
|
|
// Use the host linker to perform generic offloading. Use the same libraries
|
|
|
|
// and paths as the host application does.
|
|
|
|
SmallVector<StringRef, 16> CmdArgs;
|
|
|
|
CmdArgs.push_back(LinkerUserPath);
|
|
|
|
CmdArgs.push_back("-m");
|
|
|
|
CmdArgs.push_back(getLDMOption(TheTriple));
|
|
|
|
CmdArgs.push_back("-shared");
|
|
|
|
for (auto AI = HostLinkerArgs.begin(), AE = HostLinkerArgs.end(); AI != AE;
|
|
|
|
++AI) {
|
|
|
|
StringRef Arg = *AI;
|
|
|
|
if (Arg.startswith("-L"))
|
|
|
|
CmdArgs.push_back(Arg);
|
|
|
|
else if (Arg.startswith("-l"))
|
|
|
|
CmdArgs.push_back(Arg);
|
|
|
|
else if (Arg.startswith("--as-needed"))
|
|
|
|
CmdArgs.push_back(Arg);
|
|
|
|
else if (Arg.startswith("--no-as-needed"))
|
|
|
|
CmdArgs.push_back(Arg);
|
|
|
|
else if (Arg.startswith("-rpath")) {
|
|
|
|
CmdArgs.push_back(Arg);
|
2022-03-02 12:38:29 -05:00
|
|
|
CmdArgs.push_back(*std::next(AI));
|
2022-02-11 21:58:15 -05:00
|
|
|
} else if (Arg.startswith("-dynamic-linker")) {
|
|
|
|
CmdArgs.push_back(Arg);
|
2022-03-02 12:38:29 -05:00
|
|
|
CmdArgs.push_back(*std::next(AI));
|
2022-02-11 21:58:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
CmdArgs.push_back("-Bsymbolic");
|
|
|
|
CmdArgs.push_back("-o");
|
|
|
|
CmdArgs.push_back(TempFile);
|
|
|
|
|
|
|
|
// Add extracted input files.
|
|
|
|
for (StringRef Input : InputFiles)
|
|
|
|
CmdArgs.push_back(Input);
|
|
|
|
|
2022-04-19 14:14:16 -04:00
|
|
|
if (Error Err = executeCommands(LinkerUserPath, CmdArgs))
|
2022-04-19 18:40:15 -04:00
|
|
|
return std::move(Err);
|
2022-02-11 21:58:15 -05:00
|
|
|
|
|
|
|
return static_cast<std::string>(TempFile);
|
|
|
|
}
|
|
|
|
} // namespace generic
|
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
Expected<std::string> linkDevice(ArrayRef<std::string> InputFiles,
|
2022-01-04 17:20:04 -05:00
|
|
|
Triple TheTriple, StringRef Arch) {
|
|
|
|
switch (TheTriple.getArch()) {
|
|
|
|
case Triple::nvptx:
|
|
|
|
case Triple::nvptx64:
|
2022-01-25 11:23:27 -05:00
|
|
|
return nvptx::link(InputFiles, TheTriple, Arch);
|
2022-01-04 17:20:04 -05:00
|
|
|
case Triple::amdgcn:
|
2022-01-25 11:23:27 -05:00
|
|
|
return amdgcn::link(InputFiles, TheTriple, Arch);
|
2022-01-04 17:20:04 -05:00
|
|
|
case Triple::x86:
|
|
|
|
case Triple::x86_64:
|
2022-02-11 21:58:15 -05:00
|
|
|
case Triple::aarch64:
|
|
|
|
case Triple::aarch64_be:
|
|
|
|
case Triple::ppc64:
|
|
|
|
case Triple::ppc64le:
|
|
|
|
return generic::link(InputFiles, TheTriple, Arch);
|
2022-01-04 17:20:04 -05:00
|
|
|
default:
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
TheTriple.getArchName() +
|
|
|
|
" linking is not supported");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-07 17:12:51 -05:00
|
|
|
void diagnosticHandler(const DiagnosticInfo &DI) {
|
|
|
|
std::string ErrStorage;
|
|
|
|
raw_string_ostream OS(ErrStorage);
|
|
|
|
DiagnosticPrinterRawOStream DP(OS);
|
|
|
|
DI.print(DP);
|
|
|
|
|
|
|
|
switch (DI.getSeverity()) {
|
|
|
|
case DS_Error:
|
2022-01-12 16:14:52 -05:00
|
|
|
WithColor::error(errs(), LinkerExecutable) << ErrStorage << "\n";
|
2022-01-07 17:12:51 -05:00
|
|
|
break;
|
|
|
|
case DS_Warning:
|
2022-01-12 16:14:52 -05:00
|
|
|
WithColor::warning(errs(), LinkerExecutable) << ErrStorage << "\n";
|
2022-01-07 17:12:51 -05:00
|
|
|
break;
|
|
|
|
case DS_Note:
|
2022-01-12 16:14:52 -05:00
|
|
|
WithColor::note(errs(), LinkerExecutable) << ErrStorage << "\n";
|
2022-01-07 17:12:51 -05:00
|
|
|
break;
|
|
|
|
case DS_Remark:
|
2022-01-15 23:10:52 -05:00
|
|
|
WithColor::remark(errs()) << ErrStorage << "\n";
|
2022-01-07 17:12:51 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the target features passed in from the driver as <triple>=<features>.
|
|
|
|
std::vector<std::string> getTargetFeatures(const Triple &TheTriple) {
|
|
|
|
std::vector<std::string> Features;
|
|
|
|
auto TargetAndFeatures = StringRef(TargetFeatures).split('=');
|
|
|
|
if (TargetAndFeatures.first != TheTriple.getTriple())
|
|
|
|
return Features;
|
|
|
|
|
|
|
|
for (auto Feature : llvm::split(TargetAndFeatures.second, ','))
|
|
|
|
Features.push_back(Feature.str());
|
|
|
|
return Features;
|
|
|
|
}
|
|
|
|
|
|
|
|
CodeGenOpt::Level getCGOptLevel(unsigned OptLevel) {
|
|
|
|
switch (OptLevel) {
|
|
|
|
case 0:
|
|
|
|
return CodeGenOpt::None;
|
|
|
|
case 1:
|
|
|
|
return CodeGenOpt::Less;
|
|
|
|
case 2:
|
|
|
|
return CodeGenOpt::Default;
|
|
|
|
case 3:
|
|
|
|
return CodeGenOpt::Aggressive;
|
|
|
|
}
|
|
|
|
llvm_unreachable("Invalid optimization level");
|
|
|
|
}
|
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
template <typename ModuleHook = function_ref<bool(size_t, const Module &)>>
|
|
|
|
std::unique_ptr<lto::LTO> createLTO(
|
|
|
|
const Triple &TheTriple, StringRef Arch, bool WholeProgram,
|
|
|
|
ModuleHook Hook = [](size_t, const Module &) { return true; }) {
|
2022-01-07 17:12:51 -05:00
|
|
|
lto::Config Conf;
|
|
|
|
lto::ThinBackend Backend;
|
|
|
|
// TODO: Handle index-only thin-LTO
|
2022-04-01 09:42:21 -04:00
|
|
|
Backend =
|
|
|
|
lto::createInProcessThinBackend(llvm::heavyweight_hardware_concurrency());
|
2022-01-07 17:12:51 -05:00
|
|
|
|
|
|
|
Conf.CPU = Arch.str();
|
|
|
|
Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(TheTriple);
|
|
|
|
|
|
|
|
Conf.MAttrs = getTargetFeatures(TheTriple);
|
|
|
|
Conf.CGOptLevel = getCGOptLevel(OptLevel[1] - '0');
|
|
|
|
Conf.OptLevel = OptLevel[1] - '0';
|
2022-04-11 17:26:33 -04:00
|
|
|
if (Conf.OptLevel > 0)
|
|
|
|
Conf.UseDefaultPipeline = true;
|
2022-01-07 17:12:51 -05:00
|
|
|
Conf.DefaultTriple = TheTriple.getTriple();
|
|
|
|
Conf.DiagHandler = diagnosticHandler;
|
|
|
|
|
|
|
|
Conf.PTO.LoopVectorization = Conf.OptLevel > 1;
|
|
|
|
Conf.PTO.SLPVectorization = Conf.OptLevel > 1;
|
|
|
|
|
2022-01-16 16:06:59 -05:00
|
|
|
if (SaveTemps) {
|
|
|
|
auto HandleError = [&](Error Err) {
|
|
|
|
logAllUnhandledErrors(std::move(Err),
|
|
|
|
WithColor::error(errs(), LinkerExecutable));
|
|
|
|
exit(1);
|
|
|
|
};
|
|
|
|
Conf.PostInternalizeModuleHook = [&](size_t, const Module &M) {
|
|
|
|
SmallString<128> TempFile;
|
2022-01-18 10:56:12 -05:00
|
|
|
if (Error Err = createOutputFile(sys::path::filename(ExecutableName) +
|
|
|
|
"-device-" + TheTriple.getTriple(),
|
|
|
|
"bc", TempFile))
|
2022-01-16 16:06:59 -05:00
|
|
|
HandleError(std::move(Err));
|
|
|
|
|
|
|
|
std::error_code EC;
|
|
|
|
raw_fd_ostream LinkedBitcode(TempFile, EC, sys::fs::OF_None);
|
|
|
|
if (EC)
|
|
|
|
HandleError(errorCodeToError(EC));
|
|
|
|
WriteBitcodeToFile(M, LinkedBitcode);
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
}
|
2022-01-15 23:10:52 -05:00
|
|
|
Conf.PostOptModuleHook = Hook;
|
2022-01-07 17:12:51 -05:00
|
|
|
if (TheTriple.isNVPTX())
|
|
|
|
Conf.CGFileType = CGFT_AssemblyFile;
|
|
|
|
else
|
|
|
|
Conf.CGFileType = CGFT_ObjectFile;
|
|
|
|
|
|
|
|
// TODO: Handle remark files
|
|
|
|
Conf.HasWholeProgramVisibility = WholeProgram;
|
|
|
|
|
|
|
|
return std::make_unique<lto::LTO>(std::move(Conf), Backend);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if \p S is valid as a C language identifier and will be given
|
|
|
|
// `__start_` and `__stop_` symbols.
|
|
|
|
bool isValidCIdentifier(StringRef S) {
|
|
|
|
return !S.empty() && (isAlpha(S[0]) || S[0] == '_') &&
|
|
|
|
std::all_of(S.begin() + 1, S.end(),
|
|
|
|
[](char C) { return C == '_' || isAlnum(C); });
|
|
|
|
}
|
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
Error linkBitcodeFiles(SmallVectorImpl<std::string> &InputFiles,
|
2022-04-22 13:19:16 -04:00
|
|
|
const Triple &TheTriple, StringRef Arch,
|
|
|
|
bool &WholeProgram) {
|
2022-01-07 17:12:51 -05:00
|
|
|
SmallVector<std::unique_ptr<MemoryBuffer>, 4> SavedBuffers;
|
|
|
|
SmallVector<std::unique_ptr<lto::InputFile>, 4> BitcodeFiles;
|
2022-01-11 15:50:39 -05:00
|
|
|
SmallVector<std::string, 4> NewInputFiles;
|
2022-02-16 16:30:45 -05:00
|
|
|
DenseSet<StringRef> UsedInRegularObj;
|
|
|
|
DenseSet<StringRef> UsedInSharedLib;
|
2022-02-22 21:21:33 -05:00
|
|
|
BumpPtrAllocator Alloc;
|
2022-02-16 16:30:45 -05:00
|
|
|
StringSaver Saver(Alloc);
|
2022-01-07 17:12:51 -05:00
|
|
|
|
|
|
|
// Search for bitcode files in the input and create an LTO input file. If it
|
|
|
|
// is not a bitcode file, scan its symbol table for symbols we need to
|
|
|
|
// save.
|
|
|
|
for (StringRef File : InputFiles) {
|
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
|
|
|
|
MemoryBuffer::getFileOrSTDIN(File);
|
|
|
|
if (std::error_code EC = BufferOrErr.getError())
|
|
|
|
return createFileError(File, EC);
|
2022-02-21 14:39:58 -05:00
|
|
|
MemoryBufferRef Buffer = **BufferOrErr;
|
2022-01-07 17:12:51 -05:00
|
|
|
|
|
|
|
file_magic Type = identify_magic((*BufferOrErr)->getBuffer());
|
2022-02-21 14:39:58 -05:00
|
|
|
switch (Type) {
|
|
|
|
case file_magic::bitcode: {
|
|
|
|
Expected<std::unique_ptr<lto::InputFile>> InputFileOrErr =
|
|
|
|
llvm::lto::InputFile::create(Buffer);
|
|
|
|
if (!InputFileOrErr)
|
|
|
|
return InputFileOrErr.takeError();
|
|
|
|
|
|
|
|
// Save the input file and the buffer associated with its memory.
|
|
|
|
BitcodeFiles.push_back(std::move(*InputFileOrErr));
|
|
|
|
SavedBuffers.push_back(std::move(*BufferOrErr));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case file_magic::cuda_fatbinary: {
|
|
|
|
// Cuda fatbinaries made by Clang almost almost have an object eighty
|
|
|
|
// bytes from the beginning. This should be sufficient to identify the
|
|
|
|
// symbols.
|
|
|
|
Buffer = MemoryBufferRef(
|
|
|
|
(*BufferOrErr)->getBuffer().drop_front(FatbinaryOffset), "FatBinary");
|
|
|
|
LLVM_FALLTHROUGH;
|
|
|
|
}
|
|
|
|
case file_magic::elf_relocatable:
|
|
|
|
case file_magic::elf_shared_object:
|
|
|
|
case file_magic::macho_object:
|
|
|
|
case file_magic::coff_object: {
|
2022-01-07 17:12:51 -05:00
|
|
|
Expected<std::unique_ptr<ObjectFile>> ObjFile =
|
2022-02-21 14:39:58 -05:00
|
|
|
ObjectFile::createObjectFile(Buffer);
|
2022-01-07 17:12:51 -05:00
|
|
|
if (!ObjFile)
|
2022-02-21 14:39:58 -05:00
|
|
|
continue;
|
2022-01-07 17:12:51 -05:00
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
NewInputFiles.push_back(File.str());
|
2022-01-07 17:12:51 -05:00
|
|
|
for (auto &Sym : (*ObjFile)->symbols()) {
|
|
|
|
Expected<StringRef> Name = Sym.getName();
|
|
|
|
if (!Name)
|
|
|
|
return Name.takeError();
|
|
|
|
|
2022-01-13 22:59:05 -05:00
|
|
|
// Record if we've seen these symbols in any object or shared libraries.
|
2022-01-25 11:23:27 -05:00
|
|
|
if ((*ObjFile)->isRelocatableObject())
|
2022-02-16 16:30:45 -05:00
|
|
|
UsedInRegularObj.insert(Saver.save(*Name));
|
2022-01-25 11:23:27 -05:00
|
|
|
else
|
2022-02-16 16:30:45 -05:00
|
|
|
UsedInSharedLib.insert(Saver.save(*Name));
|
2022-01-07 17:12:51 -05:00
|
|
|
}
|
2022-02-21 14:39:58 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
continue;
|
2022-01-07 17:12:51 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BitcodeFiles.empty())
|
2022-01-11 15:50:39 -05:00
|
|
|
return Error::success();
|
|
|
|
|
2022-01-16 16:06:59 -05:00
|
|
|
auto HandleError = [&](Error Err) {
|
|
|
|
logAllUnhandledErrors(std::move(Err),
|
2022-01-11 15:50:39 -05:00
|
|
|
WithColor::error(errs(), LinkerExecutable));
|
|
|
|
exit(1);
|
|
|
|
};
|
|
|
|
|
|
|
|
// LTO Module hook to output bitcode without running the backend.
|
2022-01-16 16:06:59 -05:00
|
|
|
auto OutputBitcode = [&](size_t Task, const Module &M) {
|
2022-01-11 15:50:39 -05:00
|
|
|
SmallString<128> TempFile;
|
2022-01-18 10:56:12 -05:00
|
|
|
if (Error Err = createOutputFile(sys::path::filename(ExecutableName) +
|
|
|
|
"-jit-" + TheTriple.getTriple(),
|
|
|
|
"bc", TempFile))
|
2022-01-16 16:06:59 -05:00
|
|
|
HandleError(std::move(Err));
|
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
std::error_code EC;
|
|
|
|
raw_fd_ostream LinkedBitcode(TempFile, EC, sys::fs::OF_None);
|
|
|
|
if (EC)
|
2022-01-16 16:06:59 -05:00
|
|
|
HandleError(errorCodeToError(EC));
|
2022-01-11 15:50:39 -05:00
|
|
|
WriteBitcodeToFile(M, LinkedBitcode);
|
|
|
|
NewInputFiles.push_back(static_cast<std::string>(TempFile));
|
|
|
|
return false;
|
|
|
|
};
|
2022-01-07 17:12:51 -05:00
|
|
|
|
2022-01-13 22:59:05 -05:00
|
|
|
// We assume visibility of the whole program if every input file was bitcode.
|
2022-04-22 13:19:16 -04:00
|
|
|
WholeProgram = BitcodeFiles.size() == InputFiles.size();
|
2022-01-16 16:06:59 -05:00
|
|
|
auto LTOBackend =
|
|
|
|
(EmbedBitcode) ? createLTO(TheTriple, Arch, WholeProgram, OutputBitcode)
|
|
|
|
: createLTO(TheTriple, Arch, WholeProgram);
|
2022-01-07 17:12:51 -05:00
|
|
|
|
2022-01-13 22:59:05 -05:00
|
|
|
// We need to resolve the symbols so the LTO backend knows which symbols need
|
|
|
|
// to be kept or can be internalized. This is a simplified symbol resolution
|
|
|
|
// scheme to approximate the full resolution a linker would do.
|
|
|
|
DenseSet<StringRef> PrevailingSymbols;
|
2022-01-07 17:12:51 -05:00
|
|
|
for (auto &BitcodeFile : BitcodeFiles) {
|
|
|
|
const auto Symbols = BitcodeFile->symbols();
|
|
|
|
SmallVector<lto::SymbolResolution, 16> Resolutions(Symbols.size());
|
|
|
|
size_t Idx = 0;
|
|
|
|
for (auto &Sym : Symbols) {
|
|
|
|
lto::SymbolResolution &Res = Resolutions[Idx++];
|
|
|
|
|
|
|
|
// We will use this as the prevailing symbol definition in LTO unless
|
2022-01-13 22:59:05 -05:00
|
|
|
// it is undefined or another definition has already been used.
|
|
|
|
Res.Prevailing =
|
2022-02-16 16:30:45 -05:00
|
|
|
!Sym.isUndefined() &&
|
|
|
|
PrevailingSymbols.insert(Saver.save(Sym.getName())).second;
|
2022-01-13 22:59:05 -05:00
|
|
|
|
|
|
|
// We need LTO to preseve the following global symbols:
|
|
|
|
// 1) Symbols used in regular objects.
|
|
|
|
// 2) Sections that will be given a __start/__stop symbol.
|
2022-02-16 16:30:45 -05:00
|
|
|
// 3) Prevailing symbols that are needed visible to external libraries.
|
2022-01-07 17:12:51 -05:00
|
|
|
Res.VisibleToRegularObj =
|
2022-02-16 16:30:45 -05:00
|
|
|
UsedInRegularObj.contains(Sym.getName()) ||
|
2022-01-07 17:12:51 -05:00
|
|
|
isValidCIdentifier(Sym.getSectionName()) ||
|
2022-01-13 22:59:05 -05:00
|
|
|
(Res.Prevailing &&
|
|
|
|
(Sym.getVisibility() != GlobalValue::HiddenVisibility &&
|
|
|
|
!Sym.canBeOmittedFromSymbolTable()));
|
|
|
|
|
|
|
|
// Identify symbols that must be exported dynamically and can be
|
|
|
|
// referenced by other files.
|
|
|
|
Res.ExportDynamic =
|
|
|
|
Sym.getVisibility() != GlobalValue::HiddenVisibility &&
|
2022-02-16 16:30:45 -05:00
|
|
|
(UsedInSharedLib.contains(Sym.getName()) ||
|
2022-01-13 22:59:05 -05:00
|
|
|
!Sym.canBeOmittedFromSymbolTable());
|
|
|
|
|
|
|
|
// The final definition will reside in this linkage unit if the symbol is
|
|
|
|
// defined and local to the module. This only checks for bitcode files,
|
|
|
|
// full assertion will require complete symbol resolution.
|
|
|
|
Res.FinalDefinitionInLinkageUnit =
|
|
|
|
Sym.getVisibility() != GlobalValue::DefaultVisibility &&
|
|
|
|
(!Sym.isUndefined() && !Sym.isCommon());
|
2022-01-07 17:12:51 -05:00
|
|
|
|
|
|
|
// We do not support linker redefined symbols (e.g. --wrap) for device
|
|
|
|
// image linking, so the symbols will not be changed after LTO.
|
|
|
|
Res.LinkerRedefined = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the bitcode file with its resolved symbols to the LTO job.
|
|
|
|
if (Error Err = LTOBackend->add(std::move(BitcodeFile), Resolutions))
|
2022-01-13 22:59:05 -05:00
|
|
|
return Err;
|
2022-01-07 17:12:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run the LTO job to compile the bitcode.
|
|
|
|
size_t MaxTasks = LTOBackend->getMaxTasks();
|
|
|
|
std::vector<SmallString<128>> Files(MaxTasks);
|
|
|
|
auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
|
|
|
|
int FD = -1;
|
|
|
|
auto &TempFile = Files[Task];
|
|
|
|
StringRef Extension = (TheTriple.isNVPTX()) ? "s" : "o";
|
2022-01-18 10:56:12 -05:00
|
|
|
if (Error Err = createOutputFile(sys::path::filename(ExecutableName) +
|
2022-01-25 11:23:27 -05:00
|
|
|
"-device-" + TheTriple.getTriple(),
|
2022-01-18 10:56:12 -05:00
|
|
|
Extension, TempFile))
|
2022-01-16 16:06:59 -05:00
|
|
|
HandleError(std::move(Err));
|
|
|
|
if (std::error_code EC = sys::fs::openFileForWrite(TempFile, FD))
|
|
|
|
HandleError(errorCodeToError(EC));
|
2022-01-07 17:12:51 -05:00
|
|
|
return std::make_unique<CachedFileStream>(
|
|
|
|
std::make_unique<llvm::raw_fd_ostream>(FD, true));
|
|
|
|
};
|
2022-01-11 15:50:39 -05:00
|
|
|
|
2022-01-07 17:12:51 -05:00
|
|
|
if (Error Err = LTOBackend->run(AddStream))
|
2022-01-13 22:59:05 -05:00
|
|
|
return Err;
|
2022-01-07 17:12:51 -05:00
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
// Is we are compiling for NVPTX we need to run the assembler first.
|
2022-01-15 23:10:52 -05:00
|
|
|
if (TheTriple.isNVPTX() && !EmbedBitcode) {
|
|
|
|
for (auto &File : Files) {
|
2022-04-22 13:19:16 -04:00
|
|
|
auto FileOrErr = nvptx::assemble(File, TheTriple, Arch, !WholeProgram);
|
2022-01-15 23:10:52 -05:00
|
|
|
if (!FileOrErr)
|
|
|
|
return FileOrErr.takeError();
|
|
|
|
File = *FileOrErr;
|
|
|
|
}
|
2022-01-07 17:12:51 -05:00
|
|
|
}
|
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
// Append the new inputs to the device linker input.
|
|
|
|
for (auto &File : Files)
|
|
|
|
NewInputFiles.push_back(static_cast<std::string>(File));
|
|
|
|
InputFiles = NewInputFiles;
|
|
|
|
|
|
|
|
return Error::success();
|
2022-01-07 17:12:51 -05:00
|
|
|
}
|
|
|
|
|
2022-01-04 17:20:04 -05:00
|
|
|
/// Runs the appropriate linking action on all the device files specified in \p
|
|
|
|
/// DeviceFiles. The linked device images are returned in \p LinkedImages.
|
|
|
|
Error linkDeviceFiles(ArrayRef<DeviceFile> DeviceFiles,
|
2022-04-12 11:21:36 -04:00
|
|
|
SmallVectorImpl<DeviceFile> &LinkedImages) {
|
|
|
|
// Get the list of inputs and active offload kinds for a specific device.
|
2022-02-21 10:08:06 -05:00
|
|
|
DenseMap<DeviceFile, SmallVector<std::string, 4>> LinkerInputMap;
|
2022-04-12 11:21:36 -04:00
|
|
|
DenseMap<DeviceFile, DenseSet<OffloadKind>> ActiveOffloadKinds;
|
2022-05-06 09:37:08 -04:00
|
|
|
SmallVector<DeviceFile, 4> LibraryFiles;
|
|
|
|
for (auto &File : DeviceFiles) {
|
2022-04-12 11:21:36 -04:00
|
|
|
if (File.IsLibrary) {
|
2022-05-06 09:37:08 -04:00
|
|
|
LibraryFiles.push_back(File);
|
2022-04-12 11:21:36 -04:00
|
|
|
} else {
|
2022-05-06 09:37:08 -04:00
|
|
|
LinkerInputMap[File].push_back(File.Filename);
|
2022-04-12 11:21:36 -04:00
|
|
|
ActiveOffloadKinds[File].insert(File.Kind);
|
|
|
|
}
|
2022-05-06 09:37:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Static libraries are loaded lazily as-needed, only add them if other files
|
|
|
|
// are present.
|
|
|
|
// TODO: We need to check the symbols as well, static libraries are only
|
|
|
|
// loaded if they contain symbols that are currently undefined or common
|
|
|
|
// in the symbol table.
|
|
|
|
for (auto &File : LibraryFiles)
|
|
|
|
if (LinkerInputMap.count(File))
|
|
|
|
LinkerInputMap[File].push_back(File.Filename);
|
2022-01-04 17:20:04 -05:00
|
|
|
|
|
|
|
// Try to link each device toolchain.
|
|
|
|
for (auto &LinkerInput : LinkerInputMap) {
|
2022-02-21 10:08:06 -05:00
|
|
|
DeviceFile &File = LinkerInput.getFirst();
|
|
|
|
Triple TheTriple = Triple(File.TheTriple);
|
2022-04-12 11:21:36 -04:00
|
|
|
auto &LinkerInputFiles = LinkerInput.getSecond();
|
2022-04-22 13:19:16 -04:00
|
|
|
bool WholeProgram = false;
|
2022-01-04 17:20:04 -05:00
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
// Run LTO on any bitcode files and replace the input with the result.
|
2022-04-12 11:21:36 -04:00
|
|
|
if (Error Err = linkBitcodeFiles(LinkerInputFiles, TheTriple, File.Arch,
|
|
|
|
WholeProgram))
|
2022-01-13 22:59:05 -05:00
|
|
|
return Err;
|
2022-01-11 15:50:39 -05:00
|
|
|
|
2022-01-15 23:10:52 -05:00
|
|
|
if (EmbedBitcode) {
|
2022-04-12 11:21:36 -04:00
|
|
|
// If we are embedding bitcode for JIT, skip the final device linking.
|
|
|
|
if (LinkerInputFiles.size() != 1 || !WholeProgram)
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
"Unable to embed bitcode image for JIT");
|
|
|
|
LinkedImages.emplace_back(OFK_OpenMP, TheTriple.getTriple(), File.Arch,
|
|
|
|
LinkerInputFiles.front());
|
2022-01-11 15:50:39 -05:00
|
|
|
continue;
|
2022-05-12 12:39:15 -04:00
|
|
|
}
|
|
|
|
if (WholeProgram && TheTriple.isNVPTX()) {
|
2022-04-12 11:21:36 -04:00
|
|
|
// If we performed LTO on NVPTX and had whole program visibility, we can
|
|
|
|
// use CUDA in non-RDC mode.
|
|
|
|
if (LinkerInputFiles.size() != 1)
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
"Invalid number of inputs for non-RDC mode");
|
|
|
|
for (OffloadKind Kind : ActiveOffloadKinds[LinkerInput.getFirst()])
|
|
|
|
LinkedImages.emplace_back(Kind, TheTriple.getTriple(), File.Arch,
|
|
|
|
LinkerInputFiles.front());
|
2022-04-22 13:19:16 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-04-12 11:21:36 -04:00
|
|
|
auto ImageOrErr = linkDevice(LinkerInputFiles, TheTriple, File.Arch);
|
2022-01-04 17:20:04 -05:00
|
|
|
if (!ImageOrErr)
|
|
|
|
return ImageOrErr.takeError();
|
|
|
|
|
2022-04-12 11:21:36 -04:00
|
|
|
// Create separate images for all the active offload kinds.
|
|
|
|
for (OffloadKind Kind : ActiveOffloadKinds[LinkerInput.getFirst()])
|
|
|
|
LinkedImages.emplace_back(Kind, TheTriple.getTriple(), File.Arch,
|
|
|
|
*ImageOrErr);
|
2022-01-04 17:20:04 -05:00
|
|
|
}
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2022-01-25 14:25:39 -05:00
|
|
|
// Compile the module to an object file using the appropriate target machine for
|
|
|
|
// the host triple.
|
|
|
|
Expected<std::string> compileModule(Module &M) {
|
|
|
|
std::string Msg;
|
|
|
|
const Target *T = TargetRegistry::lookupTarget(M.getTargetTriple(), Msg);
|
|
|
|
if (!T)
|
|
|
|
return createStringError(inconvertibleErrorCode(), Msg);
|
|
|
|
|
|
|
|
auto Options =
|
|
|
|
codegen::InitTargetOptionsFromCodeGenFlags(Triple(M.getTargetTriple()));
|
|
|
|
StringRef CPU = "";
|
|
|
|
StringRef Features = "";
|
|
|
|
std::unique_ptr<TargetMachine> TM(T->createTargetMachine(
|
|
|
|
HostTriple, CPU, Features, Options, Reloc::PIC_, M.getCodeModel()));
|
|
|
|
|
|
|
|
if (M.getDataLayout().isDefault())
|
|
|
|
M.setDataLayout(TM->createDataLayout());
|
|
|
|
|
|
|
|
SmallString<128> ObjectFile;
|
|
|
|
int FD = -1;
|
2022-05-03 20:50:26 -04:00
|
|
|
if (Error Err = createOutputFile(
|
|
|
|
sys::path::filename(ExecutableName) + "-wrapper", "o", ObjectFile))
|
2022-01-25 14:25:39 -05:00
|
|
|
return std::move(Err);
|
|
|
|
if (std::error_code EC = sys::fs::openFileForWrite(ObjectFile, FD))
|
|
|
|
return errorCodeToError(EC);
|
|
|
|
|
|
|
|
auto OS = std::make_unique<llvm::raw_fd_ostream>(FD, true);
|
|
|
|
|
|
|
|
legacy::PassManager CodeGenPasses;
|
|
|
|
TargetLibraryInfoImpl TLII(Triple(M.getTargetTriple()));
|
|
|
|
CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
|
|
|
|
if (TM->addPassesToEmitFile(CodeGenPasses, *OS, nullptr, CGFT_ObjectFile))
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
"Failed to execute host backend");
|
|
|
|
CodeGenPasses.run(M);
|
|
|
|
|
|
|
|
return static_cast<std::string>(ObjectFile);
|
|
|
|
}
|
|
|
|
|
2022-04-12 11:21:36 -04:00
|
|
|
/// Load all of the OpenMP images into a buffer and pass it to the binary
|
|
|
|
/// wrapping function to create the registration code in the module \p M.
|
|
|
|
Error wrapOpenMPImages(Module &M, ArrayRef<DeviceFile> Images) {
|
2022-01-25 17:46:01 -05:00
|
|
|
SmallVector<std::unique_ptr<MemoryBuffer>, 4> SavedBuffers;
|
|
|
|
SmallVector<ArrayRef<char>, 4> ImagesToWrap;
|
2022-04-12 11:21:36 -04:00
|
|
|
for (const DeviceFile &File : Images) {
|
2022-01-25 17:46:01 -05:00
|
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =
|
2022-04-12 11:21:36 -04:00
|
|
|
llvm::MemoryBuffer::getFileOrSTDIN(File.Filename);
|
2022-01-25 17:46:01 -05:00
|
|
|
if (std::error_code EC = ImageOrError.getError())
|
2022-04-12 11:21:36 -04:00
|
|
|
return createFileError(File.Filename, EC);
|
2022-01-25 17:46:01 -05:00
|
|
|
ImagesToWrap.emplace_back((*ImageOrError)->getBufferStart(),
|
|
|
|
(*ImageOrError)->getBufferSize());
|
|
|
|
SavedBuffers.emplace_back(std::move(*ImageOrError));
|
|
|
|
}
|
2022-01-04 17:20:04 -05:00
|
|
|
|
2022-04-12 11:21:36 -04:00
|
|
|
if (Error Err = wrapOpenMPBinaries(M, ImagesToWrap))
|
|
|
|
return Err;
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Combine all of the CUDA images into a single fatbinary and pass it to the
|
|
|
|
/// binary wrapping function to create the registration code in the module \p M.
|
|
|
|
Error wrapCudaImages(Module &M, ArrayRef<DeviceFile> Images) {
|
|
|
|
SmallVector<StringRef, 4> InputFiles;
|
|
|
|
SmallVector<StringRef, 4> Architectures;
|
|
|
|
for (const DeviceFile &File : Images) {
|
|
|
|
InputFiles.push_back(File.Filename);
|
|
|
|
Architectures.push_back(File.Arch);
|
|
|
|
}
|
|
|
|
|
|
|
|
// CUDA expects its embedded device images to be a fatbinary.
|
|
|
|
Triple TheTriple = Triple(Images.front().TheTriple);
|
|
|
|
auto FileOrErr = nvptx::fatbinary(InputFiles, TheTriple, Architectures);
|
|
|
|
if (!FileOrErr)
|
|
|
|
return FileOrErr.takeError();
|
|
|
|
|
|
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =
|
|
|
|
llvm::MemoryBuffer::getFileOrSTDIN(*FileOrErr);
|
|
|
|
if (std::error_code EC = ImageOrError.getError())
|
|
|
|
return createFileError(*FileOrErr, EC);
|
|
|
|
|
|
|
|
auto ImageToWrap = ArrayRef<char>((*ImageOrError)->getBufferStart(),
|
|
|
|
(*ImageOrError)->getBufferSize());
|
2022-01-04 17:20:04 -05:00
|
|
|
|
2022-04-12 11:21:36 -04:00
|
|
|
if (Error Err = wrapCudaBinary(M, ImageToWrap))
|
|
|
|
return Err;
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates the object file containing the device image and runtime
|
|
|
|
/// registration code from the device images stored in \p Images.
|
|
|
|
Expected<SmallVector<std::string, 2>>
|
|
|
|
wrapDeviceImages(ArrayRef<DeviceFile> Images) {
|
|
|
|
DenseMap<OffloadKind, SmallVector<DeviceFile, 2>> ImagesForKind;
|
|
|
|
for (const DeviceFile &Image : Images)
|
|
|
|
ImagesForKind[Image.Kind].push_back(Image);
|
|
|
|
|
|
|
|
SmallVector<std::string, 2> WrappedImages;
|
|
|
|
for (const auto &KindAndImages : ImagesForKind) {
|
|
|
|
LLVMContext Context;
|
|
|
|
Module M("offload.wrapper.module", Context);
|
|
|
|
M.setTargetTriple(HostTriple);
|
|
|
|
|
|
|
|
// Create registration code for the given offload kinds in the Module.
|
|
|
|
switch (KindAndImages.getFirst()) {
|
|
|
|
case OFK_OpenMP:
|
|
|
|
if (Error Err = wrapOpenMPImages(M, KindAndImages.getSecond()))
|
|
|
|
return std::move(Err);
|
|
|
|
break;
|
|
|
|
case OFK_Cuda:
|
|
|
|
if (Error Err = wrapCudaImages(M, KindAndImages.getSecond()))
|
|
|
|
return std::move(Err);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
getOffloadKindName(KindAndImages.getFirst()) +
|
|
|
|
" wrapping is not supported");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PrintWrappedModule)
|
|
|
|
llvm::errs() << M;
|
|
|
|
|
|
|
|
auto FileOrErr = compileModule(M);
|
|
|
|
if (!FileOrErr)
|
|
|
|
return FileOrErr.takeError();
|
|
|
|
WrappedImages.push_back(*FileOrErr);
|
|
|
|
}
|
2022-04-19 14:14:16 -04:00
|
|
|
|
2022-04-12 11:21:36 -04:00
|
|
|
return WrappedImages;
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
|
2022-01-05 13:21:03 -05:00
|
|
|
Optional<std::string> findFile(StringRef Dir, const Twine &Name) {
|
|
|
|
SmallString<128> Path;
|
2022-03-02 12:38:29 -05:00
|
|
|
if (Dir.startswith("="))
|
|
|
|
sys::path::append(Path, Sysroot, Dir.substr(1), Name);
|
|
|
|
else
|
|
|
|
sys::path::append(Path, Dir, Name);
|
|
|
|
|
2022-01-05 13:21:03 -05:00
|
|
|
if (sys::fs::exists(Path))
|
|
|
|
return static_cast<std::string>(Path);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
Optional<std::string> findFromSearchPaths(StringRef Name,
|
|
|
|
ArrayRef<StringRef> SearchPaths) {
|
|
|
|
for (StringRef Dir : SearchPaths)
|
|
|
|
if (Optional<std::string> File = findFile(Dir, Name))
|
|
|
|
return File;
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
Optional<std::string> searchLibraryBaseName(StringRef Name,
|
|
|
|
ArrayRef<StringRef> SearchPaths) {
|
|
|
|
for (StringRef Dir : SearchPaths) {
|
2022-01-07 17:12:51 -05:00
|
|
|
if (Optional<std::string> File = findFile(Dir, "lib" + Name + ".so"))
|
|
|
|
return None;
|
2022-01-05 13:21:03 -05:00
|
|
|
if (Optional<std::string> File = findFile(Dir, "lib" + Name + ".a"))
|
|
|
|
return File;
|
|
|
|
}
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Search for static libraries in the linker's library path given input like
|
|
|
|
/// `-lfoo` or `-l:libfoo.a`.
|
|
|
|
Optional<std::string> searchLibrary(StringRef Input,
|
|
|
|
ArrayRef<StringRef> SearchPaths) {
|
|
|
|
if (!Input.startswith("-l"))
|
|
|
|
return None;
|
|
|
|
StringRef Name = Input.drop_front(2);
|
|
|
|
if (Name.startswith(":"))
|
|
|
|
return findFromSearchPaths(Name.drop_front(), SearchPaths);
|
|
|
|
return searchLibraryBaseName(Name, SearchPaths);
|
|
|
|
}
|
|
|
|
|
2022-01-03 12:31:52 -05:00
|
|
|
} // namespace
|
|
|
|
|
2021-12-30 16:41:36 -05:00
|
|
|
int main(int argc, const char **argv) {
|
2022-01-03 12:31:52 -05:00
|
|
|
InitLLVM X(argc, argv);
|
2022-01-07 17:12:51 -05:00
|
|
|
InitializeAllTargetInfos();
|
|
|
|
InitializeAllTargets();
|
|
|
|
InitializeAllTargetMCs();
|
|
|
|
InitializeAllAsmParsers();
|
|
|
|
InitializeAllAsmPrinters();
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2022-01-04 17:20:04 -05:00
|
|
|
LinkerExecutable = argv[0];
|
2021-12-30 16:41:36 -05:00
|
|
|
sys::PrintStackTraceOnErrorSignal(argv[0]);
|
|
|
|
cl::SetVersionPrinter(PrintVersion);
|
|
|
|
cl::HideUnrelatedOptions(ClangLinkerWrapperCategory);
|
|
|
|
cl::ParseCommandLineOptions(
|
|
|
|
argc, argv,
|
|
|
|
"A wrapper utility over the host linker. It scans the input files for\n"
|
|
|
|
"sections that require additional processing prior to linking. The tool\n"
|
|
|
|
"will then transparently pass all arguments and input to the specified\n"
|
|
|
|
"host linker to create the final binary.\n");
|
|
|
|
|
|
|
|
if (Help) {
|
|
|
|
cl::PrintHelpMessage();
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto reportError = [argv](Error E) {
|
|
|
|
logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
|
2022-01-04 17:20:04 -05:00
|
|
|
return EXIT_FAILURE;
|
2021-12-30 16:41:36 -05:00
|
|
|
};
|
|
|
|
|
2022-02-03 16:41:47 -05:00
|
|
|
if (!CudaPath.empty())
|
|
|
|
CudaBinaryPath = CudaPath + "/bin";
|
|
|
|
|
2022-03-02 12:38:29 -05:00
|
|
|
auto RootIt = llvm::find_if(HostLinkerArgs, [](StringRef Arg) {
|
|
|
|
return Arg.startswith("--sysroot=");
|
|
|
|
});
|
|
|
|
if (RootIt != HostLinkerArgs.end())
|
|
|
|
Sysroot = StringRef(*RootIt).split('=').second;
|
|
|
|
|
|
|
|
ExecutableName = *std::next(llvm::find(HostLinkerArgs, "-o"));
|
2022-01-04 17:20:04 -05:00
|
|
|
SmallVector<std::string, 16> LinkerArgs;
|
|
|
|
for (const std::string &Arg : HostLinkerArgs)
|
|
|
|
LinkerArgs.push_back(Arg);
|
|
|
|
|
2022-01-05 13:21:03 -05:00
|
|
|
SmallVector<StringRef, 16> LibraryPaths;
|
2022-01-25 11:23:27 -05:00
|
|
|
for (StringRef Arg : LinkerArgs) {
|
2022-01-05 13:21:03 -05:00
|
|
|
if (Arg.startswith("-L"))
|
|
|
|
LibraryPaths.push_back(Arg.drop_front(2));
|
2022-01-18 10:56:12 -05:00
|
|
|
}
|
2022-01-05 13:21:03 -05:00
|
|
|
|
2022-05-12 12:39:15 -04:00
|
|
|
// Try to extract device code from the linker input.
|
2022-01-04 17:20:04 -05:00
|
|
|
SmallVector<DeviceFile, 4> DeviceFiles;
|
2022-01-03 12:31:52 -05:00
|
|
|
for (std::string &Arg : LinkerArgs) {
|
2022-01-18 10:56:12 -05:00
|
|
|
if (Arg == ExecutableName)
|
|
|
|
continue;
|
|
|
|
|
2022-01-05 13:21:03 -05:00
|
|
|
// Search for static libraries in the library link path.
|
|
|
|
std::string Filename = Arg;
|
|
|
|
if (Optional<std::string> Library = searchLibrary(Arg, LibraryPaths))
|
|
|
|
Filename = *Library;
|
|
|
|
|
2022-02-22 21:13:03 -05:00
|
|
|
if (sys::fs::exists(Filename) && !sys::fs::is_directory(Filename)) {
|
2022-01-03 12:31:52 -05:00
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
|
2022-01-05 13:21:03 -05:00
|
|
|
MemoryBuffer::getFileOrSTDIN(Filename);
|
2022-01-03 12:31:52 -05:00
|
|
|
if (std::error_code EC = BufferOrErr.getError())
|
2022-01-05 13:21:03 -05:00
|
|
|
return reportError(createFileError(Filename, EC));
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2022-05-12 12:39:15 -04:00
|
|
|
if (Error Err = extractFromBuffer(std::move(*BufferOrErr), DeviceFiles))
|
|
|
|
return reportError(std::move(Err));
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-25 11:23:27 -05:00
|
|
|
// Add the device bitcode libraries to the device files if any were passed in.
|
|
|
|
for (StringRef LibraryStr : BitcodeLibraries)
|
|
|
|
DeviceFiles.push_back(getBitcodeLibrary(LibraryStr));
|
2022-01-11 10:53:59 -05:00
|
|
|
|
2022-01-04 17:20:04 -05:00
|
|
|
// Link the device images extracted from the linker input.
|
2022-04-12 11:21:36 -04:00
|
|
|
SmallVector<DeviceFile, 4> LinkedImages;
|
2022-01-25 11:23:27 -05:00
|
|
|
if (Error Err = linkDeviceFiles(DeviceFiles, LinkedImages))
|
2022-01-04 17:20:04 -05:00
|
|
|
return reportError(std::move(Err));
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2022-01-04 17:20:04 -05:00
|
|
|
// Wrap each linked device image into a linkable host binary and add it to the
|
|
|
|
// link job's inputs.
|
2022-01-25 17:46:01 -05:00
|
|
|
auto FileOrErr = wrapDeviceImages(LinkedImages);
|
|
|
|
if (!FileOrErr)
|
|
|
|
return reportError(FileOrErr.takeError());
|
2022-04-13 11:48:07 -04:00
|
|
|
|
|
|
|
// We need to insert the new files next to the old ones to make sure they're
|
|
|
|
// linked with the same libraries / arguments.
|
|
|
|
if (!FileOrErr->empty()) {
|
2022-05-12 12:39:15 -04:00
|
|
|
auto *FirstInput = std::next(llvm::find_if(LinkerArgs, [](StringRef Str) {
|
2022-04-13 11:48:07 -04:00
|
|
|
return sys::fs::exists(Str) && !sys::fs::is_directory(Str) &&
|
|
|
|
Str != ExecutableName;
|
|
|
|
}));
|
|
|
|
LinkerArgs.insert(FirstInput, FileOrErr->begin(), FileOrErr->end());
|
|
|
|
}
|
2021-12-30 16:41:36 -05:00
|
|
|
|
2022-01-03 12:31:52 -05:00
|
|
|
// Run the host linking job.
|
2022-01-04 17:20:04 -05:00
|
|
|
if (Error Err = runLinker(LinkerUserPath, LinkerArgs))
|
|
|
|
return reportError(std::move(Err));
|
2021-12-30 16:41:36 -05:00
|
|
|
|
2022-01-04 17:20:04 -05:00
|
|
|
// Remove the temporary files created.
|
|
|
|
for (const auto &TempFile : TempFiles)
|
2022-01-03 12:31:52 -05:00
|
|
|
if (std::error_code EC = sys::fs::remove(TempFile))
|
|
|
|
reportError(createFileError(TempFile, EC));
|
|
|
|
|
2021-12-30 16:41:36 -05:00
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|