From cde5e5b600b06d543c19adb4017db3dd8cfb07fa Mon Sep 17 00:00:00 2001 From: Bob Haarman Date: Thu, 2 Feb 2017 23:58:14 +0000 Subject: [PATCH] refactor COFF linker to use new LTO API Summary: The COFF linker previously implemented link-time optimization using an API which has now been marked as legacy. This change refactors the COFF linker to use the new LTO API, which is also used by the ELF linker. Reviewers: pcc, ruiu Reviewed By: pcc Subscribers: mgorny, mehdi_amini Differential Revision: https://reviews.llvm.org/D29059 llvm-svn: 293967 --- lld/COFF/CMakeLists.txt | 1 + lld/COFF/Config.h | 2 + lld/COFF/Driver.cpp | 7 ++ lld/COFF/InputFiles.cpp | 97 ++++++++++--------- lld/COFF/InputFiles.h | 9 +- lld/COFF/LTO.cpp | 125 +++++++++++++++++++++++++ lld/COFF/LTO.h | 56 +++++++++++ lld/COFF/SymbolTable.cpp | 150 +++++++++++------------------- lld/COFF/SymbolTable.h | 13 ++- lld/COFF/Symbols.cpp | 9 +- lld/COFF/Symbols.h | 78 ++++++---------- lld/test/COFF/lto-comdat.ll | 79 ++++++---------- lld/test/COFF/lto-parallel.ll | 8 +- lld/test/COFF/weak-external.test | 2 +- lld/test/COFF/weak-external3.test | 2 +- 15 files changed, 378 insertions(+), 260 deletions(-) create mode 100644 lld/COFF/LTO.cpp create mode 100644 lld/COFF/LTO.h diff --git a/lld/COFF/CMakeLists.txt b/lld/COFF/CMakeLists.txt index 35fd050b955e..9773b1569886 100644 --- a/lld/COFF/CMakeLists.txt +++ b/lld/COFF/CMakeLists.txt @@ -15,6 +15,7 @@ add_lld_library(lldCOFF ICF.cpp InputFiles.cpp Librarian.cpp + LTO.cpp MapFile.cpp MarkLive.cpp ModuleDef.cpp diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h index 88bd754b87ee..80fd415a643d 100644 --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -114,6 +114,8 @@ struct Configuration { // Used for /opt:lldltojobs=N unsigned LTOJobs = 1; + // Used for /opt:lldltopartitions=N + unsigned LTOPartitions = 1; // Used for /merge:from=to (e.g. /merge:.rdata=.text) std::map Merge; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 562a7a09b275..eb2ee6e3f078 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -609,6 +609,13 @@ void LinkerDriver::link(ArrayRef ArgsArr) { fatal("/opt:lldltojobs: invalid job count: " + Jobs); continue; } + if (StringRef(S).startswith("lldltopartitions=")) { + StringRef N = StringRef(S).substr(17); + if (N.getAsInteger(10, Config->LTOPartitions) || + Config->LTOPartitions == 0) + fatal("/opt:lldltopartitions: invalid partition count: " + N); + continue; + } if (S != "ref" && S != "lbr" && S != "nolbr") fatal("/opt: unknown option: " + S); } diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp index 5f5dbfa2685e..78bf346cc0e5 100644 --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -19,8 +19,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/LTO/legacy/LTOModule.h" +#include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Support/COFF.h" @@ -45,7 +44,19 @@ using llvm::support::ulittle32_t; namespace lld { namespace coff { -LLVMContext BitcodeFile::Context; +/// Checks that Source is compatible with being a weak alias to Target. +/// If Source is Undefined and has no weak alias set, makes it a weak +/// alias to Target. +static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F, + SymbolBody *Source, SymbolBody *Target) { + auto *U = dyn_cast(Source); + if (!U) + return; + else if (!U->WeakAlias) + U->WeakAlias = Target; + else if (U->WeakAlias != Target) + Symtab->reportDuplicate(Source->symbol(), F); +} ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} @@ -175,15 +186,9 @@ void ObjectFile::initializeSymbols() { I += Sym.getNumberOfAuxSymbols(); LastSectionNumber = Sym.getSectionNumber(); } - for (auto WeakAlias : WeakAliases) { - auto *U = dyn_cast(WeakAlias.first); - if (!U) - continue; - // Report an error if two undefined symbols have different weak aliases. - if (U->WeakAlias && U->WeakAlias != SparseSymbolBodies[WeakAlias.second]) - Symtab->reportDuplicate(U->symbol(), this); - U->WeakAlias = SparseSymbolBodies[WeakAlias.second]; - } + for (auto WeakAlias : WeakAliases) + checkAndSetWeakAlias(Symtab, this, WeakAlias.first, + SparseSymbolBodies[WeakAlias.second]); } SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) { @@ -198,7 +203,10 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, if (Sym.isCommon()) { auto *C = new (Alloc) CommonChunk(Sym); Chunks.push_back(C); - return Symtab->addCommon(this, Sym, C)->body(); + COFFObj->getSymbolName(Sym, Name); + Symbol *S = + Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C); + return S->body(); } if (Sym.isAbsolute()) { COFFObj->getSymbolName(Sym, Name); @@ -245,10 +253,14 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, } DefinedRegular *B; - if (Sym.isExternal()) - B = cast(Symtab->addRegular(this, Sym, SC)->body()); - else - B = new (Alloc) DefinedRegular(this, Sym, SC); + if (Sym.isExternal()) { + COFFObj->getSymbolName(Sym, Name); + Symbol *S = + Symtab->addRegular(this, Name, SC->isCOMDAT(), Sym.getGeneric(), SC); + B = cast(S->body()); + } else + B = new (Alloc) DefinedRegular(this, /*Name*/ "", SC->isCOMDAT(), + /*IsExternal*/ false, Sym.getGeneric(), SC); if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP) SC->setSymbol(B); @@ -327,39 +339,38 @@ void ImportFile::parse() { } void BitcodeFile::parse() { - Context.enableDebugTypeODRUniquing(); - ErrorOr> ModOrErr = LTOModule::createFromBuffer( - Context, MB.getBufferStart(), MB.getBufferSize(), llvm::TargetOptions()); - M = check(std::move(ModOrErr), "could not create LTO module"); - - StringSaver Saver(Alloc); - for (unsigned I = 0, E = M->getSymbolCount(); I != E; ++I) { - lto_symbol_attributes Attrs = M->getSymbolAttributes(I); - if ((Attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL) - continue; - - StringRef SymName = Saver.save(M->getSymbolName(I)); - int SymbolDef = Attrs & LTO_SYMBOL_DEFINITION_MASK; - if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) { - SymbolBodies.push_back(Symtab->addUndefined(SymName, this, false)->body()); + Obj = check(lto::InputFile::create( + MemoryBufferRef(MB.getBuffer(), Saver.save(MB.getBufferIdentifier())))); + for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) { + StringRef SymName = Saver.save(ObjSym.getName()); + auto Flags = ObjSym.getFlags(); + Symbol *Sym; + if (Flags & object::BasicSymbolRef::SF_Undefined) { + Sym = Symtab->addUndefined(SymName, this, false); + } else if (Flags & object::BasicSymbolRef::SF_Common) { + Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize()); + } else if ((Flags & object::BasicSymbolRef::SF_Weak) && + (Flags & object::BasicSymbolRef::SF_Indirect)) { + // Weak external. + Sym = Symtab->addUndefined(SymName, this, true); + std::string Fallback = ObjSym.getCOFFWeakExternalFallback(); + SymbolBody *Alias = Symtab->addUndefined(Saver.save(Fallback)); + checkAndSetWeakAlias(Symtab, this, Sym->body(), Alias); } else { - bool Replaceable = - (SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || // common - (Attrs & LTO_SYMBOL_COMDAT) || // comdat - (SymbolDef == LTO_SYMBOL_DEFINITION_WEAK && // weak external - (Attrs & LTO_SYMBOL_ALIAS))); - SymbolBodies.push_back( - Symtab->addBitcode(this, SymName, Replaceable)->body()); + Expected ComdatIndex = ObjSym.getComdatIndex(); + bool IsCOMDAT = ComdatIndex && *ComdatIndex != -1; + Sym = Symtab->addRegular(this, SymName, IsCOMDAT); } + SymbolBodies.push_back(Sym->body()); } - - Directives = M->getLinkerOpts(); + Directives = check(Obj->getLinkerOpts()); } MachineTypes BitcodeFile::getMachineType() { - if (!M) + Expected ET = getBitcodeTargetTriple(MB); + if (!ET) return IMAGE_FILE_MACHINE_UNKNOWN; - switch (Triple(M->getTargetTriple()).getArch()) { + switch (Triple(*ET).getArch()) { case Triple::x86_64: return AMD64; case Triple::x86: diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h index 1b5d42939cca..54f5d6e70e7a 100644 --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -13,8 +13,7 @@ #include "lld/Core/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/LTO/legacy/LTOModule.h" +#include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Support/StringSaver.h" @@ -25,7 +24,6 @@ namespace lld { namespace coff { -using llvm::LTOModule; using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::MachineTypes; using llvm::object::Archive; @@ -191,16 +189,13 @@ public: static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } std::vector &getSymbols() { return SymbolBodies; } MachineTypes getMachineType() override; - std::unique_ptr takeModule() { return std::move(M); } - - static llvm::LLVMContext Context; + std::unique_ptr Obj; private: void parse() override; std::vector SymbolBodies; llvm::BumpPtrAllocator Alloc; - std::unique_ptr M; }; } // namespace coff diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp new file mode 100644 index 000000000000..1353cb28c0c6 --- /dev/null +++ b/lld/COFF/LTO.cpp @@ -0,0 +1,125 @@ +//===- 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 "Error.h" +#include "InputFiles.h" +#include "Symbols.h" +#include "lld/Core/TargetOptionsCommandFlags.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/LTO/Config.h" +#include "llvm/LTO/LTO.h" +#include "llvm/Object/SymbolicFile.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace llvm::object; + +using namespace lld; +using namespace lld::coff; + +static void diagnosticHandler(const DiagnosticInfo &DI) { + SmallString<128> ErrStorage; + raw_svector_ostream OS(ErrStorage); + DiagnosticPrinterRawOStream DP(OS); + DI.print(DP); + warn(ErrStorage); +} + +static void checkError(Error E) { + handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error { + error(EIB.message()); + return Error::success(); + }); +} + +static std::unique_ptr createLTO() { + lto::Config Conf; + Conf.Options = InitTargetOptionsFromCodeGenFlags(); + Conf.RelocModel = Reloc::PIC_; + Conf.DisableVerify = true; + Conf.DiagHandler = diagnosticHandler; + Conf.OptLevel = Config->LTOOptLevel; + lto::ThinBackend Backend; + if (Config->LTOJobs != -1u) + Backend = lto::createInProcessThinBackend(Config->LTOJobs); + return llvm::make_unique(std::move(Conf), Backend, + Config->LTOPartitions); +} + +BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {} + +BitcodeCompiler::~BitcodeCompiler() = default; + +static void undefine(Symbol *S) { + replaceBody(S, S->body()->getName()); +} + +void BitcodeCompiler::add(BitcodeFile &F) { + lto::InputFile &Obj = *F.Obj; + unsigned SymNum = 0; + std::vector SymBodies = F.getSymbols(); + std::vector Resols(SymBodies.size()); + + // Provide a resolution to the LTO API for each symbol. + for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) { + SymbolBody *B = SymBodies[SymNum]; + Symbol *Sym = B->symbol(); + lto::SymbolResolution &R = Resols[SymNum]; + ++SymNum; + + // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile + // reports two symbols for module ASM defined. Without this check, lld + // flags an undefined in IR with a definition in ASM as prevailing. + // Once IRObjectFile is fixed to report only one symbol this hack can + // be removed. + R.Prevailing = + !(ObjSym.getFlags() & object::BasicSymbolRef::SF_Undefined) && + B->getFile() == &F; + R.VisibleToRegularObj = Sym->IsUsedInRegularObj; + if (R.Prevailing) + undefine(Sym); + } + checkError(LTOObj->add(std::move(F.Obj), Resols)); +} + +// Merge all the bitcode files we have seen, codegen the result +// and return the resulting ObjectFile(s). +std::vector BitcodeCompiler::compile() { + std::vector Ret; + unsigned MaxTasks = LTOObj->getMaxTasks(); + Buff.resize(MaxTasks); + + checkError(LTOObj->run([&](size_t Task) { + return llvm::make_unique( + llvm::make_unique(Buff[Task])); + })); + + for (unsigned I = 0; I != MaxTasks; ++I) { + if (Buff[I].empty()) + continue; + Ret.push_back(make(MemoryBufferRef(Buff[I], "lto.tmp"))); + } + return Ret; +} diff --git a/lld/COFF/LTO.h b/lld/COFF/LTO.h new file mode 100644 index 000000000000..556c30159cab --- /dev/null +++ b/lld/COFF/LTO.h @@ -0,0 +1,56 @@ +//===- LTO.h ----------------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides a way to combine bitcode files into one COFF +// file by compiling them using LLVM. +// +// If LTO is in use, your input files are not in regular COFF files +// but instead LLVM bitcode files. In that case, the linker has to +// convert bitcode files into the native format so that we can create +// a COFF file that contains native code. This file provides that +// functionality. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_LTO_H +#define LLD_COFF_LTO_H + +#include "lld/Core/LLVM.h" +#include "llvm/ADT/SmallString.h" +#include +#include + +namespace llvm { +namespace lto { +class LTO; +} +} + +namespace lld { +namespace coff { + +class BitcodeFile; +class InputFile; + +class BitcodeCompiler { +public: + BitcodeCompiler(); + ~BitcodeCompiler(); + + void add(BitcodeFile &F); + std::vector compile(); + +private: + std::unique_ptr LTOObj; + std::vector> Buff; +}; +} +} + +#endif diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp index e49fe392b2de..c674347755c2 100644 --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -11,10 +11,10 @@ #include "Config.h" #include "Driver.h" #include "Error.h" +#include "LTO.h" #include "Memory.h" #include "Symbols.h" #include "llvm/IR/LLVMContext.h" -#include "llvm/LTO/legacy/LTOCodeGenerator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include @@ -24,6 +24,36 @@ using namespace llvm; namespace lld { namespace coff { +enum SymbolPreference { + SP_EXISTING = -1, + SP_CONFLICT = 0, + SP_NEW = 1, +}; + +/// Checks if an existing symbol S should be kept or replaced by a new symbol. +/// Returns SP_EXISTING when S should be kept, SP_NEW when the new symbol +/// should be kept, and SP_CONFLICT if no valid resolution exists. +static SymbolPreference compareDefined(Symbol *S, bool WasInserted, + bool NewIsCOMDAT) { + // If the symbol wasn't previously known, the new symbol wins by default. + if (WasInserted || !isa(S->body())) + return SP_NEW; + + // If the existing symbol is a DefinedRegular, both it and the new symbol + // must be comdats. In that case, we have no reason to prefer one symbol + // over the other, and we keep the existing one. If one of the symbols + // is not a comdat, we report a conflict. + if (auto *R = dyn_cast(S->body())) { + if (NewIsCOMDAT && R->isCOMDAT()) + return SP_EXISTING; + else + return SP_CONFLICT; + } + + // Existing symbol is not a DefinedRegular; new symbol wins. + return SP_NEW; +} + SymbolTable *Symtab; void SymbolTable::addFile(InputFile *File) { @@ -204,59 +234,35 @@ Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) { return S; } -Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym, +Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT, + const coff_symbol_generic *Sym, SectionChunk *C) { - StringRef Name; - F->getCOFFObj()->getSymbolName(Sym, Name); - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(Name); - S->IsUsedInRegularObj = true; - if (WasInserted || isa(S->body()) || isa(S->body())) - replaceBody(S, F, Sym, C); - else if (auto *R = dyn_cast(S->body())) { - if (!C->isCOMDAT() || !R->isCOMDAT()) - reportDuplicate(S, F); - } else if (auto *B = dyn_cast(S->body())) { - if (B->IsReplaceable) - replaceBody(S, F, Sym, C); - else if (!C->isCOMDAT()) - reportDuplicate(S, F); - } else - replaceBody(S, F, Sym, C); - return S; -} - -Symbol *SymbolTable::addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); - if (WasInserted || isa(S->body()) || isa(S->body())) { - replaceBody(S, F, N, IsReplaceable); - return S; + if (!isa(F)) + S->IsUsedInRegularObj = true; + SymbolPreference SP = compareDefined(S, WasInserted, IsCOMDAT); + if (SP == SP_CONFLICT) { + reportDuplicate(S, F); + } else if (SP == SP_NEW) { + replaceBody(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C); } - if (isa(S->body())) - return S; - if (IsReplaceable) - if (isa(S->body()) || isa(S->body())) - return S; - reportDuplicate(S, F); return S; } -Symbol *SymbolTable::addCommon(ObjectFile *F, COFFSymbolRef Sym, - CommonChunk *C) { - StringRef Name; - F->getCOFFObj()->getSymbolName(Sym, Name); +Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size, + const coff_symbol_generic *Sym, CommonChunk *C) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(Name); - S->IsUsedInRegularObj = true; + std::tie(S, WasInserted) = insert(N); + if (!isa(F)) + S->IsUsedInRegularObj = true; if (WasInserted || !isa(S->body())) - replaceBody(S, F, Sym, C); + replaceBody(S, F, N, Size, Sym, C); else if (auto *DC = dyn_cast(S->body())) - if (Sym.getValue() > DC->getSize()) - replaceBody(S, F, Sym, C); + if (Size > DC->getSize()) + replaceBody(S, F, N, Size, Sym, C); return S; } @@ -349,61 +355,15 @@ void SymbolTable::addCombinedLTOObjects() { if (BitcodeFiles.empty()) return; - // Create an object file and add it to the symbol table by replacing any - // DefinedBitcode symbols with the definitions in the object file. - LTOCodeGenerator CG(BitcodeFile::Context); - CG.setOptLevel(Config->LTOOptLevel); - for (ObjectFile *Obj : createLTOObjects(&CG)) + LTO.reset(new BitcodeCompiler); + for (BitcodeFile *F : BitcodeFiles) + LTO->add(*F); + + for (auto *File : LTO->compile()) { + auto *Obj = cast(File); + ObjectFiles.push_back(Obj); Obj->parse(); -} - -// Combine and compile bitcode files and then return the result -// as a vector of regular COFF object files. -std::vector SymbolTable::createLTOObjects(LTOCodeGenerator *CG) { - // All symbols referenced by non-bitcode objects, including GC roots, must be - // preserved. We must also replace bitcode symbols with undefined symbols so - // that they may be replaced with real definitions without conflicting. - for (BitcodeFile *File : BitcodeFiles) - for (SymbolBody *Body : File->getSymbols()) { - if (!isa(Body)) - continue; - if (Body->symbol()->IsUsedInRegularObj) - CG->addMustPreserveSymbol(Body->getName()); - replaceBody(Body->symbol(), Body->getName()); - } - - CG->setModule(BitcodeFiles[0]->takeModule()); - for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I) - CG->addModule(BitcodeFiles[I]->takeModule().get()); - - bool DisableVerify = true; -#ifdef NDEBUG - DisableVerify = false; -#endif - if (!CG->optimize(DisableVerify, false, false, false)) - fatal(""); // optimize() should have emitted any error message. - - Objs.resize(Config->LTOJobs); - // Use std::list to avoid invalidation of pointers in OSPtrs. - std::list OSs; - std::vector OSPtrs; - for (SmallString<0> &Obj : Objs) { - OSs.emplace_back(Obj); - OSPtrs.push_back(&OSs.back()); } - - if (!CG->compileOptimized(OSPtrs)) - fatal(""); // compileOptimized() should have emitted any error message. - - std::vector ObjFiles; - for (SmallString<0> &Obj : Objs) { - auto *ObjFile = make(MemoryBufferRef(Obj, "")); - ObjectFiles.push_back(ObjFile); - ObjFiles.push_back(ObjFile); - } - - return ObjFiles; } - } // namespace coff } // namespace lld diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h index 5073cccee5f8..2d747e6574bf 100644 --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -11,6 +11,7 @@ #define LLD_COFF_SYMBOL_TABLE_H #include "InputFiles.h" +#include "LTO.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" @@ -90,9 +91,12 @@ public: Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias); void addLazy(ArchiveFile *F, const Archive::Symbol Sym); Symbol *addAbsolute(StringRef N, COFFSymbolRef S); - Symbol *addRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C); - Symbol *addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable); - Symbol *addCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C); + Symbol *addRegular(InputFile *F, StringRef N, bool IsCOMDAT, + const llvm::object::coff_symbol_generic *S = nullptr, + SectionChunk *C = nullptr); + Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size, + const llvm::object::coff_symbol_generic *S = nullptr, + CommonChunk *C = nullptr); Symbol *addImportData(StringRef N, ImportFile *F); Symbol *addImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine); @@ -110,12 +114,11 @@ private: StringRef findByPrefix(StringRef Prefix); void addCombinedLTOObject(ObjectFile *Obj); - std::vector createLTOObjects(llvm::LTOCodeGenerator *CG); llvm::DenseMap Symtab; std::vector BitcodeFiles; - std::vector> Objs; + std::unique_ptr LTO; }; extern SymbolTable *Symtab; diff --git a/lld/COFF/Symbols.cpp b/lld/COFF/Symbols.cpp index c44537d37135..993e920ce7f7 100644 --- a/lld/COFF/Symbols.cpp +++ b/lld/COFF/Symbols.cpp @@ -30,7 +30,7 @@ namespace lld { namespace coff { StringRef SymbolBody::getName() { - // DefinedCOFF names are read lazily for a performance reason. + // COFF symbol names are read lazily for a performance reason. // Non-external symbol names are never used by the linker except for logging // or debugging. Their internal references are resolved not by name but by // symbol index. And because they are not external, no one can refer them by @@ -39,7 +39,7 @@ StringRef SymbolBody::getName() { // is a waste of time. if (Name.empty()) { auto *D = cast(this); - D->File->getCOFFObj()->getSymbolName(D->Sym, Name); + cast(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name); } return Name; } @@ -47,15 +47,14 @@ StringRef SymbolBody::getName() { InputFile *SymbolBody::getFile() { if (auto *Sym = dyn_cast(this)) return Sym->File; - if (auto *Sym = dyn_cast(this)) - return Sym->File; if (auto *Sym = dyn_cast(this)) return Sym->File; return nullptr; } COFFSymbolRef DefinedCOFF::getCOFFSymbol() { - size_t SymSize = File->getCOFFObj()->getSymbolTableEntrySize(); + size_t SymSize = + cast(File)->getCOFFObj()->getSymbolTableEntrySize(); if (SymSize == sizeof(coff_symbol16)) return COFFSymbolRef(reinterpret_cast(Sym)); assert(SymSize == sizeof(coff_symbol32)); diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h index 1ca7366364d7..cb9ab39c9cee 100644 --- a/lld/COFF/Symbols.h +++ b/lld/COFF/Symbols.h @@ -30,7 +30,6 @@ using llvm::object::coff_import_header; using llvm::object::coff_symbol_generic; class ArchiveFile; -class BitcodeFile; class InputFile; class ObjectFile; struct Symbol; @@ -52,13 +51,12 @@ public: DefinedImportDataKind, DefinedAbsoluteKind, DefinedRelativeKind, - DefinedBitcodeKind, UndefinedKind, LazyKind, LastDefinedCOFFKind = DefinedCommonKind, - LastDefinedKind = DefinedBitcodeKind, + LastDefinedKind = DefinedRelativeKind, }; Kind kind() const { return static_cast(SymbolKind); } @@ -81,7 +79,7 @@ protected: friend SymbolTable; explicit SymbolBody(Kind K, StringRef N = "") : SymbolKind(K), IsExternal(true), IsCOMDAT(false), - IsReplaceable(false), WrittenToSymtab(false), Name(N) {} + WrittenToSymtab(false), Name(N) {} const unsigned SymbolKind : 8; unsigned IsExternal : 1; @@ -89,11 +87,9 @@ protected: // This bit is used by the \c DefinedRegular subclass. unsigned IsCOMDAT : 1; - // This bit is used by the \c DefinedBitcode subclass. - unsigned IsReplaceable : 1; - public: - // This bit is used by Writer::createSymbolAndStringTable(). + // This bit is used by Writer::createSymbolAndStringTable() to prevent + // symbols from being written to the symbol table more than once. unsigned WrittenToSymtab : 1; protected: @@ -104,7 +100,7 @@ protected: // etc. class Defined : public SymbolBody { public: - Defined(Kind K, StringRef N = "") : SymbolBody(K, N) {} + Defined(Kind K, StringRef N) : SymbolBody(K, N) {} static bool classof(const SymbolBody *S) { return S->kind() <= LastDefinedKind; @@ -127,22 +123,25 @@ public: bool isExecutable(); }; -// Symbols defined via a COFF object file. +// Symbols defined via a COFF object file or bitcode file. For COFF files, this +// stores a coff_symbol_generic*, and names of internal symbols are lazily +// loaded through that. For bitcode files, Sym is nullptr and the name is stored +// as a StringRef. class DefinedCOFF : public Defined { friend SymbolBody; public: - DefinedCOFF(Kind K, ObjectFile *F, COFFSymbolRef S) - : Defined(K), File(F), Sym(S.getGeneric()) {} + DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S) + : Defined(K, N), File(F), Sym(S) {} static bool classof(const SymbolBody *S) { return S->kind() <= LastDefinedCOFFKind; } - ObjectFile *getFile() { return File; } + InputFile *getFile() { return File; } COFFSymbolRef getCOFFSymbol(); - ObjectFile *File; + InputFile *File; protected: const coff_symbol_generic *Sym; @@ -151,10 +150,13 @@ protected: // Regular defined symbols read from object file symbol tables. class DefinedRegular : public DefinedCOFF { public: - DefinedRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C) - : DefinedCOFF(DefinedRegularKind, F, S), Data(&C->Repl) { - IsExternal = S.isExternal(); - IsCOMDAT = C->isCOMDAT(); + DefinedRegular(InputFile *F, StringRef N, bool IsCOMDAT, + bool IsExternal = false, + const coff_symbol_generic *S = nullptr, + SectionChunk *C = nullptr) + : DefinedCOFF(DefinedRegularKind, F, N, S), Data(&C->Repl) { + this->IsExternal = IsExternal; + this->IsCOMDAT = IsCOMDAT; } static bool classof(const SymbolBody *S) { @@ -172,9 +174,11 @@ private: class DefinedCommon : public DefinedCOFF { public: - DefinedCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C) - : DefinedCOFF(DefinedCommonKind, F, S), Data(C) { - IsExternal = S.isExternal(); + DefinedCommon(InputFile *F, StringRef N, uint64_t Size, + const coff_symbol_generic *S = nullptr, + CommonChunk *C = nullptr) + : DefinedCOFF(DefinedCommonKind, F, N, S), Data(C), Size(Size) { + this->IsExternal = true; } static bool classof(const SymbolBody *S) { @@ -185,8 +189,9 @@ public: private: friend SymbolTable; - uint64_t getSize() { return Sym->Value; } + uint64_t getSize() const { return Size; } CommonChunk *Data; + uint64_t Size; }; // Absolute symbols. @@ -340,26 +345,6 @@ private: LocalImportChunk *Data; }; -class DefinedBitcode : public Defined { - friend SymbolBody; -public: - DefinedBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) - : Defined(DefinedBitcodeKind, N), File(F) { - // IsReplaceable tracks whether the bitcode symbol may be replaced with some - // other (defined, common or bitcode) symbol. This is the case for common, - // comdat and weak external symbols. We try to replace bitcode symbols with - // "real" symbols (see SymbolTable::add{Regular,Bitcode}), and resolve the - // result against the real symbol from the combined LTO object. - this->IsReplaceable = IsReplaceable; - } - - static bool classof(const SymbolBody *S) { - return S->kind() == DefinedBitcodeKind; - } - - BitcodeFile *File; -}; - inline uint64_t Defined::getRVA() { switch (kind()) { case DefinedAbsoluteKind: @@ -376,8 +361,6 @@ inline uint64_t Defined::getRVA() { return cast(this)->getRVA(); case DefinedRegularKind: return cast(this)->getRVA(); - case DefinedBitcodeKind: - llvm_unreachable("There is no address for a bitcode symbol."); case LazyKind: case UndefinedKind: llvm_unreachable("Cannot get the address for an undefined symbol."); @@ -401,10 +384,9 @@ struct Symbol { // This field is used to store the Symbol's SymbolBody. This instantiation of // AlignedCharArrayUnion gives us a struct with a char array field that is // large and aligned enough to store any derived class of SymbolBody. - llvm::AlignedCharArrayUnion + llvm::AlignedCharArrayUnion< + DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedRelative, Lazy, + Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport> Body; SymbolBody *body() { diff --git a/lld/test/COFF/lto-comdat.ll b/lld/test/COFF/lto-comdat.ll index aaa7a16c4afb..b255f69d1ab5 100644 --- a/lld/test/COFF/lto-comdat.ll +++ b/lld/test/COFF/lto-comdat.ll @@ -10,6 +10,8 @@ ; RUN: rm -f %T/comdat.lib ; RUN: llvm-ar cru %T/comdat.lib %T/comdat1.obj %T/comdat2.obj +; Check that, when we use an LTO main with LTO objects, we optimize away all +; of f1, f2, and comdat. ; RUN: lld-link /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat1.lto.obj %T/comdat2.lto.obj ; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-11 %s ; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-11 %s @@ -17,6 +19,9 @@ ; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-11 %s ; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-11 %s +; Check that, when we use a non-LTO main with LTO objects, we pick the comdat +; implementation in LTO, elide calls to it from inside LTO, and retain the +; call to comdat from main. ; RUN: lld-link /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.obj %T/comdat1.lto.obj %T/comdat2.lto.obj ; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-01 %s ; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-01 %s @@ -24,6 +29,9 @@ ; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-01 %s ; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-01 %s +; Check that, when we use an LTO main with non-LTO objects, we pick the comdat +; implementation in LTO, elide the call to it from inside LTO, and keep the +; calls to comdat from the non-LTO objects. ; RUN: lld-link /out:%T/comdat-main.exe /entry:main /subsystem:console %T/comdat-main.lto.obj %T/comdat1.obj %T/comdat2.obj ; RUN: llvm-readobj -file-headers %T/comdat-main.exe | FileCheck -check-prefix=HEADERS-10 %s ; RUN: llvm-objdump -d %T/comdat-main.exe | FileCheck -check-prefix=TEXT-10 %s @@ -46,70 +54,39 @@ ; TEXT-01-NEXT: callq 13 ; TEXT-01-NEXT: xorl %eax, %eax ; TEXT-01-NEXT: addq $40, %rsp -; TEXT-01-NEXT: retq -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: retq -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: int3 -; TEXT-01-NEXT: retq -; TEXT-01-NEXT: nopw %cs:(%rax,%rax) -; TEXT-01-NEXT: retq +; TEXT-01: retq +; TEXT-01-NOT: callq +; TEXT-01: retq +; TEXT-01-NOT: callq +; TEXT-01: retq +; TEXT-01-NOT: callq +; TEXT-01: retq +; TEXT-01-NOT: {{.}} -; HEADERS-10: AddressOfEntryPoint: 0x2030 +; HEADERS-10: AddressOfEntryPoint: 0x2020 ; TEXT-10: Disassembly of section .text: ; TEXT-10-NEXT: .text: ; TEXT-10-NEXT: subq $40, %rsp -; TEXT-10-NEXT: callq 7 -; TEXT-10-NEXT: nop -; TEXT-10-NEXT: addq $40, %rsp -; TEXT-10-NEXT: retq -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: retq -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: int3 -; TEXT-10-NEXT: subq $40, %rsp -; TEXT-10-NEXT: callq -25 +; TEXT-10-NEXT: callq 55 ; TEXT-10-NEXT: nop ; TEXT-10-NEXT: addq $40, %rsp ; TEXT-10-NEXT: retq ; TEXT-10-NEXT: int3 ; TEXT-10-NEXT: subq $40, %rsp -; TEXT-10-NEXT: callq -57 +; TEXT-10-NEXT: callq 39 +; TEXT-10-NEXT: nop +; TEXT-10-NEXT: addq $40, %rsp +; TEXT-10-NEXT: retq +; TEXT-10-NEXT: int3 +; TEXT-10-NEXT: subq $40, %rsp +; TEXT-10-NEXT: callq -41 ; TEXT-10-NEXT: callq -30 ; TEXT-10-NEXT: xorl %eax, %eax ; TEXT-10-NEXT: addq $40, %rsp ; TEXT-10-NEXT: retq +; TEXT-10-NOT: callq +; TEXT-10: retq +; TEXT-10-NOT: {{.}} target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc" diff --git a/lld/test/COFF/lto-parallel.ll b/lld/test/COFF/lto-parallel.ll index c96d666d58b0..449e3a01a853 100644 --- a/lld/test/COFF/lto-parallel.ll +++ b/lld/test/COFF/lto-parallel.ll @@ -1,19 +1,19 @@ ; RUN: llvm-as -o %t.obj %s -; RUN: lld-link /out:%t.exe /entry:foo /include:bar /opt:lldltojobs=2 /subsystem:console /lldmap:%t.map %t.obj +; RUN: lld-link /out:%t.exe /entry:foo /include:bar /opt:lldltopartitions=2 /subsystem:console /lldmap:%t.map %t.obj ; RUN: FileCheck %s < %t.map target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-windows-msvc" -; CHECK: +; CHECK: lto.tmp ; CHECK-NEXT: foo define void @foo() { call void @bar() ret void } -; CHECK: -; CHECK-NEXT: bar +; CHECK: lto.tmp +; CHECK: bar define void @bar() { call void @foo() ret void diff --git a/lld/test/COFF/weak-external.test b/lld/test/COFF/weak-external.test index 69ae1f634546..7bdadd9b1c94 100644 --- a/lld/test/COFF/weak-external.test +++ b/lld/test/COFF/weak-external.test @@ -4,7 +4,7 @@ # RUN: lld-link /out:%t2.exe /entry:g /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj # RUN: FileCheck %s < %t2.map -# CHECK: +# CHECK: lto.tmp # CHECK-NEXT: 0 g --- !COFF diff --git a/lld/test/COFF/weak-external3.test b/lld/test/COFF/weak-external3.test index cf014f6f70c7..a06ce48a61a6 100644 --- a/lld/test/COFF/weak-external3.test +++ b/lld/test/COFF/weak-external3.test @@ -5,7 +5,7 @@ # RUN: lld-link /out:%t2.exe /entry:f /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj # RUN: FileCheck --check-prefix=CHECK2 %s < %t2.map -# CHECK1: +# CHECK1: lto.tmp # CHECK1-NEXT: 0 g # CHECK2: weak-external3.test.tmp.obj