diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 1570adf13709..da69c4882ead 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1804,6 +1804,10 @@ void BitcodeFile::parseLazy() { auto *sym = symtab.insert(unique_saver().save(irSym.getName())); sym->resolve(LazySymbol{*this}); symbols[i] = sym; + } else { + // Keep copies of per-module undefined symbols for LTO::GlobalResolutions + // usage. + unique_saver().save(irSym.getName()); } } diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp index 935d0a9eab9e..f339f1c2c0ec 100644 --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -135,6 +135,7 @@ static lto::Config createConfig() { config->ltoValidateAllVtablesHaveTypeInfos; c.AllVtablesHaveTypeInfos = ctx.ltoAllVtablesHaveTypeInfos; c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty(); + c.KeepSymbolNameCopies = false; for (const llvm::StringRef &name : config->thinLTOModulesToCompile) c.ThinLTOModulesToCompile.emplace_back(name); diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h index 482b6e55a19d..a49cce9f30e2 100644 --- a/llvm/include/llvm/LTO/Config.h +++ b/llvm/include/llvm/LTO/Config.h @@ -88,6 +88,11 @@ struct Config { /// want to know a priori all possible output files. bool AlwaysEmitRegularLTOObj = false; + /// If true, the LTO instance creates copies of the symbol names for LTO::run. + /// The lld linker uses string saver to keep symbol names alive and doesn't + /// need to create copies, so it can set this field to false. + bool KeepSymbolNameCopies = true; + /// Allows non-imported definitions to get the potentially more constraining /// visibility from the prevailing definition. FromPrevailing is the default /// because it works for many binary formats. ELF can use the more optimized diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h index 949e80a43f0e..782f37dc8d44 100644 --- a/llvm/include/llvm/LTO/LTO.h +++ b/llvm/include/llvm/LTO/LTO.h @@ -15,6 +15,9 @@ #ifndef LLVM_LTO_LTO_H #define LLVM_LTO_LTO_H +#include + +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/Bitcode/BitcodeReader.h" @@ -23,6 +26,7 @@ #include "llvm/Object/IRSymtab.h" #include "llvm/Support/Caching.h" #include "llvm/Support/Error.h" +#include "llvm/Support/StringSaver.h" #include "llvm/Support/thread.h" #include "llvm/Transforms/IPO/FunctionAttrs.h" #include "llvm/Transforms/IPO/FunctionImport.h" @@ -403,10 +407,19 @@ private: }; }; + // GlobalResolutionSymbolSaver allocator. + std::unique_ptr Alloc; + + // Symbol saver for global resolution map. + std::unique_ptr GlobalResolutionSymbolSaver; + // Global mapping from mangled symbol names to resolutions. - // Make this an optional to guard against accessing after it has been reset + // Make this an unique_ptr to guard against accessing after it has been reset // (to reduce memory after we're done with it). - std::optional> GlobalResolutions; + std::unique_ptr> + GlobalResolutions; + + void releaseGlobalResolutionsMemory(); void addModuleToGlobalRes(ArrayRef Syms, ArrayRef Res, unsigned Partition, diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index 68072563cb33..5d9a5cbd18f1 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -77,6 +77,10 @@ cl::opt EnableLTOInternalization( "enable-lto-internalization", cl::init(true), cl::Hidden, cl::desc("Enable global value internalization in LTO")); +static cl::opt + LTOKeepSymbolCopies("lto-keep-symbol-copies", cl::init(false), cl::Hidden, + cl::desc("Keep copies of symbols in LTO indexing")); + /// Indicate we are linking with an allocator that supports hot/cold operator /// new interfaces. extern cl::opt SupportsHotColdNew; @@ -587,8 +591,14 @@ LTO::LTO(Config Conf, ThinBackend Backend, : Conf(std::move(Conf)), RegularLTO(ParallelCodeGenParallelismLevel, this->Conf), ThinLTO(std::move(Backend)), - GlobalResolutions(std::make_optional>()), - LTOMode(LTOMode) {} + GlobalResolutions( + std::make_unique>()), + LTOMode(LTOMode) { + if (Conf.KeepSymbolNameCopies || LTOKeepSymbolCopies) { + Alloc = std::make_unique(); + GlobalResolutionSymbolSaver = std::make_unique(*Alloc); + } +} // Requires a destructor for MapVector. LTO::~LTO() = default; @@ -606,7 +616,12 @@ void LTO::addModuleToGlobalRes(ArrayRef Syms, assert(ResI != ResE); SymbolResolution Res = *ResI++; - auto &GlobalRes = (*GlobalResolutions)[Sym.getName()]; + StringRef SymbolName = Sym.getName(); + // Keep copies of symbols if the client of LTO says so. + if (GlobalResolutionSymbolSaver && !GlobalResolutions->contains(SymbolName)) + SymbolName = GlobalResolutionSymbolSaver->save(SymbolName); + + auto &GlobalRes = (*GlobalResolutions)[SymbolName]; GlobalRes.UnnamedAddr &= Sym.isUnnamedAddr(); if (Res.Prevailing) { assert(!GlobalRes.Prevailing && @@ -660,6 +675,14 @@ void LTO::addModuleToGlobalRes(ArrayRef Syms, } } +void LTO::releaseGlobalResolutionsMemory() { + // Release GlobalResolutions dense-map itself. + GlobalResolutions.reset(); + // Release the string saver memory. + GlobalResolutionSymbolSaver.reset(); + Alloc.reset(); +} + static void writeToResolutionFile(raw_ostream &OS, InputFile *Input, ArrayRef Res) { StringRef Path = Input->getName(); @@ -1771,7 +1794,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache, // are no further accesses. We specifically want to do this before computing // cross module importing, which adds to peak memory via the computed import // and export lists. - GlobalResolutions.reset(); + releaseGlobalResolutionsMemory(); if (Conf.OptLevel > 0) ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,