diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 1570adf137093..db520178f3f53 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1744,10 +1744,15 @@ createBitcodeSymbol(Symbol *&sym, const std::vector &keptComdats, uint8_t type = objSym.isTLS() ? STT_TLS : STT_NOTYPE; uint8_t visibility = mapVisibility(objSym.getVisibility()); - // Symbols can be duplicated in bitcode files because of '#include' and - // linkonce_odr. Use unique_saver to save symbol names for de-duplication. - if (!sym) - sym = symtab.insert(unique_saver().save(objSym.getName())); + if (!sym) { + // Symbols can be duplicated in bitcode files because of '#include' and + // linkonce_odr. Use unique_saver to save symbol names for de-duplication. + // Update objSym.Name to reference (via StringRef) the string saver's copy; + // this way LTO can reference the same string saver's copy rather than + // keeping copies of its own. + objSym.Name = unique_saver().save(objSym.getName()); + sym = symtab.insert(objSym.getName()); + } int c = objSym.getComdatIndex(); if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) { @@ -1797,14 +1802,19 @@ void BitcodeFile::parse() { void BitcodeFile::parseLazy() { numSymbols = obj->symbols().size(); symbols = std::make_unique(numSymbols); - for (auto [i, irSym] : llvm::enumerate(obj->symbols())) + for (auto [i, irSym] : llvm::enumerate(obj->symbols())) { + // Symbols can be duplicated in bitcode files because of '#include' and + // linkonce_odr. Use unique_saver to save symbol names for de-duplication. + // Update objSym.Name to reference (via StringRef) the string saver's copy; + // this way LTO can reference the same string saver's copy rather than + // keeping copies of its own. + irSym.Name = unique_saver().save(irSym.getName()); if (!irSym.isUndefined()) { - // Symbols can be duplicated in bitcode files because of '#include' and - // linkonce_odr. Use unique_saver to save symbol names for de-duplication. - auto *sym = symtab.insert(unique_saver().save(irSym.getName())); + auto *sym = symtab.insert(irSym.getName()); sym->resolve(LazySymbol{*this}); symbols[i] = sym; } + } } void BitcodeFile::postParse() { diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp index 935d0a9eab9ee..f339f1c2c0ec2 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 482b6e55a19d3..a49cce9f30e20 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 949e80a43f0e8..119f872b26c4f 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" @@ -132,9 +136,9 @@ class InputFile { /// Create an InputFile. static Expected> create(MemoryBufferRef Object); - /// The purpose of this class is to only expose the symbol information that an - /// LTO client should need in order to do symbol resolution. - class Symbol : irsymtab::Symbol { + /// The purpose of this struct is to only expose the symbol information that + /// an LTO client should need in order to do symbol resolution. + struct Symbol : irsymtab::Symbol { friend LTO; public: @@ -403,10 +407,19 @@ class LTO { }; }; + // 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/include/llvm/Object/IRSymtab.h b/llvm/include/llvm/Object/IRSymtab.h index 72a51ffa1022d..4e0013ea767e3 100644 --- a/llvm/include/llvm/Object/IRSymtab.h +++ b/llvm/include/llvm/Object/IRSymtab.h @@ -169,7 +169,8 @@ Error build(ArrayRef Mods, SmallVector &Symtab, /// possibly a storage::Uncommon. struct Symbol { // Copied from storage::Symbol. - StringRef Name, IRName; + mutable StringRef Name; + StringRef IRName; int ComdatIndex; uint32_t Flags; diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index 68072563cb33d..5d9a5cbd18f15 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,