summaryrefslogtreecommitdiff
path: root/lib/LTO/LTO.cpp
diff options
context:
space:
mode:
authorPeter Collingbourne <peter@pcc.me.uk>2017-03-31 02:28:30 +0000
committerPeter Collingbourne <peter@pcc.me.uk>2017-03-31 02:28:30 +0000
commit9f482fc3fc4ccf546a0f92477154540121c74f87 (patch)
tree3a181372a8a1f1469f2e2936e8b6641924c233e4 /lib/LTO/LTO.cpp
parentf43d588ee8d56876772bcca9fc4dfe22e9ba50d3 (diff)
LTO: Reduce memory consumption by creating an in-memory symbol table for InputFiles. NFCI.
Introduce symbol table data structures that can be potentially written to disk, have the LTO library build those data structures using temporarily constructed modules and redirect the LTO library implementation to go through those data structures. This allows us to remove the LLVMContext and Modules owned by InputFile. With this change I measured a peak memory consumption decrease from 5.4GB to 2.8GB in a no-op incremental ThinLTO link of Chromium on Linux. The impact on memory consumption is larger in COFF linkers where we are currently forced to materialize all metadata in order to read linker options. Peak memory consumption linking a large piece of Chromium for Windows with full LTO and debug info decreases from >64GB (OOM) to 15GB. Part of PR27551. Differential Revision: https://reviews.llvm.org/D31364 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@299168 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/LTO/LTO.cpp')
-rw-r--r--lib/LTO/LTO.cpp212
1 files changed, 85 insertions, 127 deletions
diff --git a/lib/LTO/LTO.cpp b/lib/LTO/LTO.cpp
index f3d258e6c25..4885f65e3d2 100644
--- a/lib/LTO/LTO.cpp
+++ b/lib/LTO/LTO.cpp
@@ -305,14 +305,6 @@ void llvm::thinLTOInternalizeAndPromoteInIndex(
thinLTOInternalizeAndPromoteGUID(I.second, I.first, isExported);
}
-struct InputFile::InputModule {
- BitcodeModule BM;
- std::unique_ptr<Module> Mod;
-
- // The range of ModuleSymbolTable entries for this input module.
- size_t SymBegin, SymEnd;
-};
-
// Requires a destructor for std::vector<InputModule>.
InputFile::~InputFile() = default;
@@ -333,87 +325,51 @@ Expected<std::unique_ptr<InputFile>> InputFile::create(MemoryBufferRef Object) {
return make_error<StringError>("Bitcode file does not contain any modules",
inconvertibleErrorCode());
- // Create an InputModule for each module in the InputFile, and add it to the
- // ModuleSymbolTable.
+ File->Mods = *BMsOrErr;
+
+ LLVMContext Ctx;
+ std::vector<Module *> Mods;
+ std::vector<std::unique_ptr<Module>> OwnedMods;
for (auto BM : *BMsOrErr) {
Expected<std::unique_ptr<Module>> MOrErr =
- BM.getLazyModule(File->Ctx, /*ShouldLazyLoadMetadata*/ true,
+ BM.getLazyModule(Ctx, /*ShouldLazyLoadMetadata*/ true,
/*IsImporting*/ false);
if (!MOrErr)
return MOrErr.takeError();
- size_t SymBegin = File->SymTab.symbols().size();
- File->SymTab.addModule(MOrErr->get());
- size_t SymEnd = File->SymTab.symbols().size();
-
- for (const auto &C : (*MOrErr)->getComdatSymbolTable()) {
- auto P = File->ComdatMap.insert(
- std::make_pair(&C.second, File->Comdats.size()));
- assert(P.second);
- (void)P;
- File->Comdats.push_back(C.first());
- }
-
- File->Mods.push_back({BM, std::move(*MOrErr), SymBegin, SymEnd});
- }
-
- return std::move(File);
-}
+ if ((*MOrErr)->getDataLayoutStr().empty())
+ return make_error<StringError>("input module has no datalayout",
+ inconvertibleErrorCode());
-Expected<int> InputFile::Symbol::getComdatIndex() const {
- if (!isGV())
- return -1;
- const GlobalObject *GO = getGV()->getBaseObject();
- if (!GO)
- return make_error<StringError>("Unable to determine comdat of alias!",
- inconvertibleErrorCode());
- if (const Comdat *C = GO->getComdat()) {
- auto I = File->ComdatMap.find(C);
- assert(I != File->ComdatMap.end());
- return I->second;
+ Mods.push_back(MOrErr->get());
+ OwnedMods.push_back(std::move(*MOrErr));
}
- return -1;
-}
-Expected<std::string> InputFile::getLinkerOpts() {
- std::string LinkerOpts;
- raw_string_ostream LOS(LinkerOpts);
- // Extract linker options from module metadata.
- for (InputModule &Mod : Mods) {
- std::unique_ptr<Module> &M = Mod.Mod;
- if (auto E = M->materializeMetadata())
- return std::move(E);
- if (Metadata *Val = M->getModuleFlag("Linker Options")) {
- MDNode *LinkerOptions = cast<MDNode>(Val);
- for (const MDOperand &MDOptions : LinkerOptions->operands())
- for (const MDOperand &MDOption : cast<MDNode>(MDOptions)->operands())
- LOS << " " << cast<MDString>(MDOption)->getString();
- }
+ SmallVector<char, 0> Symtab;
+ if (Error E = irsymtab::build(Mods, Symtab, File->Strtab))
+ return std::move(E);
+
+ irsymtab::Reader R({Symtab.data(), Symtab.size()},
+ {File->Strtab.data(), File->Strtab.size()});
+ File->SourceFileName = R.getSourceFileName();
+ File->COFFLinkerOpts = R.getCOFFLinkerOpts();
+ File->ComdatTable = R.getComdatTable();
+
+ for (unsigned I = 0; I != Mods.size(); ++I) {
+ size_t Begin = File->Symbols.size();
+ for (const irsymtab::Reader::SymbolRef &Sym : R.module_symbols(I))
+ // Skip symbols that are irrelevant to LTO. Note that this condition needs
+ // to match the one in Skip() in LTO::addRegularLTO().
+ if (Sym.isGlobal() && !Sym.isFormatSpecific())
+ File->Symbols.push_back(Sym);
+ File->ModuleSymIndices.push_back({Begin, File->Symbols.size()});
}
- // Synthesize export flags for symbols with dllexport storage.
- const Triple TT(Mods[0].Mod->getTargetTriple());
- Mangler M;
- for (const ModuleSymbolTable::Symbol &Sym : SymTab.symbols())
- if (auto *GV = Sym.dyn_cast<GlobalValue*>())
- emitLinkerFlagsForGlobalCOFF(LOS, GV, TT, M);
- LOS.flush();
- return LinkerOpts;
+ return std::move(File);
}
StringRef InputFile::getName() const {
- return Mods[0].BM.getModuleIdentifier();
-}
-
-StringRef InputFile::getSourceFileName() const {
- return Mods[0].Mod->getSourceFileName();
-}
-
-iterator_range<InputFile::symbol_iterator>
-InputFile::module_symbols(InputModule &IM) {
- return llvm::make_range(
- symbol_iterator(SymTab.symbols().data() + IM.SymBegin, SymTab, this),
- symbol_iterator(SymTab.symbols().data() + IM.SymEnd, SymTab, this));
+ return Mods[0].getModuleIdentifier();
}
LTO::RegularLTOState::RegularLTOState(unsigned ParallelCodeGenParallelismLevel,
@@ -437,21 +393,17 @@ LTO::LTO(Config Conf, ThinBackend Backend,
LTO::~LTO() = default;
// Add the given symbol to the GlobalResolutions map, and resolve its partition.
-void LTO::addSymbolToGlobalRes(SmallPtrSet<GlobalValue *, 8> &Used,
- const InputFile::Symbol &Sym,
+void LTO::addSymbolToGlobalRes(const InputFile::Symbol &Sym,
SymbolResolution Res, unsigned Partition) {
- GlobalValue *GV = Sym.isGV() ? Sym.getGV() : nullptr;
-
auto &GlobalRes = GlobalResolutions[Sym.getName()];
- if (GV) {
- GlobalRes.UnnamedAddr &= GV->hasGlobalUnnamedAddr();
- if (Res.Prevailing)
- GlobalRes.IRName = GV->getName();
- }
+ GlobalRes.UnnamedAddr &= Sym.isUnnamedAddr();
+ if (Res.Prevailing)
+ GlobalRes.IRName = Sym.getIRName();
+
// Set the partition to external if we know it is used elsewhere, e.g.
// it is visible to a regular object, is referenced from llvm.compiler_used,
// or was already recorded as being referenced from a different partition.
- if (Res.VisibleToRegularObj || (GV && Used.count(GV)) ||
+ if (Res.VisibleToRegularObj || Sym.isUsed() ||
(GlobalRes.Partition != GlobalResolution::Unknown &&
GlobalRes.Partition != Partition)) {
GlobalRes.Partition = GlobalResolution::External;
@@ -495,41 +447,32 @@ Error LTO::add(std::unique_ptr<InputFile> Input,
writeToResolutionFile(*Conf.ResolutionFile, Input.get(), Res);
const SymbolResolution *ResI = Res.begin();
- for (InputFile::InputModule &IM : Input->Mods)
- if (Error Err = addModule(*Input, IM, ResI, Res.end()))
+ for (unsigned I = 0; I != Input->Mods.size(); ++I)
+ if (Error Err = addModule(*Input, I, ResI, Res.end()))
return Err;
assert(ResI == Res.end());
return Error::success();
}
-Error LTO::addModule(InputFile &Input, InputFile::InputModule &IM,
+Error LTO::addModule(InputFile &Input, unsigned ModI,
const SymbolResolution *&ResI,
const SymbolResolution *ResE) {
- // FIXME: move to backend
- Module &M = *IM.Mod;
-
- if (M.getDataLayoutStr().empty())
- return make_error<StringError>("input module has no datalayout",
- inconvertibleErrorCode());
-
- if (!Conf.OverrideTriple.empty())
- M.setTargetTriple(Conf.OverrideTriple);
- else if (M.getTargetTriple().empty())
- M.setTargetTriple(Conf.DefaultTriple);
-
- Expected<bool> HasThinLTOSummary = IM.BM.hasSummary();
+ Expected<bool> HasThinLTOSummary = Input.Mods[ModI].hasSummary();
if (!HasThinLTOSummary)
return HasThinLTOSummary.takeError();
+ auto ModSyms = Input.module_symbols(ModI);
if (*HasThinLTOSummary)
- return addThinLTO(IM.BM, M, Input.module_symbols(IM), ResI, ResE);
+ return addThinLTO(Input.Mods[ModI], ModSyms, ResI, ResE);
else
- return addRegularLTO(IM.BM, ResI, ResE);
+ return addRegularLTO(Input.Mods[ModI], ModSyms, ResI, ResE);
}
// Add a regular LTO object to the link.
-Error LTO::addRegularLTO(BitcodeModule BM, const SymbolResolution *&ResI,
+Error LTO::addRegularLTO(BitcodeModule BM,
+ ArrayRef<InputFile::Symbol> Syms,
+ const SymbolResolution *&ResI,
const SymbolResolution *ResE) {
if (!RegularLTO.CombinedModule) {
RegularLTO.CombinedModule =
@@ -550,9 +493,6 @@ Error LTO::addRegularLTO(BitcodeModule BM, const SymbolResolution *&ResI,
ModuleSymbolTable SymTab;
SymTab.addModule(&M);
- SmallPtrSet<GlobalValue *, 8> Used;
- collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false);
-
std::vector<GlobalValue *> Keep;
for (GlobalVariable &GV : M.globals())
@@ -564,17 +504,35 @@ Error LTO::addRegularLTO(BitcodeModule BM, const SymbolResolution *&ResI,
if (GlobalObject *GO = GA.getBaseObject())
AliasedGlobals.insert(GO);
- for (const InputFile::Symbol &Sym :
- make_range(InputFile::symbol_iterator(SymTab.symbols().begin(), SymTab,
- nullptr),
- InputFile::symbol_iterator(SymTab.symbols().end(), SymTab,
- nullptr))) {
+ // In this function we need IR GlobalValues matching the symbols in Syms
+ // (which is not backed by a module), so we need to enumerate them in the same
+ // order. The symbol enumeration order of a ModuleSymbolTable intentionally
+ // matches the order of an irsymtab, but when we read the irsymtab in
+ // InputFile::create we omit some symbols that are irrelevant to LTO. The
+ // Skip() function skips the same symbols from the module as InputFile does
+ // from the symbol table.
+ auto MsymI = SymTab.symbols().begin(), MsymE = SymTab.symbols().end();
+ auto Skip = [&]() {
+ while (MsymI != MsymE) {
+ auto Flags = SymTab.getSymbolFlags(*MsymI);
+ if ((Flags & object::BasicSymbolRef::SF_Global) &&
+ !(Flags & object::BasicSymbolRef::SF_FormatSpecific))
+ return;
+ ++MsymI;
+ }
+ };
+ Skip();
+
+ for (const InputFile::Symbol &Sym : Syms) {
assert(ResI != ResE);
SymbolResolution Res = *ResI++;
- addSymbolToGlobalRes(Used, Sym, Res, 0);
+ addSymbolToGlobalRes(Sym, Res, 0);
- if (Sym.isGV()) {
- GlobalValue *GV = Sym.getGV();
+ assert(MsymI != MsymE);
+ ModuleSymbolTable::Symbol Msym = *MsymI++;
+ Skip();
+
+ if (GlobalValue *GV = Msym.dyn_cast<GlobalValue *>()) {
if (Res.Prevailing) {
if (Sym.isUndefined())
continue;
@@ -612,7 +570,7 @@ Error LTO::addRegularLTO(BitcodeModule BM, const SymbolResolution *&ResI,
if (Sym.isCommon()) {
// FIXME: We should figure out what to do about commons defined by asm.
// For now they aren't reported correctly by ModuleSymbolTable.
- auto &CommonRes = RegularLTO.Commons[Sym.getGV()->getName()];
+ auto &CommonRes = RegularLTO.Commons[Sym.getIRName()];
CommonRes.Size = std::max(CommonRes.Size, Sym.getCommonSize());
CommonRes.Align = std::max(CommonRes.Align, Sym.getCommonAlignment());
CommonRes.Prevailing |= Res.Prevailing;
@@ -620,6 +578,7 @@ Error LTO::addRegularLTO(BitcodeModule BM, const SymbolResolution *&ResI,
// FIXME: use proposed local attribute for FinalDefinitionInLinkageUnit.
}
+ assert(MsymI == MsymE);
return RegularLTO.Mover->move(std::move(*MOrErr), Keep,
[](GlobalValue &, IRMover::ValueAdder) {},
@@ -627,15 +586,10 @@ Error LTO::addRegularLTO(BitcodeModule BM, const SymbolResolution *&ResI,
}
// Add a ThinLTO object to the link.
-// FIXME: This function should not need to take as many parameters once we have
-// a bitcode symbol table.
-Error LTO::addThinLTO(BitcodeModule BM, Module &M,
- iterator_range<InputFile::symbol_iterator> Syms,
+Error LTO::addThinLTO(BitcodeModule BM,
+ ArrayRef<InputFile::Symbol> Syms,
const SymbolResolution *&ResI,
const SymbolResolution *ResE) {
- SmallPtrSet<GlobalValue *, 8> Used;
- collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false);
-
Expected<std::unique_ptr<ModuleSummaryIndex>> SummaryOrErr = BM.getSummary();
if (!SummaryOrErr)
return SummaryOrErr.takeError();
@@ -645,11 +599,15 @@ Error LTO::addThinLTO(BitcodeModule BM, Module &M,
for (const InputFile::Symbol &Sym : Syms) {
assert(ResI != ResE);
SymbolResolution Res = *ResI++;
- addSymbolToGlobalRes(Used, Sym, Res, ThinLTO.ModuleMap.size() + 1);
+ addSymbolToGlobalRes(Sym, Res, ThinLTO.ModuleMap.size() + 1);
- if (Res.Prevailing && Sym.isGV())
- ThinLTO.PrevailingModuleForGUID[Sym.getGV()->getGUID()] =
- BM.getModuleIdentifier();
+ if (Res.Prevailing) {
+ if (!Sym.getIRName().empty()) {
+ auto GUID = GlobalValue::getGUID(GlobalValue::getGlobalIdentifier(
+ Sym.getIRName(), GlobalValue::ExternalLinkage, ""));
+ ThinLTO.PrevailingModuleForGUID[GUID] = BM.getModuleIdentifier();
+ }
+ }
}
if (!ThinLTO.ModuleMap.insert({BM.getModuleIdentifier(), BM}).second)