forked from OSchip/llvm-project
[lld-link] implement -start-lib and -end-lib
Summary: This implements -start-lib and -end-lib flags for lld-link, analogous to the similarly named options in ld.lld. Object files after -start-lib are included in the link only when needed to resolve undefined symbols. The -end-lib flag goes back to the normal behavior of always including object files in the link. This mimics the semantics of static libraries, but without needing to actually create the archive file. Reviewers: ruiu, smeenai, MaskRay Reviewed By: ruiu, MaskRay Subscribers: akhuang, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D66848 llvm-svn: 370487
This commit is contained in:
parent
b8a3564975
commit
fd7569c8e3
|
@ -231,7 +231,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile,
|
||||||
if (!it.second)
|
if (!it.second)
|
||||||
return; // another OBJ already scheduled this PDB for load
|
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
|
// Create an instance of TypeServerSource or an error string if the PDB couldn't
|
||||||
|
|
|
@ -170,7 +170,7 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
||||||
bool wholeArchive) {
|
bool wholeArchive, bool lazy) {
|
||||||
StringRef filename = mb->getBufferIdentifier();
|
StringRef filename = mb->getBufferIdentifier();
|
||||||
|
|
||||||
MemoryBufferRef mbref = takeBuffer(std::move(mb));
|
MemoryBufferRef mbref = takeBuffer(std::move(mb));
|
||||||
|
@ -195,11 +195,17 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
||||||
symtab->addFile(make<ArchiveFile>(mbref));
|
symtab->addFile(make<ArchiveFile>(mbref));
|
||||||
break;
|
break;
|
||||||
case file_magic::bitcode:
|
case file_magic::bitcode:
|
||||||
symtab->addFile(make<BitcodeFile>(mbref, "", 0));
|
if (lazy)
|
||||||
|
symtab->addFile(make<LazyObjFile>(mbref));
|
||||||
|
else
|
||||||
|
symtab->addFile(make<BitcodeFile>(mbref, "", 0));
|
||||||
break;
|
break;
|
||||||
case file_magic::coff_object:
|
case file_magic::coff_object:
|
||||||
case file_magic::coff_import_library:
|
case file_magic::coff_import_library:
|
||||||
symtab->addFile(make<ObjFile>(mbref));
|
if (lazy)
|
||||||
|
symtab->addFile(make<LazyObjFile>(mbref));
|
||||||
|
else
|
||||||
|
symtab->addFile(make<ObjFile>(mbref));
|
||||||
break;
|
break;
|
||||||
case file_magic::pdb:
|
case file_magic::pdb:
|
||||||
loadTypeServerSource(mbref);
|
loadTypeServerSource(mbref);
|
||||||
|
@ -220,7 +226,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
|
void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
|
||||||
auto future =
|
auto future =
|
||||||
std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
|
std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
|
||||||
std::string pathStr = path;
|
std::string pathStr = path;
|
||||||
|
@ -240,7 +246,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
|
||||||
else
|
else
|
||||||
error(msg + "; did you mean '" + nearest + "'");
|
error(msg + "; did you mean '" + nearest + "'");
|
||||||
} else
|
} else
|
||||||
driver->addBuffer(std::move(mbOrErr.first), wholeArchive);
|
driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +365,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
|
||||||
break;
|
break;
|
||||||
case OPT_defaultlib:
|
case OPT_defaultlib:
|
||||||
if (Optional<StringRef> path = findLib(arg->getValue()))
|
if (Optional<StringRef> path = findLib(arg->getValue()))
|
||||||
enqueuePath(*path, false);
|
enqueuePath(*path, false, false);
|
||||||
break;
|
break;
|
||||||
case OPT_entry:
|
case OPT_entry:
|
||||||
config->entry = addUndefined(mangle(arg->getValue()));
|
config->entry = addUndefined(mangle(arg->getValue()));
|
||||||
|
@ -1553,19 +1559,45 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a list of input files. Files can be given as arguments
|
// Create a list of input files. These can be given as OPT_INPUT options
|
||||||
// for /defaultlib option.
|
// and OPT_wholearchive_file options, and we also need to track OPT_start_lib
|
||||||
for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file))
|
// and OPT_end_lib.
|
||||||
if (Optional<StringRef> path = findFile(arg->getValue()))
|
bool inLib = false;
|
||||||
enqueuePath(*path, isWholeArchive(*path));
|
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<StringRef> path = findFile(arg->getValue()))
|
||||||
|
enqueuePath(*path, true, inLib);
|
||||||
|
break;
|
||||||
|
case OPT_INPUT:
|
||||||
|
if (Optional<StringRef> 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))
|
for (auto *arg : args.filtered(OPT_defaultlib))
|
||||||
if (Optional<StringRef> path = findLib(arg->getValue()))
|
if (Optional<StringRef> path = findLib(arg->getValue()))
|
||||||
enqueuePath(*path, false);
|
enqueuePath(*path, false, false);
|
||||||
|
|
||||||
// Windows specific -- Create a resource file containing a manifest file.
|
// Windows specific -- Create a resource file containing a manifest file.
|
||||||
if (config->manifest == Configuration::Embed)
|
if (config->manifest == Configuration::Embed)
|
||||||
addBuffer(createManifestRes(), false);
|
addBuffer(createManifestRes(), false, false);
|
||||||
|
|
||||||
// Read all input files given via the command line.
|
// Read all input files given via the command line.
|
||||||
run();
|
run();
|
||||||
|
@ -1782,7 +1814,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||||
if (args.hasArg(OPT_include_optional)) {
|
if (args.hasArg(OPT_include_optional)) {
|
||||||
// Handle /includeoptional
|
// Handle /includeoptional
|
||||||
for (auto *arg : args.filtered(OPT_include_optional))
|
for (auto *arg : args.filtered(OPT_include_optional))
|
||||||
if (dyn_cast_or_null<Lazy>(symtab->find(arg->getValue())))
|
if (dyn_cast_or_null<LazyArchive>(symtab->find(arg->getValue())))
|
||||||
addUndefined(arg->getValue());
|
addUndefined(arg->getValue());
|
||||||
while (run());
|
while (run());
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ public:
|
||||||
|
|
||||||
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
|
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
|
||||||
|
|
||||||
void enqueuePath(StringRef path, bool wholeArchive);
|
void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
|
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
|
||||||
|
@ -124,7 +124,8 @@ private:
|
||||||
StringRef findDefaultEntry();
|
StringRef findDefaultEntry();
|
||||||
WindowsSubsystem inferSubsystem();
|
WindowsSubsystem inferSubsystem();
|
||||||
|
|
||||||
void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive);
|
void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive,
|
||||||
|
bool lazy);
|
||||||
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
|
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
|
||||||
StringRef parentName, uint64_t offsetInArchive);
|
StringRef parentName, uint64_t offsetInArchive);
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,10 @@ static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ignoredSymbolName(StringRef name) {
|
||||||
|
return name == "@feat.00" || name == "@comp.id";
|
||||||
|
}
|
||||||
|
|
||||||
ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
|
ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
|
||||||
|
|
||||||
void ArchiveFile::parse() {
|
void ArchiveFile::parse() {
|
||||||
|
@ -81,7 +85,7 @@ void ArchiveFile::parse() {
|
||||||
|
|
||||||
// Read the symbol table to construct Lazy objects.
|
// Read the symbol table to construct Lazy objects.
|
||||||
for (const Archive::Symbol &sym : file->symbols())
|
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.
|
// Returns a buffer pointing to a member file containing a given symbol.
|
||||||
|
@ -116,6 +120,49 @@ std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LazyObjFile::fetch() {
|
||||||
|
if (mb.getBuffer().empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
InputFile *file;
|
||||||
|
if (isBitcode(mb))
|
||||||
|
file = make<BitcodeFile>(mb, "", 0, std::move(symbols));
|
||||||
|
else
|
||||||
|
file = make<ObjFile>(mb, std::move(symbols));
|
||||||
|
mb = {};
|
||||||
|
symtab->addFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LazyObjFile::parse() {
|
||||||
|
if (isBitcode(this->mb)) {
|
||||||
|
// Bitcode file.
|
||||||
|
std::unique_ptr<lto::InputFile> 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<COFFObjectFile>(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() {
|
void ObjFile::parse() {
|
||||||
// Parse a memory buffer as a COFF file.
|
// Parse a memory buffer as a COFF file.
|
||||||
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
|
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
|
||||||
|
@ -526,13 +573,11 @@ Optional<Symbol *> ObjFile::createDefined(
|
||||||
if (sym.isAbsolute()) {
|
if (sym.isAbsolute()) {
|
||||||
StringRef name = getName();
|
StringRef name = getName();
|
||||||
|
|
||||||
// Skip special symbols.
|
if (name == "@feat.00")
|
||||||
if (name == "@comp.id")
|
|
||||||
return nullptr;
|
|
||||||
if (name == "@feat.00") {
|
|
||||||
feat00Flags = sym.getValue();
|
feat00Flags = sym.getValue();
|
||||||
|
// Skip special symbols.
|
||||||
|
if (ignoredSymbolName(name))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
if (sym.isExternal())
|
if (sym.isExternal())
|
||||||
return symtab->addAbsolute(name, sym);
|
return symtab->addAbsolute(name, sym);
|
||||||
|
@ -782,8 +827,9 @@ void ImportFile::parse() {
|
||||||
}
|
}
|
||||||
|
|
||||||
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
||||||
uint64_t offsetInArchive)
|
uint64_t offsetInArchive,
|
||||||
: InputFile(BitcodeKind, mb) {
|
std::vector<Symbol *> &&symbols)
|
||||||
|
: InputFile(BitcodeKind, mb), symbols(std::move(symbols)) {
|
||||||
std::string path = mb.getBufferIdentifier().str();
|
std::string path = mb.getBufferIdentifier().str();
|
||||||
if (config->thinLTOIndexOnly)
|
if (config->thinLTOIndexOnly)
|
||||||
path = replaceThinLTOSuffix(mb.getBufferIdentifier());
|
path = replaceThinLTOSuffix(mb.getBufferIdentifier());
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "llvm/ADT/ArrayRef.h"
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/DenseSet.h"
|
#include "llvm/ADT/DenseSet.h"
|
||||||
|
#include "llvm/BinaryFormat/Magic.h"
|
||||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||||
#include "llvm/LTO/LTO.h"
|
#include "llvm/LTO/LTO.h"
|
||||||
#include "llvm/Object/Archive.h"
|
#include "llvm/Object/Archive.h"
|
||||||
|
@ -55,7 +56,13 @@ class TpiSource;
|
||||||
// The root class of input files.
|
// The root class of input files.
|
||||||
class InputFile {
|
class InputFile {
|
||||||
public:
|
public:
|
||||||
enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind };
|
enum Kind {
|
||||||
|
ArchiveKind,
|
||||||
|
ObjectKind,
|
||||||
|
LazyObjectKind,
|
||||||
|
ImportKind,
|
||||||
|
BitcodeKind
|
||||||
|
};
|
||||||
Kind kind() const { return fileKind; }
|
Kind kind() const { return fileKind; }
|
||||||
virtual ~InputFile() {}
|
virtual ~InputFile() {}
|
||||||
|
|
||||||
|
@ -102,10 +109,28 @@ private:
|
||||||
llvm::DenseSet<uint64_t> seen;
|
llvm::DenseSet<uint64_t> 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<Symbol *> symbols;
|
||||||
|
};
|
||||||
|
|
||||||
// .obj or .o file. This may be a member of an archive file.
|
// .obj or .o file. This may be a member of an archive file.
|
||||||
class ObjFile : public InputFile {
|
class ObjFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
|
explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
|
||||||
|
explicit ObjFile(MemoryBufferRef m, std::vector<Symbol *> &&symbols)
|
||||||
|
: InputFile(ObjectKind, m), symbols(std::move(symbols)) {}
|
||||||
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
|
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
|
||||||
void parse() override;
|
void parse() override;
|
||||||
MachineTypes getMachineType() override;
|
MachineTypes getMachineType() override;
|
||||||
|
@ -301,7 +326,11 @@ public:
|
||||||
class BitcodeFile : public InputFile {
|
class BitcodeFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
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<Symbol *> &&symbols);
|
||||||
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
|
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
|
||||||
ArrayRef<Symbol *> getSymbols() { return symbols; }
|
ArrayRef<Symbol *> getSymbols() { return symbols; }
|
||||||
MachineTypes getMachineType() override;
|
MachineTypes getMachineType() override;
|
||||||
|
@ -314,6 +343,10 @@ private:
|
||||||
std::vector<Symbol *> symbols;
|
std::vector<Symbol *> symbols;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool isBitcode(MemoryBufferRef mb) {
|
||||||
|
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
|
||||||
|
}
|
||||||
|
|
||||||
std::string replaceThinLTOSuffix(StringRef path);
|
std::string replaceThinLTOSuffix(StringRef path);
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
|
|
||||||
|
|
|
@ -162,6 +162,8 @@ def help : F<"help">;
|
||||||
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
|
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
|
||||||
|
|
||||||
// LLD extensions
|
// 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 exclude_all_symbols : F<"exclude-all-symbols">;
|
||||||
def export_all_symbols : F<"export-all-symbols">;
|
def export_all_symbols : F<"export-all-symbols">;
|
||||||
defm demangle : B<"demangle",
|
defm demangle : B<"demangle",
|
||||||
|
@ -176,6 +178,8 @@ def pdb_source_path : P<"pdbsourcepath",
|
||||||
"Base path used to make relative source file path absolute in PDB">;
|
"Base path used to make relative source file path absolute in PDB">;
|
||||||
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
|
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
|
||||||
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
|
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 :
|
def thinlto_emit_imports_files :
|
||||||
F<"thinlto-emit-imports-files">,
|
F<"thinlto-emit-imports-files">,
|
||||||
HelpText<"Emit .imports files with -thinlto-index-only">;
|
HelpText<"Emit .imports files with -thinlto-index-only">;
|
||||||
|
|
|
@ -61,6 +61,24 @@ static void errorOrWarn(const Twine &s) {
|
||||||
error(s);
|
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<LazyArchive>(s);
|
||||||
|
l->file->addMember(l->sym);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Symbol::Kind::LazyObjectKind:
|
||||||
|
cast<LazyObject>(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.
|
// 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
|
// This is generally the global variable or function whose definition contains
|
||||||
// Addr.
|
// Addr.
|
||||||
|
@ -192,16 +210,15 @@ void SymbolTable::loadMinGWAutomaticImports() {
|
||||||
|
|
||||||
if (name.startswith("__imp_"))
|
if (name.startswith("__imp_"))
|
||||||
continue;
|
continue;
|
||||||
// If we have an undefined symbol, but we have a Lazy representing a
|
// If we have an undefined symbol, but we have a lazy symbol we could
|
||||||
// symbol we could load from file, make sure to load that.
|
// load, load it.
|
||||||
Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str()));
|
Symbol *l = find(("__imp_" + name).str());
|
||||||
if (!l || l->pendingArchiveLoad)
|
if (!l || l->pendingArchiveLoad || !l->isLazy())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
log("Loading lazy " + l->getName() + " from " + l->file->getName() +
|
log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() +
|
||||||
" for automatic import");
|
" for automatic import");
|
||||||
l->pendingArchiveLoad = true;
|
forceLazy(l);
|
||||||
l->file->addMember(l->sym);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,26 +452,22 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
|
||||||
Symbol *s;
|
Symbol *s;
|
||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(name, f);
|
std::tie(s, wasInserted) = insert(name, f);
|
||||||
if (wasInserted || (isa<Lazy>(s) && isWeakAlias)) {
|
if (wasInserted || (s->isLazy() && isWeakAlias)) {
|
||||||
replaceSymbol<Undefined>(s, name);
|
replaceSymbol<Undefined>(s, name);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
if (auto *l = dyn_cast<Lazy>(s)) {
|
if (s->isLazy())
|
||||||
if (!s->pendingArchiveLoad) {
|
forceLazy(s);
|
||||||
s->pendingArchiveLoad = true;
|
|
||||||
l->file->addMember(l->sym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 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();
|
StringRef name = sym.getName();
|
||||||
Symbol *s;
|
Symbol *s;
|
||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(name);
|
std::tie(s, wasInserted) = insert(name);
|
||||||
if (wasInserted) {
|
if (wasInserted) {
|
||||||
replaceSymbol<Lazy>(s, f, sym);
|
replaceSymbol<LazyArchive>(s, f, sym);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto *u = dyn_cast<Undefined>(s);
|
auto *u = dyn_cast<Undefined>(s);
|
||||||
|
@ -464,6 +477,21 @@ void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) {
|
||||||
f->addMember(sym);
|
f->addMember(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) {
|
||||||
|
Symbol *s;
|
||||||
|
bool wasInserted;
|
||||||
|
std::tie(s, wasInserted) = insert(n, f);
|
||||||
|
if (wasInserted) {
|
||||||
|
replaceSymbol<LazyObject>(s, f, n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto *u = dyn_cast<Undefined>(s);
|
||||||
|
if (!u || u->weakAlias || s->pendingArchiveLoad)
|
||||||
|
return;
|
||||||
|
s->pendingArchiveLoad = true;
|
||||||
|
f->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) {
|
void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) {
|
||||||
std::string msg = "duplicate symbol: " + toString(*existing) + " in " +
|
std::string msg = "duplicate symbol: " + toString(*existing) + " in " +
|
||||||
toString(existing->getFile()) + " and in " +
|
toString(existing->getFile()) + " and in " +
|
||||||
|
@ -480,7 +508,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
|
||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||||
s->isUsedInRegularObj = true;
|
s->isUsedInRegularObj = true;
|
||||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
if (wasInserted || isa<Undefined>(s) || s->isLazy())
|
||||||
replaceSymbol<DefinedAbsolute>(s, n, sym);
|
replaceSymbol<DefinedAbsolute>(s, n, sym);
|
||||||
else if (!isa<DefinedCOFF>(s))
|
else if (!isa<DefinedCOFF>(s))
|
||||||
reportDuplicate(s, nullptr);
|
reportDuplicate(s, nullptr);
|
||||||
|
@ -492,7 +520,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
|
||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||||
s->isUsedInRegularObj = true;
|
s->isUsedInRegularObj = true;
|
||||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
if (wasInserted || isa<Undefined>(s) || s->isLazy())
|
||||||
replaceSymbol<DefinedAbsolute>(s, n, va);
|
replaceSymbol<DefinedAbsolute>(s, n, va);
|
||||||
else if (!isa<DefinedCOFF>(s))
|
else if (!isa<DefinedCOFF>(s))
|
||||||
reportDuplicate(s, nullptr);
|
reportDuplicate(s, nullptr);
|
||||||
|
@ -504,7 +532,7 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
|
||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||||
s->isUsedInRegularObj = true;
|
s->isUsedInRegularObj = true;
|
||||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
if (wasInserted || isa<Undefined>(s) || s->isLazy())
|
||||||
replaceSymbol<DefinedSynthetic>(s, n, c);
|
replaceSymbol<DefinedSynthetic>(s, n, c);
|
||||||
else if (!isa<DefinedCOFF>(s))
|
else if (!isa<DefinedCOFF>(s))
|
||||||
reportDuplicate(s, nullptr);
|
reportDuplicate(s, nullptr);
|
||||||
|
@ -560,7 +588,7 @@ Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) {
|
||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||||
s->isUsedInRegularObj = true;
|
s->isUsedInRegularObj = true;
|
||||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
|
if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
|
||||||
replaceSymbol<DefinedImportData>(s, n, f);
|
replaceSymbol<DefinedImportData>(s, n, f);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -575,7 +603,7 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
|
||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(name, nullptr);
|
std::tie(s, wasInserted) = insert(name, nullptr);
|
||||||
s->isUsedInRegularObj = true;
|
s->isUsedInRegularObj = true;
|
||||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
|
if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
|
||||||
replaceSymbol<DefinedImportThunk>(s, name, id, machine);
|
replaceSymbol<DefinedImportThunk>(s, name, id, machine);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -589,9 +617,12 @@ void SymbolTable::addLibcall(StringRef name) {
|
||||||
if (!sym)
|
if (!sym)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Lazy *l = dyn_cast<Lazy>(sym)) {
|
if (auto *l = dyn_cast<LazyArchive>(sym)) {
|
||||||
MemoryBufferRef mb = l->getMemberBuffer();
|
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<LazyObject>(sym)) {
|
||||||
|
if (isBitcode(o->file->mb))
|
||||||
addUndefined(sym->getName());
|
addUndefined(sym->getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ class Defined;
|
||||||
class DefinedAbsolute;
|
class DefinedAbsolute;
|
||||||
class DefinedRegular;
|
class DefinedRegular;
|
||||||
class DefinedRelative;
|
class DefinedRelative;
|
||||||
class Lazy;
|
class LazyArchive;
|
||||||
class SectionChunk;
|
class SectionChunk;
|
||||||
class Symbol;
|
class Symbol;
|
||||||
|
|
||||||
|
@ -86,7 +86,8 @@ public:
|
||||||
Symbol *addAbsolute(StringRef n, uint64_t va);
|
Symbol *addAbsolute(StringRef n, uint64_t va);
|
||||||
|
|
||||||
Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias);
|
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 *addAbsolute(StringRef n, COFFSymbolRef s);
|
||||||
Symbol *addRegular(InputFile *f, StringRef n,
|
Symbol *addRegular(InputFile *f, StringRef n,
|
||||||
const llvm::object::coff_symbol_generic *s = nullptr,
|
const llvm::object::coff_symbol_generic *s = nullptr,
|
||||||
|
|
|
@ -61,7 +61,9 @@ StringRef Symbol::getName() {
|
||||||
InputFile *Symbol::getFile() {
|
InputFile *Symbol::getFile() {
|
||||||
if (auto *sym = dyn_cast<DefinedCOFF>(this))
|
if (auto *sym = dyn_cast<DefinedCOFF>(this))
|
||||||
return sym->file;
|
return sym->file;
|
||||||
if (auto *sym = dyn_cast<Lazy>(this))
|
if (auto *sym = dyn_cast<LazyArchive>(this))
|
||||||
|
return sym->file;
|
||||||
|
if (auto *sym = dyn_cast<LazyObject>(this))
|
||||||
return sym->file;
|
return sym->file;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +121,7 @@ Defined *Undefined::getWeakAlias() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryBufferRef Lazy::getMemberBuffer() {
|
MemoryBufferRef LazyArchive::getMemberBuffer() {
|
||||||
Archive::Child c =
|
Archive::Child c =
|
||||||
CHECK(sym.getMember(),
|
CHECK(sym.getMember(),
|
||||||
"could not get the member for symbol " + toCOFFString(sym));
|
"could not get the member for symbol " + toCOFFString(sym));
|
||||||
|
|
|
@ -59,7 +59,8 @@ public:
|
||||||
DefinedSyntheticKind,
|
DefinedSyntheticKind,
|
||||||
|
|
||||||
UndefinedKind,
|
UndefinedKind,
|
||||||
LazyKind,
|
LazyArchiveKind,
|
||||||
|
LazyObjectKind,
|
||||||
|
|
||||||
LastDefinedCOFFKind = DefinedCommonKind,
|
LastDefinedCOFFKind = DefinedCommonKind,
|
||||||
LastDefinedKind = DefinedSyntheticKind,
|
LastDefinedKind = DefinedSyntheticKind,
|
||||||
|
@ -79,6 +80,10 @@ public:
|
||||||
// after calling markLive.
|
// after calling markLive.
|
||||||
bool isLive() const;
|
bool isLive() const;
|
||||||
|
|
||||||
|
bool isLazy() const {
|
||||||
|
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend SymbolTable;
|
friend SymbolTable;
|
||||||
explicit Symbol(Kind k, StringRef n = "")
|
explicit Symbol(Kind k, StringRef n = "")
|
||||||
|
@ -256,26 +261,29 @@ private:
|
||||||
// This class represents a symbol defined in an archive file. It is
|
// 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
|
// created from an archive file header, and it knows how to load an
|
||||||
// object file from an archive to replace itself with a defined
|
// object file from an archive to replace itself with a defined
|
||||||
// symbol. If the resolver finds both Undefined and Lazy for
|
// symbol. If the resolver finds both Undefined and LazyArchive for
|
||||||
// the same name, it will ask the Lazy to load a file.
|
// the same name, it will ask the LazyArchive to load a file.
|
||||||
class Lazy : public Symbol {
|
class LazyArchive : public Symbol {
|
||||||
public:
|
public:
|
||||||
Lazy(ArchiveFile *f, const Archive::Symbol s)
|
LazyArchive(ArchiveFile *f, const Archive::Symbol s)
|
||||||
: Symbol(LazyKind, s.getName()), file(f), sym(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();
|
MemoryBufferRef getMemberBuffer();
|
||||||
|
|
||||||
ArchiveFile *file;
|
ArchiveFile *file;
|
||||||
|
|
||||||
private:
|
|
||||||
friend SymbolTable;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const Archive::Symbol sym;
|
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.
|
// Undefined symbols.
|
||||||
class Undefined : public Symbol {
|
class Undefined : public Symbol {
|
||||||
public:
|
public:
|
||||||
|
@ -381,7 +389,8 @@ inline uint64_t Defined::getRVA() {
|
||||||
return cast<DefinedCommon>(this)->getRVA();
|
return cast<DefinedCommon>(this)->getRVA();
|
||||||
case DefinedRegularKind:
|
case DefinedRegularKind:
|
||||||
return cast<DefinedRegular>(this)->getRVA();
|
return cast<DefinedRegular>(this)->getRVA();
|
||||||
case LazyKind:
|
case LazyArchiveKind:
|
||||||
|
case LazyObjectKind:
|
||||||
case UndefinedKind:
|
case UndefinedKind:
|
||||||
llvm_unreachable("Cannot get the address for an undefined symbol.");
|
llvm_unreachable("Cannot get the address for an undefined symbol.");
|
||||||
}
|
}
|
||||||
|
@ -404,7 +413,8 @@ inline Chunk *Defined::getChunk() {
|
||||||
return cast<DefinedLocalImport>(this)->getChunk();
|
return cast<DefinedLocalImport>(this)->getChunk();
|
||||||
case DefinedCommonKind:
|
case DefinedCommonKind:
|
||||||
return cast<DefinedCommon>(this)->getChunk();
|
return cast<DefinedCommon>(this)->getChunk();
|
||||||
case LazyKind:
|
case LazyArchiveKind:
|
||||||
|
case LazyObjectKind:
|
||||||
case UndefinedKind:
|
case UndefinedKind:
|
||||||
llvm_unreachable("Cannot get the chunk of an undefined symbol.");
|
llvm_unreachable("Cannot get the chunk of an undefined symbol.");
|
||||||
}
|
}
|
||||||
|
@ -419,11 +429,12 @@ union SymbolUnion {
|
||||||
alignas(DefinedCommon) char b[sizeof(DefinedCommon)];
|
alignas(DefinedCommon) char b[sizeof(DefinedCommon)];
|
||||||
alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)];
|
alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)];
|
||||||
alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)];
|
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(Undefined) char f[sizeof(Undefined)];
|
||||||
alignas(DefinedImportData) char g[sizeof(DefinedImportData)];
|
alignas(DefinedImportData) char g[sizeof(DefinedImportData)];
|
||||||
alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
|
alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
|
||||||
alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
|
alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
|
||||||
|
alignas(LazyObject) char j[sizeof(LazyObject)];
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename... ArgT>
|
template <typename T, typename... ArgT>
|
||||||
|
|
|
@ -1519,7 +1519,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
|
||||||
// Absolute is never code, synthetic generally isn't and usually isn't
|
// Absolute is never code, synthetic generally isn't and usually isn't
|
||||||
// determinable.
|
// determinable.
|
||||||
break;
|
break;
|
||||||
case Symbol::LazyKind:
|
case Symbol::LazyArchiveKind:
|
||||||
|
case Symbol::LazyObjectKind:
|
||||||
case Symbol::UndefinedKind:
|
case Symbol::UndefinedKind:
|
||||||
// Undefined symbols resolve to zero, so they don't have an RVA. Lazy
|
// Undefined symbols resolve to zero, so they don't have an RVA. Lazy
|
||||||
// symbols shouldn't have relocations.
|
// symbols shouldn't have relocations.
|
||||||
|
|
|
@ -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"}
|
|
@ -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"}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue