Skip to content

Re-apply "[NFCI][LTO][lld] Optimize away symbol copies within LTO global resolution in ELF" #107792

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 9, 2024
26 changes: 18 additions & 8 deletions lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1744,10 +1744,15 @@ createBitcodeSymbol(Symbol *&sym, const std::vector<bool> &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])) {
Expand Down Expand Up @@ -1797,14 +1802,19 @@ void BitcodeFile::parse() {
void BitcodeFile::parseLazy() {
numSymbols = obj->symbols().size();
symbols = std::make_unique<Symbol *[]>(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() {
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/LTO/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 18 additions & 5 deletions llvm/include/llvm/LTO/LTO.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
#ifndef LLVM_LTO_LTO_H
#define LLVM_LTO_LTO_H

#include <memory>

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Bitcode/BitcodeReader.h"
Expand All @@ -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"
Expand Down Expand Up @@ -132,9 +136,9 @@ class InputFile {
/// Create an InputFile.
static Expected<std::unique_ptr<InputFile>> 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:
Expand Down Expand Up @@ -403,10 +407,19 @@ class LTO {
};
};

// GlobalResolutionSymbolSaver allocator.
std::unique_ptr<llvm::BumpPtrAllocator> Alloc;

// Symbol saver for global resolution map.
std::unique_ptr<llvm::StringSaver> 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<StringMap<GlobalResolution>> GlobalResolutions;
std::unique_ptr<llvm::DenseMap<StringRef, GlobalResolution>>
GlobalResolutions;

void releaseGlobalResolutionsMemory();

void addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
ArrayRef<SymbolResolution> Res, unsigned Partition,
Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/Object/IRSymtab.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ Error build(ArrayRef<Module *> Mods, SmallVector<char, 0> &Symtab,
/// possibly a storage::Uncommon.
struct Symbol {
// Copied from storage::Symbol.
StringRef Name, IRName;
mutable StringRef Name;
StringRef IRName;
int ComdatIndex;
uint32_t Flags;

Expand Down
31 changes: 27 additions & 4 deletions llvm/lib/LTO/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ cl::opt<bool> EnableLTOInternalization(
"enable-lto-internalization", cl::init(true), cl::Hidden,
cl::desc("Enable global value internalization in LTO"));

static cl::opt<bool>
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<bool> SupportsHotColdNew;
Expand Down Expand Up @@ -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<StringMap<GlobalResolution>>()),
LTOMode(LTOMode) {}
GlobalResolutions(
std::make_unique<DenseMap<StringRef, GlobalResolution>>()),
LTOMode(LTOMode) {
if (Conf.KeepSymbolNameCopies || LTOKeepSymbolCopies) {
Alloc = std::make_unique<BumpPtrAllocator>();
GlobalResolutionSymbolSaver = std::make_unique<llvm::StringSaver>(*Alloc);
}
}

// Requires a destructor for MapVector<BitcodeModule>.
LTO::~LTO() = default;
Expand All @@ -606,7 +616,12 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> 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 &&
Expand Down Expand Up @@ -660,6 +675,14 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> 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<SymbolResolution> Res) {
StringRef Path = Input->getName();
Expand Down Expand Up @@ -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,
Expand Down