llvm-project/lld/ELF/LTO.cpp

334 lines
11 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/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/LoopPassManager.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/AutoUpgrade.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Verifier.h"
#include "llvm/LTO/legacy/UpdateCompilerUsed.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/Passes/PassBuilder.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 saveBuffer(StringRef Buffer, const Twine &Path) {
std::error_code EC;
raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
if (EC)
error(EC, "cannot create " + Path);
OS << Buffer;
}
// This is for use when debugging LTO.
static void saveBCFile(Module &M, const Twine &Path) {
std::error_code EC;
raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
if (EC)
error(EC, "cannot create " + Path);
WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ true);
}
static void runNewCustomLtoPasses(Module &M, TargetMachine &TM) {
PassBuilder PB(&TM);
AAManager AA;
// Parse a custom AA pipeline if asked to.
if (!PB.parseAAPipeline(AA, Config->LtoAAPipeline)) {
error("Unable to parse AA pipeline description: " + Config->LtoAAPipeline);
return;
}
LoopAnalysisManager LAM;
FunctionAnalysisManager FAM;
CGSCCAnalysisManager CGAM;
ModuleAnalysisManager MAM;
// Register the AA manager first so that our version is the one used.
FAM.registerPass([&] { return std::move(AA); });
// Register all the basic analyses with the managers.
PB.registerModuleAnalyses(MAM);
PB.registerCGSCCAnalyses(CGAM);
PB.registerFunctionAnalyses(FAM);
PB.registerLoopAnalyses(LAM);
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
ModulePassManager MPM;
if (!Config->DisableVerify)
MPM.addPass(VerifierPass());
// Now, add all the passes we've been requested to.
if (!PB.parsePassPipeline(MPM, Config->LtoNewPmPasses)) {
error("unable to parse pass pipeline description: " +
Config->LtoNewPmPasses);
return;
}
if (!Config->DisableVerify)
MPM.addPass(VerifierPass());
MPM.run(M, MAM);
}
static void runOldLtoPasses(Module &M, TargetMachine &TM) {
// Note that the gold plugin has a similar piece of code, so
// it is probably better to move this code to a common place.
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);
}
static void runLTOPasses(Module &M, TargetMachine &TM) {
if (!Config->LtoNewPmPasses.empty()) {
// The user explicitly asked for a set of passes to be run.
// This needs the new PM to work as there's no clean way to
// pass a set of passes to run in the legacy PM.
runNewCustomLtoPasses(M, TM);
if (HasError)
return;
} else {
// Run the 'default' set of LTO passes. This code still uses
// the legacy PM as the new one is not the default.
runOldLtoPasses(M, TM);
}
if (Config->SaveTemps)
saveBCFile(M, Config->OutputFile + ".lto.opt.bc");
}
static bool shouldInternalize(const SmallPtrSet<GlobalValue *, 8> &Used,
Symbol *S, GlobalValue *GV) {
if (S->IsUsedInRegularObj || Used.count(GV))
return false;
return !S->includeInDynsym();
}
BitcodeCompiler::BitcodeCompiler()
: Combined(new Module("ld-temp.o", Driver->Context)) {}
static void undefine(Symbol *S) {
replaceBody<Undefined>(S, S->body()->getName(), STV_DEFAULT, S->body()->Type,
nullptr);
}
static void handleUndefinedAsmRefs(const BasicSymbolRef &Sym, GlobalValue *GV,
StringSet<> &AsmUndefinedRefs) {
// GV associated => not an assembly symbol, bail out.
if (GV)
return;
// This is an undefined reference to a symbol in asm. We put that in
// compiler.used, so that we can preserve it from being dropped from
// the output, without necessarily preventing its internalization.
SmallString<64> Name;
raw_svector_ostream OS(Name);
Sym.printName(OS);
AsmUndefinedRefs.insert(Name.str());
}
void BitcodeCompiler::add(BitcodeFile &F) {
std::unique_ptr<IRObjectFile> Obj = std::move(F.Obj);
std::vector<GlobalValue *> Keep;
unsigned BodyIndex = 0;
ArrayRef<Symbol *> Syms = F.getSymbols();
Module &M = Obj->getModule();
if (M.getDataLayoutStr().empty())
fatal("invalid bitcode file: " + F.getName() + " has no datalayout");
// Discard non-compatible debug infos if necessary.
M.materializeMetadata();
UpgradeDebugInfo(M);
// 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()) {
uint32_t Flags = Sym.getFlags();
GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl());
if (GV && GV->hasAppendingLinkage())
Keep.push_back(GV);
if (BitcodeFile::shouldSkip(Flags))
continue;
Symbol *S = Syms[BodyIndex++];
if (GV)
GV->setUnnamedAddr(S->HasUnnamedAddr ? GlobalValue::UnnamedAddr::Global
: GlobalValue::UnnamedAddr::None);
if (Flags & BasicSymbolRef::SF_Undefined) {
handleUndefinedAsmRefs(Sym, GV, AsmUndefinedRefs);
continue;
}
SymbolBody *B = S->body();
if (B->File != &F)
continue;
// 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 (GV && shouldInternalize(Used, S, GV))
InternalizedSyms.insert(GV->getName());
// At this point we know that either the combined LTO object will provide a
// definition of a symbol, or we will internalize it. In either case, we
// need to undefine the symbol. In the former case, the real definition
// needs to be able to replace the original definition without conflicting.
// In the latter case, we need to allow the combined LTO object to provide a
// definition with the same name, for example when doing parallel codegen.
if (auto *C = dyn_cast<DefinedCommon>(B)) {
if (auto *GO = dyn_cast<GlobalObject>(GV))
GO->setAlignment(C->Alignment);
} else {
undefine(S);
}
if (!GV)
// Module asm symbol.
continue;
switch (GV->getLinkage()) {
default:
break;
case GlobalValue::LinkOnceAnyLinkage:
GV->setLinkage(GlobalValue::WeakAnyLinkage);
break;
case GlobalValue::LinkOnceODRLinkage:
GV->setLinkage(GlobalValue::WeakODRLinkage);
break;
}
Keep.push_back(GV);
}
IRMover Mover(*Combined);
if (Error E = Mover.move(Obj->takeModule(), Keep,
[](GlobalValue &, IRMover::ValueAdder) {})) {
handleAllErrors(std::move(E), [&](const ErrorInfoBase &EIB) {
fatal("failed to link module " + F.getName() + ": " + EIB.message());
});
}
}
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 -save-temps is given, we need to save temporary objects to files.
// This is for debugging.
if (Config->SaveTemps) {
if (NumThreads == 1) {
saveBuffer(OwningData[0], Config->OutputFile + ".lto.o");
} else {
for (unsigned I = 0; I < NumThreads; ++I)
saveBuffer(OwningData[I], Config->OutputFile + Twine(I) + ".lto.o");
}
}
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() {
for (const auto &Name : InternalizedSyms) {
GlobalValue *GV = Combined->getNamedValue(Name.first());
assert(GV);
internalize(*GV);
}
std::string TheTriple = Combined->getTargetTriple();
std::string Msg;
const Target *T = TargetRegistry::lookupTarget(TheTriple, Msg);
if (!T)
fatal("target not found: " + Msg);
// LLD supports the new relocations.
TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
Options.RelaxELFRelocations = true;
auto CreateTargetMachine = [&]() {
return std::unique_ptr<TargetMachine>(T->createTargetMachine(
TheTriple, "", "", Options, Config->Pic ? Reloc::PIC_ : Reloc::Static));
};
std::unique_ptr<TargetMachine> TM = CreateTargetMachine();
// Update llvm.compiler.used so that optimizations won't strip
// off AsmUndefinedReferences.
updateCompilerUsed(*Combined, *TM, AsmUndefinedRefs);
if (Config->SaveTemps)
saveBCFile(*Combined, Config->OutputFile + ".lto.bc");
runLTOPasses(*Combined, *TM);
if (HasError)
return {};
return runSplitCodegen(CreateTargetMachine);
}