12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442 |
- //===-- 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
- //
- //===---------------------------------------------------------------------===//
- //
- // 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` 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.
- //
- //===---------------------------------------------------------------------===//
- #include "OffloadWrapper.h"
- #include "clang/Basic/Version.h"
- #include "llvm/BinaryFormat/Magic.h"
- #include "llvm/Bitcode/BitcodeWriter.h"
- #include "llvm/CodeGen/CommandFlags.h"
- #include "llvm/IR/Constants.h"
- #include "llvm/IR/DiagnosticPrinter.h"
- #include "llvm/IR/Module.h"
- #include "llvm/IRReader/IRReader.h"
- #include "llvm/LTO/LTO.h"
- #include "llvm/MC/TargetRegistry.h"
- #include "llvm/Object/Archive.h"
- #include "llvm/Object/ArchiveWriter.h"
- #include "llvm/Object/Binary.h"
- #include "llvm/Object/ELFObjectFile.h"
- #include "llvm/Object/IRObjectFile.h"
- #include "llvm/Object/ObjectFile.h"
- #include "llvm/Object/OffloadBinary.h"
- #include "llvm/Option/ArgList.h"
- #include "llvm/Option/OptTable.h"
- #include "llvm/Option/Option.h"
- #include "llvm/Support/CommandLine.h"
- #include "llvm/Support/Errc.h"
- #include "llvm/Support/FileOutputBuffer.h"
- #include "llvm/Support/FileSystem.h"
- #include "llvm/Support/Host.h"
- #include "llvm/Support/InitLLVM.h"
- #include "llvm/Support/MemoryBuffer.h"
- #include "llvm/Support/Parallel.h"
- #include "llvm/Support/Path.h"
- #include "llvm/Support/Program.h"
- #include "llvm/Support/Signals.h"
- #include "llvm/Support/SourceMgr.h"
- #include "llvm/Support/StringSaver.h"
- #include "llvm/Support/TargetSelect.h"
- #include "llvm/Support/WithColor.h"
- #include "llvm/Support/raw_ostream.h"
- #include "llvm/Target/TargetMachine.h"
- #include <atomic>
- #include <optional>
- using namespace llvm;
- using namespace llvm::opt;
- using namespace llvm::object;
- /// Path of the current binary.
- static const char *LinkerExecutable;
- /// Ssave intermediary results.
- static bool SaveTemps = false;
- /// Print arguments without executing.
- static bool DryRun = false;
- /// Print verbose output.
- static bool Verbose = false;
- /// Filename of the executable being created.
- static StringRef ExecutableName;
- /// Binary path for the CUDA installation.
- static std::string CudaBinaryPath;
- /// Mutex lock to protect writes to shared TempFiles in parallel.
- static std::mutex TempFilesMutex;
- /// Temporary files created by the linker wrapper.
- static std::list<SmallString<128>> TempFiles;
- /// Codegen flags for LTO backend.
- static codegen::RegisterCodeGenFlags CodeGenFlags;
- /// Global flag to indicate that the LTO pipeline threw an error.
- static std::atomic<bool> LTOError;
- using OffloadingImage = OffloadBinary::OffloadingImage;
- namespace llvm {
- // Provide DenseMapInfo so that OffloadKind can be used in a DenseMap.
- 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; }
- static bool isEqual(const OffloadKind &LHS, const OffloadKind &RHS) {
- return LHS == RHS;
- }
- };
- } // namespace llvm
- namespace {
- using std::error_code;
- /// 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.
- #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELPTEXT, METAVAR, VALUES) \
- OPT_##ID,
- #include "LinkerWrapperOpts.inc"
- LastOption
- #undef OPTION
- };
- #define PREFIX(NAME, VALUE) \
- static constexpr StringLiteral NAME##_init[] = VALUE; \
- static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
- std::size(NAME##_init) - 1);
- #include "LinkerWrapperOpts.inc"
- #undef PREFIX
- static constexpr OptTable::Info InfoTable[] = {
- #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELPTEXT, METAVAR, VALUES) \
- {PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, \
- PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS, VALUES},
- #include "LinkerWrapperOpts.inc"
- #undef OPTION
- };
- class WrapperOptTable : public opt::GenericOptTable {
- public:
- WrapperOptTable() : opt::GenericOptTable(InfoTable) {}
- };
- const OptTable &getOptTable() {
- static const WrapperOptTable *Table = []() {
- auto Result = std::make_unique<WrapperOptTable>();
- return Result.release();
- }();
- return *Table;
- }
- void printCommands(ArrayRef<StringRef> CmdArgs) {
- if (CmdArgs.empty())
- return;
- llvm::errs() << " \"" << CmdArgs.front() << "\" ";
- for (auto IC = std::next(CmdArgs.begin()), IE = CmdArgs.end(); IC != IE; ++IC)
- llvm::errs() << *IC << (std::next(IC) != IE ? " " : "\n");
- }
- [[noreturn]] void reportError(Error E) {
- outs().flush();
- logAllUnhandledErrors(std::move(E),
- WithColor::error(errs(), LinkerExecutable));
- exit(EXIT_FAILURE);
- }
- /// Create an extra user-specified \p OffloadFile.
- /// TODO: We should find a way to wrap these as libraries instead.
- Expected<OffloadFile> getInputBitcodeLibrary(StringRef Input) {
- auto [Device, Path] = StringRef(Input).split('=');
- auto [String, Arch] = Device.rsplit('-');
- auto [Kind, Triple] = String.split('-');
- llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> ImageOrError =
- llvm::MemoryBuffer::getFileOrSTDIN(Path);
- if (std::error_code EC = ImageOrError.getError())
- return createFileError(Path, EC);
- OffloadingImage Image{};
- Image.TheImageKind = IMG_Bitcode;
- Image.TheOffloadKind = getOffloadKind(Kind);
- Image.StringData = {{"triple", Triple}, {"arch", Arch}};
- Image.Image = std::move(*ImageOrError);
- std::unique_ptr<MemoryBuffer> Binary = OffloadBinary::write(Image);
- auto NewBinaryOrErr = OffloadBinary::create(*Binary);
- if (!NewBinaryOrErr)
- return NewBinaryOrErr.takeError();
- return OffloadFile(std::move(*NewBinaryOrErr), std::move(Binary));
- }
- 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();
- }
- /// Get a temporary filename suitable for output.
- Expected<StringRef> createOutputFile(const Twine &Prefix, StringRef Extension) {
- std::scoped_lock<decltype(TempFilesMutex)> Lock(TempFilesMutex);
- SmallString<128> OutputFile;
- if (SaveTemps) {
- (Prefix + "." + Extension).toNullTerminatedStringRef(OutputFile);
- } else {
- if (std::error_code EC =
- sys::fs::createTemporaryFile(Prefix, Extension, OutputFile))
- return createFileError(OutputFile, EC);
- }
- TempFiles.emplace_back(std::move(OutputFile));
- return TempFiles.back();
- }
- /// 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;
- }
- /// 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);
- if (Arg->getOption().matches(OPT_o))
- 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))
- return Err;
- return Error::success();
- }
- void printVersion(raw_ostream &OS) {
- OS << clang::getClangToolFullVersion("clang-linker-wrapper") << '\n';
- }
- namespace nvptx {
- Expected<StringRef>
- fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
- const ArgList &Args) {
- llvm::TimeTraceScope TimeScope("NVPTX fatbinary");
- // NVPTX uses the fatbinary program to bundle the linked images.
- Expected<std::string> FatBinaryPath =
- findProgram("fatbinary", {CudaBinaryPath + "/bin"});
- if (!FatBinaryPath)
- return FatBinaryPath.takeError();
- llvm::Triple Triple(
- Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
- // Create a new file to write the linked device image to.
- auto TempFileOrErr =
- createOutputFile(sys::path::filename(ExecutableName), "fatbin");
- if (!TempFileOrErr)
- return TempFileOrErr.takeError();
- SmallVector<StringRef, 16> CmdArgs;
- CmdArgs.push_back(*FatBinaryPath);
- CmdArgs.push_back(Triple.isArch64Bit() ? "-64" : "-32");
- CmdArgs.push_back("--create");
- CmdArgs.push_back(*TempFileOrErr);
- for (const auto &[File, Arch] : InputFiles)
- CmdArgs.push_back(
- Args.MakeArgString("--image=profile=" + Arch + ",file=" + File));
- if (Error Err = executeCommands(*FatBinaryPath, CmdArgs))
- return std::move(Err);
- return *TempFileOrErr;
- }
- } // namespace nvptx
- namespace amdgcn {
- Expected<StringRef>
- fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
- const ArgList &Args) {
- llvm::TimeTraceScope TimeScope("AMDGPU Fatbinary");
- // 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.
- auto TempFileOrErr =
- createOutputFile(sys::path::filename(ExecutableName), "hipfb");
- 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"};
- for (const auto &[File, Arch] : InputFiles)
- Targets.push_back(Saver.save("hipv4-amdgcn-amd-amdhsa--" + Arch));
- CmdArgs.push_back(Saver.save(llvm::join(Targets, ",")));
- CmdArgs.push_back("-input=/dev/null");
- for (const auto &[File, Arch] : InputFiles)
- CmdArgs.push_back(Saver.save("-input=" + File));
- CmdArgs.push_back(Saver.save("-output=" + *TempFileOrErr));
- if (Error Err = executeCommands(*OffloadBundlerPath, CmdArgs))
- return std::move(Err);
- return *TempFileOrErr;
- }
- } // namespace amdgcn
- namespace generic {
- Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
- llvm::TimeTraceScope TimeScope("Clang");
- // Use `clang` to invoke the appropriate device tools.
- 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);
- if (Arch.empty())
- Arch = "native";
- // Create a new file to write the linked device image to. Assume that the
- // input filename already has the device and architecture.
- auto TempFileOrErr =
- createOutputFile(sys::path::filename(ExecutableName) + "." +
- Triple.getArchName() + "." + Arch,
- "img");
- 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),
- Args.MakeArgString("-" + OptLevel),
- "-Wl,--no-undefined",
- };
- // If this is CPU offloading we copy the input libraries.
- if (!Triple.isAMDGPU() && !Triple.isNVPTX()) {
- CmdArgs.push_back("-Bsymbolic");
- CmdArgs.push_back("-shared");
- ArgStringList LinkerArgs;
- for (const opt::Arg *Arg :
- Args.filtered(OPT_library, OPT_rpath, OPT_library_path))
- Arg->render(Args, LinkerArgs);
- llvm::copy(LinkerArgs, std::back_inserter(CmdArgs));
- }
- if (Args.hasArg(OPT_debug))
- CmdArgs.push_back("-g");
- if (SaveTemps)
- CmdArgs.push_back("-save-temps");
- if (Verbose)
- CmdArgs.push_back("-v");
- if (!CudaBinaryPath.empty())
- CmdArgs.push_back(Args.MakeArgString("--cuda-path=" + CudaBinaryPath));
- for (StringRef Arg : Args.getAllArgValues(OPT_ptxas_arg))
- llvm::copy(SmallVector<StringRef>({"-Xcuda-ptxas", Arg}),
- std::back_inserter(CmdArgs));
- for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))
- CmdArgs.push_back(Args.MakeArgString("-Wl," + Arg));
- for (StringRef InputFile : InputFiles)
- CmdArgs.push_back(InputFile);
- if (Error Err = executeCommands(*ClangPath, CmdArgs))
- return std::move(Err);
- return *TempFileOrErr;
- }
- } // namespace generic
- Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles,
- const ArgList &Args) {
- const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
- switch (Triple.getArch()) {
- case Triple::nvptx:
- case Triple::nvptx64:
- case Triple::amdgcn:
- case Triple::x86:
- case Triple::x86_64:
- case Triple::aarch64:
- case Triple::aarch64_be:
- case Triple::ppc64:
- case Triple::ppc64le:
- return generic::clang(InputFiles, Args);
- default:
- return createStringError(inconvertibleErrorCode(),
- Triple.getArchName() +
- " linking is not supported");
- }
- }
- 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:
- WithColor::error(errs(), LinkerExecutable) << ErrStorage << "\n";
- LTOError = true;
- break;
- case DS_Warning:
- WithColor::warning(errs(), LinkerExecutable) << ErrStorage << "\n";
- break;
- case DS_Note:
- WithColor::note(errs(), LinkerExecutable) << ErrStorage << "\n";
- break;
- case DS_Remark:
- WithColor::remark(errs()) << ErrStorage << "\n";
- break;
- }
- }
- // 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());
- }
- return UnifiedFeatures;
- }
- template <typename ModuleHook = function_ref<bool(size_t, const Module &)>>
- std::unique_ptr<lto::LTO> createLTO(
- const ArgList &Args, const std::vector<std::string> &Features,
- ModuleHook Hook = [](size_t, const Module &) { return true; }) {
- const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
- StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
- lto::Config Conf;
- lto::ThinBackend Backend;
- // TODO: Handle index-only thin-LTO
- Backend =
- lto::createInProcessThinBackend(llvm::heavyweight_hardware_concurrency());
- Conf.CPU = Arch.str();
- Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple);
- StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2");
- Conf.MAttrs = Features;
- std::optional<CodeGenOpt::Level> CGOptLevelOrNone =
- CodeGenOpt::parseLevel(OptLevel[1]);
- assert(CGOptLevelOrNone && "Invalid optimization level");
- Conf.CGOptLevel = *CGOptLevelOrNone;
- Conf.OptLevel = OptLevel[1] - '0';
- Conf.DefaultTriple = Triple.getTriple();
- LTOError = false;
- Conf.DiagHandler = diagnosticHandler;
- Conf.PTO.LoopVectorization = Conf.OptLevel > 1;
- Conf.PTO.SLPVectorization = Conf.OptLevel > 1;
- if (SaveTemps) {
- std::string TempName = (sys::path::filename(ExecutableName) + "." +
- Triple.getTriple() + "." + Arch)
- .str();
- Conf.PostInternalizeModuleHook = [=](size_t Task, const Module &M) {
- 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";
- error_code EC;
- raw_fd_ostream LinkedBitcode(File, EC, sys::fs::OF_None);
- if (EC)
- reportError(errorCodeToError(EC));
- WriteBitcodeToFile(M, LinkedBitcode);
- return true;
- };
- }
- Conf.PostOptModuleHook = Hook;
- Conf.CGFileType =
- (Triple.isNVPTX() || SaveTemps) ? CGFT_AssemblyFile : CGFT_ObjectFile;
- // TODO: Handle remark files
- Conf.HasWholeProgramVisibility = Args.hasArg(OPT_whole_program);
- 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] == '_') &&
- llvm::all_of(llvm::drop_begin(S),
- [](char C) { return C == '_' || isAlnum(C); });
- }
- Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles,
- SmallVectorImpl<StringRef> &OutputFiles,
- const ArgList &Args) {
- llvm::TimeTraceScope TimeScope("Link bitcode files");
- const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
- StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
- SmallVector<OffloadFile, 4> BitcodeInputFiles;
- DenseSet<StringRef> UsedInRegularObj;
- DenseSet<StringRef> UsedInSharedLib;
- BumpPtrAllocator Alloc;
- StringSaver Saver(Alloc);
- // 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 (OffloadFile &File : InputFiles) {
- MemoryBufferRef Buffer = MemoryBufferRef(File.getBinary()->getImage(), "");
- file_magic Type = identify_magic(Buffer.getBuffer());
- switch (Type) {
- case file_magic::bitcode: {
- BitcodeInputFiles.emplace_back(std::move(File));
- continue;
- }
- case file_magic::elf_relocatable:
- case file_magic::elf_shared_object: {
- Expected<std::unique_ptr<ObjectFile>> ObjFile =
- ObjectFile::createObjectFile(Buffer);
- if (!ObjFile)
- continue;
- for (SymbolRef Sym : (*ObjFile)->symbols()) {
- Expected<StringRef> Name = Sym.getName();
- if (!Name)
- return Name.takeError();
- // Record if we've seen these symbols in any object or shared libraries.
- if ((*ObjFile)->isRelocatableObject())
- UsedInRegularObj.insert(Saver.save(*Name));
- else
- UsedInSharedLib.insert(Saver.save(*Name));
- }
- continue;
- }
- default:
- continue;
- }
- }
- if (BitcodeInputFiles.empty())
- return Error::success();
- // Remove all the bitcode files that we moved from the original input.
- llvm::erase_if(InputFiles, [](OffloadFile &F) { return !F.getBinary(); });
- // LTO Module hook to output bitcode without running the backend.
- SmallVector<StringRef, 4> BitcodeOutput;
- auto OutputBitcode = [&](size_t, const Module &M) {
- auto TempFileOrErr = createOutputFile(sys::path::filename(ExecutableName) +
- "-jit-" + Triple.getTriple(),
- "bc");
- if (!TempFileOrErr)
- reportError(TempFileOrErr.takeError());
- std::error_code EC;
- raw_fd_ostream LinkedBitcode(*TempFileOrErr, EC, sys::fs::OF_None);
- if (EC)
- reportError(errorCodeToError(EC));
- WriteBitcodeToFile(M, LinkedBitcode);
- BitcodeOutput.push_back(*TempFileOrErr);
- return false;
- };
- // We assume visibility of the whole program if every input file was bitcode.
- auto Features = getTargetFeatures(BitcodeInputFiles);
- auto LTOBackend = Args.hasArg(OPT_embed_bitcode)
- ? createLTO(Args, Features, OutputBitcode)
- : createLTO(Args, Features);
- // 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.
- uint64_t Idx = 0;
- DenseSet<StringRef> PrevailingSymbols;
- for (auto &BitcodeInput : BitcodeInputFiles) {
- // Get a semi-unique buffer identifier for Thin-LTO.
- StringRef Identifier = Saver.save(
- std::to_string(Idx++) + "." +
- BitcodeInput.getBinary()->getMemoryBufferRef().getBufferIdentifier());
- MemoryBufferRef Buffer =
- MemoryBufferRef(BitcodeInput.getBinary()->getImage(), Identifier);
- 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();
- 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
- // it is undefined or another definition has already been used.
- Res.Prevailing =
- !Sym.isUndefined() &&
- PrevailingSymbols.insert(Saver.save(Sym.getName())).second;
- // 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.
- // 3) Prevailing symbols that are needed visible to external libraries.
- Res.VisibleToRegularObj =
- UsedInRegularObj.contains(Sym.getName()) ||
- isValidCIdentifier(Sym.getSectionName()) ||
- (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 &&
- (UsedInSharedLib.contains(Sym.getName()) ||
- !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());
- // 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(*BitcodeFileOrErr), Resolutions))
- return Err;
- }
- // Run the LTO job to compile the bitcode.
- size_t MaxTasks = LTOBackend->getMaxTasks();
- SmallVector<StringRef> Files(MaxTasks);
- auto AddStream =
- [&](size_t Task,
- const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> {
- int FD = -1;
- auto &TempFile = Files[Task];
- StringRef Extension = (Triple.isNVPTX() || SaveTemps) ? "s" : "o";
- std::string TaskStr = Task ? "." + std::to_string(Task) : "";
- auto TempFileOrErr =
- createOutputFile(sys::path::filename(ExecutableName) + "." +
- Triple.getTriple() + "." + Arch + TaskStr,
- Extension);
- if (!TempFileOrErr)
- reportError(TempFileOrErr.takeError());
- TempFile = *TempFileOrErr;
- if (std::error_code EC = sys::fs::openFileForWrite(TempFile, FD))
- reportError(errorCodeToError(EC));
- return std::make_unique<CachedFileStream>(
- std::make_unique<llvm::raw_fd_ostream>(FD, true));
- };
- if (Error Err = LTOBackend->run(AddStream))
- return Err;
- if (LTOError)
- return createStringError(inconvertibleErrorCode(),
- "Errors encountered inside the LTO pipeline.");
- // If we are embedding bitcode we only need the intermediate output.
- bool SingleOutput = Files.size() == 1;
- if (Args.hasArg(OPT_embed_bitcode)) {
- if (BitcodeOutput.size() != 1 || !SingleOutput)
- return createStringError(inconvertibleErrorCode(),
- "Cannot embed bitcode with multiple files.");
- OutputFiles.push_back(Args.MakeArgString(BitcodeOutput.front()));
- return Error::success();
- }
- // Append the new inputs to the device linker input.
- for (StringRef File : Files)
- OutputFiles.push_back(File);
- return Error::success();
- }
- Expected<StringRef> writeOffloadFile(const OffloadFile &File) {
- const OffloadBinary &Binary = *File.getBinary();
- StringRef Prefix =
- sys::path::stem(Binary.getMemoryBufferRef().getBufferIdentifier());
- StringRef Suffix = getImageKindName(Binary.getImageKind());
- auto TempFileOrErr = createOutputFile(
- Prefix + "-" + Binary.getTriple() + "-" + Binary.getArch(), Suffix);
- if (!TempFileOrErr)
- return TempFileOrErr.takeError();
- Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
- FileOutputBuffer::create(*TempFileOrErr, Binary.getImage().size());
- if (!OutputOrErr)
- return OutputOrErr.takeError();
- std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
- llvm::copy(Binary.getImage(), Output->getBufferStart());
- if (Error E = Output->commit())
- return std::move(E);
- return *TempFileOrErr;
- }
- // Compile the module to an object file using the appropriate target machine for
- // the host triple.
- Expected<StringRef> compileModule(Module &M) {
- llvm::TimeTraceScope TimeScope("Compile module");
- 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(M.getTargetTriple(), CPU, Features, Options,
- Reloc::PIC_, M.getCodeModel()));
- if (M.getDataLayout().isDefault())
- M.setDataLayout(TM->createDataLayout());
- int FD = -1;
- auto TempFileOrErr = createOutputFile(
- sys::path::filename(ExecutableName) + ".image.wrapper", "o");
- if (!TempFileOrErr)
- return TempFileOrErr.takeError();
- if (std::error_code EC = sys::fs::openFileForWrite(*TempFileOrErr, 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 *TempFileOrErr;
- }
- /// Creates the object file containing the device image and runtime
- /// registration code from the device images stored in \p Images.
- Expected<StringRef>
- wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,
- const ArgList &Args, OffloadKind Kind) {
- llvm::TimeTraceScope TimeScope("Wrap bundled images");
- 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);
- M.setTargetTriple(
- Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
- 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;
- case OFK_HIP:
- if (Error Err = wrapHIPBinary(M, BuffersToWrap.front()))
- return std::move(Err);
- break;
- default:
- return createStringError(inconvertibleErrorCode(),
- getOffloadKindName(Kind) +
- " wrapping is not supported");
- }
- if (Args.hasArg(OPT_print_wrapped_module))
- errs() << M;
- auto FileOrErr = compileModule(M);
- if (!FileOrErr)
- return FileOrErr.takeError();
- return *FileOrErr;
- }
- Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
- bundleOpenMP(ArrayRef<OffloadingImage> Images) {
- SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
- for (const OffloadingImage &Image : Images)
- Buffers.emplace_back(OffloadBinary::write(Image));
- return std::move(Buffers);
- }
- Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
- bundleCuda(ArrayRef<OffloadingImage> Images, const ArgList &Args) {
- 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);
- SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
- if (std::error_code EC = ImageOrError.getError())
- return createFileError(*FileOrErr, EC);
- Buffers.emplace_back(std::move(*ImageOrError));
- return std::move(Buffers);
- }
- Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
- bundleHIP(ArrayRef<OffloadingImage> Images, const ArgList &Args) {
- 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 = amdgcn::fatbinary(InputFiles, Args);
- if (!FileOrErr)
- return FileOrErr.takeError();
- llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =
- llvm::MemoryBuffer::getFileOrSTDIN(*FileOrErr);
- SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
- if (std::error_code EC = ImageOrError.getError())
- return createFileError(*FileOrErr, EC);
- Buffers.emplace_back(std::move(*ImageOrError));
- return std::move(Buffers);
- }
- /// Transforms the input \p Images into the binary format the runtime expects
- /// for the given \p Kind.
- Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
- bundleLinkedOutput(ArrayRef<OffloadingImage> Images, const ArgList &Args,
- OffloadKind Kind) {
- llvm::TimeTraceScope TimeScope("Bundle linked output");
- switch (Kind) {
- case OFK_OpenMP:
- return bundleOpenMP(Images);
- case OFK_Cuda:
- return bundleCuda(Images, Args);
- case OFK_HIP:
- return bundleHIP(Images, Args);
- default:
- return createStringError(inconvertibleErrorCode(),
- getOffloadKindName(Kind) +
- " bundling is not supported");
- }
- }
- /// 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)) {
- auto [Triple, Value] = Arg.split('=');
- if (Value.empty())
- DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),
- Args.MakeArgString(Triple));
- else if (Triple == DAL.getLastArgValue(OPT_triple_EQ))
- DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),
- Args.MakeArgString(Value));
- }
- return DAL;
- }
- /// Transforms all the extracted offloading input files into an image that can
- /// be registered by the runtime.
- Expected<SmallVector<StringRef>>
- linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles,
- const InputArgList &Args, char **Argv, int Argc) {
- llvm::TimeTraceScope TimeScope("Handle all device input");
- DenseMap<OffloadFile::TargetID, SmallVector<OffloadFile>> InputMap;
- for (auto &File : LinkerInputFiles)
- InputMap[File].emplace_back(std::move(File));
- LinkerInputFiles.clear();
- 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 {
- llvm::TimeTraceScope TimeScope("Link device input");
- // 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);
- DenseSet<OffloadKind> ActiveOffloadKinds;
- for (const auto &File : Input)
- if (File.getBinary()->getOffloadKind() != OFK_None)
- ActiveOffloadKinds.insert(File.getBinary()->getOffloadKind());
- // First link and remove all the input files containing bitcode.
- SmallVector<StringRef> InputFiles;
- if (Error Err = linkBitcodeFiles(Input, InputFiles, LinkerArgs))
- return Err;
- // Write any remaining device inputs to an output file for the linker.
- for (const OffloadFile &File : Input) {
- auto FileNameOrErr = writeOffloadFile(File);
- if (!FileNameOrErr)
- return FileNameOrErr.takeError();
- InputFiles.emplace_back(*FileNameOrErr);
- }
- // Link the remaining device files using the device linker.
- auto OutputOrErr = !Args.hasArg(OPT_embed_bitcode)
- ? linkDevice(InputFiles, LinkerArgs)
- : InputFiles.front();
- if (!OutputOrErr)
- return OutputOrErr.takeError();
- // 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);
- if (std::error_code EC = FileOrErr.getError()) {
- if (DryRun)
- FileOrErr = MemoryBuffer::getMemBuffer("");
- else
- return createFileError(*OutputOrErr, EC);
- }
- std::scoped_lock<decltype(ImageMtx)> Guard(ImageMtx);
- OffloadingImage TheImage{};
- TheImage.TheImageKind =
- Args.hasArg(OPT_embed_bitcode) ? IMG_Bitcode : IMG_Object;
- TheImage.TheOffloadKind = Kind;
- TheImage.StringData = {
- {"triple",
- Args.MakeArgString(LinkerArgs.getLastArgValue(OPT_triple_EQ))},
- {"arch",
- Args.MakeArgString(LinkerArgs.getLastArgValue(OPT_arch_EQ))}};
- TheImage.Image = std::move(*FileOrErr);
- Images[Kind].emplace_back(std::move(TheImage));
- }
- return Error::success();
- });
- if (Err)
- return std::move(Err);
- // Create a binary image of each offloading image and embed it into a new
- // object file.
- SmallVector<StringRef> WrappedOutput;
- 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) {
- return A.StringData["triple"] > B.StringData["triple"] ||
- A.StringData["arch"] > B.StringData["arch"] ||
- A.TheOffloadKind < B.TheOffloadKind;
- });
- auto BundledImagesOrErr = bundleLinkedOutput(Input, Args, Kind);
- if (!BundledImagesOrErr)
- return BundledImagesOrErr.takeError();
- auto OutputOrErr = wrapDeviceImages(*BundledImagesOrErr, Args, Kind);
- if (!OutputOrErr)
- return OutputOrErr.takeError();
- WrappedOutput.push_back(*OutputOrErr);
- }
- return WrappedOutput;
- }
- std::optional<std::string> findFile(StringRef Dir, StringRef Root,
- const Twine &Name) {
- SmallString<128> Path;
- if (Dir.startswith("="))
- sys::path::append(Path, Root, Dir.substr(1), Name);
- else
- sys::path::append(Path, Dir, Name);
- if (sys::fs::exists(Path))
- return static_cast<std::string>(Path);
- return std::nullopt;
- }
- std::optional<std::string>
- findFromSearchPaths(StringRef Name, StringRef Root,
- ArrayRef<StringRef> SearchPaths) {
- for (StringRef Dir : SearchPaths)
- if (std::optional<std::string> File = findFile(Dir, Root, Name))
- return File;
- return std::nullopt;
- }
- std::optional<std::string>
- searchLibraryBaseName(StringRef Name, StringRef Root,
- ArrayRef<StringRef> SearchPaths) {
- for (StringRef Dir : SearchPaths) {
- if (std::optional<std::string> File =
- findFile(Dir, Root, "lib" + Name + ".so"))
- return File;
- if (std::optional<std::string> File =
- findFile(Dir, Root, "lib" + Name + ".a"))
- return File;
- }
- return std::nullopt;
- }
- /// Search for static libraries in the linker's library path given input like
- /// `-lfoo` or `-l:libfoo.a`.
- std::optional<std::string> searchLibrary(StringRef Input, StringRef Root,
- ArrayRef<StringRef> SearchPaths) {
- if (Input.startswith(":"))
- return findFromSearchPaths(Input.drop_front(), Root, SearchPaths);
- return searchLibraryBaseName(Input, Root, SearchPaths);
- }
- /// 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.
- Expected<bool> getSymbolsFromBitcode(MemoryBufferRef Buffer, StringSaver &Saver,
- DenseMap<StringRef, Symbol> &Syms) {
- Expected<IRSymtabFile> IRSymtabOrErr = readIRSymtab(Buffer);
- if (!IRSymtabOrErr)
- return IRSymtabOrErr.takeError();
- bool ShouldExtract = false;
- 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;
- auto &OldSym = Syms[Saver.save(Sym.getName())];
- // 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.
- bool NewGlobalSymbol =
- ((NewSymbol || (OldSym & Sym_Undefined)) && !Sym.isUndefined() &&
- !Sym.canBeOmittedFromSymbolTable() &&
- (Sym.getVisibility() != GlobalValue::HiddenVisibility));
- ShouldExtract |= ResolvesStrongReference | NewGlobalSymbol;
- // Update this symbol in the "table" with the new information.
- if (OldSym & Sym_Undefined && !Sym.isUndefined())
- OldSym = static_cast<Symbol>(OldSym & ~Sym_Undefined);
- if (Sym.isUndefined() && NewSymbol)
- OldSym = static_cast<Symbol>(OldSym | Sym_Undefined);
- if (Sym.isWeak())
- OldSym = static_cast<Symbol>(OldSym | Sym_Weak);
- }
- }
- return ShouldExtract;
- }
- /// Scan the symbols from an ObjectFile \p Obj and record if we need to extract
- /// any symbols from it.
- Expected<bool> getSymbolsFromObject(const ObjectFile &Obj, StringSaver &Saver,
- DenseMap<StringRef, Symbol> &Syms) {
- bool ShouldExtract = false;
- 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;
- auto &OldSym = Syms[Saver.save(*NameOrErr)];
- // 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.
- bool NewGlobalSymbol = ((NewSymbol || (OldSym & Sym_Undefined)) &&
- !(*FlagsOrErr & SymbolRef::SF_Undefined) &&
- !(*FlagsOrErr & SymbolRef::SF_Hidden));
- ShouldExtract |= ResolvesStrongReference | NewGlobalSymbol;
- // Update this symbol in the "table" with the new information.
- if (OldSym & Sym_Undefined && !(*FlagsOrErr & SymbolRef::SF_Undefined))
- OldSym = static_cast<Symbol>(OldSym & ~Sym_Undefined);
- if (*FlagsOrErr & SymbolRef::SF_Undefined && NewSymbol)
- OldSym = static_cast<Symbol>(OldSym | Sym_Undefined);
- if (*FlagsOrErr & SymbolRef::SF_Weak)
- OldSym = static_cast<Symbol>(OldSym | Sym_Weak);
- }
- 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.
- Expected<bool> getSymbols(StringRef Image, StringSaver &Saver,
- DenseMap<StringRef, Symbol> &Syms) {
- MemoryBufferRef Buffer = MemoryBufferRef(Image, "");
- switch (identify_magic(Image)) {
- case file_magic::bitcode:
- return getSymbolsFromBitcode(Buffer, Saver, Syms);
- case file_magic::elf_relocatable: {
- Expected<std::unique_ptr<ObjectFile>> ObjFile =
- ObjectFile::createObjectFile(Buffer);
- if (!ObjFile)
- return ObjFile.takeError();
- return getSymbolsFromObject(**ObjFile, Saver, Syms);
- }
- 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.
- Expected<SmallVector<OffloadFile>> getDeviceInput(const ArgList &Args) {
- llvm::TimeTraceScope TimeScope("ExtractDeviceCode");
- StringRef Root = Args.getLastArgValue(OPT_sysroot_EQ);
- SmallVector<StringRef> LibraryPaths;
- for (const opt::Arg *Arg : Args.filtered(OPT_library_path))
- LibraryPaths.push_back(Arg->getValue());
- BumpPtrAllocator Alloc;
- StringSaver Saver(Alloc);
- // Try to extract device code from the linker input files.
- SmallVector<OffloadFile> InputFiles;
- DenseMap<OffloadFile::TargetID, DenseMap<StringRef, Symbol>> Syms;
- for (const opt::Arg *Arg : Args.filtered(OPT_INPUT, OPT_library)) {
- 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))
- continue;
- ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
- MemoryBuffer::getFileOrSTDIN(*Filename);
- if (std::error_code EC = BufferOrErr.getError())
- return createFileError(*Filename, EC);
- MemoryBufferRef Buffer = **BufferOrErr;
- if (identify_magic(Buffer.getBuffer()) == file_magic::elf_shared_object)
- continue;
- SmallVector<OffloadFile> Binaries;
- if (Error Err = extractOffloadBinaries(Buffer, Binaries))
- return std::move(Err);
- // 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.
- if (IsArchive && !Syms.count(Binary))
- continue;
- Expected<bool> ExtractOrErr =
- getSymbols(Binary.getBinary()->getImage(), Saver, Syms[Binary]);
- if (!ExtractOrErr)
- return ExtractOrErr.takeError();
- Extracted = IsArchive && *ExtractOrErr;
- if (!IsArchive || Extracted)
- InputFiles.emplace_back(std::move(Binary));
- // If we extracted any files we need to check all the symbols again.
- if (Extracted)
- break;
- }
- }
- }
- for (StringRef Library : Args.getAllArgValues(OPT_bitcode_library_EQ)) {
- auto FileOrErr = getInputBitcodeLibrary(Library);
- if (!FileOrErr)
- return FileOrErr.takeError();
- InputFiles.push_back(std::move(*FileOrErr));
- }
- return std::move(InputFiles);
- }
- } // 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;
- }
- // 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]);
- Verbose = Args.hasArg(OPT_verbose);
- DryRun = Args.hasArg(OPT_dry_run);
- SaveTemps = Args.hasArg(OPT_save_temps);
- ExecutableName = Args.getLastArgValue(OPT_o, "a.out");
- CudaBinaryPath = Args.getLastArgValue(OPT_cuda_path_EQ).str();
- 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);
- }
- 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.
- auto FilesOrErr =
- linkAndWrapDeviceFiles(*DeviceInputFiles, Args, Argv, Argc);
- 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();
- }
- // Remove the temporary files created.
- if (!SaveTemps)
- for (const auto &TempFile : TempFiles)
- if (std::error_code EC = sys::fs::remove(TempFile))
- reportError(createFileError(TempFile, EC));
- return EXIT_SUCCESS;
- }
|