cl::desc("Path for the target bitcode library"),
cl::cat(ClangLinkerWrapperCategory));
+static cl::opt<bool> EmbedBC(
+ "target-embed-bc", cl::ZeroOrMore,
+ cl::desc("Embed linked bitcode instead of an executable device image."),
+ cl::init(false), cl::cat(ClangLinkerWrapperCategory));
+
// Do not parse linker options.
static cl::list<std::string>
- HostLinkerArgs(cl::Sink, cl::desc("<options to be passed to linker>..."));
+ HostLinkerArgs(cl::Positional,
+ cl::desc("<options to be passed to linker>..."));
/// Path of the current binary.
-static std::string LinkerExecutable;
+static const char *LinkerExecutable;
/// Temporary files created by the linker wrapper.
static SmallVector<std::string, 16> TempFiles;
std::unique_ptr<MemoryBuffer> Buffer =
MemoryBuffer::getMemBuffer(Library.getMemoryBufferRef(), false);
- if (Error Err = writeArchive(TempFile, Members, true, Library.kind(),
- true, Library.isThin(), std::move(Buffer)))
+ if (Error Err = writeArchive(TempFile, Members, true, Library.kind(), true,
+ Library.isThin(), std::move(Buffer)))
return std::move(Err);
return static_cast<std::string>(TempFile);
return static_cast<std::string>(TempFile);
}
-Expected<std::string> link(ArrayRef<StringRef> InputFiles,
+Expected<std::string> link(ArrayRef<std::string> InputFiles,
ArrayRef<std::string> LinkerArgs, Triple TheTriple,
StringRef Arch) {
// NVPTX uses the nvlink binary to link device object files.
CmdArgs.push_back(Arg);
// Add extracted input files.
- for (auto Input : InputFiles)
+ for (StringRef Input : InputFiles)
CmdArgs.push_back(Input);
if (sys::ExecuteAndWait(*NvlinkPath, CmdArgs))
}
} // namespace nvptx
-Expected<std::string> linkDevice(ArrayRef<StringRef> InputFiles,
+Expected<std::string> linkDevice(ArrayRef<std::string> InputFiles,
ArrayRef<std::string> LinkerArgs,
Triple TheTriple, StringRef Arch) {
switch (TheTriple.getArch()) {
llvm_unreachable("Invalid optimization level");
}
-std::unique_ptr<lto::LTO> createLTO(const Triple &TheTriple, StringRef Arch,
- bool WholeProgram) {
+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; }) {
lto::Config Conf;
lto::ThinBackend Backend;
// TODO: Handle index-only thin-LTO
Conf.PTO.LoopVectorization = Conf.OptLevel > 1;
Conf.PTO.SLPVectorization = Conf.OptLevel > 1;
- // TODO: Handle outputting bitcode using a module hook.
+ Conf.PostInternalizeModuleHook = Hook;
if (TheTriple.isNVPTX())
Conf.CGFileType = CGFT_AssemblyFile;
else
[](char C) { return C == '_' || isAlnum(C); });
}
-Expected<Optional<std::string>> linkBitcodeFiles(ArrayRef<StringRef> InputFiles,
- const Triple &TheTriple,
- StringRef Arch) {
+Error linkBitcodeFiles(SmallVectorImpl<std::string> &InputFiles,
+ const Triple &TheTriple, StringRef Arch) {
SmallVector<std::unique_ptr<MemoryBuffer>, 4> SavedBuffers;
SmallVector<std::unique_ptr<lto::InputFile>, 4> BitcodeFiles;
+ SmallVector<std::string, 4> NewInputFiles;
StringMap<bool> UsedInRegularObj;
// Search for bitcode files in the input and create an LTO input file. If it
if (!ObjFile)
return ObjFile.takeError();
+ NewInputFiles.push_back(File.str());
for (auto &Sym : (*ObjFile)->symbols()) {
Expected<StringRef> Name = Sym.getName();
if (!Name)
}
if (BitcodeFiles.empty())
- return None;
+ return Error::success();
+
+ auto HandleError = [&](std::error_code EC) {
+ logAllUnhandledErrors(errorCodeToError(EC),
+ WithColor::error(errs(), LinkerExecutable));
+ exit(1);
+ };
+
+ // LTO Module hook to output bitcode without running the backend.
+ auto LinkOnly = [&](size_t Task, const Module &M) {
+ SmallString<128> TempFile;
+ if (std::error_code EC = sys::fs::createTemporaryFile(
+ "jit-" + TheTriple.getTriple(), "bc", TempFile))
+ HandleError(EC);
+ std::error_code EC;
+ raw_fd_ostream LinkedBitcode(TempFile, EC, sys::fs::OF_None);
+ if (EC)
+ HandleError(EC);
+ WriteBitcodeToFile(M, LinkedBitcode);
+ TempFiles.push_back(static_cast<std::string>(TempFile));
+ NewInputFiles.push_back(static_cast<std::string>(TempFile));
+ return false;
+ };
// We have visibility of the whole program if every input is bitcode, all
// inputs are statically linked so there should be no external references.
bool WholeProgram = BitcodeFiles.size() == InputFiles.size();
- StringMap<bool> PrevailingSymbols;
+ auto LTOBackend = (EmbedBC)
+ ? createLTO(TheTriple, Arch, WholeProgram, LinkOnly)
+ : createLTO(TheTriple, Arch, WholeProgram);
// TODO: Run more tests to verify that this is correct.
// Create the LTO instance with the necessary config and add the bitcode files
// 2. We do not support relocatable object files.
// 3. All inputs are relocatable object files extracted from host binaries, so
// there is no resolution to a dynamic library.
- auto LTOBackend = createLTO(TheTriple, Arch, WholeProgram);
+ StringMap<bool> PrevailingSymbols;
for (auto &BitcodeFile : BitcodeFiles) {
const auto Symbols = BitcodeFile->symbols();
SmallVector<lto::SymbolResolution, 16> Resolutions(Symbols.size());
StringRef Extension = (TheTriple.isNVPTX()) ? "s" : "o";
if (std::error_code EC = sys::fs::createTemporaryFile(
"lto-" + TheTriple.getTriple(), Extension, FD, TempFile))
- return nullptr;
+ HandleError(EC);
TempFiles.push_back(static_cast<std::string>(TempFile));
return std::make_unique<CachedFileStream>(
std::make_unique<llvm::raw_fd_ostream>(FD, true));
};
+
if (Error Err = LTOBackend->run(AddStream))
return std::move(Err);
+ // Is we are compiling for NVPTX we need to run the assembler first.
for (auto &File : Files) {
- if (!TheTriple.isNVPTX())
+ if (!TheTriple.isNVPTX() || EmbedBC)
continue;
auto FileOrErr = nvptx::assemble(File, TheTriple, Arch);
File = *FileOrErr;
}
- return static_cast<std::string>(Files.front());
+ // 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();
}
/// Runs the appropriate linking action on all the device files specified in \p
ArrayRef<std::string> LinkerArgs,
SmallVectorImpl<std::string> &LinkedImages) {
// Get the list of inputs for a specific device.
- StringMap<SmallVector<StringRef, 4>> LinkerInputMap;
+ StringMap<SmallVector<std::string, 4>> LinkerInputMap;
for (auto &File : DeviceFiles)
LinkerInputMap[StringRef(File)].push_back(File.Filename);
Triple TheTriple(TargetFeatures.first);
StringRef Arch(TargetFeatures.second);
- // TODO: Run LTO or bitcode linking before the final link job.
- auto ObjectOrErr =
- linkBitcodeFiles(LinkerInput.getValue(), TheTriple, Arch);
- if (!ObjectOrErr)
- return ObjectOrErr.takeError();
- if ((*ObjectOrErr).hasValue())
- LinkerInput.getValue() = {**ObjectOrErr};
+ // Run LTO on any bitcode files and replace the input with the result.
+ if (Error Err = linkBitcodeFiles(LinkerInput.getValue(), TheTriple, Arch))
+ return std::move(Err);
+
+ // If we are embedding bitcode for JIT, skip the final device linking.
+ if (EmbedBC) {
+ assert(!LinkerInput.getValue().empty() && "No bitcode image to embed");
+ LinkedImages.push_back(LinkerInput.getValue().front());
+ continue;
+ }
auto ImageOrErr =
linkDevice(LinkerInput.getValue(), LinkerArgs, TheTriple, Arch);