Index: COFF/CMakeLists.txt =================================================================== --- COFF/CMakeLists.txt +++ COFF/CMakeLists.txt @@ -15,6 +15,7 @@ ICF.cpp InputFiles.cpp Librarian.cpp + LTO.cpp MapFile.cpp MarkLive.cpp ModuleDef.cpp Index: COFF/Config.h =================================================================== --- COFF/Config.h +++ COFF/Config.h @@ -114,6 +114,7 @@ // Used for /opt:lldltojobs=N unsigned LTOJobs = 1; + unsigned LTOPartitions = 1; // Used for /merge:from=to (e.g. /merge:.rdata=.text) std::map Merge; Index: COFF/Driver.h =================================================================== --- COFF/Driver.h +++ COFF/Driver.h @@ -116,6 +116,7 @@ void enqueueTask(std::function Task); bool run(); + void runLoop(); // Driver is the owner of all opened files. // InputFiles have MemoryBufferRefs to them. Index: COFF/Driver.cpp =================================================================== --- COFF/Driver.cpp +++ COFF/Driver.cpp @@ -436,6 +436,44 @@ return DidWork; } +void LinkerDriver::runLoop() { + // This code may add new undefined symbols to the link, which may enqueue more + // symbol resolution tasks, so we need to continue executing tasks until we + // converge. + do { + // Windows specific -- if entry point is not found, + // search for its mangled names. + if (Config->Entry) + Symtab.mangleMaybe(Config->Entry); + + // Windows specific -- Make sure we resolve all dllexported symbols. + for (Export &E : Config->Exports) { + if (!E.ForwardTo.empty()) + continue; + E.Sym = addUndefined(E.Name); + if (!E.Directives) + Symtab.mangleMaybe(E.Sym); + } + + // Add weak aliases. Weak aliases is a mechanism to give remaining + // undefined symbols final chance to be resolved successfully. + for (auto Pair : Config->AlternateNames) { + StringRef From = Pair.first; + StringRef To = Pair.second; + Symbol *Sym = Symtab.find(From); + if (!Sym) + continue; + if (auto *U = dyn_cast(Sym->body())) + if (!U->WeakAlias) + U->WeakAlias = Symtab.addUndefined(To); + } + + // Windows specific -- if __load_config_used can be resolved, resolve it. + if (Symtab.findUnderscore("_load_config_used")) + addUndefined(mangle("_load_config_used")); + } while (run()); +} + void LinkerDriver::link(ArrayRef ArgsArr) { // If the first command line argument is "/lib", link.exe acts like lib.exe. // We call our own implementation of lib.exe that understands bitcode files. @@ -772,46 +810,12 @@ Symtab.addAbsolute(mangle("__guard_fids_count"), 0); Symtab.addAbsolute(mangle("__guard_flags"), 0x100); - // This code may add new undefined symbols to the link, which may enqueue more - // symbol resolution tasks, so we need to continue executing tasks until we - // converge. - do { - // Windows specific -- if entry point is not found, - // search for its mangled names. - if (Config->Entry) - Symtab.mangleMaybe(Config->Entry); - - // Windows specific -- Make sure we resolve all dllexported symbols. - for (Export &E : Config->Exports) { - if (!E.ForwardTo.empty()) - continue; - E.Sym = addUndefined(E.Name); - if (!E.Directives) - Symtab.mangleMaybe(E.Sym); - } - - // Add weak aliases. Weak aliases is a mechanism to give remaining - // undefined symbols final chance to be resolved successfully. - for (auto Pair : Config->AlternateNames) { - StringRef From = Pair.first; - StringRef To = Pair.second; - Symbol *Sym = Symtab.find(From); - if (!Sym) - continue; - if (auto *U = dyn_cast(Sym->body())) - if (!U->WeakAlias) - U->WeakAlias = Symtab.addUndefined(To); - } - - // Windows specific -- if __load_config_used can be resolved, resolve it. - if (Symtab.findUnderscore("_load_config_used")) - addUndefined(mangle("_load_config_used")); - } while (run()); + runLoop(); // Do LTO by compiling bitcode input files to a set of native COFF files then // link those files. Symtab.addCombinedLTOObjects(); - run(); + runLoop(); // Make sure we have resolved all symbols. Symtab.reportRemainingUndefines(); Index: COFF/InputFiles.h =================================================================== --- COFF/InputFiles.h +++ COFF/InputFiles.h @@ -14,7 +14,7 @@ #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 +25,6 @@ namespace lld { namespace coff { -using llvm::LTOModule; using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::MachineTypes; using llvm::object::Archive; @@ -66,6 +65,17 @@ // Returns .drectve section contents if exist. StringRef getDirectives() { return StringRef(Directives).trim(); } + // Filename of .a which contained this file. If this file was + // not in an archive file, it is the empty string. We use this + // string for creating error messages. + StringRef ArchiveName; + + // If this file is in an archive, the member contains the offset of + // the file in the archive. Otherwise, it's just zero. We store this + // field so that we can pass it to lib/LTO in order to disambiguate + // between objects. + uint64_t OffsetInArchive; + protected: InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {} @@ -191,7 +201,7 @@ 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); } + std::unique_ptr Obj; static llvm::LLVMContext Context; @@ -200,8 +210,10 @@ std::vector SymbolBodies; llvm::BumpPtrAllocator Alloc; - std::unique_ptr M; }; + +InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "", + uint64_t OffsetInArchive = 0); } // namespace coff std::string toString(coff::InputFile *File); Index: COFF/InputFiles.cpp =================================================================== --- COFF/InputFiles.cpp +++ COFF/InputFiles.cpp @@ -19,8 +19,8 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" +#include "llvm/Bitcode/BitcodeReader.h" #include "llvm/IR/LLVMContext.h" -#include "llvm/LTO/legacy/LTOModule.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Support/COFF.h" @@ -249,8 +249,10 @@ DefinedRegular *B; if (Sym.isExternal()) B = cast(Symtab->addRegular(this, Sym, SC)->body()); - else - B = new (Alloc) DefinedRegular(this, Sym, SC); + else { + getCOFFObj()->getSymbolName(Sym.getGeneric(), Name); + B = new (Alloc) DefinedRegular(this, Name, Sym.isWeakExternal(), Sym, SC); + } if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP) SC->setSymbol(B); @@ -330,38 +332,40 @@ 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(ArchiveName + MB.getBufferIdentifier() + + utostr(OffsetInArchive))))); + + for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) { + StringRef SymName = Saver.save(ObjSym.getName()); + auto Flags = ObjSym.getFlags(); + Expected ComdatIndex = ObjSym.getComdatIndex(); + if (Flags & object::BasicSymbolRef::SF_Undefined) { + SymbolBodies.push_back( + Symtab->addUndefined(SymName, this, false)->body()); + } else if (Flags & object::BasicSymbolRef::SF_Common) { + SymbolBodies.push_back(Symtab->addCommon(this, SymName)->body()); } 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))); + bool IsCOMDAT = ComdatIndex && *ComdatIndex != -1; + // Comdat symbols from bitcode files always have the weak flag set. + // By contrast, comdat symbols from COFF files are strong. This would + // make bitcode comdat symbols always lose to COFF comdat symbols. + // To avoid that, we consider bitcode comdat symbols strong, just like + // COFF comdat symbols. + bool IsWeak = (Flags & object::BasicSymbolRef::SF_Weak) && !IsCOMDAT; SymbolBodies.push_back( - Symtab->addBitcode(this, SymName, Replaceable)->body()); + Symtab->addRegular(this, SymName, IsCOMDAT, IsWeak)->body()); } } - Directives = M->getLinkerOpts(); + Directives = 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: @@ -372,6 +376,14 @@ return IMAGE_FILE_MACHINE_UNKNOWN; } } + +InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName, + uint64_t OffsetInArchive) { + auto *F = make(MB); + F->ArchiveName = ArchiveName; + F->OffsetInArchive = OffsetInArchive; + return F; +} } // namespace coff } // namespace lld Index: COFF/LTO.h =================================================================== --- /dev/null +++ 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 ELF +// file by compiling them using LLVM. +// +// If LTO is in use, your input files are not in regular ELF 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 +// an ELF 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 Index: COFF/LTO.cpp =================================================================== --- COFF/LTO.cpp +++ COFF/LTO.cpp @@ -12,17 +12,16 @@ #include "Error.h" #include "InputFiles.h" #include "Symbols.h" +#include "lld/Config/TargetOptionsCommandFlags.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" -#include "llvm/CodeGen/CommandFlags.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/ELF.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" @@ -36,19 +35,9 @@ 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("cannot create " + Path + ": " + EC.message()); - OS << Buffer; -} +using namespace lld::coff; static void diagnosticHandler(const DiagnosticInfo &DI) { SmallString<128> ErrStorage; @@ -67,27 +56,14 @@ static std::unique_ptr createLTO() { lto::Config Conf; - - // LLD supports the new relocations. Conf.Options = InitTargetOptionsFromCodeGenFlags(); - Conf.Options.RelaxELFRelocations = true; - - Conf.RelocModel = Config->Pic ? Reloc::PIC_ : Reloc::Static; - Conf.DisableVerify = Config->DisableVerify; + Conf.RelocModel = Reloc::PIC_; + Conf.DisableVerify = true; Conf.DiagHandler = diagnosticHandler; - Conf.OptLevel = Config->LTOO; - - // Set up a custom pipeline if we've been asked to. - Conf.OptPipeline = Config->LTONewPmPasses; - Conf.AAPipeline = Config->LTOAAPipeline; - - if (Config->SaveTemps) - checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".", - /*UseInputModulePath*/ true)); - + Conf.OptLevel = Config->LTOOptLevel; lto::ThinBackend Backend; - if (Config->ThinLTOJobs != -1u) - Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs); + if (Config->LTOJobs != -1u) + Backend = lto::createInProcessThinBackend(Config->LTOJobs); return llvm::make_unique(std::move(Conf), Backend, Config->LTOPartitions); } @@ -96,23 +72,22 @@ BitcodeCompiler::~BitcodeCompiler() = default; -template static void undefine(Symbol *S) { - replaceBody>(S, S->body()->getName(), /*IsLocal=*/false, - STV_DEFAULT, S->body()->Type, nullptr); +static void undefine(Symbol *S) { + replaceBody(S, S->body()->getName()); } -template void BitcodeCompiler::add(BitcodeFile &F) { +void BitcodeCompiler::add(BitcodeFile &F) { lto::InputFile &Obj = *F.Obj; unsigned SymNum = 0; - std::vector Syms = F.getSymbols(); - std::vector Resols(Syms.size()); + 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()) { - Symbol *Sym = Syms[SymNum]; + SymbolBody *B = SymBodies[SymNum]; + Symbol *Sym = B->symbol(); lto::SymbolResolution &R = Resols[SymNum]; ++SymNum; - SymbolBody *B = Sym->body(); // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile // reports two symbols for module ASM defined. Without this check, lld @@ -121,12 +96,12 @@ // be removed. R.Prevailing = !(ObjSym.getFlags() & object::BasicSymbolRef::SF_Undefined) && - B->File == &F; + B->getFile() == &F; - R.VisibleToRegularObj = - Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym()); + R.VisibleToRegularObj = Sym->IsUsedInRegularObj || + (R.Prevailing && ObjSym.hasDLLExportStorageClass()); if (R.Prevailing) - undefine(Sym); + undefine(Sym); } checkError(LTOObj->add(std::move(F.Obj), Resols)); } @@ -146,19 +121,8 @@ for (unsigned I = 0; I != MaxTasks; ++I) { if (Buff[I].empty()) continue; - if (Config->SaveTemps) { - if (I == 0) - saveBuffer(Buff[I], Config->OutputFile + ".lto.o"); - else - saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o"); - } InputFile *Obj = createObjectFile(MemoryBufferRef(Buff[I], "lto.tmp")); Ret.push_back(Obj); } return Ret; } - -template void BitcodeCompiler::template add(BitcodeFile &); -template void BitcodeCompiler::template add(BitcodeFile &); -template void BitcodeCompiler::template add(BitcodeFile &); -template void BitcodeCompiler::template add(BitcodeFile &); Index: COFF/MapFile.cpp =================================================================== --- COFF/MapFile.cpp +++ COFF/MapFile.cpp @@ -78,8 +78,7 @@ ArrayRef Syms = File->getSymbols(); for (SymbolBody *Sym : Syms) { auto *DR = dyn_cast(Sym); - if (!DR || DR->getChunk() != SC || - DR->getCOFFSymbol().isSectionDefinition()) + if (!DR || DR->getChunk() != SC || DR->isSectionDefinition()) continue; writeSymbolLine(OS, DR->getRVA(), SC->getSize(), toString(*Sym)); OS << '\n'; Index: COFF/SymbolTable.h =================================================================== --- COFF/SymbolTable.h +++ 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,8 +91,9 @@ Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias); void addLazy(ArchiveFile *F, const Archive::Symbol Sym); Symbol *addAbsolute(StringRef N, COFFSymbolRef S); + Symbol *addRegular(BitcodeFile *F, StringRef N, bool IsCOMDAT, bool IsWeak); Symbol *addRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C); - Symbol *addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable); + Symbol *addCommon(BitcodeFile *F, StringRef N); Symbol *addCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C); Symbol *addImportData(StringRef N, ImportFile *F); Symbol *addImportThunk(StringRef Name, DefinedImportData *S, @@ -110,12 +112,11 @@ 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; Index: COFF/SymbolTable.cpp =================================================================== --- COFF/SymbolTable.cpp +++ 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,51 @@ 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, bool NewIsWeak, + bool NewIsBitcode = false) { + // If the symbol wasn't previously known, the new symbol wins by default. + if (WasInserted || !isa(S->body())) + return SP_NEW; + auto *R = dyn_cast(S->body()); + + // Strong symbols win over weak symbols. + bool ExistingIsWeak = R && R->isWeak(); + if (NewIsWeak && !ExistingIsWeak) + return SP_EXISTING; + if (!NewIsWeak && ExistingIsWeak) + return SP_NEW; + + // Non-comdat symbols win over comdat symbols. + bool ExistingIsCOMDAT = R && R->isCOMDAT(); + if (NewIsCOMDAT && !ExistingIsCOMDAT) + return SP_EXISTING; + if (!NewIsCOMDAT && ExistingIsCOMDAT) + return SP_NEW; + + // If both symbols are strong and non-comdat, that's an error. + if (!NewIsWeak && !NewIsCOMDAT) + return SP_CONFLICT; + + // Picking either symbol would be correct. In this case, we prefer a symbol + // that + // comes from a bitcode file, because we can apply more optimization that way. + if (NewIsBitcode && !isa(S->body()->getFile())) + return SP_NEW; + + // If we have no reason to prefer either symbol, we keep the existing one. + return SP_EXISTING; +} + SymbolTable *Symtab; void SymbolTable::addFile(InputFile *File) { @@ -175,7 +220,7 @@ S->IsUsedInRegularObj = true; if (WasInserted || isa(S->body()) || isa(S->body())) replaceBody(S, N, Sym); - else if (!isa(S->body())) + else if (!isa(S->body())) reportDuplicate(S, nullptr); return S; } @@ -187,7 +232,7 @@ S->IsUsedInRegularObj = true; if (WasInserted || isa(S->body()) || isa(S->body())) replaceBody(S, N, VA); - else if (!isa(S->body())) + else if (!isa(S->body())) reportDuplicate(S, nullptr); return S; } @@ -199,11 +244,26 @@ S->IsUsedInRegularObj = true; if (WasInserted || isa(S->body()) || isa(S->body())) replaceBody(S, N, VA); - else if (!isa(S->body())) + else if (!isa(S->body())) reportDuplicate(S, nullptr); return S; } +Symbol *SymbolTable::addRegular(BitcodeFile *F, StringRef N, bool IsCOMDAT, + bool IsWeak) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + SymbolPreference SP = + compareDefined(S, WasInserted, IsCOMDAT, IsWeak, /*NewIsBitcode*/ true); + if (SP == SP_CONFLICT) { + reportDuplicate(S, F); + } else if (SP == SP_NEW) { + replaceBody(S, F, N, IsCOMDAT, IsWeak); + } + return S; +} + Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym, SectionChunk *C) { StringRef Name; @@ -212,35 +272,22 @@ 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); + bool IsWeak = Sym.isWeakExternal(); + SymbolPreference SP = compareDefined(S, WasInserted, C->isCOMDAT(), IsWeak); + if (SP == SP_CONFLICT) { + reportDuplicate(S, F); + } else if (SP == SP_NEW) { + replaceBody(S, F, Name, IsWeak, Sym, C); + } return S; } -Symbol *SymbolTable::addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) { +Symbol *SymbolTable::addCommon(BitcodeFile *F, StringRef N) { 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(S->body())) - return S; - if (IsReplaceable) - if (isa(S->body()) || isa(S->body())) - return S; - reportDuplicate(S, F); + if (WasInserted || !isa(S->body())) + replaceBody(S, F, N); return S; } @@ -252,11 +299,11 @@ bool WasInserted; std::tie(S, WasInserted) = insert(Name); S->IsUsedInRegularObj = true; - if (WasInserted || !isa(S->body())) - replaceBody(S, F, Sym, C); + if (WasInserted || !isa(S->body())) + replaceBody(S, F, Name, Sym, C); else if (auto *DC = dyn_cast(S->body())) if (Sym.getValue() > DC->getSize()) - replaceBody(S, F, Sym, C); + replaceBody(S, F, Name, Sym, C); return S; } @@ -267,7 +314,7 @@ S->IsUsedInRegularObj = true; if (WasInserted || isa(S->body()) || isa(S->body())) replaceBody(S, N, F); - else if (!isa(S->body())) + else if (!isa(S->body())) reportDuplicate(S, nullptr); return S; } @@ -280,7 +327,7 @@ S->IsUsedInRegularObj = true; if (WasInserted || isa(S->body()) || isa(S->body())) replaceBody(S, Name, ID, Machine); - else if (!isa(S->body())) + else if (!isa(S->body())) reportDuplicate(S, nullptr); return S; } @@ -349,61 +396,12 @@ 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)) - 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. + LTO.reset(new BitcodeCompiler); + for (BitcodeFile *F : BitcodeFiles) + LTO->add(*F); - std::vector ObjFiles; - for (SmallString<0> &Obj : Objs) { - auto *ObjFile = make(MemoryBufferRef(Obj, "")); - ObjectFiles.push_back(ObjFile); - ObjFiles.push_back(ObjFile); - } - - return ObjFiles; + for (InputFile *File : LTO->compile()) + addFile(File); } - } // namespace coff } // namespace lld Index: COFF/Symbols.h =================================================================== --- COFF/Symbols.h +++ COFF/Symbols.h @@ -28,9 +28,9 @@ using llvm::object::COFFSymbolRef; using llvm::object::coff_import_header; using llvm::object::coff_symbol_generic; +using llvm::object::BasicSymbolRef; class ArchiveFile; -class BitcodeFile; class InputFile; class ObjectFile; struct Symbol; @@ -52,13 +52,12 @@ DefinedImportDataKind, DefinedAbsoluteKind, DefinedRelativeKind, - DefinedBitcodeKind, UndefinedKind, LazyKind, - LastDefinedCOFFKind = DefinedCommonKind, - LastDefinedKind = DefinedBitcodeKind, + LastDefinedByFileKind = DefinedCommonKind, + LastDefinedKind = DefinedRelativeKind, }; Kind kind() const { return static_cast(SymbolKind); } @@ -67,7 +66,7 @@ bool isExternal() { return IsExternal; } // Returns the symbol name. - StringRef getName(); + StringRef getName() const { return Name; }; // Returns the file from which this symbol was created. InputFile *getFile(); @@ -79,9 +78,9 @@ protected: friend SymbolTable; - explicit SymbolBody(Kind K, StringRef N = "") + 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 +88,9 @@ // 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 symobl table more than once. unsigned WrittenToSymtab : 1; protected: @@ -104,7 +101,11 @@ // etc. class Defined : public SymbolBody { public: - Defined(Kind K, StringRef N = "") : SymbolBody(K, N) {} + Defined(Kind K, StringRef N, bool IsWeak = false, + uint8_t StorageClass = llvm::COFF::IMAGE_SYM_CLASS_EXTERNAL, + llvm::COFF::SymbolBaseType Type = llvm::COFF::IMAGE_SYM_TYPE_NULL) + : SymbolBody(K, N), IsWeak(IsWeak), StorageClass(StorageClass), + Type(Type) {} static bool classof(const SymbolBody *S) { return S->kind() <= LastDefinedKind; @@ -122,60 +123,87 @@ // Used to implement SECTION relocation type. uint64_t getSectionIndex(); + /// Returns the COFF symbol storage class. + uint8_t getStorageClass() const { return StorageClass; } + + /// Returns the COFF symbol type. + llvm::COFF::SymbolBaseType getType() const { return Type; } + // Returns true if this symbol points to an executable (e.g. .text) section. // Used to implement ARM relocations. bool isExecutable(); + + bool isWeak() const { return IsWeak; } + +private: + unsigned IsWeak : 1; + uint8_t StorageClass; + llvm::COFF::SymbolBaseType Type; }; -// Symbols defined via a COFF object file. -class DefinedCOFF : public Defined { +// Symbols defined by a file. +class DefinedByFile : public Defined { friend SymbolBody; public: - DefinedCOFF(Kind K, ObjectFile *F, COFFSymbolRef S) - : Defined(K), File(F), Sym(S.getGeneric()) {} + DefinedByFile( + Kind K, InputFile *F, StringRef N, bool IsWeak = false, + uint8_t StorageClass = llvm::COFF::IMAGE_SYM_CLASS_EXTERNAL, + llvm::COFF::SymbolBaseType Type = llvm::COFF::IMAGE_SYM_TYPE_NULL) + : Defined(K, N, IsWeak, StorageClass, Type), File(F) {} static bool classof(const SymbolBody *S) { - return S->kind() <= LastDefinedCOFFKind; + return S->kind() <= LastDefinedByFileKind; } - ObjectFile *getFile() { return File; } - - COFFSymbolRef getCOFFSymbol(); - - ObjectFile *File; + InputFile *getFile() { return File; } -protected: - const coff_symbol_generic *Sym; + InputFile *File; }; // Regular defined symbols read from object file symbol tables. -class DefinedRegular : public DefinedCOFF { +class DefinedRegular : public DefinedByFile { public: - DefinedRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C) - : DefinedCOFF(DefinedRegularKind, F, S), Data(&C->Repl) { + DefinedRegular(ObjectFile *F, StringRef N, bool IsWeak, COFFSymbolRef S, + SectionChunk *C) + : DefinedByFile(DefinedRegularKind, F, N, IsWeak, S.getStorageClass(), + static_cast(S.getType())), + Data(&C->Repl), IsSectionDefinition(S.isSectionDefinition()), + Value(S.getGeneric()->Value) { IsExternal = S.isExternal(); IsCOMDAT = C->isCOMDAT(); } + DefinedRegular(InputFile *F, StringRef N, bool IsCOMDAT, bool IsWeak) + : DefinedByFile(DefinedRegularKind, F, N, IsWeak), + IsSectionDefinition(false) { + this->IsCOMDAT = IsCOMDAT; + } static bool classof(const SymbolBody *S) { return S->kind() == DefinedRegularKind; } - uint64_t getRVA() { return (*Data)->getRVA() + Sym->Value; } - bool isCOMDAT() { return IsCOMDAT; } + uint64_t getRVA() { return (*Data)->getRVA() + Value; } + bool isCOMDAT() const { return IsCOMDAT; } SectionChunk *getChunk() { return *Data; } - uint32_t getValue() { return Sym->Value; } + uint32_t getValue() const { return Value; } + bool isSectionDefinition() const { return IsSectionDefinition; } private: SectionChunk **Data; + bool IsSectionDefinition; + uint32_t Value; }; -class DefinedCommon : public DefinedCOFF { +class DefinedCommon : public DefinedByFile { public: - DefinedCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C) - : DefinedCOFF(DefinedCommonKind, F, S), Data(C) { + DefinedCommon(ObjectFile *F, StringRef N, COFFSymbolRef S, CommonChunk *C) + : DefinedByFile(DefinedCommonKind, F, N, S.getStorageClass(), + static_cast(S.getType())), + Data(C), Size(S.getGeneric()->Value) { IsExternal = S.isExternal(); } + DefinedCommon(InputFile *F, StringRef N) + : DefinedByFile(DefinedCommonKind, F, N), Size(0) {} static bool classof(const SymbolBody *S) { return S->kind() == DefinedCommonKind; @@ -185,8 +213,9 @@ private: friend SymbolTable; - uint64_t getSize() { return Sym->Value; } + uint64_t getSize() const { return Size; } CommonChunk *Data; + uint32_t Size; }; // Absolute symbols. @@ -340,26 +369,6 @@ 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 +385,6 @@ 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 +408,9 @@ // 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() { Index: COFF/Symbols.cpp =================================================================== --- COFF/Symbols.cpp +++ COFF/Symbols.cpp @@ -29,39 +29,14 @@ namespace lld { namespace coff { -StringRef SymbolBody::getName() { - // DefinedCOFF 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 - // name. Object files contain lots of non-external symbols, and creating - // StringRefs for them (which involves lots of strlen() on the string table) - // is a waste of time. - if (Name.empty()) { - auto *D = cast(this); - D->File->getCOFFObj()->getSymbolName(D->Sym, Name); - } - return Name; -} - InputFile *SymbolBody::getFile() { - if (auto *Sym = dyn_cast(this)) - return Sym->File; - if (auto *Sym = dyn_cast(this)) + 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(); - if (SymSize == sizeof(coff_symbol16)) - return COFFSymbolRef(reinterpret_cast(Sym)); - assert(SymSize == sizeof(coff_symbol32)); - return COFFSymbolRef(reinterpret_cast(Sym)); -} - DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine) : Defined(DefinedImportThunkKind, Name) { Index: COFF/Writer.cpp =================================================================== --- COFF/Writer.cpp +++ COFF/Writer.cpp @@ -446,14 +446,8 @@ memcpy(Sym.Name.ShortName, Name.data(), Name.size()); } - if (auto *D = dyn_cast(Def)) { - COFFSymbolRef Ref = D->getCOFFSymbol(); - Sym.Type = Ref.getType(); - Sym.StorageClass = Ref.getStorageClass(); - } else { - Sym.Type = IMAGE_SYM_TYPE_NULL; - Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; - } + Sym.Type = Def->getType(); + Sym.StorageClass = Def->getStorageClass(); Sym.NumberOfAuxSymbols = 0; switch (Def->kind()) { Index: ELF/LTO.cpp =================================================================== --- ELF/LTO.cpp +++ ELF/LTO.cpp @@ -12,11 +12,11 @@ #include "Error.h" #include "InputFiles.h" #include "Symbols.h" +#include "lld/Config/TargetOptionsCommandFlags.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" -#include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/LTO/Config.h" #include "llvm/LTO/LTO.h" Index: include/lld/Config/TargetOptionsCommandFlags.h =================================================================== --- /dev/null +++ include/lld/Config/TargetOptionsCommandFlags.h @@ -0,0 +1,18 @@ +//===-- TargetOptionsCommandFlags.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Helper to create TargetOptions from command line flags. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Target/TargetOptions.h" + +namespace lld { +llvm::TargetOptions InitTargetOptionsFromCodeGenFlags(); +} Index: lib/Config/CMakeLists.txt =================================================================== --- lib/Config/CMakeLists.txt +++ lib/Config/CMakeLists.txt @@ -1,4 +1,5 @@ add_lld_library(lldConfig + TargetOptionsCommandFlags.cpp Version.cpp ADDITIONAL_HEADER_DIRS Index: lib/Config/TargetOptionsCommandFlags.cpp =================================================================== --- /dev/null +++ lib/Config/TargetOptionsCommandFlags.cpp @@ -0,0 +1,31 @@ +//===-- TargetOptionsCommandFlags.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file exists as a place for global variables defined in LLVM's +// CodeGen/CommandFlags.h. By putting the resulting object file in +// an archive and linking with it, the definitions will automatically be +// included when needed and skipped when already present. +// +//===----------------------------------------------------------------------===// + +#include "lld/Config/TargetOptionsCommandFlags.h" + +#include "llvm/CodeGen/CommandFlags.h" +#include "llvm/Target/TargetOptions.h" + +namespace lld { + +// Define an externally visible version of +// InitTargetOptionsFromCodeGenFlags, so that its functionality can be +// used without having to include llvm/CodeGen/CommandFlags.h, which +// would lead to multiple definitions of the command line flags. +llvm::TargetOptions InitTargetOptionsFromCodeGenFlags() { + return ::InitTargetOptionsFromCodeGenFlags(); +} +} Index: test/COFF/lto-comdat.ll =================================================================== --- test/COFF/lto-comdat.ll +++ 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 @@ -41,75 +49,44 @@ ; TEXT-01: Disassembly of section .text: ; TEXT-01-NEXT: .text: ; TEXT-01-NEXT: subq $40, %rsp -; TEXT-01-NEXT: callq 39 -; TEXT-01-NEXT: callq 50 -; TEXT-01-NEXT: callq 13 +; TEXT-01-NEXT: callq 23 +; TEXT-01-NEXT: callq 34 +; TEXT-01-NEXT: callq 45 ; 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: callq 55 ; 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 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 -57 +; 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" Index: test/COFF/lto-parallel.ll =================================================================== --- test/COFF/lto-parallel.ll +++ test/COFF/lto-parallel.ll @@ -5,15 +5,14 @@ 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: bar define void @bar() { call void @foo() ret void Index: test/COFF/weak-external.test =================================================================== --- test/COFF/weak-external.test +++ 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 Index: test/COFF/weak-external3.test =================================================================== --- test/COFF/weak-external3.test +++ 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