https://github.com/mysterymath updated https://github.com/llvm/llvm-project/pull/164916
>From 935b972840238daf88a41954f63e6a8b6918d764 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Thu, 7 Aug 2025 16:05:09 -0700 Subject: [PATCH 1/9] [LTO][LLD] Prevent invalid LTO libfunc transforms This patch ensures that: 1) New bitcode is not extracted for libfuncs after LTO occurs, and 2) Extracted bitcode for libfuncs is considered external, since new calls to it may be emitted. --- clang/lib/CodeGen/BackendUtil.cpp | 10 +-- lld/ELF/Driver.cpp | 19 +++++- lld/ELF/LTO.cpp | 5 +- lld/ELF/LTO.h | 3 +- lld/test/ELF/lto/libcall-archive-bitcode.test | 41 ++++++++++++ llvm/include/llvm/LTO/LTO.h | 24 ++++++- llvm/include/llvm/LTO/LTOBackend.h | 7 ++- llvm/lib/LTO/LTO.cpp | 62 +++++++++++++------ llvm/lib/LTO/LTOBackend.cpp | 43 ++++++++++--- llvm/lib/LTO/LTOCodeGenerator.cpp | 4 +- llvm/lib/Object/CMakeLists.txt | 1 + llvm/lib/Object/IRSymtab.cpp | 8 ++- .../X86/libcall-external-bitcode.ll | 20 ++++++ .../X86/libcall-external-not-bitcode.ll | 20 ++++++ llvm/test/LTO/Resolution/X86/libcall-in-tu.ll | 34 ++++++++++ llvm/tools/llvm-lto2/llvm-lto2.cpp | 7 +++ 16 files changed, 266 insertions(+), 42 deletions(-) create mode 100644 lld/test/ELF/lto/libcall-archive-bitcode.test create mode 100644 llvm/test/LTO/Resolution/X86/libcall-external-bitcode.ll create mode 100644 llvm/test/LTO/Resolution/X86/libcall-external-not-bitcode.ll create mode 100644 llvm/test/LTO/Resolution/X86/libcall-in-tu.ll diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 26794a9cbc11d..862075db2dc5f 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1408,11 +1408,11 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex, // FIXME: Both ExecuteAction and thinBackend set up optimization remarks for // the same context. finalizeLLVMOptimizationRemarks(M->getContext()); - if (Error E = - thinBackend(Conf, -1, AddStream, *M, *CombinedIndex, ImportList, - ModuleToDefinedGVSummaries[M->getModuleIdentifier()], - /*ModuleMap=*/nullptr, Conf.CodeGenOnly, - /*IRAddStream=*/nullptr, CGOpts.CmdArgs)) { + if (Error E = thinBackend( + Conf, -1, AddStream, *M, *CombinedIndex, ImportList, + ModuleToDefinedGVSummaries[M->getModuleIdentifier()], + /*ModuleMap=*/nullptr, Conf.CodeGenOnly, /*BitcodeLibFuncs=*/{}, + /*IRAddStream=*/nullptr, CGOpts.CmdArgs)) { handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { errs() << "Error running ThinLTO backend: " << EIB.message() << '\n'; }); diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 8647752be31fe..b0834e6c26b7a 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2701,15 +2701,30 @@ static void markBuffersAsDontNeed(Ctx &ctx, bool skipLinkedOutput) { template <class ELFT> void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) { llvm::TimeTraceScope timeScope("LTO"); + // Capture the triple before moving the bitcode into the bitcode compiler. + std::optional<llvm::Triple> tt; + if (!ctx.bitcodeFiles.empty()) + tt = llvm::Triple(ctx.bitcodeFiles.front()->obj->getTargetTriple()); // Compile bitcode files and replace bitcode symbols. lto.reset(new BitcodeCompiler(ctx)); for (BitcodeFile *file : ctx.bitcodeFiles) lto->add(*file); - if (!ctx.bitcodeFiles.empty()) + llvm::BumpPtrAllocator alloc; + llvm::StringSaver saver(alloc); + SmallVector<StringRef> bitcodeLibFuncs; + if (!ctx.bitcodeFiles.empty()) { markBuffersAsDontNeed(ctx, skipLinkedOutput); + for (StringRef libFunc : lto::LTO::getLibFuncSymbols(*tt, saver)) { + Symbol *sym = ctx.symtab->find(libFunc); + if (!sym) + continue; + if (isa<BitcodeFile>(sym->file)) + bitcodeLibFuncs.push_back(libFunc); + } + } - ltoObjectFiles = lto->compile(); + ltoObjectFiles = lto->compile(bitcodeLibFuncs); for (auto &file : ltoObjectFiles) { auto *obj = cast<ObjFile<ELFT>>(file.get()); obj->parse(/*ignoreComdats=*/true); diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp index 44a679498ed1d..49f6d15b346ae 100644 --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -316,7 +316,10 @@ static void thinLTOCreateEmptyIndexFiles(Ctx &ctx) { // Merge all the bitcode files we have seen, codegen the result // and return the resulting ObjectFile(s). -SmallVector<std::unique_ptr<InputFile>, 0> BitcodeCompiler::compile() { +SmallVector<std::unique_ptr<InputFile>, 0> +BitcodeCompiler::compile(const SmallVector<StringRef> &bitcodeLibFuncs) { + ltoObj->setBitcodeLibFuncs(bitcodeLibFuncs); + unsigned maxTasks = ltoObj->getMaxTasks(); buf.resize(maxTasks); files.resize(maxTasks); diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h index acf3bcff7f2f1..8207e91460785 100644 --- a/lld/ELF/LTO.h +++ b/lld/ELF/LTO.h @@ -42,7 +42,8 @@ class BitcodeCompiler { ~BitcodeCompiler(); void add(BitcodeFile &f); - SmallVector<std::unique_ptr<InputFile>, 0> compile(); + SmallVector<std::unique_ptr<InputFile>, 0> + compile(const SmallVector<StringRef> &bitcodeLibFuncs); private: Ctx &ctx; diff --git a/lld/test/ELF/lto/libcall-archive-bitcode.test b/lld/test/ELF/lto/libcall-archive-bitcode.test new file mode 100644 index 0000000000000..20735b5c89c99 --- /dev/null +++ b/lld/test/ELF/lto/libcall-archive-bitcode.test @@ -0,0 +1,41 @@ +; REQUIRES: x86 + +; RUN: rm -rf %t && split-file %s %t && cd %t +; RUN: llvm-as main.ll -o main.o +; RUN: llvm-as bcmp.ll -o bcmp.o +; RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux-gnu memcmp.s -o memcmp.o +; RUN: llvm-ar rc libc.a bcmp.o memcmp.o + +;; Ensure that no memcmp->bcmp translation occurs during LTO because bcmp is in +;; bitcode, but was not brought into the link. This would fail the link by +;; extracting bitcode after LTO. +; RUN: ld.lld -o out main.o -L. -lc +; RUN: llvm-nm out | FileCheck %s + +;--- bcmp.ll +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i32 @bcmp(ptr %0, ptr %1, i64 %2) { + ret i32 0 +} + +;--- memcmp.s +.globl memcmp +memcmp: + ret + +;--- main.ll +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i1 @_start(ptr %0, ptr %1, i64 %2) { + %cmp = call i32 @memcmp(ptr %0, ptr %1, i64 %2) + %eq = icmp eq i32 %cmp, 0 + ret i1 %eq +} + +; CHECK-NOT: bcmp +; CHECK: memcmp +declare i32 @memcmp(ptr, ptr, i64) + diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h index 819be1909ec12..66a3a80d7d61e 100644 --- a/llvm/include/llvm/LTO/LTO.h +++ b/llvm/include/llvm/LTO/LTO.h @@ -287,7 +287,8 @@ class ThinBackendProc { using ThinBackendFunction = std::function<std::unique_ptr<ThinBackendProc>( const Config &C, ModuleSummaryIndex &CombinedIndex, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, - AddStreamFn AddStream, FileCache Cache)>; + AddStreamFn AddStream, FileCache Cache, + const SmallVector<StringRef> &BitcodeLibFuncs)>; /// This type defines the behavior following the thin-link phase during ThinLTO. /// It encapsulates a backend function and a strategy for thread pool @@ -302,10 +303,11 @@ struct ThinBackend { std::unique_ptr<ThinBackendProc> operator()( const Config &Conf, ModuleSummaryIndex &CombinedIndex, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, - AddStreamFn AddStream, FileCache Cache) { + AddStreamFn AddStream, FileCache Cache, + const SmallVector<StringRef> &BitcodeLibFuncs) { assert(isValid() && "Invalid backend function"); return Func(Conf, CombinedIndex, ModuleToDefinedGVSummaries, - std::move(AddStream), std::move(Cache)); + std::move(AddStream), std::move(Cache), BitcodeLibFuncs); } ThreadPoolStrategy getParallelism() const { return Parallelism; } bool isValid() const { return static_cast<bool>(Func); } @@ -423,6 +425,12 @@ class LTO { LLVM_ABI Error add(std::unique_ptr<InputFile> Obj, ArrayRef<SymbolResolution> Res); + /// Set the list of functions implemented in bitcode across the link, whether + /// extracted or not. Such functions may not be referenced if they were not + /// extracted by the time LTO occurs. + LLVM_ABI void + setBitcodeLibFuncs(const SmallVector<StringRef> &BitcodeLibFuncs); + /// Returns an upper bound on the number of tasks that the client may expect. /// This may only be called after all IR object files have been added. For a /// full description of tasks see LTOBackend.h. @@ -443,6 +451,14 @@ class LTO { LLVM_ABI static SmallVector<const char *> getRuntimeLibcallSymbols(const Triple &TT); + /// Static method that returns a list of library function symbols that can be + /// generated by LTO but might not be visible from bitcode symbol table. + /// Unlike the runtime libcalls, the linker can report to the code generator + /// which of these are actually available in the link, and the code generator + /// can then only reference that set of symbols. + LLVM_ABI static SmallVector<StringRef> + getLibFuncSymbols(const Triple &TT, llvm::StringSaver &Saver); + private: Config Conf; @@ -615,6 +631,8 @@ class LTO { // Diagnostic optimization remarks file LLVMRemarkFileHandle DiagnosticOutputFile; + SmallVector<StringRef> BitcodeLibFuncs; + public: virtual Expected<std::shared_ptr<lto::InputFile>> addInput(std::unique_ptr<lto::InputFile> InputPtr) { diff --git a/llvm/include/llvm/LTO/LTOBackend.h b/llvm/include/llvm/LTO/LTOBackend.h index 48ad5aa64f61f..6a7d7e0d87ac9 100644 --- a/llvm/include/llvm/LTO/LTOBackend.h +++ b/llvm/include/llvm/LTO/LTOBackend.h @@ -39,13 +39,15 @@ LLVM_ABI bool opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod, bool IsThinLTO, ModuleSummaryIndex *ExportSummary, const ModuleSummaryIndex *ImportSummary, - const std::vector<uint8_t> &CmdArgs); + const std::vector<uint8_t> &CmdArgs, + const SmallVector<StringRef> &BitcodeLibFuncs); /// Runs a regular LTO backend. The regular LTO backend can also act as the /// regular LTO phase of ThinLTO, which may need to access the combined index. LLVM_ABI Error backend(const Config &C, AddStreamFn AddStream, unsigned ParallelCodeGenParallelismLevel, Module &M, - ModuleSummaryIndex &CombinedIndex); + ModuleSummaryIndex &CombinedIndex, + const SmallVector<StringRef> &BitcodeLibFuncs); /// Runs a ThinLTO backend. /// If \p ModuleMap is not nullptr, all the module files to be imported have @@ -62,6 +64,7 @@ thinBackend(const Config &C, unsigned Task, AddStreamFn AddStream, Module &M, const FunctionImporter::ImportMapTy &ImportList, const GVSummaryMapTy &DefinedGlobals, MapVector<StringRef, BitcodeModule> *ModuleMap, bool CodeGenOnly, + const SmallVector<StringRef> &BitcodeLibFuncs, AddStreamFn IRAddStream = nullptr, const std::vector<uint8_t> &CmdArgs = std::vector<uint8_t>()); diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index ff6762ebb59be..ee8db650cf20e 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -771,6 +771,10 @@ Error LTO::add(std::unique_ptr<InputFile> InputPtr, return Error::success(); } +void LTO::setBitcodeLibFuncs(const SmallVector<StringRef> &BitcodeLibFuncs) { + this->BitcodeLibFuncs = BitcodeLibFuncs; +} + Expected<ArrayRef<SymbolResolution>> LTO::addModule(InputFile &Input, ArrayRef<SymbolResolution> InputRes, unsigned ModI, ArrayRef<SymbolResolution> Res) { @@ -1400,9 +1404,9 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) { } if (!RegularLTO.EmptyCombinedModule || Conf.AlwaysEmitRegularLTOObj) { - if (Error Err = - backend(Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel, - *RegularLTO.CombinedModule, ThinLTO.CombinedIndex)) + if (Error Err = backend( + Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel, + *RegularLTO.CombinedModule, ThinLTO.CombinedIndex, BitcodeLibFuncs)) return Err; } @@ -1422,6 +1426,21 @@ SmallVector<const char *> LTO::getRuntimeLibcallSymbols(const Triple &TT) { return LibcallSymbols; } +SmallVector<StringRef> LTO::getLibFuncSymbols(const Triple &TT, + StringSaver &Saver) { + auto TLII = std::make_unique<TargetLibraryInfoImpl>(TT); + TargetLibraryInfo TLI(*TLII); + SmallVector<StringRef> LibFuncSymbols; + LibFuncSymbols.reserve(LibFunc::NumLibFuncs); + for (unsigned I = 0, E = static_cast<unsigned>(LibFunc::NumLibFuncs); I != E; + ++I) { + LibFunc F = static_cast<LibFunc>(I); + if (TLI.has(F)) + LibFuncSymbols.push_back(Saver.save(TLI.getName(F)).data()); + } + return LibFuncSymbols; +} + Error ThinBackendProc::emitFiles( const FunctionImporter::ImportMapTy &ImportList, llvm::StringRef ModulePath, const std::string &NewModulePath) const { @@ -1499,6 +1518,7 @@ class CGThinBackend : public ThinBackendProc { class InProcessThinBackend : public CGThinBackend { protected: FileCache Cache; + const SmallVector<StringRef> &BitcodeLibFuncs; public: InProcessThinBackend( @@ -1506,11 +1526,12 @@ class InProcessThinBackend : public CGThinBackend { ThreadPoolStrategy ThinLTOParallelism, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, AddStreamFn AddStream, FileCache Cache, lto::IndexWriteCallback OnWrite, - bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles) + bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles, + const SmallVector<StringRef> &BitcodeLibFuncs) : CGThinBackend(Conf, CombinedIndex, ModuleToDefinedGVSummaries, AddStream, OnWrite, ShouldEmitIndexFiles, ShouldEmitImportsFiles, ThinLTOParallelism), - Cache(std::move(Cache)) {} + Cache(std::move(Cache)), BitcodeLibFuncs(BitcodeLibFuncs) {} virtual Error runThinLTOBackendThread( AddStreamFn AddStream, FileCache Cache, unsigned Task, BitcodeModule BM, @@ -1531,7 +1552,7 @@ class InProcessThinBackend : public CGThinBackend { return thinBackend(Conf, Task, AddStream, **MOrErr, CombinedIndex, ImportList, DefinedGlobals, &ModuleMap, - Conf.CodeGenOnly); + Conf.CodeGenOnly, BitcodeLibFuncs); }; if (ShouldEmitIndexFiles) { if (auto E = emitFiles(ImportList, ModuleID, ModuleID.str())) @@ -1616,13 +1637,14 @@ class FirstRoundThinBackend : public InProcessThinBackend { const Config &Conf, ModuleSummaryIndex &CombinedIndex, ThreadPoolStrategy ThinLTOParallelism, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, - AddStreamFn CGAddStream, FileCache CGCache, AddStreamFn IRAddStream, + AddStreamFn CGAddStream, FileCache CGCache, + const SmallVector<StringRef> &BitcodeLibFuncs, AddStreamFn IRAddStream, FileCache IRCache) : InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism, ModuleToDefinedGVSummaries, std::move(CGAddStream), std::move(CGCache), /*OnWrite=*/nullptr, /*ShouldEmitIndexFiles=*/false, - /*ShouldEmitImportsFiles=*/false), + /*ShouldEmitImportsFiles=*/false, BitcodeLibFuncs), IRAddStream(std::move(IRAddStream)), IRCache(std::move(IRCache)) {} Error runThinLTOBackendThread( @@ -1645,7 +1667,7 @@ class FirstRoundThinBackend : public InProcessThinBackend { return thinBackend(Conf, Task, CGAddStream, **MOrErr, CombinedIndex, ImportList, DefinedGlobals, &ModuleMap, - Conf.CodeGenOnly, IRAddStream); + Conf.CodeGenOnly, BitcodeLibFuncs, IRAddStream); }; // Like InProcessThinBackend, we produce index files as needed for // FirstRoundThinBackend. However, these files are not generated for @@ -1712,6 +1734,7 @@ class SecondRoundThinBackend : public InProcessThinBackend { ThreadPoolStrategy ThinLTOParallelism, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, AddStreamFn AddStream, FileCache Cache, + const SmallVector<StringRef> &BitcodeLibFuncs, std::unique_ptr<SmallVector<StringRef>> IRFiles, stable_hash CombinedCGDataHash) : InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism, @@ -1719,7 +1742,7 @@ class SecondRoundThinBackend : public InProcessThinBackend { std::move(Cache), /*OnWrite=*/nullptr, /*ShouldEmitIndexFiles=*/false, - /*ShouldEmitImportsFiles=*/false), + /*ShouldEmitImportsFiles=*/false, BitcodeLibFuncs), IRFiles(std::move(IRFiles)), CombinedCGDataHash(CombinedCGDataHash) {} Error runThinLTOBackendThread( @@ -1740,7 +1763,7 @@ class SecondRoundThinBackend : public InProcessThinBackend { return thinBackend(Conf, Task, AddStream, *LoadedModule, CombinedIndex, ImportList, DefinedGlobals, &ModuleMap, - /*CodeGenOnly=*/true); + /*CodeGenOnly=*/true, BitcodeLibFuncs); }; if (!Cache.isValid() || !CombinedIndex.modulePaths().count(ModuleID) || all_of(CombinedIndex.getModuleHash(ModuleID), @@ -1779,11 +1802,12 @@ ThinBackend lto::createInProcessThinBackend(ThreadPoolStrategy Parallelism, auto Func = [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, - AddStreamFn AddStream, FileCache Cache) { + AddStreamFn AddStream, FileCache Cache, + const SmallVector<StringRef> &BitcodeLibFuncs) { return std::make_unique<InProcessThinBackend>( Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, AddStream, Cache, OnWrite, ShouldEmitIndexFiles, - ShouldEmitImportsFiles); + ShouldEmitImportsFiles, BitcodeLibFuncs); }; return ThinBackend(Func, Parallelism); } @@ -1900,7 +1924,8 @@ ThinBackend lto::createWriteIndexesThinBackend( auto Func = [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, - AddStreamFn AddStream, FileCache Cache) { + AddStreamFn AddStream, FileCache Cache, + const SmallVector<StringRef> &BitcodeLibFuncs) { return std::make_unique<WriteIndexesThinBackend>( Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, OldPrefix, NewPrefix, NativeObjectPrefix, ShouldEmitImportsFiles, @@ -2118,7 +2143,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache, if (!CodeGenDataThinLTOTwoRounds) { std::unique_ptr<ThinBackendProc> BackendProc = ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, - AddStream, Cache); + AddStream, Cache, BitcodeLibFuncs); return RunBackends(BackendProc.get()); } @@ -2141,7 +2166,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache, LLVM_DEBUG(dbgs() << "[TwoRounds] Running the first round of codegen\n"); auto FirstRoundLTO = std::make_unique<FirstRoundThinBackend>( Conf, ThinLTO.CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, - CG.AddStream, CG.Cache, IR.AddStream, IR.Cache); + CG.AddStream, CG.Cache, BitcodeLibFuncs, IR.AddStream, IR.Cache); if (Error E = RunBackends(FirstRoundLTO.get())) return E; @@ -2157,7 +2182,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache, LLVM_DEBUG(dbgs() << "[TwoRounds] Running the second round of codegen\n"); auto SecondRoundLTO = std::make_unique<SecondRoundThinBackend>( Conf, ThinLTO.CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, - AddStream, Cache, IR.getResult(), CombinedHash); + AddStream, Cache, BitcodeLibFuncs, IR.getResult(), CombinedHash); return RunBackends(SecondRoundLTO.get()); } @@ -2664,7 +2689,8 @@ ThinBackend lto::createOutOfProcessThinBackend( auto Func = [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, - AddStreamFn AddStream, FileCache Cache) { + AddStreamFn AddStream, FileCache Cache, + const SmallVector<StringRef> &BitcodeLibFuncs) { return std::make_unique<OutOfProcessThinBackend>( Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, AddStream, Cache, OnWrite, ShouldEmitIndexFiles, diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp index e998ac961e24a..b7ff4bddaf9e2 100644 --- a/llvm/lib/LTO/LTOBackend.cpp +++ b/llvm/lib/LTO/LTOBackend.cpp @@ -240,7 +240,8 @@ createTargetMachine(const Config &Conf, const Target *TheTarget, Module &M) { static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM, unsigned OptLevel, bool IsThinLTO, ModuleSummaryIndex *ExportSummary, - const ModuleSummaryIndex *ImportSummary) { + const ModuleSummaryIndex *ImportSummary, + const DenseSet<StringRef> &BitcodeLibFuncs) { std::optional<PGOOptions> PGOOpt; if (!Conf.SampleProfile.empty()) PGOOpt = PGOOptions(Conf.SampleProfile, "", Conf.ProfileRemapping, @@ -282,6 +283,28 @@ static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM, new TargetLibraryInfoImpl(TM->getTargetTriple(), TM->Options.VecLib)); if (Conf.Freestanding) TLII->disableAllFunctions(); + + TargetLibraryInfo TLI(*TLII); + for (unsigned I = 0, E = static_cast<unsigned>(LibFunc::NumLibFuncs); I != E; + ++I) { + LibFunc F = static_cast<LibFunc>(I); + StringRef Name = TLI.getName(F); + GlobalValue *Val = Mod.getNamedValue(Name); + + // LibFuncs present in the current TU can always be referenced. + if (Val && !Val->isDeclaration()) + continue; + + // LibFuncs not implemented in bitcode can always be referenced. + if (!BitcodeLibFuncs.contains(Name)) + continue; + + // FIXME: Functions that are somewhere in a ThinLTO link (just not imported + // in this module) should not be disabled, as they have already been + // extracted. + TLII->setUnavailable(F); + } + FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); // Parse a custom AA pipeline if asked to. @@ -365,7 +388,8 @@ static bool isEmptyModule(const Module &Mod) { bool lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod, bool IsThinLTO, ModuleSummaryIndex *ExportSummary, const ModuleSummaryIndex *ImportSummary, - const std::vector<uint8_t> &CmdArgs) { + const std::vector<uint8_t> &CmdArgs, + const SmallVector<StringRef> &BitcodeLibFuncs) { llvm::TimeTraceScope timeScope("opt"); if (EmbedBitcode == LTOBitcodeEmbedding::EmbedPostMergePreOptimized) { // FIXME: the motivation for capturing post-merge bitcode and command line @@ -390,9 +414,11 @@ bool lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod, // analysis in the case of a ThinLTO build where this might be an empty // regular LTO combined module, with a large combined index from ThinLTO. if (!isEmptyModule(Mod)) { + DenseSet<StringRef> BitcodeLibFuncsSet(BitcodeLibFuncs.begin(), + BitcodeLibFuncs.end()); // FIXME: Plumb the combined index into the new pass manager. runNewPMPasses(Conf, Mod, TM, Conf.OptLevel, IsThinLTO, ExportSummary, - ImportSummary); + ImportSummary, BitcodeLibFuncsSet); } return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod); } @@ -558,7 +584,8 @@ Error lto::finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile) { Error lto::backend(const Config &C, AddStreamFn AddStream, unsigned ParallelCodeGenParallelismLevel, Module &Mod, - ModuleSummaryIndex &CombinedIndex) { + ModuleSummaryIndex &CombinedIndex, + const SmallVector<StringRef> &BitcodeLibFuncs) { llvm::TimeTraceScope timeScope("LTO backend"); Expected<const Target *> TOrErr = initAndLookupTarget(C, Mod); if (!TOrErr) @@ -570,7 +597,7 @@ Error lto::backend(const Config &C, AddStreamFn AddStream, if (!C.CodeGenOnly) { if (!opt(C, TM.get(), 0, Mod, /*IsThinLTO=*/false, /*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr, - /*CmdArgs*/ std::vector<uint8_t>())) + /*CmdArgs*/ std::vector<uint8_t>(), BitcodeLibFuncs)) return Error::success(); } @@ -610,7 +637,9 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream, const FunctionImporter::ImportMapTy &ImportList, const GVSummaryMapTy &DefinedGlobals, MapVector<StringRef, BitcodeModule> *ModuleMap, - bool CodeGenOnly, AddStreamFn IRAddStream, + bool CodeGenOnly, + const SmallVector<StringRef> &BitcodeLibFuncs, + AddStreamFn IRAddStream, const std::vector<uint8_t> &CmdArgs) { llvm::TimeTraceScope timeScope("Thin backend", Mod.getModuleIdentifier()); Expected<const Target *> TOrErr = initAndLookupTarget(Conf, Mod); @@ -649,7 +678,7 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream, // Perform optimization and code generation for ThinLTO. if (!opt(Conf, TM, Task, Mod, /*IsThinLTO=*/true, /*ExportSummary=*/nullptr, /*ImportSummary=*/&CombinedIndex, - CmdArgs)) + CmdArgs, BitcodeLibFuncs)) return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); // Save the current module before the first codegen round. diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp index 8aa404da15286..599c5c2eb5f84 100644 --- a/llvm/lib/LTO/LTOCodeGenerator.cpp +++ b/llvm/lib/LTO/LTOCodeGenerator.cpp @@ -614,7 +614,7 @@ bool LTOCodeGenerator::optimize() { TargetMach = createTargetMachine(); if (!opt(Config, TargetMach.get(), 0, *MergedModule, /*IsThinLTO=*/false, /*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr, - /*CmdArgs*/ std::vector<uint8_t>())) { + /*CmdArgs*/ std::vector<uint8_t>(), {})) { emitError("LTO middle-end optimizations failed"); return false; } @@ -639,7 +639,7 @@ bool LTOCodeGenerator::compileOptimized(AddStreamFn AddStream, Config.CodeGenOnly = true; Error Err = backend(Config, AddStream, ParallelismLevel, *MergedModule, - CombinedIndex); + CombinedIndex, {}); assert(!Err && "unexpected code-generation failure"); (void)Err; diff --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt index 0f6d2f7c59a5c..c48d251249488 100644 --- a/llvm/lib/Object/CMakeLists.txt +++ b/llvm/lib/Object/CMakeLists.txt @@ -51,6 +51,7 @@ add_llvm_component_library(LLVMObject BinaryFormat MCParser Support + Target TargetParser TextAPI ) diff --git a/llvm/lib/Object/IRSymtab.cpp b/llvm/lib/Object/IRSymtab.cpp index a45b34eb2e706..92ed072a80267 100644 --- a/llvm/lib/Object/IRSymtab.cpp +++ b/llvm/lib/Object/IRSymtab.cpp @@ -13,6 +13,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Config/llvm-config.h" #include "llvm/IR/Comdat.h" @@ -75,12 +76,14 @@ struct Builder { Builder(SmallVector<char, 0> &Symtab, StringTableBuilder &StrtabBuilder, BumpPtrAllocator &Alloc, const Triple &TT) : Symtab(Symtab), StrtabBuilder(StrtabBuilder), Saver(Alloc), TT(TT), - Libcalls(TT) {} + TLII(TT), TLI(TLII), Libcalls(TT) {} DenseMap<const Comdat *, int> ComdatMap; Mangler Mang; const Triple &TT; + TargetLibraryInfoImpl TLII; + TargetLibraryInfo TLI; // FIXME: This shouldn't be here. RTLIB::RuntimeLibcallsInfo Libcalls; @@ -95,6 +98,9 @@ struct Builder { std::vector<storage::Str> DependentLibraries; bool isPreservedName(StringRef Name) { + LibFunc F; + if (TLI.getLibFunc(Name, F) && TLI.has(F)) + return true; return Libcalls.getSupportedLibcallImpl(Name) != RTLIB::Unsupported; } diff --git a/llvm/test/LTO/Resolution/X86/libcall-external-bitcode.ll b/llvm/test/LTO/Resolution/X86/libcall-external-bitcode.ll new file mode 100644 index 0000000000000..95a599fe75e8b --- /dev/null +++ b/llvm/test/LTO/Resolution/X86/libcall-external-bitcode.ll @@ -0,0 +1,20 @@ +; RUN: opt %s -o %t -module-summary -mtriple x86_64-unknown-linux-musl +; RUN: llvm-lto2 run -o %t2 \ +; RUN: -r %t,foo,plx \ +; RUN: -r %t,memcmp,x \ +; RUN: -r %t,bcmp,pl --bitcode-libfuncs=bcmp %t -save-temps +; RUN: llvm-dis %t2.1.4.opt.bc -o - | FileCheck %s + +define i1 @foo(ptr %0, ptr %1, i64 %2) { + ; CHECK-LABEL: define{{.*}}i1 @foo + ; CHECK-NEXT: %cmp = {{.*}}call i32 @memcmp + ; CHECK-NEXT: %eq = icmp eq i32 %cmp, 0 + ; CHECK-NEXT: ret i1 %eq + + %cmp = call i32 @memcmp(ptr %0, ptr %1, i64 %2) + %eq = icmp eq i32 %cmp, 0 + ret i1 %eq +} + +declare i32 @memcmp(ptr, ptr, i64) +declare i32 @bcmp(ptr, ptr, i64) diff --git a/llvm/test/LTO/Resolution/X86/libcall-external-not-bitcode.ll b/llvm/test/LTO/Resolution/X86/libcall-external-not-bitcode.ll new file mode 100644 index 0000000000000..2e6cc798d22cd --- /dev/null +++ b/llvm/test/LTO/Resolution/X86/libcall-external-not-bitcode.ll @@ -0,0 +1,20 @@ +; RUN: opt %s -o %t -module-summary -mtriple x86_64-unknown-linux-musl +; RUN: llvm-lto2 run -o %t2 \ +; RUN: -r %t,foo,plx \ +; RUN: -r %t,memcmp,x \ +; RUN: -r %t,bcmp,pl %t -save-temps +; RUN: llvm-dis %t2.1.4.opt.bc -o - | FileCheck %s + +define i1 @foo(ptr %0, ptr %1, i64 %2) { + ; CHECK-LABEL: define{{.*}}i1 @foo + ; CHECK-NEXT: %bcmp = {{.*}}call i32 @bcmp + ; CHECK-NEXT: %eq = icmp eq i32 %bcmp, 0 + ; CHECK-NEXT: ret i1 %eq + + %cmp = call i32 @memcmp(ptr %0, ptr %1, i64 %2) + %eq = icmp eq i32 %cmp, 0 + ret i1 %eq +} + +declare i32 @memcmp(ptr, ptr, i64) +declare i32 @bcmp(ptr, ptr, i64) diff --git a/llvm/test/LTO/Resolution/X86/libcall-in-tu.ll b/llvm/test/LTO/Resolution/X86/libcall-in-tu.ll new file mode 100644 index 0000000000000..948f21a6536ca --- /dev/null +++ b/llvm/test/LTO/Resolution/X86/libcall-in-tu.ll @@ -0,0 +1,34 @@ +;; This test comes from a real world scenario in LTO, where the definition of +;; bcmp was deleted because it has no uses, but later instcombine re-introduced +;; a call to bcmp() as part of SimplifyLibCalls. Such deletions must not be +;; allowed. + +; RUN: opt %s -o %t -module-summary -mtriple x86_64-unknown-linux-musl +; RUN: llvm-lto2 run -o %t2 \ +; RUN: -r %t,foo,plx \ +; RUN: -r %t,memcmp,x \ +; RUN: -r %t,bcmp,pl \ +; RUN: -r %t,bcmp_impl,x %t -save-temps +; RUN: llvm-dis %t2.1.4.opt.bc -o - | FileCheck %s + +define i1 @foo(ptr %0, ptr %1, i64 %2) { + ; CHECK-LABEL: define{{.*}}i1 @foo + ; CHECK-NEXT: %bcmp = {{.*}}call i32 @bcmp + ; CHECK-NEXT: %eq = icmp eq i32 %bcmp, 0 + ; CHECK-NEXT: ret i1 %eq + + %cmp = call i32 @memcmp(ptr %0, ptr %1, i64 %2) + %eq = icmp eq i32 %cmp, 0 + ret i1 %eq +} + +declare i32 @memcmp(ptr, ptr, i64) +declare i32 @bcmp_impl(ptr, ptr, i64) + +;; Ensure bcmp is not removed from module because it is external. +; CHECK: define dso_local i32 @bcmp +define i32 @bcmp(ptr %0, ptr %1, i64 %2) noinline { + %r = call i32 @bcmp_impl(ptr %0, ptr %1, i64 %2) + ret i32 %r +} + diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp index 955c1130e9f4c..9be00a4495b36 100644 --- a/llvm/tools/llvm-lto2/llvm-lto2.cpp +++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp @@ -234,6 +234,10 @@ static cl::opt<bool> AllVtablesHaveTypeInfos("all-vtables-have-type-infos", cl::Hidden, cl::desc("All vtables have type infos")); +static cl::list<std::string> + BitcodeLibFuncs("bitcode-libfuncs", cl::Hidden, + cl::desc("set of libfuncs implemented in bitcode")); + static cl::opt<bool> TimeTrace("time-trace", cl::desc("Record time trace")); static cl::opt<unsigned> TimeTraceGranularity( @@ -496,6 +500,9 @@ static int run(int argc, char **argv) { if (HasErrors) return 1; + Lto.setBitcodeLibFuncs( + SmallVector<StringRef>(BitcodeLibFuncs.begin(), BitcodeLibFuncs.end())); + auto AddStream = [&](size_t Task, const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> { >From ab00fa7692a5da8bc9127892e674b248880f3ea3 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Thu, 11 Dec 2025 15:33:23 -0800 Subject: [PATCH 2/9] Move bitcodeLibFuncs from compile() to setter --- lld/ELF/LTO.cpp | 10 ++++++---- lld/ELF/LTO.h | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp index 49f6d15b346ae..f704ed2ee362b 100644 --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -316,10 +316,7 @@ static void thinLTOCreateEmptyIndexFiles(Ctx &ctx) { // Merge all the bitcode files we have seen, codegen the result // and return the resulting ObjectFile(s). -SmallVector<std::unique_ptr<InputFile>, 0> -BitcodeCompiler::compile(const SmallVector<StringRef> &bitcodeLibFuncs) { - ltoObj->setBitcodeLibFuncs(bitcodeLibFuncs); - +SmallVector<std::unique_ptr<InputFile>, 0> BitcodeCompiler::compile() { unsigned maxTasks = ltoObj->getMaxTasks(); buf.resize(maxTasks); files.resize(maxTasks); @@ -430,3 +427,8 @@ BitcodeCompiler::compile(const SmallVector<StringRef> &bitcodeLibFuncs) { } return ret; } + +void BitcodeCompiler::setBitcodeLibFuncs( + const SmallVector<StringRef> &bitcodeLibFuncs) { + ltoObj->setBitcodeLibFuncs(bitcodeLibFuncs); +} diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h index 8207e91460785..b9547202901fd 100644 --- a/lld/ELF/LTO.h +++ b/lld/ELF/LTO.h @@ -42,8 +42,8 @@ class BitcodeCompiler { ~BitcodeCompiler(); void add(BitcodeFile &f); - SmallVector<std::unique_ptr<InputFile>, 0> - compile(const SmallVector<StringRef> &bitcodeLibFuncs); + SmallVector<std::unique_ptr<InputFile>, 0> compile(); + void setBitcodeLibFuncs(const SmallVector<StringRef> &bitcodeLibFuncs); private: Ctx &ctx; >From 4b5f55c4a24c74cb6c967f6ee741ad002b203fa0 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Thu, 11 Dec 2025 15:35:22 -0800 Subject: [PATCH 3/9] Densify bitcode file finding --- lld/ELF/Driver.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index b0834e6c26b7a..3a0028c92d060 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2712,19 +2712,18 @@ void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) { llvm::BumpPtrAllocator alloc; llvm::StringSaver saver(alloc); - SmallVector<StringRef> bitcodeLibFuncs; if (!ctx.bitcodeFiles.empty()) { markBuffersAsDontNeed(ctx, skipLinkedOutput); - for (StringRef libFunc : lto::LTO::getLibFuncSymbols(*tt, saver)) { - Symbol *sym = ctx.symtab->find(libFunc); - if (!sym) - continue; - if (isa<BitcodeFile>(sym->file)) + + SmallVector<StringRef> bitcodeLibFuncs; + for (StringRef libFunc : lto::LTO::getLibFuncSymbols(*tt, saver)) + if (Symbol *sym = ctx.symtab->find(libFunc); + sym && isa<BitcodeFile>(sym->file)) bitcodeLibFuncs.push_back(libFunc); - } + lto->setBitcodeLibFuncs(bitcodeLibFuncs); } - ltoObjectFiles = lto->compile(bitcodeLibFuncs); + ltoObjectFiles = lto->compile(); for (auto &file : ltoObjectFiles) { auto *obj = cast<ObjFile<ELFT>>(file.get()); obj->parse(/*ignoreComdats=*/true); >From 9a8faead110f7c662761c70b910f3925d452530f Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Thu, 11 Dec 2025 15:39:54 -0800 Subject: [PATCH 4/9] Add comment about triple assumption --- lld/ELF/Driver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 3a0028c92d060..b7a6aa89efb96 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2702,6 +2702,8 @@ template <class ELFT> void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) { llvm::TimeTraceScope timeScope("LTO"); // Capture the triple before moving the bitcode into the bitcode compiler. + // Note that this assumes that the set of possible libfuncs is roughly + // equivalent for all bitcode translation units. std::optional<llvm::Triple> tt; if (!ctx.bitcodeFiles.empty()) tt = llvm::Triple(ctx.bitcodeFiles.front()->obj->getTargetTriple()); >From 5758497738692098fd9f153cd8c701ac1009538b Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 13 Jan 2026 16:26:00 -0800 Subject: [PATCH 5/9] Remove optional from triple in LLD Driver.cpp --- lld/ELF/Driver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index b7a6aa89efb96..5c83b5295939f 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2704,7 +2704,7 @@ void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) { // Capture the triple before moving the bitcode into the bitcode compiler. // Note that this assumes that the set of possible libfuncs is roughly // equivalent for all bitcode translation units. - std::optional<llvm::Triple> tt; + llvm::Triple tt; if (!ctx.bitcodeFiles.empty()) tt = llvm::Triple(ctx.bitcodeFiles.front()->obj->getTargetTriple()); // Compile bitcode files and replace bitcode symbols. @@ -2718,7 +2718,7 @@ void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) { markBuffersAsDontNeed(ctx, skipLinkedOutput); SmallVector<StringRef> bitcodeLibFuncs; - for (StringRef libFunc : lto::LTO::getLibFuncSymbols(*tt, saver)) + for (StringRef libFunc : lto::LTO::getLibFuncSymbols(tt, saver)) if (Symbol *sym = ctx.symtab->find(libFunc); sym && isa<BitcodeFile>(sym->file)) bitcodeLibFuncs.push_back(libFunc); >From eca8a7e1958c8c2bf3d4763aa27e33cb9b21dfa9 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Tue, 13 Jan 2026 16:33:46 -0800 Subject: [PATCH 6/9] Replace SmallVector& with ArrayRef --- lld/ELF/LTO.cpp | 3 +-- lld/ELF/LTO.h | 2 +- llvm/include/llvm/LTO/LTO.h | 7 +++---- llvm/include/llvm/LTO/LTOBackend.h | 21 ++++++++++----------- llvm/lib/LTO/LTO.cpp | 19 ++++++++++--------- llvm/lib/LTO/LTOBackend.cpp | 7 +++---- 6 files changed, 28 insertions(+), 31 deletions(-) diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp index f704ed2ee362b..e795ca572f12a 100644 --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -428,7 +428,6 @@ SmallVector<std::unique_ptr<InputFile>, 0> BitcodeCompiler::compile() { return ret; } -void BitcodeCompiler::setBitcodeLibFuncs( - const SmallVector<StringRef> &bitcodeLibFuncs) { +void BitcodeCompiler::setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs) { ltoObj->setBitcodeLibFuncs(bitcodeLibFuncs); } diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h index b9547202901fd..c8cb2156d90ca 100644 --- a/lld/ELF/LTO.h +++ b/lld/ELF/LTO.h @@ -43,7 +43,7 @@ class BitcodeCompiler { void add(BitcodeFile &f); SmallVector<std::unique_ptr<InputFile>, 0> compile(); - void setBitcodeLibFuncs(const SmallVector<StringRef> &bitcodeLibFuncs); + void setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs); private: Ctx &ctx; diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h index 66a3a80d7d61e..45cd90ed4734c 100644 --- a/llvm/include/llvm/LTO/LTO.h +++ b/llvm/include/llvm/LTO/LTO.h @@ -288,7 +288,7 @@ using ThinBackendFunction = std::function<std::unique_ptr<ThinBackendProc>( const Config &C, ModuleSummaryIndex &CombinedIndex, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, AddStreamFn AddStream, FileCache Cache, - const SmallVector<StringRef> &BitcodeLibFuncs)>; + ArrayRef<StringRef> BitcodeLibFuncs)>; /// This type defines the behavior following the thin-link phase during ThinLTO. /// It encapsulates a backend function and a strategy for thread pool @@ -304,7 +304,7 @@ struct ThinBackend { const Config &Conf, ModuleSummaryIndex &CombinedIndex, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, AddStreamFn AddStream, FileCache Cache, - const SmallVector<StringRef> &BitcodeLibFuncs) { + ArrayRef<StringRef> BitcodeLibFuncs) { assert(isValid() && "Invalid backend function"); return Func(Conf, CombinedIndex, ModuleToDefinedGVSummaries, std::move(AddStream), std::move(Cache), BitcodeLibFuncs); @@ -428,8 +428,7 @@ class LTO { /// Set the list of functions implemented in bitcode across the link, whether /// extracted or not. Such functions may not be referenced if they were not /// extracted by the time LTO occurs. - LLVM_ABI void - setBitcodeLibFuncs(const SmallVector<StringRef> &BitcodeLibFuncs); + LLVM_ABI void setBitcodeLibFuncs(ArrayRef<StringRef> BitcodeLibFuncs); /// Returns an upper bound on the number of tasks that the client may expect. /// This may only be called after all IR object files have been added. For a diff --git a/llvm/include/llvm/LTO/LTOBackend.h b/llvm/include/llvm/LTO/LTOBackend.h index 6a7d7e0d87ac9..4bb38529ec754 100644 --- a/llvm/include/llvm/LTO/LTOBackend.h +++ b/llvm/include/llvm/LTO/LTOBackend.h @@ -40,14 +40,14 @@ LLVM_ABI bool opt(const Config &Conf, TargetMachine *TM, unsigned Task, ModuleSummaryIndex *ExportSummary, const ModuleSummaryIndex *ImportSummary, const std::vector<uint8_t> &CmdArgs, - const SmallVector<StringRef> &BitcodeLibFuncs); + ArrayRef<StringRef> BitcodeLibFuncs); /// Runs a regular LTO backend. The regular LTO backend can also act as the /// regular LTO phase of ThinLTO, which may need to access the combined index. LLVM_ABI Error backend(const Config &C, AddStreamFn AddStream, unsigned ParallelCodeGenParallelismLevel, Module &M, ModuleSummaryIndex &CombinedIndex, - const SmallVector<StringRef> &BitcodeLibFuncs); + ArrayRef<StringRef> BitcodeLibFuncs); /// Runs a ThinLTO backend. /// If \p ModuleMap is not nullptr, all the module files to be imported have @@ -58,15 +58,14 @@ LLVM_ABI Error backend(const Config &C, AddStreamFn AddStream, /// the backend will skip optimization and only perform code generation. If /// \p IRAddStream is not nullptr, it will be called just before code generation /// to serialize the optimized IR. -LLVM_ABI Error -thinBackend(const Config &C, unsigned Task, AddStreamFn AddStream, Module &M, - const ModuleSummaryIndex &CombinedIndex, - const FunctionImporter::ImportMapTy &ImportList, - const GVSummaryMapTy &DefinedGlobals, - MapVector<StringRef, BitcodeModule> *ModuleMap, bool CodeGenOnly, - const SmallVector<StringRef> &BitcodeLibFuncs, - AddStreamFn IRAddStream = nullptr, - const std::vector<uint8_t> &CmdArgs = std::vector<uint8_t>()); +LLVM_ABI Error thinBackend( + const Config &C, unsigned Task, AddStreamFn AddStream, Module &M, + const ModuleSummaryIndex &CombinedIndex, + const FunctionImporter::ImportMapTy &ImportList, + const GVSummaryMapTy &DefinedGlobals, + MapVector<StringRef, BitcodeModule> *ModuleMap, bool CodeGenOnly, + ArrayRef<StringRef> BitcodeLibFuncs, AddStreamFn IRAddStream = nullptr, + const std::vector<uint8_t> &CmdArgs = std::vector<uint8_t>()); LLVM_ABI Error finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile); diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index ee8db650cf20e..d8186c27536dc 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -771,8 +771,9 @@ Error LTO::add(std::unique_ptr<InputFile> InputPtr, return Error::success(); } -void LTO::setBitcodeLibFuncs(const SmallVector<StringRef> &BitcodeLibFuncs) { - this->BitcodeLibFuncs = BitcodeLibFuncs; +void LTO::setBitcodeLibFuncs(ArrayRef<StringRef> BitcodeLibFuncs) { + this->BitcodeLibFuncs.clear(); + this->BitcodeLibFuncs.append(BitcodeLibFuncs.begin(), BitcodeLibFuncs.end()); } Expected<ArrayRef<SymbolResolution>> @@ -1518,7 +1519,7 @@ class CGThinBackend : public ThinBackendProc { class InProcessThinBackend : public CGThinBackend { protected: FileCache Cache; - const SmallVector<StringRef> &BitcodeLibFuncs; + ArrayRef<StringRef> BitcodeLibFuncs; public: InProcessThinBackend( @@ -1527,7 +1528,7 @@ class InProcessThinBackend : public CGThinBackend { const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, AddStreamFn AddStream, FileCache Cache, lto::IndexWriteCallback OnWrite, bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles, - const SmallVector<StringRef> &BitcodeLibFuncs) + ArrayRef<StringRef> BitcodeLibFuncs) : CGThinBackend(Conf, CombinedIndex, ModuleToDefinedGVSummaries, AddStream, OnWrite, ShouldEmitIndexFiles, ShouldEmitImportsFiles, ThinLTOParallelism), @@ -1638,7 +1639,7 @@ class FirstRoundThinBackend : public InProcessThinBackend { ThreadPoolStrategy ThinLTOParallelism, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, AddStreamFn CGAddStream, FileCache CGCache, - const SmallVector<StringRef> &BitcodeLibFuncs, AddStreamFn IRAddStream, + ArrayRef<StringRef> BitcodeLibFuncs, AddStreamFn IRAddStream, FileCache IRCache) : InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism, ModuleToDefinedGVSummaries, std::move(CGAddStream), @@ -1734,7 +1735,7 @@ class SecondRoundThinBackend : public InProcessThinBackend { ThreadPoolStrategy ThinLTOParallelism, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, AddStreamFn AddStream, FileCache Cache, - const SmallVector<StringRef> &BitcodeLibFuncs, + ArrayRef<StringRef> BitcodeLibFuncs, std::unique_ptr<SmallVector<StringRef>> IRFiles, stable_hash CombinedCGDataHash) : InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism, @@ -1803,7 +1804,7 @@ ThinBackend lto::createInProcessThinBackend(ThreadPoolStrategy Parallelism, [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, AddStreamFn AddStream, FileCache Cache, - const SmallVector<StringRef> &BitcodeLibFuncs) { + ArrayRef<StringRef> BitcodeLibFuncs) { return std::make_unique<InProcessThinBackend>( Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, AddStream, Cache, OnWrite, ShouldEmitIndexFiles, @@ -1925,7 +1926,7 @@ ThinBackend lto::createWriteIndexesThinBackend( [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, AddStreamFn AddStream, FileCache Cache, - const SmallVector<StringRef> &BitcodeLibFuncs) { + ArrayRef<StringRef> BitcodeLibFuncs) { return std::make_unique<WriteIndexesThinBackend>( Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, OldPrefix, NewPrefix, NativeObjectPrefix, ShouldEmitImportsFiles, @@ -2690,7 +2691,7 @@ ThinBackend lto::createOutOfProcessThinBackend( [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, AddStreamFn AddStream, FileCache Cache, - const SmallVector<StringRef> &BitcodeLibFuncs) { + ArrayRef<StringRef> BitcodeLibFuncs) { return std::make_unique<OutOfProcessThinBackend>( Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, AddStream, Cache, OnWrite, ShouldEmitIndexFiles, diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp index b7ff4bddaf9e2..fc004ec409d94 100644 --- a/llvm/lib/LTO/LTOBackend.cpp +++ b/llvm/lib/LTO/LTOBackend.cpp @@ -389,7 +389,7 @@ bool lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod, bool IsThinLTO, ModuleSummaryIndex *ExportSummary, const ModuleSummaryIndex *ImportSummary, const std::vector<uint8_t> &CmdArgs, - const SmallVector<StringRef> &BitcodeLibFuncs) { + ArrayRef<StringRef> BitcodeLibFuncs) { llvm::TimeTraceScope timeScope("opt"); if (EmbedBitcode == LTOBitcodeEmbedding::EmbedPostMergePreOptimized) { // FIXME: the motivation for capturing post-merge bitcode and command line @@ -585,7 +585,7 @@ Error lto::finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile) { Error lto::backend(const Config &C, AddStreamFn AddStream, unsigned ParallelCodeGenParallelismLevel, Module &Mod, ModuleSummaryIndex &CombinedIndex, - const SmallVector<StringRef> &BitcodeLibFuncs) { + ArrayRef<StringRef> BitcodeLibFuncs) { llvm::TimeTraceScope timeScope("LTO backend"); Expected<const Target *> TOrErr = initAndLookupTarget(C, Mod); if (!TOrErr) @@ -637,8 +637,7 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream, const FunctionImporter::ImportMapTy &ImportList, const GVSummaryMapTy &DefinedGlobals, MapVector<StringRef, BitcodeModule> *ModuleMap, - bool CodeGenOnly, - const SmallVector<StringRef> &BitcodeLibFuncs, + bool CodeGenOnly, ArrayRef<StringRef> BitcodeLibFuncs, AddStreamFn IRAddStream, const std::vector<uint8_t> &CmdArgs) { llvm::TimeTraceScope timeScope("Thin backend", Mod.getModuleIdentifier()); >From 4e7384d45aa944ab478dae7096601277f07cd7b7 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Thu, 15 Jan 2026 11:07:13 -0800 Subject: [PATCH 7/9] Add FIXMEs for TLI usage and Target dep --- llvm/lib/Object/CMakeLists.txt | 2 ++ llvm/lib/Object/IRSymtab.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt index c48d251249488..8b08f18b935cf 100644 --- a/llvm/lib/Object/CMakeLists.txt +++ b/llvm/lib/Object/CMakeLists.txt @@ -51,6 +51,8 @@ add_llvm_component_library(LLVMObject BinaryFormat MCParser Support + # FIXME: Target dep should be removed once TLI and RuntimeLibcalls are fused + # and moved out of Target. Target TargetParser TextAPI diff --git a/llvm/lib/Object/IRSymtab.cpp b/llvm/lib/Object/IRSymtab.cpp index 92ed072a80267..43c2292baeb3f 100644 --- a/llvm/lib/Object/IRSymtab.cpp +++ b/llvm/lib/Object/IRSymtab.cpp @@ -82,9 +82,9 @@ struct Builder { Mangler Mang; const Triple &TT; + // FIXME: These shouldn't be here. TargetLibraryInfoImpl TLII; TargetLibraryInfo TLI; - // FIXME: This shouldn't be here. RTLIB::RuntimeLibcallsInfo Libcalls; std::vector<storage::Comdat> Comdats; >From a7db97f97c514e2894e6e61e137bd69ac6c1b926 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Thu, 15 Jan 2026 11:17:28 -0800 Subject: [PATCH 8/9] More idiomatic LibFunc iteration --- llvm/lib/LTO/LTO.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index d8186c27536dc..085d0d4a587f4 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -1433,8 +1433,7 @@ SmallVector<StringRef> LTO::getLibFuncSymbols(const Triple &TT, TargetLibraryInfo TLI(*TLII); SmallVector<StringRef> LibFuncSymbols; LibFuncSymbols.reserve(LibFunc::NumLibFuncs); - for (unsigned I = 0, E = static_cast<unsigned>(LibFunc::NumLibFuncs); I != E; - ++I) { + for (unsigned I = LibFunc::Begin_LibFunc; I != LibFunc::End_LibFunc; ++I) { LibFunc F = static_cast<LibFunc>(I); if (TLI.has(F)) LibFuncSymbols.push_back(Saver.save(TLI.getName(F)).data()); >From 54ea0afaa618362a449c5f0be7e3f46f4c97dd6a Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh <[email protected]> Date: Thu, 15 Jan 2026 14:10:25 -0800 Subject: [PATCH 9/9] Move isPreservedName out of IRSymtab into LTO's Symbol --- llvm/include/llvm/LTO/LTO.h | 6 +++++- llvm/lib/LTO/LTO.cpp | 22 +++++++++++++++++++--- llvm/lib/LTO/ThinLTOCodeGenerator.cpp | 9 ++++++--- llvm/lib/Object/CMakeLists.txt | 3 --- llvm/lib/Object/IRSymtab.cpp | 18 ++---------------- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h index 45cd90ed4734c..3fab39b1eb90d 100644 --- a/llvm/include/llvm/LTO/LTO.h +++ b/llvm/include/llvm/LTO/LTO.h @@ -16,6 +16,7 @@ #define LLVM_LTO_LTO_H #include "llvm/IR/LLVMRemarkStreamer.h" +#include "llvm/IR/RuntimeLibcalls.h" #include "llvm/Support/Compiler.h" #include <memory> @@ -167,6 +168,9 @@ class InputFile { using irsymtab::Symbol::getSectionName; using irsymtab::Symbol::isExecutable; using irsymtab::Symbol::isUsed; + + bool isPreserved(const TargetLibraryInfo &TLI, + const RTLIB::RuntimeLibcallsInfo &Libcalls) const; }; /// A range over the symbols in this InputFile. @@ -589,7 +593,7 @@ class LTO { void addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms, ArrayRef<SymbolResolution> Res, unsigned Partition, - bool InSummary); + bool InSummary, const Triple &TT); // These functions take a range of symbol resolutions and consume the // resolutions used by a single input module. Functions return ranges refering diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index 085d0d4a587f4..2c314a40899ea 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -597,6 +597,15 @@ Expected<std::unique_ptr<InputFile>> InputFile::create(MemoryBufferRef Object) { return std::move(File); } +bool InputFile::Symbol::isPreserved( + const TargetLibraryInfo &TLI, + const RTLIB::RuntimeLibcallsInfo &Libcalls) const { + LibFunc F; + if (TLI.getLibFunc(IRName, F) && TLI.has(F)) + return true; + return Libcalls.getSupportedLibcallImpl(IRName) != RTLIB::Unsupported; +} + StringRef InputFile::getName() const { return Mods[0].getModuleIdentifier(); } @@ -640,11 +649,15 @@ LTO::~LTO() = default; // their partitions. void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms, ArrayRef<SymbolResolution> Res, - unsigned Partition, bool InSummary) { + unsigned Partition, bool InSummary, + const Triple &TT) { llvm::TimeTraceScope timeScope("LTO add module to global resolution"); auto *ResI = Res.begin(); auto *ResE = Res.end(); (void)ResE; + RTLIB::RuntimeLibcallsInfo Libcalls(TT); + TargetLibraryInfoImpl TLII(TT); + TargetLibraryInfo TLI(TLII); for (const InputFile::Symbol &Sym : Syms) { assert(ResI != ResE); SymbolResolution Res = *ResI++; @@ -687,11 +700,14 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms, GlobalRes.VisibleOutsideSummary = true; } + bool IsPreserved = Sym.isPreserved(TLI, Libcalls); + // Set the partition to external if we know it is re-defined by the linker // with -defsym or -wrap options, used elsewhere, e.g. it is visible to a // regular object, is referenced from llvm.compiler.used/llvm.used, or was // already recorded as being referenced from a different partition. if (Res.LinkerRedefined || Res.VisibleToRegularObj || Sym.isUsed() || + IsPreserved || (GlobalRes.Partition != GlobalResolution::Unknown && GlobalRes.Partition != Partition)) { GlobalRes.Partition = GlobalResolution::External; @@ -702,7 +718,7 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms, // Flag as visible outside of summary if visible from a regular object or // from a module that does not have a summary. GlobalRes.VisibleOutsideSummary |= - (Res.VisibleToRegularObj || Sym.isUsed() || !InSummary); + (Res.VisibleToRegularObj || Sym.isUsed() || IsPreserved || !InSummary); GlobalRes.ExportDynamic |= Res.ExportDynamic; } @@ -814,7 +830,7 @@ LTO::addModule(InputFile &Input, ArrayRef<SymbolResolution> InputRes, auto ModSyms = Input.module_symbols(ModI); addModuleToGlobalRes(ModSyms, Res, IsThinLTO ? ThinLTO.ModuleMap.size() + 1 : 0, - LTOInfo->HasSummary); + LTOInfo->HasSummary, Triple(Input.getTargetTriple())); if (IsThinLTO) return addThinLTO(BM, ModSyms, Res); diff --git a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp index 961dd0ee43370..44634833f8d70 100644 --- a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp +++ b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp @@ -290,11 +290,14 @@ static void optimizeModule(Module &TheModule, TargetMachine &TM, static void addUsedSymbolToPreservedGUID(const lto::InputFile &File, DenseSet<GlobalValue::GUID> &PreservedGUID) { - for (const auto &Sym : File.symbols()) { - if (Sym.isUsed()) + Triple TT(File.getTargetTriple()); + RTLIB::RuntimeLibcallsInfo Libcalls(TT); + TargetLibraryInfoImpl TLII(TT); + TargetLibraryInfo TLI(TLII); + for (const auto &Sym : File.symbols()) + if (Sym.isUsed() || Sym.isPreserved(TLI, Libcalls)) PreservedGUID.insert( GlobalValue::getGUIDAssumingExternalLinkage(Sym.getIRName())); - } } // Convert the PreservedSymbols map from "Name" based to "GUID" based. diff --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt index 8b08f18b935cf..0f6d2f7c59a5c 100644 --- a/llvm/lib/Object/CMakeLists.txt +++ b/llvm/lib/Object/CMakeLists.txt @@ -51,9 +51,6 @@ add_llvm_component_library(LLVMObject BinaryFormat MCParser Support - # FIXME: Target dep should be removed once TLI and RuntimeLibcalls are fused - # and moved out of Target. - Target TargetParser TextAPI ) diff --git a/llvm/lib/Object/IRSymtab.cpp b/llvm/lib/Object/IRSymtab.cpp index 43c2292baeb3f..be8e88738eab9 100644 --- a/llvm/lib/Object/IRSymtab.cpp +++ b/llvm/lib/Object/IRSymtab.cpp @@ -23,7 +23,6 @@ #include "llvm/IR/Mangler.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" -#include "llvm/IR/RuntimeLibcalls.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/ModuleSymbolTable.h" #include "llvm/Object/SymbolicFile.h" @@ -75,18 +74,12 @@ struct Builder { // so this provides somewhere to store any strings that we create. Builder(SmallVector<char, 0> &Symtab, StringTableBuilder &StrtabBuilder, BumpPtrAllocator &Alloc, const Triple &TT) - : Symtab(Symtab), StrtabBuilder(StrtabBuilder), Saver(Alloc), TT(TT), - TLII(TT), TLI(TLII), Libcalls(TT) {} + : Symtab(Symtab), StrtabBuilder(StrtabBuilder), Saver(Alloc), TT(TT) {} DenseMap<const Comdat *, int> ComdatMap; Mangler Mang; const Triple &TT; - // FIXME: These shouldn't be here. - TargetLibraryInfoImpl TLII; - TargetLibraryInfo TLI; - RTLIB::RuntimeLibcallsInfo Libcalls; - std::vector<storage::Comdat> Comdats; std::vector<storage::Module> Mods; std::vector<storage::Symbol> Syms; @@ -97,13 +90,6 @@ struct Builder { std::vector<storage::Str> DependentLibraries; - bool isPreservedName(StringRef Name) { - LibFunc F; - if (TLI.getLibFunc(Name, F) && TLI.has(F)) - return true; - return Libcalls.getSupportedLibcallImpl(Name) != RTLIB::Unsupported; - } - void setStr(storage::Str &S, StringRef Value) { S.Offset = StrtabBuilder.add(Value); S.Size = Value.size(); @@ -275,7 +261,7 @@ Error Builder::addSymbol(const ModuleSymbolTable &Msymtab, StringRef GVName = GV->getName(); setStr(Sym.IRName, GVName); - if (Used.count(GV) || isPreservedName(GVName)) + if (Used.count(GV)) Sym.Flags |= 1 << storage::Symbol::FB_used; if (GV->isThreadLocal()) Sym.Flags |= 1 << storage::Symbol::FB_tls; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
