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
|
2022-06-30 11:41:38 -04:00
|
|
|
// device offloading data stored in sections `.llvm.offloading` 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.
|
2022-01-03 12:31:52 -05:00
|
|
|
//
|
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"
|
2022-07-07 12:23:44 -04:00
|
|
|
#include "llvm/Object/ELFObjectFile.h"
|
2022-06-06 11:36:45 -04:00
|
|
|
#include "llvm/Object/IRObjectFile.h"
|
2022-01-03 12:31:52 -05:00
|
|
|
#include "llvm/Object/ObjectFile.h"
|
2022-03-25 11:03:02 -04:00
|
|
|
#include "llvm/Object/OffloadBinary.h"
|
2022-07-06 22:58:52 -04:00
|
|
|
#include "llvm/Option/ArgList.h"
|
|
|
|
#include "llvm/Option/OptTable.h"
|
|
|
|
#include "llvm/Option/Option.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-03 12:31:52 -05:00
|
|
|
#include "llvm/Support/InitLLVM.h"
|
2021-12-30 16:41:36 -05:00
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
2022-10-25 12:28:28 -05:00
|
|
|
#include "llvm/Support/Parallel.h"
|
2021-12-30 16:41:36 -05:00
|
|
|
#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"
|
2023-02-10 09:59:46 +00:00
|
|
|
#include "llvm/TargetParser/Host.h"
|
2022-07-25 14:15:10 +02:00
|
|
|
#include <atomic>
|
2023-01-02 15:54:57 -08:00
|
|
|
#include <optional>
|
2021-12-30 16:41:36 -05:00
|
|
|
|
|
|
|
using namespace llvm;
|
2022-07-06 22:58:52 -04:00
|
|
|
using namespace llvm::opt;
|
2022-01-03 12:31:52 -05:00
|
|
|
using namespace llvm::object;
|
2021-12-30 16:41:36 -05:00
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
/// Path of the current binary.
|
|
|
|
static const char *LinkerExecutable;
|
2022-01-12 16:14:52 -05:00
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
/// Ssave intermediary results.
|
|
|
|
static bool SaveTemps = false;
|
2022-01-16 16:06:59 -05:00
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
/// Print arguments without executing.
|
|
|
|
static bool DryRun = false;
|
2022-02-03 16:41:47 -05:00
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
/// Print verbose output.
|
|
|
|
static bool Verbose = false;
|
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-02-03 16:41:47 -05:00
|
|
|
/// Binary path for the CUDA installation.
|
|
|
|
static std::string CudaBinaryPath;
|
|
|
|
|
2023-01-31 09:50:40 -06:00
|
|
|
/// Mutex lock to protect writes to shared TempFiles in parallel.
|
|
|
|
static std::mutex TempFilesMutex;
|
|
|
|
|
2022-01-05 13:21:03 -05:00
|
|
|
/// Temporary files created by the linker wrapper.
|
2022-06-22 11:17:14 -04:00
|
|
|
static std::list<SmallString<128>> 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-07-07 11:28:32 -04:00
|
|
|
/// Global flag to indicate that the LTO pipeline threw an error.
|
|
|
|
static std::atomic<bool> LTOError;
|
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
using OffloadingImage = OffloadBinary::OffloadingImage;
|
|
|
|
|
2022-02-21 10:08:06 -05:00
|
|
|
namespace llvm {
|
2022-06-06 11:36:45 -04:00
|
|
|
// Provide DenseMapInfo so that OffloadKind can be used in a DenseMap.
|
2022-04-12 11:21:36 -04:00
|
|
|
template <> struct DenseMapInfo<OffloadKind> {
|
|
|
|
static inline OffloadKind getEmptyKey() { return OFK_LAST; }
|
|
|
|
static inline OffloadKind getTombstoneKey() {
|
|
|
|
return static_cast<OffloadKind>(OFK_LAST + 1);
|
|
|
|
}
|
2022-06-06 11:36:45 -04:00
|
|
|
static unsigned getHashValue(const OffloadKind &Val) { return Val; }
|
2022-04-12 11:21:36 -04:00
|
|
|
|
|
|
|
static bool isEqual(const OffloadKind &LHS, const OffloadKind &RHS) {
|
|
|
|
return LHS == RHS;
|
|
|
|
}
|
|
|
|
};
|
2022-02-21 10:08:06 -05:00
|
|
|
} // namespace llvm
|
2022-01-03 12:31:52 -05:00
|
|
|
|
|
|
|
namespace {
|
2022-07-12 10:10:54 -04:00
|
|
|
using std::error_code;
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
/// Must not overlap with llvm::opt::DriverFlag.
|
|
|
|
enum WrapperFlags {
|
|
|
|
WrapperOnlyOption = (1 << 4), // Options only used by the linker wrapper.
|
|
|
|
DeviceOnlyOption = (1 << 5), // Options only used for device linking.
|
|
|
|
};
|
|
|
|
|
|
|
|
enum ID {
|
|
|
|
OPT_INVALID = 0, // This is not an option ID.
|
2023-08-04 11:19:09 -07:00
|
|
|
#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
|
2022-07-06 22:58:52 -04:00
|
|
|
#include "LinkerWrapperOpts.inc"
|
|
|
|
LastOption
|
|
|
|
#undef OPTION
|
|
|
|
};
|
|
|
|
|
2022-12-26 09:19:09 +01:00
|
|
|
#define PREFIX(NAME, VALUE) \
|
|
|
|
static constexpr StringLiteral NAME##_init[] = VALUE; \
|
|
|
|
static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
|
|
|
|
std::size(NAME##_init) - 1);
|
2022-07-06 22:58:52 -04:00
|
|
|
#include "LinkerWrapperOpts.inc"
|
|
|
|
#undef PREFIX
|
|
|
|
|
2022-12-23 13:25:58 +01:00
|
|
|
static constexpr OptTable::Info InfoTable[] = {
|
2023-08-04 11:19:09 -07:00
|
|
|
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
|
2022-07-06 22:58:52 -04:00
|
|
|
#include "LinkerWrapperOpts.inc"
|
|
|
|
#undef OPTION
|
|
|
|
};
|
|
|
|
|
2022-12-30 08:32:59 +01:00
|
|
|
class WrapperOptTable : public opt::GenericOptTable {
|
2022-07-06 22:58:52 -04:00
|
|
|
public:
|
2022-12-30 08:32:59 +01:00
|
|
|
WrapperOptTable() : opt::GenericOptTable(InfoTable) {}
|
2022-07-06 22:58:52 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
const OptTable &getOptTable() {
|
|
|
|
static const WrapperOptTable *Table = []() {
|
|
|
|
auto Result = std::make_unique<WrapperOptTable>();
|
|
|
|
return Result.release();
|
|
|
|
}();
|
|
|
|
return *Table;
|
|
|
|
}
|
|
|
|
|
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-07-08 11:00:14 -04:00
|
|
|
[[noreturn]] void reportError(Error E) {
|
|
|
|
outs().flush();
|
|
|
|
logAllUnhandledErrors(std::move(E),
|
|
|
|
WithColor::error(errs(), LinkerExecutable));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
/// Create an extra user-specified \p OffloadFile.
|
|
|
|
/// TODO: We should find a way to wrap these as libraries instead.
|
|
|
|
Expected<OffloadFile> getInputBitcodeLibrary(StringRef Input) {
|
2022-08-08 13:32:48 -04:00
|
|
|
auto [Device, Path] = StringRef(Input).split('=');
|
|
|
|
auto [String, Arch] = Device.rsplit('-');
|
|
|
|
auto [Kind, Triple] = String.split('-');
|
2022-06-06 11:36:45 -04:00
|
|
|
|
|
|
|
llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> ImageOrError =
|
2022-08-08 13:32:48 -04:00
|
|
|
llvm::MemoryBuffer::getFileOrSTDIN(Path);
|
2022-06-06 11:36:45 -04:00
|
|
|
if (std::error_code EC = ImageOrError.getError())
|
2022-08-08 13:32:48 -04:00
|
|
|
return createFileError(Path, EC);
|
2022-06-06 11:36:45 -04:00
|
|
|
|
|
|
|
OffloadingImage Image{};
|
|
|
|
Image.TheImageKind = IMG_Bitcode;
|
2022-08-08 13:32:48 -04:00
|
|
|
Image.TheOffloadKind = getOffloadKind(Kind);
|
2023-02-04 12:54:26 -08:00
|
|
|
Image.StringData["triple"] = Triple;
|
|
|
|
Image.StringData["arch"] = Arch;
|
2022-06-06 11:36:45 -04:00
|
|
|
Image.Image = std::move(*ImageOrError);
|
|
|
|
|
2023-09-01 09:19:25 -07:00
|
|
|
std::unique_ptr<MemoryBuffer> Binary =
|
|
|
|
MemoryBuffer::getMemBufferCopy(OffloadBinary::write(Image));
|
2022-06-06 11:36:45 -04:00
|
|
|
auto NewBinaryOrErr = OffloadBinary::create(*Binary);
|
|
|
|
if (!NewBinaryOrErr)
|
|
|
|
return NewBinaryOrErr.takeError();
|
|
|
|
return OffloadFile(std::move(*NewBinaryOrErr), std::move(Binary));
|
|
|
|
}
|
|
|
|
|
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-01-25 11:23:27 -05:00
|
|
|
/// Get a temporary filename suitable for output.
|
2022-06-22 11:17:14 -04:00
|
|
|
Expected<StringRef> createOutputFile(const Twine &Prefix, StringRef Extension) {
|
2023-01-31 09:50:40 -06:00
|
|
|
std::scoped_lock<decltype(TempFilesMutex)> Lock(TempFilesMutex);
|
2022-06-22 11:17:14 -04:00
|
|
|
SmallString<128> OutputFile;
|
|
|
|
if (SaveTemps) {
|
|
|
|
(Prefix + "." + Extension).toNullTerminatedStringRef(OutputFile);
|
2022-01-16 16:06:59 -05:00
|
|
|
} else {
|
2022-06-22 11:17:14 -04:00
|
|
|
if (std::error_code EC =
|
|
|
|
sys::fs::createTemporaryFile(Prefix, Extension, OutputFile))
|
|
|
|
return createFileError(OutputFile, EC);
|
2022-01-16 16:06:59 -05:00
|
|
|
}
|
|
|
|
|
2022-07-08 11:37:15 -04:00
|
|
|
TempFiles.emplace_back(std::move(OutputFile));
|
2022-06-22 11:17:14 -04:00
|
|
|
return TempFiles.back();
|
2022-01-16 16:06:59 -05:00
|
|
|
}
|
|
|
|
|
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-07-13 14:55:55 -04:00
|
|
|
/// Runs the wrapped linker job with the newly created input.
|
|
|
|
Error runLinker(ArrayRef<StringRef> Files, const ArgList &Args) {
|
|
|
|
llvm::TimeTraceScope TimeScope("Execute host linker");
|
|
|
|
|
|
|
|
// Render the linker arguments and add the newly created image. We add it
|
|
|
|
// after the output file to ensure it is linked with the correct libraries.
|
|
|
|
StringRef LinkerPath = Args.getLastArgValue(OPT_linker_path_EQ);
|
|
|
|
ArgStringList NewLinkerArgs;
|
|
|
|
for (const opt::Arg *Arg : Args) {
|
|
|
|
// Do not forward arguments only intended for the linker wrapper.
|
|
|
|
if (Arg->getOption().hasFlag(WrapperOnlyOption))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Arg->render(Args, NewLinkerArgs);
|
2023-11-22 20:23:23 -06:00
|
|
|
if (Arg->getOption().matches(OPT_o) || Arg->getOption().matches(OPT_out))
|
2022-07-13 14:55:55 -04:00
|
|
|
llvm::transform(Files, std::back_inserter(NewLinkerArgs),
|
|
|
|
[&](StringRef Arg) { return Args.MakeArgString(Arg); });
|
|
|
|
}
|
|
|
|
|
|
|
|
SmallVector<StringRef> LinkerArgs({LinkerPath});
|
|
|
|
for (StringRef Arg : NewLinkerArgs)
|
|
|
|
LinkerArgs.push_back(Arg);
|
|
|
|
if (Error Err = executeCommands(LinkerPath, LinkerArgs))
|
2022-04-19 14:14:16 -04:00
|
|
|
return Err;
|
2021-12-30 16:41:36 -05:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
void printVersion(raw_ostream &OS) {
|
2021-12-30 16:41:36 -05:00
|
|
|
OS << clang::getClangToolFullVersion("clang-linker-wrapper") << '\n';
|
|
|
|
}
|
|
|
|
|
2022-01-04 17:20:04 -05:00
|
|
|
namespace nvptx {
|
2022-06-22 11:17:14 -04:00
|
|
|
Expected<StringRef>
|
2022-06-06 11:36:45 -04:00
|
|
|
fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
|
2022-07-06 22:58:52 -04:00
|
|
|
const ArgList &Args) {
|
2022-07-13 14:55:55 -04:00
|
|
|
llvm::TimeTraceScope TimeScope("NVPTX fatbinary");
|
2022-04-12 11:21:36 -04:00
|
|
|
// NVPTX uses the fatbinary program to bundle the linked images.
|
|
|
|
Expected<std::string> FatBinaryPath =
|
2023-01-19 11:20:07 -06:00
|
|
|
findProgram("fatbinary", {CudaBinaryPath + "/bin"});
|
2022-04-12 11:21:36 -04:00
|
|
|
if (!FatBinaryPath)
|
|
|
|
return FatBinaryPath.takeError();
|
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
llvm::Triple Triple(
|
|
|
|
Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
|
|
|
|
|
2022-04-12 11:21:36 -04:00
|
|
|
// Create a new file to write the linked device image to.
|
2023-01-12 16:42:30 -06:00
|
|
|
auto TempFileOrErr =
|
|
|
|
createOutputFile(sys::path::filename(ExecutableName), "fatbin");
|
2022-06-22 11:17:14 -04:00
|
|
|
if (!TempFileOrErr)
|
|
|
|
return TempFileOrErr.takeError();
|
2022-04-12 11:21:36 -04:00
|
|
|
|
|
|
|
SmallVector<StringRef, 16> CmdArgs;
|
|
|
|
CmdArgs.push_back(*FatBinaryPath);
|
2022-07-06 22:58:52 -04:00
|
|
|
CmdArgs.push_back(Triple.isArch64Bit() ? "-64" : "-32");
|
2022-04-12 11:21:36 -04:00
|
|
|
CmdArgs.push_back("--create");
|
2022-06-22 11:17:14 -04:00
|
|
|
CmdArgs.push_back(*TempFileOrErr);
|
2022-08-08 13:32:48 -04:00
|
|
|
for (const auto &[File, Arch] : InputFiles)
|
2022-07-08 13:33:46 -04:00
|
|
|
CmdArgs.push_back(
|
2022-08-08 13:32:48 -04:00
|
|
|
Args.MakeArgString("--image=profile=" + Arch + ",file=" + File));
|
2022-04-12 11:21:36 -04:00
|
|
|
|
|
|
|
if (Error Err = executeCommands(*FatBinaryPath, CmdArgs))
|
|
|
|
return std::move(Err);
|
|
|
|
|
2022-06-22 11:17:14 -04:00
|
|
|
return *TempFileOrErr;
|
2022-04-12 11:21:36 -04:00
|
|
|
}
|
2022-01-04 17:20:04 -05:00
|
|
|
} // namespace nvptx
|
2022-07-06 22:58:52 -04:00
|
|
|
|
2022-01-13 12:42:02 -05:00
|
|
|
namespace amdgcn {
|
2022-06-30 07:59:24 -04:00
|
|
|
Expected<StringRef>
|
|
|
|
fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
|
|
|
|
const ArgList &Args) {
|
2022-07-13 14:55:55 -04:00
|
|
|
llvm::TimeTraceScope TimeScope("AMDGPU Fatbinary");
|
|
|
|
|
2022-06-30 07:59:24 -04:00
|
|
|
// AMDGPU uses the clang-offload-bundler to bundle the linked images.
|
|
|
|
Expected<std::string> OffloadBundlerPath = findProgram(
|
|
|
|
"clang-offload-bundler", {getMainExecutable("clang-offload-bundler")});
|
|
|
|
if (!OffloadBundlerPath)
|
|
|
|
return OffloadBundlerPath.takeError();
|
|
|
|
|
|
|
|
llvm::Triple Triple(
|
|
|
|
Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
|
|
|
|
|
|
|
|
// Create a new file to write the linked device image to.
|
2023-01-12 16:42:30 -06:00
|
|
|
auto TempFileOrErr =
|
|
|
|
createOutputFile(sys::path::filename(ExecutableName), "hipfb");
|
2022-06-30 07:59:24 -04:00
|
|
|
if (!TempFileOrErr)
|
|
|
|
return TempFileOrErr.takeError();
|
|
|
|
|
|
|
|
BumpPtrAllocator Alloc;
|
|
|
|
StringSaver Saver(Alloc);
|
|
|
|
|
|
|
|
SmallVector<StringRef, 16> CmdArgs;
|
|
|
|
CmdArgs.push_back(*OffloadBundlerPath);
|
|
|
|
CmdArgs.push_back("-type=o");
|
|
|
|
CmdArgs.push_back("-bundle-align=4096");
|
|
|
|
|
|
|
|
SmallVector<StringRef> Targets = {"-targets=host-x86_64-unknown-linux"};
|
2022-08-08 13:32:48 -04:00
|
|
|
for (const auto &[File, Arch] : InputFiles)
|
|
|
|
Targets.push_back(Saver.save("hipv4-amdgcn-amd-amdhsa--" + Arch));
|
2022-06-30 07:59:24 -04:00
|
|
|
CmdArgs.push_back(Saver.save(llvm::join(Targets, ",")));
|
|
|
|
|
|
|
|
CmdArgs.push_back("-input=/dev/null");
|
2022-08-08 13:32:48 -04:00
|
|
|
for (const auto &[File, Arch] : InputFiles)
|
|
|
|
CmdArgs.push_back(Saver.save("-input=" + File));
|
2022-06-30 07:59:24 -04:00
|
|
|
|
|
|
|
CmdArgs.push_back(Saver.save("-output=" + *TempFileOrErr));
|
|
|
|
|
|
|
|
if (Error Err = executeCommands(*OffloadBundlerPath, CmdArgs))
|
|
|
|
return std::move(Err);
|
|
|
|
|
|
|
|
return *TempFileOrErr;
|
|
|
|
}
|
2022-01-13 12:42:02 -05:00
|
|
|
} // namespace amdgcn
|
2022-01-04 17:20:04 -05:00
|
|
|
|
2022-02-11 21:58:15 -05:00
|
|
|
namespace generic {
|
2023-01-19 11:20:07 -06:00
|
|
|
Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
|
|
|
|
llvm::TimeTraceScope TimeScope("Clang");
|
|
|
|
// Use `clang` to invoke the appropriate device tools.
|
2023-01-11 14:57:39 -06:00
|
|
|
Expected<std::string> ClangPath =
|
|
|
|
findProgram("clang", {getMainExecutable("clang")});
|
|
|
|
if (!ClangPath)
|
|
|
|
return ClangPath.takeError();
|
|
|
|
|
|
|
|
const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
|
|
|
|
StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
|
2023-01-19 11:20:07 -06:00
|
|
|
if (Arch.empty())
|
|
|
|
Arch = "native";
|
2023-01-11 14:57:39 -06:00
|
|
|
// Create a new file to write the linked device image to. Assume that the
|
|
|
|
// input filename already has the device and architecture.
|
2023-01-19 11:20:07 -06:00
|
|
|
auto TempFileOrErr =
|
|
|
|
createOutputFile(sys::path::filename(ExecutableName) + "." +
|
|
|
|
Triple.getArchName() + "." + Arch,
|
|
|
|
"img");
|
2023-01-11 14:57:39 -06:00
|
|
|
if (!TempFileOrErr)
|
|
|
|
return TempFileOrErr.takeError();
|
|
|
|
|
|
|
|
StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2");
|
|
|
|
SmallVector<StringRef, 16> CmdArgs{
|
|
|
|
*ClangPath,
|
|
|
|
"-o",
|
|
|
|
*TempFileOrErr,
|
|
|
|
Args.MakeArgString("--target=" + Triple.getTriple()),
|
|
|
|
Triple.isAMDGPU() ? Args.MakeArgString("-mcpu=" + Arch)
|
|
|
|
: Args.MakeArgString("-march=" + Arch),
|
2023-01-19 11:20:07 -06:00
|
|
|
Args.MakeArgString("-" + OptLevel),
|
|
|
|
"-Wl,--no-undefined",
|
2023-01-11 14:57:39 -06:00
|
|
|
};
|
|
|
|
|
2023-02-01 13:48:27 -06:00
|
|
|
for (StringRef InputFile : InputFiles)
|
|
|
|
CmdArgs.push_back(InputFile);
|
|
|
|
|
2023-01-19 11:20:07 -06:00
|
|
|
// If this is CPU offloading we copy the input libraries.
|
|
|
|
if (!Triple.isAMDGPU() && !Triple.isNVPTX()) {
|
2023-01-26 15:00:24 -06:00
|
|
|
CmdArgs.push_back("-Wl,-Bsymbolic");
|
2023-01-19 11:20:07 -06:00
|
|
|
CmdArgs.push_back("-shared");
|
|
|
|
ArgStringList LinkerArgs;
|
2023-02-01 12:01:44 -06:00
|
|
|
for (const opt::Arg *Arg : Args.filtered(OPT_library, OPT_library_path))
|
2023-01-19 11:20:07 -06:00
|
|
|
Arg->render(Args, LinkerArgs);
|
2023-02-01 12:01:44 -06:00
|
|
|
for (const opt::Arg *Arg : Args.filtered(OPT_rpath))
|
|
|
|
LinkerArgs.push_back(
|
|
|
|
Args.MakeArgString("-Wl,-rpath," + StringRef(Arg->getValue())));
|
2023-01-19 11:20:07 -06:00
|
|
|
llvm::copy(LinkerArgs, std::back_inserter(CmdArgs));
|
|
|
|
}
|
2023-01-11 14:57:39 -06:00
|
|
|
|
2023-08-17 07:45:40 -05:00
|
|
|
// Pass on -mllvm options to the clang invocation.
|
|
|
|
for (const opt::Arg *Arg : Args.filtered(OPT_mllvm)) {
|
|
|
|
CmdArgs.push_back("-mllvm");
|
|
|
|
CmdArgs.push_back(Arg->getValue());
|
|
|
|
}
|
|
|
|
|
2023-01-19 11:20:07 -06:00
|
|
|
if (Args.hasArg(OPT_debug))
|
|
|
|
CmdArgs.push_back("-g");
|
2023-01-11 14:57:39 -06:00
|
|
|
|
2023-01-19 11:20:07 -06:00
|
|
|
if (SaveTemps)
|
|
|
|
CmdArgs.push_back("-save-temps");
|
2022-07-06 22:58:52 -04:00
|
|
|
|
2023-01-19 11:20:07 -06:00
|
|
|
if (Verbose)
|
|
|
|
CmdArgs.push_back("-v");
|
2022-02-11 21:58:15 -05:00
|
|
|
|
2023-01-19 11:20:07 -06:00
|
|
|
if (!CudaBinaryPath.empty())
|
|
|
|
CmdArgs.push_back(Args.MakeArgString("--cuda-path=" + CudaBinaryPath));
|
2022-07-06 22:58:52 -04:00
|
|
|
|
2023-01-19 11:20:07 -06:00
|
|
|
for (StringRef Arg : Args.getAllArgValues(OPT_ptxas_arg))
|
2023-10-25 10:41:30 -05:00
|
|
|
llvm::copy(
|
|
|
|
SmallVector<StringRef>({"-Xcuda-ptxas", Args.MakeArgString(Arg)}),
|
|
|
|
std::back_inserter(CmdArgs));
|
2022-07-06 22:58:52 -04:00
|
|
|
|
2023-01-19 11:20:07 -06:00
|
|
|
for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))
|
|
|
|
CmdArgs.push_back(Args.MakeArgString("-Wl," + Arg));
|
2022-02-11 21:58:15 -05:00
|
|
|
|
2023-06-08 08:28:07 -05:00
|
|
|
for (StringRef Arg : Args.getAllArgValues(OPT_builtin_bitcode_EQ)) {
|
|
|
|
if (llvm::Triple(Arg.split('=').first) == Triple)
|
|
|
|
CmdArgs.append({"-Xclang", "-mlink-builtin-bitcode", "-Xclang",
|
|
|
|
Args.MakeArgString(Arg.split('=').second)});
|
|
|
|
}
|
|
|
|
|
|
|
|
// The OpenMPOpt pass can introduce new calls and is expensive, we do not want
|
|
|
|
// this when running CodeGen through clang.
|
|
|
|
if (Args.hasArg(OPT_clang_backend) || Args.hasArg(OPT_builtin_bitcode_EQ))
|
|
|
|
CmdArgs.append({"-mllvm", "-openmp-opt-disable"});
|
|
|
|
|
2023-01-19 11:20:07 -06:00
|
|
|
if (Error Err = executeCommands(*ClangPath, CmdArgs))
|
2022-04-19 18:40:15 -04:00
|
|
|
return std::move(Err);
|
2022-02-11 21:58:15 -05:00
|
|
|
|
2022-06-22 11:17:14 -04:00
|
|
|
return *TempFileOrErr;
|
2022-02-11 21:58:15 -05:00
|
|
|
}
|
|
|
|
} // namespace generic
|
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles,
|
|
|
|
const ArgList &Args) {
|
|
|
|
const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
|
|
|
|
switch (Triple.getArch()) {
|
2022-01-04 17:20:04 -05:00
|
|
|
case Triple::nvptx:
|
|
|
|
case Triple::nvptx64:
|
|
|
|
case Triple::amdgcn:
|
|
|
|
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:
|
2023-01-19 11:20:07 -06:00
|
|
|
return generic::clang(InputFiles, Args);
|
2022-01-04 17:20:04 -05:00
|
|
|
default:
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
2022-07-06 22:58:52 -04:00
|
|
|
Triple.getArchName() +
|
2022-01-04 17:20:04 -05:00
|
|
|
" 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-07-07 11:28:32 -04:00
|
|
|
LTOError = true;
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-13 15:26:55 -04:00
|
|
|
// Get the list of target features from the input file and unify them such that
|
|
|
|
// if there are multiple +xxx or -xxx features we only keep the last one.
|
|
|
|
std::vector<std::string> getTargetFeatures(ArrayRef<OffloadFile> InputFiles) {
|
|
|
|
SmallVector<StringRef> Features;
|
|
|
|
for (const OffloadFile &File : InputFiles) {
|
|
|
|
for (auto Arg : llvm::split(File.getBinary()->getString("feature"), ","))
|
|
|
|
Features.emplace_back(Arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only add a feature if it hasn't been seen before starting from the end.
|
|
|
|
std::vector<std::string> UnifiedFeatures;
|
|
|
|
DenseSet<StringRef> UsedFeatures;
|
|
|
|
for (StringRef Feature : llvm::reverse(Features)) {
|
|
|
|
if (UsedFeatures.insert(Feature.drop_front()).second)
|
|
|
|
UnifiedFeatures.push_back(Feature.str());
|
|
|
|
}
|
2022-01-07 17:12:51 -05:00
|
|
|
|
2022-06-13 15:26:55 -04:00
|
|
|
return UnifiedFeatures;
|
2022-01-07 17:12:51 -05:00
|
|
|
}
|
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
template <typename ModuleHook = function_ref<bool(size_t, const Module &)>>
|
|
|
|
std::unique_ptr<lto::LTO> createLTO(
|
2022-07-06 22:58:52 -04:00
|
|
|
const ArgList &Args, const std::vector<std::string> &Features,
|
2022-01-11 15:50:39 -05:00
|
|
|
ModuleHook Hook = [](size_t, const Module &) { return true; }) {
|
2022-07-06 22:58:52 -04:00
|
|
|
const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
|
|
|
|
StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
|
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();
|
2022-07-06 22:58:52 -04:00
|
|
|
Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple);
|
2023-11-01 07:46:16 -05:00
|
|
|
Conf.Freestanding = true;
|
2022-01-07 17:12:51 -05:00
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2");
|
2022-06-13 15:26:55 -04:00
|
|
|
Conf.MAttrs = Features;
|
2023-09-14 14:10:14 -07:00
|
|
|
std::optional<CodeGenOptLevel> CGOptLevelOrNone =
|
2023-01-16 23:55:22 +00:00
|
|
|
CodeGenOpt::parseLevel(OptLevel[1]);
|
|
|
|
assert(CGOptLevelOrNone && "Invalid optimization level");
|
|
|
|
Conf.CGOptLevel = *CGOptLevelOrNone;
|
2022-01-07 17:12:51 -05:00
|
|
|
Conf.OptLevel = OptLevel[1] - '0';
|
2022-07-06 22:58:52 -04:00
|
|
|
Conf.DefaultTriple = Triple.getTriple();
|
2022-07-07 11:28:32 -04:00
|
|
|
|
|
|
|
LTOError = false;
|
2022-01-07 17:12:51 -05:00
|
|
|
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) {
|
2023-01-12 16:42:30 -06:00
|
|
|
std::string TempName = (sys::path::filename(ExecutableName) + "." +
|
|
|
|
Triple.getTriple() + "." + Arch)
|
2022-07-08 11:37:15 -04:00
|
|
|
.str();
|
2022-07-12 10:10:54 -04:00
|
|
|
Conf.PostInternalizeModuleHook = [=](size_t Task, const Module &M) {
|
2023-01-12 16:42:30 -06:00
|
|
|
std::string File =
|
|
|
|
!Task ? TempName + ".postlink.bc"
|
|
|
|
: TempName + "." + std::to_string(Task) + ".postlink.bc";
|
|
|
|
error_code EC;
|
|
|
|
raw_fd_ostream LinkedBitcode(File, EC, sys::fs::OF_None);
|
|
|
|
if (EC)
|
|
|
|
reportError(errorCodeToError(EC));
|
|
|
|
WriteBitcodeToFile(M, LinkedBitcode);
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
Conf.PreCodeGenModuleHook = [=](size_t Task, const Module &M) {
|
|
|
|
std::string File =
|
|
|
|
!Task ? TempName + ".postopt.bc"
|
|
|
|
: TempName + "." + std::to_string(Task) + ".postopt.bc";
|
2022-07-12 10:10:54 -04:00
|
|
|
error_code EC;
|
2022-07-12 19:40:54 -04:00
|
|
|
raw_fd_ostream LinkedBitcode(File, EC, sys::fs::OF_None);
|
2022-01-16 16:06:59 -05:00
|
|
|
if (EC)
|
2022-07-08 11:00:14 -04:00
|
|
|
reportError(errorCodeToError(EC));
|
2022-01-16 16:06:59 -05:00
|
|
|
WriteBitcodeToFile(M, LinkedBitcode);
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
}
|
2022-01-15 23:10:52 -05:00
|
|
|
Conf.PostOptModuleHook = Hook;
|
2023-09-14 14:10:14 -07:00
|
|
|
Conf.CGFileType = (Triple.isNVPTX() || SaveTemps)
|
|
|
|
? CodeGenFileType::AssemblyFile
|
|
|
|
: CodeGenFileType::ObjectFile;
|
2022-01-07 17:12:51 -05:00
|
|
|
|
|
|
|
// TODO: Handle remark files
|
2022-07-06 22:58:52 -04:00
|
|
|
Conf.HasWholeProgramVisibility = Args.hasArg(OPT_whole_program);
|
2022-01-07 17:12:51 -05:00
|
|
|
|
|
|
|
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] == '_') &&
|
2022-08-14 16:25:36 -07:00
|
|
|
llvm::all_of(llvm::drop_begin(S),
|
|
|
|
[](char C) { return C == '_' || isAlnum(C); });
|
2022-01-07 17:12:51 -05:00
|
|
|
}
|
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles,
|
2022-06-22 11:17:14 -04:00
|
|
|
SmallVectorImpl<StringRef> &OutputFiles,
|
2022-07-06 22:58:52 -04:00
|
|
|
const ArgList &Args) {
|
2022-07-13 14:55:55 -04:00
|
|
|
llvm::TimeTraceScope TimeScope("Link bitcode files");
|
2022-07-06 22:58:52 -04:00
|
|
|
const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
|
2023-01-12 16:42:30 -06:00
|
|
|
StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
|
2022-07-06 22:58:52 -04:00
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
SmallVector<OffloadFile, 4> BitcodeInputFiles;
|
2023-10-04 14:13:52 -05:00
|
|
|
DenseSet<StringRef> StrongResolutions;
|
2022-02-16 16:30:45 -05:00
|
|
|
DenseSet<StringRef> UsedInRegularObj;
|
|
|
|
DenseSet<StringRef> UsedInSharedLib;
|
2022-07-08 13:33:46 -04:00
|
|
|
BumpPtrAllocator Alloc;
|
|
|
|
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
|
2022-06-06 11:36:45 -04:00
|
|
|
// is not a bitcode file, scan its symbol table for symbols we need to save.
|
|
|
|
for (OffloadFile &File : InputFiles) {
|
|
|
|
MemoryBufferRef Buffer = MemoryBufferRef(File.getBinary()->getImage(), "");
|
|
|
|
|
|
|
|
file_magic Type = identify_magic(Buffer.getBuffer());
|
2022-02-21 14:39:58 -05:00
|
|
|
switch (Type) {
|
|
|
|
case file_magic::bitcode: {
|
2023-10-04 14:13:52 -05:00
|
|
|
Expected<IRSymtabFile> IRSymtabOrErr = readIRSymtab(Buffer);
|
|
|
|
if (!IRSymtabOrErr)
|
|
|
|
return IRSymtabOrErr.takeError();
|
|
|
|
|
|
|
|
// Check for any strong resolutions we need to preserve.
|
|
|
|
for (unsigned I = 0; I != IRSymtabOrErr->Mods.size(); ++I) {
|
|
|
|
for (const auto &Sym : IRSymtabOrErr->TheReader.module_symbols(I)) {
|
|
|
|
if (!Sym.isFormatSpecific() && Sym.isGlobal() && !Sym.isWeak() &&
|
|
|
|
!Sym.isUndefined())
|
|
|
|
StrongResolutions.insert(Saver.save(Sym.Name));
|
|
|
|
}
|
|
|
|
}
|
2022-06-06 11:36:45 -04:00
|
|
|
BitcodeInputFiles.emplace_back(std::move(File));
|
2022-02-21 14:39:58 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case file_magic::elf_relocatable:
|
2022-07-12 11:40:38 -04:00
|
|
|
case file_magic::elf_shared_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-07-12 11:40:38 -04:00
|
|
|
for (SymbolRef Sym : (*ObjFile)->symbols()) {
|
2022-01-07 17:12:51 -05:00
|
|
|
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-07-08 13:33:46 -04:00
|
|
|
UsedInRegularObj.insert(Saver.save(*Name));
|
2022-01-25 11:23:27 -05:00
|
|
|
else
|
2022-07-08 13:33:46 -04: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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
if (BitcodeInputFiles.empty())
|
2022-01-11 15:50:39 -05:00
|
|
|
return Error::success();
|
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
// Remove all the bitcode files that we moved from the original input.
|
|
|
|
llvm::erase_if(InputFiles, [](OffloadFile &F) { return !F.getBinary(); });
|
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
// LTO Module hook to output bitcode without running the backend.
|
2023-06-08 08:28:07 -05:00
|
|
|
SmallVector<StringRef> BitcodeOutput;
|
2022-07-12 19:40:54 -04:00
|
|
|
auto OutputBitcode = [&](size_t, const Module &M) {
|
2022-06-22 11:17:14 -04:00
|
|
|
auto TempFileOrErr = createOutputFile(sys::path::filename(ExecutableName) +
|
2022-07-06 22:58:52 -04:00
|
|
|
"-jit-" + Triple.getTriple(),
|
2022-06-22 11:17:14 -04:00
|
|
|
"bc");
|
|
|
|
if (!TempFileOrErr)
|
2022-07-08 11:00:14 -04:00
|
|
|
reportError(TempFileOrErr.takeError());
|
2022-01-16 16:06:59 -05:00
|
|
|
|
2022-01-11 15:50:39 -05:00
|
|
|
std::error_code EC;
|
2022-06-22 11:17:14 -04:00
|
|
|
raw_fd_ostream LinkedBitcode(*TempFileOrErr, EC, sys::fs::OF_None);
|
2022-01-11 15:50:39 -05:00
|
|
|
if (EC)
|
2022-07-08 11:00:14 -04:00
|
|
|
reportError(errorCodeToError(EC));
|
2022-01-11 15:50:39 -05:00
|
|
|
WriteBitcodeToFile(M, LinkedBitcode);
|
2022-06-22 11:17:14 -04:00
|
|
|
BitcodeOutput.push_back(*TempFileOrErr);
|
2022-01-11 15:50:39 -05:00
|
|
|
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-06-13 15:26:55 -04:00
|
|
|
auto Features = getTargetFeatures(BitcodeInputFiles);
|
2023-06-08 08:28:07 -05:00
|
|
|
auto LTOBackend = Args.hasArg(OPT_embed_bitcode) ||
|
|
|
|
Args.hasArg(OPT_builtin_bitcode_EQ) ||
|
|
|
|
Args.hasArg(OPT_clang_backend)
|
2022-07-06 22:58:52 -04:00
|
|
|
? createLTO(Args, Features, OutputBitcode)
|
|
|
|
: createLTO(Args, Features);
|
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.
|
2022-07-12 10:10:54 -04:00
|
|
|
uint64_t Idx = 0;
|
2022-01-13 22:59:05 -05:00
|
|
|
DenseSet<StringRef> PrevailingSymbols;
|
2022-06-06 11:36:45 -04:00
|
|
|
for (auto &BitcodeInput : BitcodeInputFiles) {
|
2022-07-12 10:10:54 -04:00
|
|
|
// Get a semi-unique buffer identifier for Thin-LTO.
|
|
|
|
StringRef Identifier = Saver.save(
|
|
|
|
std::to_string(Idx++) + "." +
|
|
|
|
BitcodeInput.getBinary()->getMemoryBufferRef().getBufferIdentifier());
|
2022-06-06 11:36:45 -04:00
|
|
|
MemoryBufferRef Buffer =
|
2022-07-12 10:10:54 -04:00
|
|
|
MemoryBufferRef(BitcodeInput.getBinary()->getImage(), Identifier);
|
2022-06-06 11:36:45 -04:00
|
|
|
Expected<std::unique_ptr<lto::InputFile>> BitcodeFileOrErr =
|
|
|
|
llvm::lto::InputFile::create(Buffer);
|
|
|
|
if (!BitcodeFileOrErr)
|
|
|
|
return BitcodeFileOrErr.takeError();
|
|
|
|
|
|
|
|
// Save the input file and the buffer associated with its memory.
|
|
|
|
const auto Symbols = (*BitcodeFileOrErr)->symbols();
|
2022-01-07 17:12:51 -05:00
|
|
|
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-07-08 13:33:46 -04:00
|
|
|
!Sym.isUndefined() &&
|
2023-10-04 14:13:52 -05:00
|
|
|
!(Sym.isWeak() && StrongResolutions.contains(Sym.getName())) &&
|
2022-07-08 13:33:46 -04:00
|
|
|
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.
|
2022-06-06 11:36:45 -04:00
|
|
|
if (Error Err = LTOBackend->add(std::move(*BitcodeFileOrErr), 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();
|
2022-06-22 11:17:14 -04:00
|
|
|
SmallVector<StringRef> Files(MaxTasks);
|
2022-11-22 13:46:42 -08:00
|
|
|
auto AddStream =
|
|
|
|
[&](size_t Task,
|
|
|
|
const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> {
|
2022-01-07 17:12:51 -05:00
|
|
|
int FD = -1;
|
|
|
|
auto &TempFile = Files[Task];
|
2023-01-11 14:57:39 -06:00
|
|
|
StringRef Extension = (Triple.isNVPTX() || SaveTemps) ? "s" : "o";
|
2022-07-12 19:40:54 -04:00
|
|
|
std::string TaskStr = Task ? "." + std::to_string(Task) : "";
|
2022-07-12 10:10:54 -04:00
|
|
|
auto TempFileOrErr =
|
2023-01-12 16:42:30 -06:00
|
|
|
createOutputFile(sys::path::filename(ExecutableName) + "." +
|
|
|
|
Triple.getTriple() + "." + Arch + TaskStr,
|
2022-07-12 10:10:54 -04:00
|
|
|
Extension);
|
2022-06-22 11:17:14 -04:00
|
|
|
if (!TempFileOrErr)
|
2022-07-08 11:00:14 -04:00
|
|
|
reportError(TempFileOrErr.takeError());
|
2022-06-22 11:17:14 -04:00
|
|
|
TempFile = *TempFileOrErr;
|
2022-01-16 16:06:59 -05:00
|
|
|
if (std::error_code EC = sys::fs::openFileForWrite(TempFile, FD))
|
2022-07-08 11:00:14 -04:00
|
|
|
reportError(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-07-07 11:28:32 -04:00
|
|
|
if (LTOError)
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
"Errors encountered inside the LTO pipeline.");
|
|
|
|
|
2022-05-24 13:43:52 -04:00
|
|
|
// If we are embedding bitcode we only need the intermediate output.
|
2022-07-12 10:10:54 -04:00
|
|
|
bool SingleOutput = Files.size() == 1;
|
2022-07-06 22:58:52 -04:00
|
|
|
if (Args.hasArg(OPT_embed_bitcode)) {
|
2022-07-12 10:10:54 -04:00
|
|
|
if (BitcodeOutput.size() != 1 || !SingleOutput)
|
2022-06-06 11:36:45 -04:00
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
"Cannot embed bitcode with multiple files.");
|
2022-12-05 07:48:30 -06:00
|
|
|
OutputFiles.push_back(Args.MakeArgString(BitcodeOutput.front()));
|
2022-05-24 13:43:52 -04:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2023-06-08 08:28:07 -05:00
|
|
|
// Append the new inputs to the device linker input. If the user requested an
|
|
|
|
// internalizing link we need to pass the bitcode to clang.
|
|
|
|
for (StringRef File :
|
|
|
|
Args.hasArg(OPT_clang_backend) || Args.hasArg(OPT_builtin_bitcode_EQ)
|
|
|
|
? BitcodeOutput
|
|
|
|
: Files)
|
2022-06-22 11:17:14 -04:00
|
|
|
OutputFiles.push_back(File);
|
2022-01-11 15:50:39 -05:00
|
|
|
|
|
|
|
return Error::success();
|
2022-01-07 17:12:51 -05:00
|
|
|
}
|
|
|
|
|
2022-06-22 11:17:14 -04:00
|
|
|
Expected<StringRef> writeOffloadFile(const OffloadFile &File) {
|
2022-06-06 11:36:45 -04:00
|
|
|
const OffloadBinary &Binary = *File.getBinary();
|
2022-05-06 09:37:08 -04:00
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
StringRef Prefix =
|
|
|
|
sys::path::stem(Binary.getMemoryBufferRef().getBufferIdentifier());
|
|
|
|
StringRef Suffix = getImageKindName(Binary.getImageKind());
|
2022-01-11 15:50:39 -05:00
|
|
|
|
2022-06-22 11:17:14 -04:00
|
|
|
auto TempFileOrErr = createOutputFile(
|
|
|
|
Prefix + "-" + Binary.getTriple() + "-" + Binary.getArch(), Suffix);
|
|
|
|
if (!TempFileOrErr)
|
|
|
|
return TempFileOrErr.takeError();
|
2022-04-22 13:19:16 -04:00
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
|
2022-06-22 11:17:14 -04:00
|
|
|
FileOutputBuffer::create(*TempFileOrErr, Binary.getImage().size());
|
2022-06-06 11:36:45 -04:00
|
|
|
if (!OutputOrErr)
|
|
|
|
return OutputOrErr.takeError();
|
|
|
|
std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
|
2022-08-18 14:59:34 -04:00
|
|
|
llvm::copy(Binary.getImage(), Output->getBufferStart());
|
2022-06-06 11:36:45 -04:00
|
|
|
if (Error E = Output->commit())
|
2022-06-22 09:37:54 -04:00
|
|
|
return std::move(E);
|
2022-01-04 17:20:04 -05:00
|
|
|
|
2022-06-22 11:17:14 -04:00
|
|
|
return *TempFileOrErr;
|
2022-01-04 17:20:04 -05:00
|
|
|
}
|
|
|
|
|
2022-01-25 14:25:39 -05:00
|
|
|
// Compile the module to an object file using the appropriate target machine for
|
|
|
|
// the host triple.
|
2023-09-28 11:22:11 -05:00
|
|
|
Expected<StringRef> compileModule(Module &M, OffloadKind Kind) {
|
2022-07-13 14:55:55 -04:00
|
|
|
llvm::TimeTraceScope TimeScope("Compile module");
|
2022-01-25 14:25:39 -05:00
|
|
|
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 = "";
|
2022-07-06 22:58:52 -04:00
|
|
|
std::unique_ptr<TargetMachine> TM(
|
|
|
|
T->createTargetMachine(M.getTargetTriple(), CPU, Features, Options,
|
|
|
|
Reloc::PIC_, M.getCodeModel()));
|
2022-01-25 14:25:39 -05:00
|
|
|
|
|
|
|
if (M.getDataLayout().isDefault())
|
|
|
|
M.setDataLayout(TM->createDataLayout());
|
|
|
|
|
|
|
|
int FD = -1;
|
2023-09-28 11:22:11 -05:00
|
|
|
auto TempFileOrErr =
|
|
|
|
createOutputFile(sys::path::filename(ExecutableName) + "." +
|
|
|
|
getOffloadKindName(Kind) + ".image.wrapper",
|
|
|
|
"o");
|
2022-06-22 11:17:14 -04:00
|
|
|
if (!TempFileOrErr)
|
|
|
|
return TempFileOrErr.takeError();
|
|
|
|
if (std::error_code EC = sys::fs::openFileForWrite(*TempFileOrErr, FD))
|
2022-01-25 14:25:39 -05:00
|
|
|
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));
|
2023-09-14 14:10:14 -07:00
|
|
|
if (TM->addPassesToEmitFile(CodeGenPasses, *OS, nullptr,
|
|
|
|
CodeGenFileType::ObjectFile))
|
2022-01-25 14:25:39 -05:00
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
"Failed to execute host backend");
|
|
|
|
CodeGenPasses.run(M);
|
|
|
|
|
2022-06-22 11:17:14 -04:00
|
|
|
return *TempFileOrErr;
|
2022-01-25 14:25:39 -05:00
|
|
|
}
|
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
/// Creates the object file containing the device image and runtime
|
|
|
|
/// registration code from the device images stored in \p Images.
|
2022-06-22 11:17:14 -04:00
|
|
|
Expected<StringRef>
|
2022-06-06 11:36:45 -04:00
|
|
|
wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,
|
2022-07-06 22:58:52 -04:00
|
|
|
const ArgList &Args, OffloadKind Kind) {
|
2022-07-13 14:55:55 -04:00
|
|
|
llvm::TimeTraceScope TimeScope("Wrap bundled images");
|
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
SmallVector<ArrayRef<char>, 4> BuffersToWrap;
|
|
|
|
for (const auto &Buffer : Buffers)
|
|
|
|
BuffersToWrap.emplace_back(
|
|
|
|
ArrayRef<char>(Buffer->getBufferStart(), Buffer->getBufferSize()));
|
|
|
|
|
|
|
|
LLVMContext Context;
|
|
|
|
Module M("offload.wrapper.module", Context);
|
2022-07-06 22:58:52 -04:00
|
|
|
M.setTargetTriple(
|
|
|
|
Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
|
2022-06-06 11:36:45 -04:00
|
|
|
|
|
|
|
switch (Kind) {
|
|
|
|
case OFK_OpenMP:
|
|
|
|
if (Error Err = wrapOpenMPBinaries(M, BuffersToWrap))
|
|
|
|
return std::move(Err);
|
|
|
|
break;
|
|
|
|
case OFK_Cuda:
|
|
|
|
if (Error Err = wrapCudaBinary(M, BuffersToWrap.front()))
|
|
|
|
return std::move(Err);
|
|
|
|
break;
|
2022-06-30 07:59:24 -04:00
|
|
|
case OFK_HIP:
|
|
|
|
if (Error Err = wrapHIPBinary(M, BuffersToWrap.front()))
|
|
|
|
return std::move(Err);
|
|
|
|
break;
|
2022-06-06 11:36:45 -04:00
|
|
|
default:
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
getOffloadKindName(Kind) +
|
|
|
|
" wrapping is not supported");
|
2022-01-25 17:46:01 -05:00
|
|
|
}
|
2022-01-04 17:20:04 -05:00
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
if (Args.hasArg(OPT_print_wrapped_module))
|
|
|
|
errs() << M;
|
2023-02-06 12:33:25 -06:00
|
|
|
if (Args.hasArg(OPT_save_temps)) {
|
|
|
|
int FD = -1;
|
|
|
|
auto TempFileOrErr =
|
|
|
|
createOutputFile(sys::path::filename(ExecutableName) + "." +
|
|
|
|
getOffloadKindName(Kind) + ".image.wrapper",
|
|
|
|
"bc");
|
|
|
|
if (!TempFileOrErr)
|
|
|
|
return TempFileOrErr.takeError();
|
|
|
|
if (std::error_code EC = sys::fs::openFileForWrite(*TempFileOrErr, FD))
|
|
|
|
return errorCodeToError(EC);
|
|
|
|
llvm::raw_fd_ostream OS(FD, true);
|
|
|
|
WriteBitcodeToFile(M, OS);
|
|
|
|
}
|
2022-06-06 11:36:45 -04:00
|
|
|
|
2023-09-28 11:22:11 -05:00
|
|
|
auto FileOrErr = compileModule(M, Kind);
|
2022-06-06 11:36:45 -04:00
|
|
|
if (!FileOrErr)
|
|
|
|
return FileOrErr.takeError();
|
|
|
|
return *FileOrErr;
|
2022-04-12 11:21:36 -04:00
|
|
|
}
|
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
|
|
|
|
bundleOpenMP(ArrayRef<OffloadingImage> Images) {
|
|
|
|
SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
|
|
|
|
for (const OffloadingImage &Image : Images)
|
2023-09-01 09:19:25 -07:00
|
|
|
Buffers.emplace_back(
|
|
|
|
MemoryBuffer::getMemBufferCopy(OffloadBinary::write(Image)));
|
2022-06-06 11:36:45 -04:00
|
|
|
|
|
|
|
return std::move(Buffers);
|
|
|
|
}
|
|
|
|
|
|
|
|
Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
|
2022-07-06 22:58:52 -04:00
|
|
|
bundleCuda(ArrayRef<OffloadingImage> Images, const ArgList &Args) {
|
2022-06-30 07:59:24 -04:00
|
|
|
SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles;
|
|
|
|
for (const OffloadingImage &Image : Images)
|
|
|
|
InputFiles.emplace_back(std::make_pair(Image.Image->getBufferIdentifier(),
|
|
|
|
Image.StringData.lookup("arch")));
|
|
|
|
|
|
|
|
Triple TheTriple = Triple(Images.front().StringData.lookup("triple"));
|
|
|
|
auto FileOrErr = nvptx::fatbinary(InputFiles, Args);
|
|
|
|
if (!FileOrErr)
|
|
|
|
return FileOrErr.takeError();
|
|
|
|
|
|
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =
|
|
|
|
llvm::MemoryBuffer::getFileOrSTDIN(*FileOrErr);
|
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
|
2022-06-30 07:59:24 -04:00
|
|
|
if (std::error_code EC = ImageOrError.getError())
|
|
|
|
return createFileError(*FileOrErr, EC);
|
|
|
|
Buffers.emplace_back(std::move(*ImageOrError));
|
|
|
|
|
|
|
|
return std::move(Buffers);
|
|
|
|
}
|
2022-04-12 11:21:36 -04:00
|
|
|
|
2022-06-30 07:59:24 -04:00
|
|
|
Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
|
|
|
|
bundleHIP(ArrayRef<OffloadingImage> Images, const ArgList &Args) {
|
2022-06-06 11:36:45 -04:00
|
|
|
SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles;
|
|
|
|
for (const OffloadingImage &Image : Images)
|
|
|
|
InputFiles.emplace_back(std::make_pair(Image.Image->getBufferIdentifier(),
|
|
|
|
Image.StringData.lookup("arch")));
|
|
|
|
|
|
|
|
Triple TheTriple = Triple(Images.front().StringData.lookup("triple"));
|
2022-06-30 07:59:24 -04:00
|
|
|
auto FileOrErr = amdgcn::fatbinary(InputFiles, Args);
|
2022-04-12 11:21:36 -04:00
|
|
|
if (!FileOrErr)
|
|
|
|
return FileOrErr.takeError();
|
|
|
|
|
|
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =
|
|
|
|
llvm::MemoryBuffer::getFileOrSTDIN(*FileOrErr);
|
2022-06-30 07:59:24 -04:00
|
|
|
|
|
|
|
SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
|
2022-04-12 11:21:36 -04:00
|
|
|
if (std::error_code EC = ImageOrError.getError())
|
|
|
|
return createFileError(*FileOrErr, EC);
|
2022-06-06 11:36:45 -04:00
|
|
|
Buffers.emplace_back(std::move(*ImageOrError));
|
2022-04-12 11:21:36 -04:00
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
return std::move(Buffers);
|
|
|
|
}
|
2022-01-04 17:20:04 -05:00
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
/// Transforms the input \p Images into the binary format the runtime expects
|
|
|
|
/// for the given \p Kind.
|
|
|
|
Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
|
2022-07-06 22:58:52 -04:00
|
|
|
bundleLinkedOutput(ArrayRef<OffloadingImage> Images, const ArgList &Args,
|
|
|
|
OffloadKind Kind) {
|
2022-07-13 14:55:55 -04:00
|
|
|
llvm::TimeTraceScope TimeScope("Bundle linked output");
|
2022-06-06 11:36:45 -04:00
|
|
|
switch (Kind) {
|
|
|
|
case OFK_OpenMP:
|
|
|
|
return bundleOpenMP(Images);
|
|
|
|
case OFK_Cuda:
|
2022-07-06 22:58:52 -04:00
|
|
|
return bundleCuda(Images, Args);
|
2022-06-30 07:59:24 -04:00
|
|
|
case OFK_HIP:
|
|
|
|
return bundleHIP(Images, Args);
|
2022-06-06 11:36:45 -04:00
|
|
|
default:
|
|
|
|
return createStringError(inconvertibleErrorCode(),
|
|
|
|
getOffloadKindName(Kind) +
|
|
|
|
" bundling is not supported");
|
|
|
|
}
|
2022-04-12 11:21:36 -04:00
|
|
|
}
|
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
/// Returns a new ArgList containg arguments used for the device linking phase.
|
|
|
|
DerivedArgList getLinkerArgs(ArrayRef<OffloadFile> Input,
|
|
|
|
const InputArgList &Args) {
|
|
|
|
DerivedArgList DAL = DerivedArgList(DerivedArgList(Args));
|
|
|
|
for (Arg *A : Args)
|
|
|
|
DAL.append(A);
|
|
|
|
|
|
|
|
// Set the subarchitecture and target triple for this compilation.
|
|
|
|
const OptTable &Tbl = getOptTable();
|
|
|
|
DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch_EQ),
|
|
|
|
Args.MakeArgString(Input.front().getBinary()->getArch()));
|
|
|
|
DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_triple_EQ),
|
|
|
|
Args.MakeArgString(Input.front().getBinary()->getTriple()));
|
|
|
|
|
|
|
|
// If every input file is bitcode we have whole program visibility as we do
|
|
|
|
// only support static linking with bitcode.
|
|
|
|
auto ContainsBitcode = [](const OffloadFile &F) {
|
|
|
|
return identify_magic(F.getBinary()->getImage()) == file_magic::bitcode;
|
|
|
|
};
|
|
|
|
if (llvm::all_of(Input, ContainsBitcode))
|
|
|
|
DAL.AddFlagArg(nullptr, Tbl.getOption(OPT_whole_program));
|
|
|
|
|
|
|
|
// Forward '-Xoffload-linker' options to the appropriate backend.
|
|
|
|
for (StringRef Arg : Args.getAllArgValues(OPT_device_linker_args_EQ)) {
|
2022-08-08 13:32:48 -04:00
|
|
|
auto [Triple, Value] = Arg.split('=');
|
|
|
|
if (Value.empty())
|
2022-07-06 22:58:52 -04:00
|
|
|
DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),
|
2022-08-08 13:32:48 -04:00
|
|
|
Args.MakeArgString(Triple));
|
|
|
|
else if (Triple == DAL.getLastArgValue(OPT_triple_EQ))
|
2022-07-06 22:58:52 -04:00
|
|
|
DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),
|
2022-08-08 13:32:48 -04:00
|
|
|
Args.MakeArgString(Value));
|
2022-07-06 22:58:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return DAL;
|
|
|
|
}
|
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
/// Transforms all the extracted offloading input files into an image that can
|
|
|
|
/// be registered by the runtime.
|
2022-06-22 11:17:14 -04:00
|
|
|
Expected<SmallVector<StringRef>>
|
2022-07-06 22:58:52 -04:00
|
|
|
linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles,
|
2022-10-25 12:28:28 -05:00
|
|
|
const InputArgList &Args, char **Argv, int Argc) {
|
2022-07-13 14:55:55 -04:00
|
|
|
llvm::TimeTraceScope TimeScope("Handle all device input");
|
|
|
|
|
2022-10-25 12:28:28 -05:00
|
|
|
DenseMap<OffloadFile::TargetID, SmallVector<OffloadFile>> InputMap;
|
2022-06-06 11:36:45 -04:00
|
|
|
for (auto &File : LinkerInputFiles)
|
2022-10-25 12:28:28 -05:00
|
|
|
InputMap[File].emplace_back(std::move(File));
|
2022-06-06 11:36:45 -04:00
|
|
|
LinkerInputFiles.clear();
|
|
|
|
|
2022-10-25 12:28:28 -05:00
|
|
|
SmallVector<SmallVector<OffloadFile>> InputsForTarget;
|
|
|
|
for (auto &[ID, Input] : InputMap)
|
|
|
|
InputsForTarget.emplace_back(std::move(Input));
|
|
|
|
InputMap.clear();
|
|
|
|
|
|
|
|
std::mutex ImageMtx;
|
|
|
|
DenseMap<OffloadKind, SmallVector<OffloadingImage>> Images;
|
|
|
|
auto Err = parallelForEachError(InputsForTarget, [&](auto &Input) -> Error {
|
2022-07-13 14:55:55 -04:00
|
|
|
llvm::TimeTraceScope TimeScope("Link device input");
|
|
|
|
|
2022-10-25 12:28:28 -05:00
|
|
|
// Each thread needs its own copy of the base arguments to maintain
|
|
|
|
// per-device argument storage of synthetic strings.
|
|
|
|
const OptTable &Tbl = getOptTable();
|
|
|
|
BumpPtrAllocator Alloc;
|
|
|
|
StringSaver Saver(Alloc);
|
|
|
|
auto BaseArgs =
|
|
|
|
Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [](StringRef Err) {
|
|
|
|
reportError(createStringError(inconvertibleErrorCode(), Err));
|
|
|
|
});
|
|
|
|
auto LinkerArgs = getLinkerArgs(Input, BaseArgs);
|
2022-06-06 11:36:45 -04:00
|
|
|
|
|
|
|
DenseSet<OffloadKind> ActiveOffloadKinds;
|
|
|
|
for (const auto &File : Input)
|
2022-11-29 09:42:03 -06:00
|
|
|
if (File.getBinary()->getOffloadKind() != OFK_None)
|
|
|
|
ActiveOffloadKinds.insert(File.getBinary()->getOffloadKind());
|
2022-06-06 11:36:45 -04:00
|
|
|
|
|
|
|
// First link and remove all the input files containing bitcode.
|
2022-06-22 11:17:14 -04:00
|
|
|
SmallVector<StringRef> InputFiles;
|
2022-07-06 22:58:52 -04:00
|
|
|
if (Error Err = linkBitcodeFiles(Input, InputFiles, LinkerArgs))
|
2022-11-11 11:53:05 -08:00
|
|
|
return Err;
|
2022-06-06 11:36:45 -04:00
|
|
|
|
2022-10-25 12:28:28 -05:00
|
|
|
// Write any remaining device inputs to an output file for the linker.
|
2022-06-06 11:36:45 -04:00
|
|
|
for (const OffloadFile &File : Input) {
|
|
|
|
auto FileNameOrErr = writeOffloadFile(File);
|
|
|
|
if (!FileNameOrErr)
|
|
|
|
return FileNameOrErr.takeError();
|
|
|
|
InputFiles.emplace_back(*FileNameOrErr);
|
2022-04-12 11:21:36 -04:00
|
|
|
}
|
|
|
|
|
2022-10-25 12:28:28 -05:00
|
|
|
// Link the remaining device files using the device linker.
|
2023-01-19 11:20:07 -06:00
|
|
|
auto OutputOrErr = !Args.hasArg(OPT_embed_bitcode)
|
|
|
|
? linkDevice(InputFiles, LinkerArgs)
|
|
|
|
: InputFiles.front();
|
2022-06-06 11:36:45 -04:00
|
|
|
if (!OutputOrErr)
|
|
|
|
return OutputOrErr.takeError();
|
2022-04-12 11:21:36 -04:00
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
// Store the offloading image for each linked output file.
|
|
|
|
for (OffloadKind Kind : ActiveOffloadKinds) {
|
|
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileOrErr =
|
|
|
|
llvm::MemoryBuffer::getFileOrSTDIN(*OutputOrErr);
|
2023-01-12 10:41:18 -06:00
|
|
|
if (std::error_code EC = FileOrErr.getError()) {
|
|
|
|
if (DryRun)
|
|
|
|
FileOrErr = MemoryBuffer::getMemBuffer("");
|
|
|
|
else
|
|
|
|
return createFileError(*OutputOrErr, EC);
|
|
|
|
}
|
2022-06-06 11:36:45 -04:00
|
|
|
|
2023-01-31 09:50:40 -06:00
|
|
|
std::scoped_lock<decltype(ImageMtx)> Guard(ImageMtx);
|
2022-06-06 11:36:45 -04:00
|
|
|
OffloadingImage TheImage{};
|
2022-12-05 07:48:30 -06:00
|
|
|
TheImage.TheImageKind =
|
|
|
|
Args.hasArg(OPT_embed_bitcode) ? IMG_Bitcode : IMG_Object;
|
2022-06-06 11:36:45 -04:00
|
|
|
TheImage.TheOffloadKind = Kind;
|
2023-02-06 12:33:25 -06:00
|
|
|
TheImage.StringData["triple"] =
|
|
|
|
Args.MakeArgString(LinkerArgs.getLastArgValue(OPT_triple_EQ));
|
|
|
|
TheImage.StringData["arch"] =
|
|
|
|
Args.MakeArgString(LinkerArgs.getLastArgValue(OPT_arch_EQ));
|
2022-06-06 11:36:45 -04:00
|
|
|
TheImage.Image = std::move(*FileOrErr);
|
2022-10-25 12:28:28 -05:00
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
Images[Kind].emplace_back(std::move(TheImage));
|
|
|
|
}
|
2022-10-25 12:28:28 -05:00
|
|
|
return Error::success();
|
|
|
|
});
|
|
|
|
if (Err)
|
|
|
|
return std::move(Err);
|
2022-06-06 11:36:45 -04:00
|
|
|
|
|
|
|
// Create a binary image of each offloading image and embed it into a new
|
|
|
|
// object file.
|
2022-06-22 11:17:14 -04:00
|
|
|
SmallVector<StringRef> WrappedOutput;
|
2022-10-25 12:28:28 -05:00
|
|
|
for (auto &[Kind, Input] : Images) {
|
|
|
|
// We sort the entries before bundling so they appear in a deterministic
|
|
|
|
// order in the final binary.
|
|
|
|
llvm::sort(Input, [](OffloadingImage &A, OffloadingImage &B) {
|
2023-01-15 20:56:34 +01:00
|
|
|
return A.StringData["triple"] > B.StringData["triple"] ||
|
|
|
|
A.StringData["arch"] > B.StringData["arch"] ||
|
2022-10-25 12:28:28 -05:00
|
|
|
A.TheOffloadKind < B.TheOffloadKind;
|
|
|
|
});
|
2022-08-08 13:32:48 -04:00
|
|
|
auto BundledImagesOrErr = bundleLinkedOutput(Input, Args, Kind);
|
2022-06-06 11:36:45 -04:00
|
|
|
if (!BundledImagesOrErr)
|
|
|
|
return BundledImagesOrErr.takeError();
|
2022-07-06 22:58:52 -04:00
|
|
|
auto OutputOrErr = wrapDeviceImages(*BundledImagesOrErr, Args, Kind);
|
2022-06-06 11:36:45 -04:00
|
|
|
if (!OutputOrErr)
|
|
|
|
return OutputOrErr.takeError();
|
|
|
|
WrappedOutput.push_back(*OutputOrErr);
|
2022-04-12 11:21:36 -04:00
|
|
|
}
|
2022-04-19 14:14:16 -04:00
|
|
|
|
2022-06-06 11:36:45 -04:00
|
|
|
return WrappedOutput;
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
|
2023-01-02 15:54:57 -08:00
|
|
|
std::optional<std::string> findFile(StringRef Dir, StringRef Root,
|
|
|
|
const Twine &Name) {
|
2022-01-05 13:21:03 -05:00
|
|
|
SmallString<128> Path;
|
2022-03-02 12:38:29 -05:00
|
|
|
if (Dir.startswith("="))
|
2022-07-06 22:58:52 -04:00
|
|
|
sys::path::append(Path, Root, Dir.substr(1), Name);
|
2022-03-02 12:38:29 -05:00
|
|
|
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);
|
2022-12-03 11:54:46 -08:00
|
|
|
return std::nullopt;
|
2022-01-05 13:21:03 -05:00
|
|
|
}
|
|
|
|
|
2023-01-02 15:54:57 -08:00
|
|
|
std::optional<std::string>
|
|
|
|
findFromSearchPaths(StringRef Name, StringRef Root,
|
|
|
|
ArrayRef<StringRef> SearchPaths) {
|
2022-01-05 13:21:03 -05:00
|
|
|
for (StringRef Dir : SearchPaths)
|
2023-01-02 15:54:57 -08:00
|
|
|
if (std::optional<std::string> File = findFile(Dir, Root, Name))
|
2022-01-05 13:21:03 -05:00
|
|
|
return File;
|
2022-12-03 11:54:46 -08:00
|
|
|
return std::nullopt;
|
2022-01-05 13:21:03 -05:00
|
|
|
}
|
|
|
|
|
2023-01-02 15:54:57 -08:00
|
|
|
std::optional<std::string>
|
|
|
|
searchLibraryBaseName(StringRef Name, StringRef Root,
|
|
|
|
ArrayRef<StringRef> SearchPaths) {
|
2022-01-05 13:21:03 -05:00
|
|
|
for (StringRef Dir : SearchPaths) {
|
2023-01-02 15:54:57 -08:00
|
|
|
if (std::optional<std::string> File =
|
|
|
|
findFile(Dir, Root, "lib" + Name + ".so"))
|
2022-11-01 18:16:47 +00:00
|
|
|
return File;
|
2023-01-02 15:54:57 -08:00
|
|
|
if (std::optional<std::string> File =
|
|
|
|
findFile(Dir, Root, "lib" + Name + ".a"))
|
2022-01-05 13:21:03 -05:00
|
|
|
return File;
|
|
|
|
}
|
2022-12-03 11:54:46 -08:00
|
|
|
return std::nullopt;
|
2022-01-05 13:21:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Search for static libraries in the linker's library path given input like
|
|
|
|
/// `-lfoo` or `-l:libfoo.a`.
|
2023-01-02 15:54:57 -08:00
|
|
|
std::optional<std::string> searchLibrary(StringRef Input, StringRef Root,
|
|
|
|
ArrayRef<StringRef> SearchPaths) {
|
2023-11-22 20:23:23 -06:00
|
|
|
if (Input.startswith(":") || Input.ends_with(".lib"))
|
2022-07-06 22:58:52 -04:00
|
|
|
return findFromSearchPaths(Input.drop_front(), Root, SearchPaths);
|
|
|
|
return searchLibraryBaseName(Input, Root, SearchPaths);
|
2022-01-05 13:21:03 -05:00
|
|
|
}
|
|
|
|
|
2023-01-24 11:04:47 -06:00
|
|
|
/// Common redeclaration of needed symbol flags.
|
|
|
|
enum Symbol : uint32_t {
|
|
|
|
Sym_None = 0,
|
|
|
|
Sym_Undefined = 1U << 1,
|
|
|
|
Sym_Weak = 1U << 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Scan the symbols from a BitcodeFile \p Buffer and record if we need to
|
|
|
|
/// extract any symbols from it.
|
2023-03-17 15:10:25 -05:00
|
|
|
Expected<bool> getSymbolsFromBitcode(MemoryBufferRef Buffer, OffloadKind Kind,
|
2023-05-31 15:47:43 -05:00
|
|
|
bool IsArchive, StringSaver &Saver,
|
2023-01-24 11:04:47 -06:00
|
|
|
DenseMap<StringRef, Symbol> &Syms) {
|
|
|
|
Expected<IRSymtabFile> IRSymtabOrErr = readIRSymtab(Buffer);
|
|
|
|
if (!IRSymtabOrErr)
|
|
|
|
return IRSymtabOrErr.takeError();
|
|
|
|
|
2023-05-31 15:47:43 -05:00
|
|
|
bool ShouldExtract = !IsArchive;
|
|
|
|
DenseMap<StringRef, Symbol> TmpSyms;
|
2023-01-24 11:04:47 -06:00
|
|
|
for (unsigned I = 0; I != IRSymtabOrErr->Mods.size(); ++I) {
|
|
|
|
for (const auto &Sym : IRSymtabOrErr->TheReader.module_symbols(I)) {
|
|
|
|
if (Sym.isFormatSpecific() || !Sym.isGlobal())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bool NewSymbol = Syms.count(Sym.getName()) == 0;
|
2023-05-31 15:47:43 -05:00
|
|
|
auto OldSym = NewSymbol ? Sym_None : Syms[Sym.getName()];
|
2023-01-24 11:04:47 -06:00
|
|
|
|
|
|
|
// We will extract if it defines a currenlty undefined non-weak symbol.
|
|
|
|
bool ResolvesStrongReference =
|
|
|
|
((OldSym & Sym_Undefined && !(OldSym & Sym_Weak)) &&
|
|
|
|
!Sym.isUndefined());
|
|
|
|
// We will extract if it defines a new global symbol visible to the host.
|
2023-03-17 15:10:25 -05:00
|
|
|
// This is only necessary for code targeting an offloading language.
|
2023-01-24 11:04:47 -06:00
|
|
|
bool NewGlobalSymbol =
|
|
|
|
((NewSymbol || (OldSym & Sym_Undefined)) && !Sym.isUndefined() &&
|
2023-03-17 15:10:25 -05:00
|
|
|
!Sym.canBeOmittedFromSymbolTable() && Kind != object::OFK_None &&
|
2023-01-24 11:04:47 -06:00
|
|
|
(Sym.getVisibility() != GlobalValue::HiddenVisibility));
|
|
|
|
ShouldExtract |= ResolvesStrongReference | NewGlobalSymbol;
|
|
|
|
|
|
|
|
// Update this symbol in the "table" with the new information.
|
|
|
|
if (OldSym & Sym_Undefined && !Sym.isUndefined())
|
2023-05-31 15:47:43 -05:00
|
|
|
TmpSyms[Saver.save(Sym.getName())] =
|
|
|
|
static_cast<Symbol>(OldSym & ~Sym_Undefined);
|
2023-01-24 11:04:47 -06:00
|
|
|
if (Sym.isUndefined() && NewSymbol)
|
2023-05-31 15:47:43 -05:00
|
|
|
TmpSyms[Saver.save(Sym.getName())] =
|
|
|
|
static_cast<Symbol>(OldSym | Sym_Undefined);
|
2023-01-24 11:04:47 -06:00
|
|
|
if (Sym.isWeak())
|
2023-05-31 15:47:43 -05:00
|
|
|
TmpSyms[Saver.save(Sym.getName())] =
|
|
|
|
static_cast<Symbol>(OldSym | Sym_Weak);
|
2023-01-24 11:04:47 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-31 15:47:43 -05:00
|
|
|
// If the file gets extracted we update the table with the new symbols.
|
|
|
|
if (ShouldExtract)
|
|
|
|
Syms.insert(std::begin(TmpSyms), std::end(TmpSyms));
|
|
|
|
|
2023-01-24 11:04:47 -06:00
|
|
|
return ShouldExtract;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Scan the symbols from an ObjectFile \p Obj and record if we need to extract
|
|
|
|
/// any symbols from it.
|
2023-03-17 15:10:25 -05:00
|
|
|
Expected<bool> getSymbolsFromObject(const ObjectFile &Obj, OffloadKind Kind,
|
2023-05-31 15:47:43 -05:00
|
|
|
bool IsArchive, StringSaver &Saver,
|
2023-01-24 11:04:47 -06:00
|
|
|
DenseMap<StringRef, Symbol> &Syms) {
|
2023-05-31 15:47:43 -05:00
|
|
|
bool ShouldExtract = !IsArchive;
|
|
|
|
DenseMap<StringRef, Symbol> TmpSyms;
|
2023-01-24 11:04:47 -06:00
|
|
|
for (SymbolRef Sym : Obj.symbols()) {
|
|
|
|
auto FlagsOrErr = Sym.getFlags();
|
|
|
|
if (!FlagsOrErr)
|
|
|
|
return FlagsOrErr.takeError();
|
|
|
|
|
|
|
|
if (!(*FlagsOrErr & SymbolRef::SF_Global) ||
|
|
|
|
(*FlagsOrErr & SymbolRef::SF_FormatSpecific))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto NameOrErr = Sym.getName();
|
|
|
|
if (!NameOrErr)
|
|
|
|
return NameOrErr.takeError();
|
|
|
|
|
|
|
|
bool NewSymbol = Syms.count(*NameOrErr) == 0;
|
2023-05-31 15:47:43 -05:00
|
|
|
auto OldSym = NewSymbol ? Sym_None : Syms[*NameOrErr];
|
2023-01-24 11:04:47 -06:00
|
|
|
|
|
|
|
// We will extract if it defines a currenlty undefined non-weak symbol.
|
|
|
|
bool ResolvesStrongReference = (OldSym & Sym_Undefined) &&
|
|
|
|
!(OldSym & Sym_Weak) &&
|
|
|
|
!(*FlagsOrErr & SymbolRef::SF_Undefined);
|
|
|
|
|
|
|
|
// We will extract if it defines a new global symbol visible to the host.
|
2023-03-17 15:10:25 -05:00
|
|
|
// This is only necessary for code targeting an offloading language.
|
|
|
|
bool NewGlobalSymbol =
|
|
|
|
((NewSymbol || (OldSym & Sym_Undefined)) &&
|
|
|
|
!(*FlagsOrErr & SymbolRef::SF_Undefined) && Kind != object::OFK_None &&
|
|
|
|
!(*FlagsOrErr & SymbolRef::SF_Hidden));
|
2023-01-24 11:04:47 -06:00
|
|
|
ShouldExtract |= ResolvesStrongReference | NewGlobalSymbol;
|
|
|
|
|
|
|
|
// Update this symbol in the "table" with the new information.
|
|
|
|
if (OldSym & Sym_Undefined && !(*FlagsOrErr & SymbolRef::SF_Undefined))
|
2023-05-31 15:47:43 -05:00
|
|
|
TmpSyms[Saver.save(*NameOrErr)] =
|
|
|
|
static_cast<Symbol>(OldSym & ~Sym_Undefined);
|
2023-01-24 11:04:47 -06:00
|
|
|
if (*FlagsOrErr & SymbolRef::SF_Undefined && NewSymbol)
|
2023-05-31 15:47:43 -05:00
|
|
|
TmpSyms[Saver.save(*NameOrErr)] =
|
|
|
|
static_cast<Symbol>(OldSym | Sym_Undefined);
|
2023-01-24 11:04:47 -06:00
|
|
|
if (*FlagsOrErr & SymbolRef::SF_Weak)
|
2023-05-31 15:47:43 -05:00
|
|
|
TmpSyms[Saver.save(*NameOrErr)] = static_cast<Symbol>(OldSym | Sym_Weak);
|
2023-01-24 11:04:47 -06:00
|
|
|
}
|
2023-05-31 15:47:43 -05:00
|
|
|
|
|
|
|
// If the file gets extracted we update the table with the new symbols.
|
|
|
|
if (ShouldExtract)
|
|
|
|
Syms.insert(std::begin(TmpSyms), std::end(TmpSyms));
|
|
|
|
|
2023-01-24 11:04:47 -06:00
|
|
|
return ShouldExtract;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempt to 'resolve' symbols found in input files. We use this to
|
|
|
|
/// determine if an archive member needs to be extracted. An archive member
|
|
|
|
/// will be extracted if any of the following is true.
|
|
|
|
/// 1) It defines an undefined symbol in a regular object filie.
|
|
|
|
/// 2) It defines a global symbol without hidden visibility that has not
|
|
|
|
/// yet been defined.
|
2023-05-31 15:47:43 -05:00
|
|
|
Expected<bool> getSymbols(StringRef Image, OffloadKind Kind, bool IsArchive,
|
|
|
|
StringSaver &Saver,
|
2023-01-24 11:04:47 -06:00
|
|
|
DenseMap<StringRef, Symbol> &Syms) {
|
|
|
|
MemoryBufferRef Buffer = MemoryBufferRef(Image, "");
|
|
|
|
switch (identify_magic(Image)) {
|
|
|
|
case file_magic::bitcode:
|
2023-05-31 15:47:43 -05:00
|
|
|
return getSymbolsFromBitcode(Buffer, Kind, IsArchive, Saver, Syms);
|
2023-01-24 11:04:47 -06:00
|
|
|
case file_magic::elf_relocatable: {
|
|
|
|
Expected<std::unique_ptr<ObjectFile>> ObjFile =
|
|
|
|
ObjectFile::createObjectFile(Buffer);
|
|
|
|
if (!ObjFile)
|
|
|
|
return ObjFile.takeError();
|
2023-05-31 15:47:43 -05:00
|
|
|
return getSymbolsFromObject(**ObjFile, Kind, IsArchive, Saver, Syms);
|
2023-01-24 11:04:47 -06:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Search the input files and libraries for embedded device offloading code
|
|
|
|
/// and add it to the list of files to be linked. Files coming from static
|
|
|
|
/// libraries are only added to the input if they are used by an existing
|
|
|
|
/// input file.
|
2022-07-13 14:55:55 -04:00
|
|
|
Expected<SmallVector<OffloadFile>> getDeviceInput(const ArgList &Args) {
|
|
|
|
llvm::TimeTraceScope TimeScope("ExtractDeviceCode");
|
2022-07-06 22:58:52 -04:00
|
|
|
|
|
|
|
StringRef Root = Args.getLastArgValue(OPT_sysroot_EQ);
|
|
|
|
SmallVector<StringRef> LibraryPaths;
|
2023-11-22 20:23:23 -06:00
|
|
|
for (const opt::Arg *Arg : Args.filtered(OPT_library_path, OPT_libpath))
|
2022-07-06 22:58:52 -04:00
|
|
|
LibraryPaths.push_back(Arg->getValue());
|
|
|
|
|
2023-01-24 11:04:47 -06:00
|
|
|
BumpPtrAllocator Alloc;
|
|
|
|
StringSaver Saver(Alloc);
|
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
// Try to extract device code from the linker input files.
|
|
|
|
SmallVector<OffloadFile> InputFiles;
|
2023-01-24 11:04:47 -06:00
|
|
|
DenseMap<OffloadFile::TargetID, DenseMap<StringRef, Symbol>> Syms;
|
2023-11-22 20:23:23 -06:00
|
|
|
bool WholeArchive = Args.hasArg(OPT_wholearchive_flag) ? true : false;
|
2023-01-27 11:29:47 -06:00
|
|
|
for (const opt::Arg *Arg : Args.filtered(
|
|
|
|
OPT_INPUT, OPT_library, OPT_whole_archive, OPT_no_whole_archive)) {
|
|
|
|
if (Arg->getOption().matches(OPT_whole_archive) ||
|
|
|
|
Arg->getOption().matches(OPT_no_whole_archive)) {
|
|
|
|
WholeArchive = Arg->getOption().matches(OPT_whole_archive);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-01-24 11:04:47 -06:00
|
|
|
std::optional<std::string> Filename =
|
|
|
|
Arg->getOption().matches(OPT_library)
|
|
|
|
? searchLibrary(Arg->getValue(), Root, LibraryPaths)
|
|
|
|
: std::string(Arg->getValue());
|
|
|
|
|
|
|
|
if (!Filename && Arg->getOption().matches(OPT_library))
|
|
|
|
reportError(createStringError(inconvertibleErrorCode(),
|
|
|
|
"unable to find library -l%s",
|
|
|
|
Arg->getValue()));
|
|
|
|
|
|
|
|
if (!Filename || !sys::fs::exists(*Filename) ||
|
|
|
|
sys::fs::is_directory(*Filename))
|
2022-01-18 10:56:12 -05:00
|
|
|
continue;
|
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
|
2023-01-24 11:04:47 -06:00
|
|
|
MemoryBuffer::getFileOrSTDIN(*Filename);
|
2022-07-06 22:58:52 -04:00
|
|
|
if (std::error_code EC = BufferOrErr.getError())
|
2023-01-24 11:04:47 -06:00
|
|
|
return createFileError(*Filename, EC);
|
2022-07-06 22:58:52 -04:00
|
|
|
|
2023-01-24 11:04:47 -06:00
|
|
|
MemoryBufferRef Buffer = **BufferOrErr;
|
|
|
|
if (identify_magic(Buffer.getBuffer()) == file_magic::elf_shared_object)
|
2022-10-26 21:07:12 +00:00
|
|
|
continue;
|
|
|
|
|
2023-01-24 11:04:47 -06:00
|
|
|
SmallVector<OffloadFile> Binaries;
|
|
|
|
if (Error Err = extractOffloadBinaries(Buffer, Binaries))
|
2022-08-18 14:59:34 -04:00
|
|
|
return std::move(Err);
|
2022-07-06 22:58:52 -04:00
|
|
|
|
2023-01-24 11:04:47 -06:00
|
|
|
// We only extract archive members that are needed.
|
|
|
|
bool IsArchive = identify_magic(Buffer.getBuffer()) == file_magic::archive;
|
|
|
|
bool Extracted = true;
|
|
|
|
while (Extracted) {
|
|
|
|
Extracted = false;
|
|
|
|
for (OffloadFile &Binary : Binaries) {
|
|
|
|
if (!Binary.getBinary())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// If we don't have an object file for this architecture do not
|
|
|
|
// extract.
|
2023-01-27 11:29:47 -06:00
|
|
|
if (IsArchive && !WholeArchive && !Syms.count(Binary))
|
2023-01-24 11:04:47 -06:00
|
|
|
continue;
|
|
|
|
|
2023-05-31 15:47:43 -05:00
|
|
|
Expected<bool> ExtractOrErr =
|
|
|
|
getSymbols(Binary.getBinary()->getImage(),
|
|
|
|
Binary.getBinary()->getOffloadKind(), IsArchive, Saver,
|
|
|
|
Syms[Binary]);
|
2023-01-24 11:04:47 -06:00
|
|
|
if (!ExtractOrErr)
|
|
|
|
return ExtractOrErr.takeError();
|
|
|
|
|
2023-05-31 15:47:43 -05:00
|
|
|
Extracted = !WholeArchive && *ExtractOrErr;
|
2023-01-24 11:04:47 -06:00
|
|
|
|
2023-01-27 11:29:47 -06:00
|
|
|
if (!IsArchive || WholeArchive || Extracted)
|
2023-01-24 11:04:47 -06:00
|
|
|
InputFiles.emplace_back(std::move(Binary));
|
|
|
|
|
|
|
|
// If we extracted any files we need to check all the symbols again.
|
|
|
|
if (Extracted)
|
|
|
|
break;
|
|
|
|
}
|
2022-01-03 12:31:52 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-06 22:58:52 -04:00
|
|
|
for (StringRef Library : Args.getAllArgValues(OPT_bitcode_library_EQ)) {
|
2022-06-06 11:36:45 -04:00
|
|
|
auto FileOrErr = getInputBitcodeLibrary(Library);
|
|
|
|
if (!FileOrErr)
|
2022-08-18 14:59:34 -04:00
|
|
|
return FileOrErr.takeError();
|
2022-07-08 13:33:46 -04:00
|
|
|
InputFiles.push_back(std::move(*FileOrErr));
|
2022-06-06 11:36:45 -04:00
|
|
|
}
|
|
|
|
|
2022-07-13 15:44:08 -04:00
|
|
|
return std::move(InputFiles);
|
2022-07-13 14:55:55 -04:00
|
|
|
}
|
2022-04-13 11:48:07 -04:00
|
|
|
|
2022-07-13 14:55:55 -04:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
int main(int Argc, char **Argv) {
|
|
|
|
InitLLVM X(Argc, Argv);
|
|
|
|
InitializeAllTargetInfos();
|
|
|
|
InitializeAllTargets();
|
|
|
|
InitializeAllTargetMCs();
|
|
|
|
InitializeAllAsmParsers();
|
|
|
|
InitializeAllAsmPrinters();
|
|
|
|
|
|
|
|
LinkerExecutable = Argv[0];
|
|
|
|
sys::PrintStackTraceOnErrorSignal(Argv[0]);
|
|
|
|
|
|
|
|
const OptTable &Tbl = getOptTable();
|
|
|
|
BumpPtrAllocator Alloc;
|
|
|
|
StringSaver Saver(Alloc);
|
|
|
|
auto Args = Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [&](StringRef Err) {
|
|
|
|
reportError(createStringError(inconvertibleErrorCode(), Err));
|
|
|
|
});
|
|
|
|
|
|
|
|
if (Args.hasArg(OPT_help) || Args.hasArg(OPT_help_hidden)) {
|
|
|
|
Tbl.printHelp(
|
|
|
|
outs(),
|
|
|
|
"clang-linker-wrapper [options] -- <options to passed to the linker>",
|
|
|
|
"\nA wrapper utility over the host linker. It scans the input files\n"
|
|
|
|
"for sections that require additional processing prior to linking.\n"
|
|
|
|
"The will then transparently pass all arguments and input to the\n"
|
|
|
|
"specified host linker to create the final binary.\n",
|
|
|
|
Args.hasArg(OPT_help_hidden), Args.hasArg(OPT_help_hidden));
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
if (Args.hasArg(OPT_v)) {
|
|
|
|
printVersion(outs());
|
|
|
|
return EXIT_SUCCESS;
|
2022-04-13 11:48:07 -04:00
|
|
|
}
|
2021-12-30 16:41:36 -05:00
|
|
|
|
2022-07-18 12:43:50 -04:00
|
|
|
// This forwards '-mllvm' arguments to LLVM if present.
|
|
|
|
SmallVector<const char *> NewArgv = {Argv[0]};
|
|
|
|
for (const opt::Arg *Arg : Args.filtered(OPT_mllvm))
|
|
|
|
NewArgv.push_back(Arg->getValue());
|
|
|
|
for (const opt::Arg *Arg : Args.filtered(OPT_offload_opt_eq_minus))
|
|
|
|
NewArgv.push_back(Args.MakeArgString(StringRef("-") + Arg->getValue()));
|
|
|
|
cl::ParseCommandLineOptions(NewArgv.size(), &NewArgv[0]);
|
2022-07-13 14:55:55 -04:00
|
|
|
|
|
|
|
Verbose = Args.hasArg(OPT_verbose);
|
|
|
|
DryRun = Args.hasArg(OPT_dry_run);
|
|
|
|
SaveTemps = Args.hasArg(OPT_save_temps);
|
|
|
|
CudaBinaryPath = Args.getLastArgValue(OPT_cuda_path_EQ).str();
|
|
|
|
|
2023-11-22 20:23:23 -06:00
|
|
|
llvm::Triple Triple(
|
|
|
|
Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
|
|
|
|
if (Args.hasArg(OPT_o))
|
|
|
|
ExecutableName = Args.getLastArgValue(OPT_o, "a.out");
|
|
|
|
else if (Args.hasArg(OPT_out))
|
|
|
|
ExecutableName = Args.getLastArgValue(OPT_out, "a.exe");
|
|
|
|
else
|
|
|
|
ExecutableName = Triple.isOSWindows() ? "a.exe" : "a.out";
|
|
|
|
|
2022-10-25 12:28:28 -05:00
|
|
|
parallel::strategy = hardware_concurrency(1);
|
|
|
|
if (auto *Arg = Args.getLastArg(OPT_wrapper_jobs)) {
|
|
|
|
unsigned Threads = 0;
|
|
|
|
if (!llvm::to_integer(Arg->getValue(), Threads) || Threads == 0)
|
|
|
|
reportError(createStringError(
|
|
|
|
inconvertibleErrorCode(), "%s: expected a positive integer, got '%s'",
|
|
|
|
Arg->getSpelling().data(), Arg->getValue()));
|
|
|
|
parallel::strategy = hardware_concurrency(Threads);
|
|
|
|
}
|
|
|
|
|
2022-07-13 14:55:55 -04:00
|
|
|
if (Args.hasArg(OPT_wrapper_time_trace_eq)) {
|
|
|
|
unsigned Granularity;
|
|
|
|
Args.getLastArgValue(OPT_wrapper_time_trace_granularity, "500")
|
|
|
|
.getAsInteger(10, Granularity);
|
|
|
|
timeTraceProfilerInitialize(Granularity, Argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
llvm::TimeTraceScope TimeScope("Execute linker wrapper");
|
|
|
|
|
|
|
|
// Extract the device input files stored in the host fat binary.
|
|
|
|
auto DeviceInputFiles = getDeviceInput(Args);
|
|
|
|
if (!DeviceInputFiles)
|
|
|
|
reportError(DeviceInputFiles.takeError());
|
|
|
|
|
|
|
|
// Link and wrap the device images extracted from the linker input.
|
2022-10-25 12:28:28 -05:00
|
|
|
auto FilesOrErr =
|
|
|
|
linkAndWrapDeviceFiles(*DeviceInputFiles, Args, Argv, Argc);
|
2022-07-13 14:55:55 -04:00
|
|
|
if (!FilesOrErr)
|
|
|
|
reportError(FilesOrErr.takeError());
|
|
|
|
|
|
|
|
// Run the host linking job with the rendered arguments.
|
|
|
|
if (Error Err = runLinker(*FilesOrErr, Args))
|
|
|
|
reportError(std::move(Err));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const opt::Arg *Arg = Args.getLastArg(OPT_wrapper_time_trace_eq)) {
|
|
|
|
if (Error Err = timeTraceProfilerWrite(Arg->getValue(), ExecutableName))
|
|
|
|
reportError(std::move(Err));
|
|
|
|
timeTraceProfilerCleanup();
|
|
|
|
}
|
2021-12-30 16:41:36 -05:00
|
|
|
|
2022-01-04 17:20:04 -05:00
|
|
|
// Remove the temporary files created.
|
2022-06-22 11:17:14 -04:00
|
|
|
if (!SaveTemps)
|
|
|
|
for (const auto &TempFile : TempFiles)
|
|
|
|
if (std::error_code EC = sys::fs::remove(TempFile))
|
|
|
|
reportError(createFileError(TempFile, EC));
|
2022-01-03 12:31:52 -05:00
|
|
|
|
2021-12-30 16:41:36 -05:00
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|