Index: lld/trunk/COFF/DebugTypes.cpp =================================================================== --- lld/trunk/COFF/DebugTypes.cpp +++ lld/trunk/COFF/DebugTypes.cpp @@ -231,7 +231,7 @@ if (!it.second) return; // another OBJ already scheduled this PDB for load - driver->enqueuePath(*p, false); + driver->enqueuePath(*p, false, false); } // Create an instance of TypeServerSource or an error string if the PDB couldn't Index: lld/trunk/COFF/Driver.h =================================================================== --- lld/trunk/COFF/Driver.h +++ lld/trunk/COFF/Driver.h @@ -77,7 +77,7 @@ MemoryBufferRef takeBuffer(std::unique_ptr mb); - void enqueuePath(StringRef path, bool wholeArchive); + void enqueuePath(StringRef path, bool wholeArchive, bool lazy); private: std::unique_ptr tar; // for /linkrepro @@ -124,7 +124,8 @@ StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); - void addBuffer(std::unique_ptr mb, bool wholeArchive); + void addBuffer(std::unique_ptr mb, bool wholeArchive, + bool lazy); void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, StringRef parentName, uint64_t offsetInArchive); Index: lld/trunk/COFF/Driver.cpp =================================================================== --- lld/trunk/COFF/Driver.cpp +++ lld/trunk/COFF/Driver.cpp @@ -170,7 +170,7 @@ } void LinkerDriver::addBuffer(std::unique_ptr mb, - bool wholeArchive) { + bool wholeArchive, bool lazy) { StringRef filename = mb->getBufferIdentifier(); MemoryBufferRef mbref = takeBuffer(std::move(mb)); @@ -195,11 +195,17 @@ symtab->addFile(make(mbref)); break; case file_magic::bitcode: - symtab->addFile(make(mbref, "", 0)); + if (lazy) + symtab->addFile(make(mbref)); + else + symtab->addFile(make(mbref, "", 0)); break; case file_magic::coff_object: case file_magic::coff_import_library: - symtab->addFile(make(mbref)); + if (lazy) + symtab->addFile(make(mbref)); + else + symtab->addFile(make(mbref)); break; case file_magic::pdb: loadTypeServerSource(mbref); @@ -220,7 +226,7 @@ } } -void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) { +void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) { auto future = std::make_shared>(createFutureForFile(path)); std::string pathStr = path; @@ -240,7 +246,7 @@ else error(msg + "; did you mean '" + nearest + "'"); } else - driver->addBuffer(std::move(mbOrErr.first), wholeArchive); + driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy); }); } @@ -359,7 +365,7 @@ break; case OPT_defaultlib: if (Optional path = findLib(arg->getValue())) - enqueuePath(*path, false); + enqueuePath(*path, false, false); break; case OPT_entry: config->entry = addUndefined(mangle(arg->getValue())); @@ -1553,19 +1559,45 @@ return false; }; - // Create a list of input files. Files can be given as arguments - // for /defaultlib option. - for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file)) - if (Optional path = findFile(arg->getValue())) - enqueuePath(*path, isWholeArchive(*path)); + // Create a list of input files. These can be given as OPT_INPUT options + // and OPT_wholearchive_file options, and we also need to track OPT_start_lib + // and OPT_end_lib. + bool inLib = false; + for (auto *arg : args) { + switch (arg->getOption().getID()) { + case OPT_end_lib: + if (!inLib) + error("stray " + arg->getSpelling()); + inLib = false; + break; + case OPT_start_lib: + if (inLib) + error("nested " + arg->getSpelling()); + inLib = true; + break; + case OPT_wholearchive_file: + if (Optional path = findFile(arg->getValue())) + enqueuePath(*path, true, inLib); + break; + case OPT_INPUT: + if (Optional path = findFile(arg->getValue())) + enqueuePath(*path, isWholeArchive(*path), inLib); + break; + default: + // Ignore other options. + break; + } + } + // Process files specified as /defaultlib. These should be enequeued after + // other files, which is why they are in a separate loop. for (auto *arg : args.filtered(OPT_defaultlib)) if (Optional path = findLib(arg->getValue())) - enqueuePath(*path, false); + enqueuePath(*path, false, false); // Windows specific -- Create a resource file containing a manifest file. if (config->manifest == Configuration::Embed) - addBuffer(createManifestRes(), false); + addBuffer(createManifestRes(), false, false); // Read all input files given via the command line. run(); @@ -1782,7 +1814,7 @@ if (args.hasArg(OPT_include_optional)) { // Handle /includeoptional for (auto *arg : args.filtered(OPT_include_optional)) - if (dyn_cast_or_null(symtab->find(arg->getValue()))) + if (dyn_cast_or_null(symtab->find(arg->getValue()))) addUndefined(arg->getValue()); while (run()); } Index: lld/trunk/COFF/InputFiles.h =================================================================== --- lld/trunk/COFF/InputFiles.h +++ lld/trunk/COFF/InputFiles.h @@ -14,6 +14,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/BinaryFormat/Magic.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" @@ -55,7 +56,13 @@ // The root class of input files. class InputFile { public: - enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind }; + enum Kind { + ArchiveKind, + ObjectKind, + LazyObjectKind, + ImportKind, + BitcodeKind + }; Kind kind() const { return fileKind; } virtual ~InputFile() {} @@ -102,10 +109,28 @@ llvm::DenseSet seen; }; +// .obj or .o file between -start-lib and -end-lib. +class LazyObjFile : public InputFile { +public: + explicit LazyObjFile(MemoryBufferRef m) : InputFile(LazyObjectKind, m) {} + static bool classof(const InputFile *f) { + return f->kind() == LazyObjectKind; + } + // Makes this object file part of the link. + void fetch(); + // Adds the symbols in this file to the symbol table as LazyObject symbols. + void parse() override; + +private: + std::vector symbols; +}; + // .obj or .o file. This may be a member of an archive file. class ObjFile : public InputFile { public: explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {} + explicit ObjFile(MemoryBufferRef m, std::vector &&symbols) + : InputFile(ObjectKind, m), symbols(std::move(symbols)) {} static bool classof(const InputFile *f) { return f->kind() == ObjectKind; } void parse() override; MachineTypes getMachineType() override; @@ -301,7 +326,11 @@ class BitcodeFile : public InputFile { public: BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive); + uint64_t offsetInArchive) + : BitcodeFile(mb, archiveName, offsetInArchive, {}) {} + explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName, + uint64_t offsetInArchive, + std::vector &&symbols); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } ArrayRef getSymbols() { return symbols; } MachineTypes getMachineType() override; @@ -314,6 +343,10 @@ std::vector symbols; }; +inline bool isBitcode(MemoryBufferRef mb) { + return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode; +} + std::string replaceThinLTOSuffix(StringRef path); } // namespace coff Index: lld/trunk/COFF/InputFiles.cpp =================================================================== --- lld/trunk/COFF/InputFiles.cpp +++ lld/trunk/COFF/InputFiles.cpp @@ -73,6 +73,10 @@ } } +static bool ignoredSymbolName(StringRef name) { + return name == "@feat.00" || name == "@comp.id"; +} + ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {} void ArchiveFile::parse() { @@ -81,7 +85,7 @@ // Read the symbol table to construct Lazy objects. for (const Archive::Symbol &sym : file->symbols()) - symtab->addLazy(this, sym); + symtab->addLazyArchive(this, sym); } // Returns a buffer pointing to a member file containing a given symbol. @@ -116,6 +120,49 @@ return v; } +void LazyObjFile::fetch() { + if (mb.getBuffer().empty()) + return; + + InputFile *file; + if (isBitcode(mb)) + file = make(mb, "", 0, std::move(symbols)); + else + file = make(mb, std::move(symbols)); + mb = {}; + symtab->addFile(file); +} + +void LazyObjFile::parse() { + if (isBitcode(this->mb)) { + // Bitcode file. + std::unique_ptr obj = + CHECK(lto::InputFile::create(this->mb), this); + for (const lto::InputFile::Symbol &sym : obj->symbols()) { + if (!sym.isUndefined()) + symtab->addLazyObject(this, sym.getName()); + } + return; + } + + // Native object file. + COFFObjectFile *coffObj = + dyn_cast(CHECK(createBinary(mb), this).get()); + uint32_t numSymbols = coffObj->getNumberOfSymbols(); + for (uint32_t i = 0; i < numSymbols; ++i) { + COFFSymbolRef coffSym = check(coffObj->getSymbol(i)); + if (coffSym.isUndefined() || !coffSym.isExternal() || + coffSym.isWeakExternal()) + continue; + StringRef name; + coffObj->getSymbolName(coffSym, name); + if (coffSym.isAbsolute() && ignoredSymbolName(name)) + continue; + symtab->addLazyObject(this, name); + i += coffSym.getNumberOfAuxSymbols(); + } +} + void ObjFile::parse() { // Parse a memory buffer as a COFF file. std::unique_ptr bin = CHECK(createBinary(mb), this); @@ -526,13 +573,11 @@ if (sym.isAbsolute()) { StringRef name = getName(); - // Skip special symbols. - if (name == "@comp.id") - return nullptr; - if (name == "@feat.00") { + if (name == "@feat.00") feat00Flags = sym.getValue(); + // Skip special symbols. + if (ignoredSymbolName(name)) return nullptr; - } if (sym.isExternal()) return symtab->addAbsolute(name, sym); @@ -782,8 +827,9 @@ } BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive) - : InputFile(BitcodeKind, mb) { + uint64_t offsetInArchive, + std::vector &&symbols) + : InputFile(BitcodeKind, mb), symbols(std::move(symbols)) { std::string path = mb.getBufferIdentifier().str(); if (config->thinLTOIndexOnly) path = replaceThinLTOSuffix(mb.getBufferIdentifier()); Index: lld/trunk/COFF/Options.td =================================================================== --- lld/trunk/COFF/Options.td +++ lld/trunk/COFF/Options.td @@ -162,6 +162,8 @@ def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias; // LLD extensions +def end_lib : F<"end-lib">, + HelpText<"End a grouping of objects that should be treated as if they were together in an archive">; def exclude_all_symbols : F<"exclude-all-symbols">; def export_all_symbols : F<"export-all-symbols">; defm demangle : B<"demangle", @@ -176,6 +178,8 @@ "Base path used to make relative source file path absolute in PDB">; def rsp_quoting : Joined<["--"], "rsp-quoting=">, HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; +def start_lib : F<"start-lib">, + HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">; def thinlto_emit_imports_files : F<"thinlto-emit-imports-files">, HelpText<"Emit .imports files with -thinlto-index-only">; Index: lld/trunk/COFF/SymbolTable.h =================================================================== --- lld/trunk/COFF/SymbolTable.h +++ lld/trunk/COFF/SymbolTable.h @@ -29,7 +29,7 @@ class DefinedAbsolute; class DefinedRegular; class DefinedRelative; -class Lazy; +class LazyArchive; class SectionChunk; class Symbol; @@ -86,7 +86,8 @@ Symbol *addAbsolute(StringRef n, uint64_t va); Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias); - void addLazy(ArchiveFile *f, const Archive::Symbol &sym); + void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym); + void addLazyObject(LazyObjFile *f, StringRef n); Symbol *addAbsolute(StringRef n, COFFSymbolRef s); Symbol *addRegular(InputFile *f, StringRef n, const llvm::object::coff_symbol_generic *s = nullptr, Index: lld/trunk/COFF/SymbolTable.cpp =================================================================== --- lld/trunk/COFF/SymbolTable.cpp +++ lld/trunk/COFF/SymbolTable.cpp @@ -61,6 +61,24 @@ error(s); } +// Causes the file associated with a lazy symbol to be linked in. +static void forceLazy(Symbol *s) { + s->pendingArchiveLoad = true; + switch (s->kind()) { + case Symbol::Kind::LazyArchiveKind: { + auto *l = cast(s); + l->file->addMember(l->sym); + break; + } + case Symbol::Kind::LazyObjectKind: + cast(s)->file->fetch(); + break; + default: + llvm_unreachable( + "symbol passed to forceLazy is not a LazyArchive or LazyObject"); + } +} + // Returns the symbol in SC whose value is <= Addr that is closest to Addr. // This is generally the global variable or function whose definition contains // Addr. @@ -192,16 +210,15 @@ if (name.startswith("__imp_")) continue; - // If we have an undefined symbol, but we have a Lazy representing a - // symbol we could load from file, make sure to load that. - Lazy *l = dyn_cast_or_null(find(("__imp_" + name).str())); - if (!l || l->pendingArchiveLoad) + // If we have an undefined symbol, but we have a lazy symbol we could + // load, load it. + Symbol *l = find(("__imp_" + name).str()); + if (!l || l->pendingArchiveLoad || !l->isLazy()) continue; - log("Loading lazy " + l->getName() + " from " + l->file->getName() + + log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() + " for automatic import"); - l->pendingArchiveLoad = true; - l->file->addMember(l->sym); + forceLazy(l); } } @@ -435,26 +452,22 @@ Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(name, f); - if (wasInserted || (isa(s) && isWeakAlias)) { + if (wasInserted || (s->isLazy() && isWeakAlias)) { replaceSymbol(s, name); return s; } - if (auto *l = dyn_cast(s)) { - if (!s->pendingArchiveLoad) { - s->pendingArchiveLoad = true; - l->file->addMember(l->sym); - } - } + if (s->isLazy()) + forceLazy(s); return s; } -void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) { +void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) { StringRef name = sym.getName(); Symbol *s; bool wasInserted; std::tie(s, wasInserted) = insert(name); if (wasInserted) { - replaceSymbol(s, f, sym); + replaceSymbol(s, f, sym); return; } auto *u = dyn_cast(s); @@ -464,6 +477,21 @@ f->addMember(sym); } +void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) { + Symbol *s; + bool wasInserted; + std::tie(s, wasInserted) = insert(n, f); + if (wasInserted) { + replaceSymbol(s, f, n); + return; + } + auto *u = dyn_cast(s); + if (!u || u->weakAlias || s->pendingArchiveLoad) + return; + s->pendingArchiveLoad = true; + f->fetch(); +} + void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) { std::string msg = "duplicate symbol: " + toString(*existing) + " in " + toString(existing->getFile()) + " and in " + @@ -480,7 +508,7 @@ bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) + if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, sym); else if (!isa(s)) reportDuplicate(s, nullptr); @@ -492,7 +520,7 @@ bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) + if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, va); else if (!isa(s)) reportDuplicate(s, nullptr); @@ -504,7 +532,7 @@ bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) + if (wasInserted || isa(s) || s->isLazy()) replaceSymbol(s, n, c); else if (!isa(s)) reportDuplicate(s, nullptr); @@ -560,7 +588,7 @@ bool wasInserted; std::tie(s, wasInserted) = insert(n, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) { + if (wasInserted || isa(s) || s->isLazy()) { replaceSymbol(s, n, f); return s; } @@ -575,7 +603,7 @@ bool wasInserted; std::tie(s, wasInserted) = insert(name, nullptr); s->isUsedInRegularObj = true; - if (wasInserted || isa(s) || isa(s)) { + if (wasInserted || isa(s) || s->isLazy()) { replaceSymbol(s, name, id, machine); return s; } @@ -589,9 +617,12 @@ if (!sym) return; - if (Lazy *l = dyn_cast(sym)) { + if (auto *l = dyn_cast(sym)) { MemoryBufferRef mb = l->getMemberBuffer(); - if (identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode) + if (isBitcode(mb)) + addUndefined(sym->getName()); + } else if (LazyObject *o = dyn_cast(sym)) { + if (isBitcode(o->file->mb)) addUndefined(sym->getName()); } } Index: lld/trunk/COFF/Symbols.h =================================================================== --- lld/trunk/COFF/Symbols.h +++ lld/trunk/COFF/Symbols.h @@ -59,7 +59,8 @@ DefinedSyntheticKind, UndefinedKind, - LazyKind, + LazyArchiveKind, + LazyObjectKind, LastDefinedCOFFKind = DefinedCommonKind, LastDefinedKind = DefinedSyntheticKind, @@ -79,6 +80,10 @@ // after calling markLive. bool isLive() const; + bool isLazy() const { + return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind; + } + protected: friend SymbolTable; explicit Symbol(Kind k, StringRef n = "") @@ -256,26 +261,29 @@ // This class represents a symbol defined in an archive file. It is // created from an archive file header, and it knows how to load an // object file from an archive to replace itself with a defined -// symbol. If the resolver finds both Undefined and Lazy for -// the same name, it will ask the Lazy to load a file. -class Lazy : public Symbol { +// symbol. If the resolver finds both Undefined and LazyArchive for +// the same name, it will ask the LazyArchive to load a file. +class LazyArchive : public Symbol { public: - Lazy(ArchiveFile *f, const Archive::Symbol s) - : Symbol(LazyKind, s.getName()), file(f), sym(s) {} + LazyArchive(ArchiveFile *f, const Archive::Symbol s) + : Symbol(LazyArchiveKind, s.getName()), file(f), sym(s) {} - static bool classof(const Symbol *s) { return s->kind() == LazyKind; } + static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; } MemoryBufferRef getMemberBuffer(); ArchiveFile *file; - -private: - friend SymbolTable; - -private: const Archive::Symbol sym; }; +class LazyObject : public Symbol { +public: + LazyObject(LazyObjFile *f, StringRef n) + : Symbol(LazyObjectKind, n), file(f) {} + static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; } + LazyObjFile *file; +}; + // Undefined symbols. class Undefined : public Symbol { public: @@ -381,7 +389,8 @@ return cast(this)->getRVA(); case DefinedRegularKind: return cast(this)->getRVA(); - case LazyKind: + case LazyArchiveKind: + case LazyObjectKind: case UndefinedKind: llvm_unreachable("Cannot get the address for an undefined symbol."); } @@ -404,7 +413,8 @@ return cast(this)->getChunk(); case DefinedCommonKind: return cast(this)->getChunk(); - case LazyKind: + case LazyArchiveKind: + case LazyObjectKind: case UndefinedKind: llvm_unreachable("Cannot get the chunk of an undefined symbol."); } @@ -419,11 +429,12 @@ alignas(DefinedCommon) char b[sizeof(DefinedCommon)]; alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)]; alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)]; - alignas(Lazy) char e[sizeof(Lazy)]; + alignas(LazyArchive) char e[sizeof(LazyArchive)]; alignas(Undefined) char f[sizeof(Undefined)]; alignas(DefinedImportData) char g[sizeof(DefinedImportData)]; alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)]; alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)]; + alignas(LazyObject) char j[sizeof(LazyObject)]; }; template Index: lld/trunk/COFF/Symbols.cpp =================================================================== --- lld/trunk/COFF/Symbols.cpp +++ lld/trunk/COFF/Symbols.cpp @@ -61,7 +61,9 @@ InputFile *Symbol::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; } @@ -119,7 +121,7 @@ return nullptr; } -MemoryBufferRef Lazy::getMemberBuffer() { +MemoryBufferRef LazyArchive::getMemberBuffer() { Archive::Child c = CHECK(sym.getMember(), "could not get the member for symbol " + toCOFFString(sym)); Index: lld/trunk/COFF/Writer.cpp =================================================================== --- lld/trunk/COFF/Writer.cpp +++ lld/trunk/COFF/Writer.cpp @@ -1519,7 +1519,8 @@ // Absolute is never code, synthetic generally isn't and usually isn't // determinable. break; - case Symbol::LazyKind: + case Symbol::LazyArchiveKind: + case Symbol::LazyObjectKind: case Symbol::UndefinedKind: // Undefined symbols resolve to zero, so they don't have an RVA. Lazy // symbols shouldn't have relocations. Index: lld/trunk/test/COFF/Inputs/start-lib1.ll =================================================================== --- lld/trunk/test/COFF/Inputs/start-lib1.ll +++ lld/trunk/test/COFF/Inputs/start-lib1.ll @@ -0,0 +1,13 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +declare i32 @bar() + +define i32 @foo() { + %1 = call i32 () @bar() + %2 = add i32 %1, 1 + ret i32 %2 +} + +!llvm.linker.options = !{!0} +!0 = !{!"/INCLUDE:foo"} Index: lld/trunk/test/COFF/Inputs/start-lib2.ll =================================================================== --- lld/trunk/test/COFF/Inputs/start-lib2.ll +++ lld/trunk/test/COFF/Inputs/start-lib2.ll @@ -0,0 +1,9 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define i32 @bar() { + ret i32 1 +} + +!llvm.linker.options = !{!0} +!0 = !{!"/INCLUDE:bar"} Index: lld/trunk/test/COFF/start-lib-cmd-diagnostics.ll =================================================================== --- lld/trunk/test/COFF/start-lib-cmd-diagnostics.ll +++ lld/trunk/test/COFF/start-lib-cmd-diagnostics.ll @@ -0,0 +1,19 @@ +; REQUIRES: x86 +; +; We need an input file to lld, so create one. +; RUN: llc -filetype=obj %s -o %t.obj + +; RUN: not lld-link %t.obj -end-lib 2>&1 \ +; RUN: | FileCheck --check-prefix=STRAY_END %s +; STRAY_END: stray -end-lib + +; RUN: not lld-link -start-lib -start-lib %t.obj 2>&1 \ +; RUN: | FileCheck --check-prefix=NESTED_START %s +; NESTED_START: nested -start-lib + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define void @main() { + ret void +} Index: lld/trunk/test/COFF/start-lib.ll =================================================================== --- lld/trunk/test/COFF/start-lib.ll +++ lld/trunk/test/COFF/start-lib.ll @@ -0,0 +1,43 @@ +; REQUIRES: x86 +; +; RUN: llc -filetype=obj %s -o %t.obj +; RUN: llc -filetype=obj %p/Inputs/start-lib1.ll -o %t1.obj +; RUN: llc -filetype=obj %p/Inputs/start-lib2.ll -o %t2.obj +; RUN: opt -thinlto-bc %s -o %t.bc +; RUN: opt -thinlto-bc %p/Inputs/start-lib1.ll -o %t1.bc +; RUN: opt -thinlto-bc %p/Inputs/start-lib2.ll -o %t2.bc +; +; RUN: lld-link -out:%t1.exe -entry:main -opt:noref -lldmap:%t1.map \ +; RUN: %t.obj %t1.obj %t2.obj +; RUN: FileCheck --check-prefix=TEST1 %s < %t1.map +; RUN: lld-link -out:%t1.exe -entry:main -opt:noref -lldmap:%t1.thinlto.map \ +; RUN: %t.bc %t1.bc %t2.bc +; RUN: FileCheck --check-prefix=TEST1 %s < %t1.thinlto.map +; TEST1: foo +; TEST1: bar +; +; RUN: lld-link -out:%t2.exe -entry:main -opt:noref -lldmap:%t2.map \ +; RUN: %t.obj -start-lib %t1.obj -end-lib %t2.obj +; RUN: FileCheck --check-prefix=TEST2 %s < %t2.map +; RUN: lld-link -out:%t2.exe -entry:main -opt:noref -lldmap:%t2.thinlto.map \ +; RUN: %t.bc -start-lib %t1.bc -end-lib %t2.bc +; RUN: FileCheck --check-prefix=TEST2 %s < %t2.thinlto.map +; TEST2-NOT: Name: foo +; TEST2: bar +; TEST2-NOT: Name: foo +; +; RUN: lld-link -out:%t3.exe -entry:main -opt:noref -lldmap:%t3.map \ +; RUN: %t.obj -start-lib %t1.obj %t2.obj +; RUN: FileCheck --check-prefix=TEST3 %s < %t3.map +; RUN: lld-link -out:%t3.exe -entry:main -opt:noref -lldmap:%t3.thinlto.map \ +; RUN: %t.bc -start-lib %t1.bc %t2.bc +; RUN: FileCheck --check-prefix=TEST3 %s < %t3.thinlto.map +; TEST3-NOT: foo +; TEST3-NOT: bar + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define void @main() { + ret void +}