Using multiple context used to be a really big memory saving because we could free memory from each file while the linker proceeded with the symbol resolution. We are getting lazier about reading data from the bitcode, so I was curious if this was still a good tradeoff. One thing that is a bit annoying is that we still have to copy the symbol names. The problem is that the names are stored in the Module and get freed when we move the module bits during linking. Long term I think the solution is to add a symbol table to the bitcode. That way IRObject file will not need to use a Module or a Context and we can drop it while still keeping a StringRef to the names. This patch is still be an interesting medium term improvement. When linking llvm-as without debug info this patch is a small speedup: master: 29.861877513 seconds patch: 29.814533787 seconds With debug info the numbers are master: 34.765181469 seconds patch: 34.563351584 seconds The peak memory usage when linking llvm-as with debug info was master: 599.10MB patch: 600.13MB llvm-svn: 267921
211 lines
6.6 KiB
C++
211 lines
6.6 KiB
C++
//===- LTO.cpp ------------------------------------------------------------===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "LTO.h"
|
|
#include "Config.h"
|
|
#include "Driver.h"
|
|
#include "Error.h"
|
|
#include "InputFiles.h"
|
|
#include "Symbols.h"
|
|
#include "llvm/Analysis/TargetLibraryInfo.h"
|
|
#include "llvm/Analysis/TargetTransformInfo.h"
|
|
#include "llvm/Bitcode/ReaderWriter.h"
|
|
#include "llvm/CodeGen/CommandFlags.h"
|
|
#include "llvm/CodeGen/ParallelCG.h"
|
|
#include "llvm/IR/LegacyPassManager.h"
|
|
#include "llvm/Linker/IRMover.h"
|
|
#include "llvm/Support/StringSaver.h"
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
#include "llvm/Target/TargetMachine.h"
|
|
#include "llvm/Transforms/IPO.h"
|
|
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
|
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::object;
|
|
using namespace llvm::ELF;
|
|
|
|
using namespace lld;
|
|
using namespace lld::elf;
|
|
|
|
// This is for use when debugging LTO.
|
|
static void saveLtoObjectFile(StringRef Buffer, unsigned I, bool Many) {
|
|
SmallString<128> Filename = Config->OutputFile;
|
|
if (Many)
|
|
Filename += utostr(I);
|
|
Filename += ".lto.o";
|
|
std::error_code EC;
|
|
raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::F_None);
|
|
check(EC);
|
|
OS << Buffer;
|
|
}
|
|
|
|
// This is for use when debugging LTO.
|
|
static void saveBCFile(Module &M, StringRef Suffix) {
|
|
std::error_code EC;
|
|
raw_fd_ostream OS(Config->OutputFile.str() + Suffix.str(), EC,
|
|
sys::fs::OpenFlags::F_None);
|
|
check(EC);
|
|
WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ true);
|
|
}
|
|
|
|
// Run LTO passes.
|
|
// Note that the gold plugin has a similar piece of code, so
|
|
// it is probably better to move this code to a common place.
|
|
static void runLTOPasses(Module &M, TargetMachine &TM) {
|
|
legacy::PassManager LtoPasses;
|
|
LtoPasses.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis()));
|
|
PassManagerBuilder PMB;
|
|
PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM.getTargetTriple()));
|
|
PMB.Inliner = createFunctionInliningPass();
|
|
PMB.VerifyInput = PMB.VerifyOutput = !Config->DisableVerify;
|
|
PMB.LoopVectorize = true;
|
|
PMB.SLPVectorize = true;
|
|
PMB.OptLevel = Config->LtoO;
|
|
PMB.populateLTOPassManager(LtoPasses);
|
|
LtoPasses.run(M);
|
|
|
|
if (Config->SaveTemps)
|
|
saveBCFile(M, ".lto.opt.bc");
|
|
}
|
|
|
|
static bool shouldInternalize(const SmallPtrSet<GlobalValue *, 8> &Used,
|
|
SymbolBody &B, GlobalValue *GV) {
|
|
if (B.Backref->IsUsedInRegularObj)
|
|
return false;
|
|
|
|
if (Used.count(GV))
|
|
return false;
|
|
|
|
return !B.Backref->includeInDynsym();
|
|
}
|
|
|
|
BitcodeCompiler::BitcodeCompiler()
|
|
: Combined(new llvm::Module("ld-temp.o", Driver->Context)),
|
|
Mover(*Combined) {}
|
|
|
|
void BitcodeCompiler::add(BitcodeFile &F) {
|
|
std::unique_ptr<IRObjectFile> Obj = std::move(F.Obj);
|
|
std::vector<GlobalValue *> Keep;
|
|
unsigned BodyIndex = 0;
|
|
ArrayRef<SymbolBody *> Bodies = F.getSymbols();
|
|
|
|
Module &M = Obj->getModule();
|
|
if (M.getDataLayoutStr().empty())
|
|
fatal("invalid bitcode file: " + F.getName() + " has no datalayout");
|
|
|
|
// If a symbol appears in @llvm.used, the linker is required
|
|
// to treat the symbol as there is a reference to the symbol
|
|
// that it cannot see. Therefore, we can't internalize.
|
|
SmallPtrSet<GlobalValue *, 8> Used;
|
|
collectUsedGlobalVariables(M, Used, /* CompilerUsed */ false);
|
|
|
|
for (const BasicSymbolRef &Sym : Obj->symbols()) {
|
|
GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl());
|
|
// Ignore module asm symbols.
|
|
if (!GV)
|
|
continue;
|
|
if (GV->hasAppendingLinkage()) {
|
|
Keep.push_back(GV);
|
|
continue;
|
|
}
|
|
if (BitcodeFile::shouldSkip(Sym))
|
|
continue;
|
|
SymbolBody *B = Bodies[BodyIndex++];
|
|
if (!B || &B->repl() != B || !isa<DefinedBitcode>(B))
|
|
continue;
|
|
switch (GV->getLinkage()) {
|
|
default:
|
|
break;
|
|
case llvm::GlobalValue::LinkOnceAnyLinkage:
|
|
GV->setLinkage(GlobalValue::WeakAnyLinkage);
|
|
break;
|
|
case llvm::GlobalValue::LinkOnceODRLinkage:
|
|
GV->setLinkage(GlobalValue::WeakODRLinkage);
|
|
break;
|
|
}
|
|
|
|
// We collect the set of symbols we want to internalize here
|
|
// and change the linkage after the IRMover executed, i.e. after
|
|
// we imported the symbols and satisfied undefined references
|
|
// to it. We can't just change linkage here because otherwise
|
|
// the IRMover will just rename the symbol.
|
|
if (shouldInternalize(Used, *B, GV))
|
|
InternalizedSyms.insert(GV->getName());
|
|
|
|
Keep.push_back(GV);
|
|
}
|
|
|
|
Mover.move(Obj->takeModule(), Keep,
|
|
[](GlobalValue &, IRMover::ValueAdder) {});
|
|
}
|
|
|
|
static void internalize(GlobalValue &GV) {
|
|
assert(!GV.hasLocalLinkage() &&
|
|
"Trying to internalize a symbol with local linkage!");
|
|
GV.setLinkage(GlobalValue::InternalLinkage);
|
|
}
|
|
|
|
std::vector<std::unique_ptr<InputFile>> BitcodeCompiler::runSplitCodegen(
|
|
const std::function<std::unique_ptr<TargetMachine>()> &TMFactory) {
|
|
unsigned NumThreads = Config->LtoJobs;
|
|
OwningData.resize(NumThreads);
|
|
|
|
std::list<raw_svector_ostream> OSs;
|
|
std::vector<raw_pwrite_stream *> OSPtrs;
|
|
for (SmallString<0> &Obj : OwningData) {
|
|
OSs.emplace_back(Obj);
|
|
OSPtrs.push_back(&OSs.back());
|
|
}
|
|
|
|
splitCodeGen(std::move(Combined), OSPtrs, {}, TMFactory);
|
|
|
|
std::vector<std::unique_ptr<InputFile>> ObjFiles;
|
|
for (SmallString<0> &Obj : OwningData)
|
|
ObjFiles.push_back(createObjectFile(
|
|
MemoryBufferRef(Obj, "LLD-INTERNAL-combined-lto-object")));
|
|
|
|
if (Config->SaveTemps)
|
|
for (unsigned I = 0; I < NumThreads; ++I)
|
|
saveLtoObjectFile(OwningData[I], I, NumThreads > 1);
|
|
|
|
return ObjFiles;
|
|
}
|
|
|
|
// Merge all the bitcode files we have seen, codegen the result
|
|
// and return the resulting ObjectFile.
|
|
std::vector<std::unique_ptr<InputFile>> BitcodeCompiler::compile() {
|
|
TheTriple = Combined->getTargetTriple();
|
|
for (const auto &Name : InternalizedSyms) {
|
|
GlobalValue *GV = Combined->getNamedValue(Name.first());
|
|
assert(GV);
|
|
internalize(*GV);
|
|
}
|
|
|
|
if (Config->SaveTemps)
|
|
saveBCFile(*Combined, ".lto.bc");
|
|
|
|
std::string Msg;
|
|
const Target *T = TargetRegistry::lookupTarget(TheTriple, Msg);
|
|
if (!T)
|
|
fatal("target not found: " + Msg);
|
|
TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
|
|
Reloc::Model R = Config->Pic ? Reloc::PIC_ : Reloc::Static;
|
|
|
|
auto CreateTargetMachine = [&]() {
|
|
return std::unique_ptr<TargetMachine>(
|
|
T->createTargetMachine(TheTriple, "", "", Options, R));
|
|
};
|
|
|
|
std::unique_ptr<TargetMachine> TM = CreateTargetMachine();
|
|
runLTOPasses(*Combined, *TM);
|
|
|
|
return runSplitCodegen(CreateTargetMachine);
|
|
}
|