summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/include/lld/Core/ArchiveLibraryFile.h9
-rw-r--r--lld/include/lld/Core/File.h8
-rw-r--r--lld/include/lld/Core/Resolver.h6
-rw-r--r--lld/lib/Core/Resolver.cpp30
-rw-r--r--lld/lib/ReaderWriter/FileArchive.cpp50
-rw-r--r--lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp11
6 files changed, 106 insertions, 8 deletions
diff --git a/lld/include/lld/Core/ArchiveLibraryFile.h b/lld/include/lld/Core/ArchiveLibraryFile.h
index a6f0a3f2b89..65f393ec495 100644
--- a/lld/include/lld/Core/ArchiveLibraryFile.h
+++ b/lld/include/lld/Core/ArchiveLibraryFile.h
@@ -11,6 +11,7 @@
#define LLD_CORE_ARCHIVE_LIBRARY_FILE_H
#include "lld/Core/File.h"
+#include "lld/Core/Parallel.h"
#include <set>
namespace lld {
@@ -37,9 +38,15 @@ public:
virtual std::error_code
parseAllMembers(std::vector<std::unique_ptr<File>> &result) = 0;
+ // Parses a member file containing a given symbol, so that when you
+ // need the file find() can return that immediately. Calling this function
+ // has no side effect other than pre-instantiating a file. Calling this
+ // function doesn't affect correctness.
+ virtual void preload(TaskGroup &group, StringRef symbolName) {}
+
/// Returns a set of all defined symbols in the archive, i.e. all
/// resolvable symbol using this file.
- virtual std::set<StringRef> getDefinedSymbols() const {
+ virtual std::set<StringRef> getDefinedSymbols() {
return std::set<StringRef>();
}
diff --git a/lld/include/lld/Core/File.h b/lld/include/lld/Core/File.h
index a60985068ef..e78b7279699 100644
--- a/lld/include/lld/Core/File.h
+++ b/lld/include/lld/Core/File.h
@@ -164,6 +164,14 @@ public:
std::error_code parse();
+ // This function is called just before the core linker tries to use
+ // a file. Currently the PECOFF reader uses this to trigger the
+ // driver to parse .drectve section (which contains command line options).
+ // If you want to do something having side effects, don't do that in
+ // doParse() because a file could be pre-loaded speculatively.
+ // Use this hook instead.
+ virtual void beforeLink() {}
+
// Usually each file owns a std::unique_ptr<MemoryBuffer>.
// However, there's one special case. If a file is an archive file,
// the archive file and its children all shares the same memory buffer.
diff --git a/lld/include/lld/Core/Resolver.h b/lld/include/lld/Core/Resolver.h
index 7ccd0d2f618..63ed68b2561 100644
--- a/lld/include/lld/Core/Resolver.h
+++ b/lld/include/lld/Core/Resolver.h
@@ -10,6 +10,7 @@
#ifndef LLD_CORE_RESOLVER_H
#define LLD_CORE_RESOLVER_H
+#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/File.h"
#include "lld/Core/SharedLibraryFile.h"
#include "lld/Core/Simple.h"
@@ -63,6 +64,7 @@ private:
void maybeAddSectionGroupOrGnuLinkOnce(const DefinedAtom &atom);
/// \brief The main function that iterates over the files to resolve
+ void makePreloadArchiveMap();
bool resolveUndefines();
void updateReferences();
void deadStripOptimize();
@@ -73,6 +75,7 @@ private:
void markLive(const Atom *atom);
void addAtoms(const std::vector<const DefinedAtom *>&);
+ void maybePreloadArchiveMember(StringRef sym);
class MergedFile : public SimpleFile {
public:
@@ -93,6 +96,9 @@ private:
std::vector<File *> _files;
std::map<File *, bool> _newUndefinesAdded;
size_t _fileIndex;
+
+ // Preloading
+ std::map<StringRef, ArchiveLibraryFile *> _archiveMap;
};
} // namespace lld
diff --git a/lld/lib/Core/Resolver.cpp b/lld/lib/Core/Resolver.cpp
index aed9927c6f6..79432bebb84 100644
--- a/lld/lib/Core/Resolver.cpp
+++ b/lld/lib/Core/Resolver.cpp
@@ -31,9 +31,12 @@ bool Resolver::handleFile(const File &file) {
bool undefAdded = false;
for (const DefinedAtom *atom : file.defined())
doDefinedAtom(*atom);
- for (const UndefinedAtom *atom : file.undefined())
- if (doUndefinedAtom(*atom))
+ for (const UndefinedAtom *atom : file.undefined()) {
+ if (doUndefinedAtom(*atom)) {
undefAdded = true;
+ maybePreloadArchiveMember(atom->name());
+ }
+ }
for (const SharedLibraryAtom *atom : file.sharedLibrary())
doSharedLibraryAtom(*atom);
for (const AbsoluteAtom *atom : file.absolute())
@@ -229,6 +232,17 @@ void Resolver::addAtoms(const std::vector<const DefinedAtom *> &newAtoms) {
doDefinedAtom(*newAtom);
}
+// Instantiate an archive file member if there's a file containing a
+// defined symbol for a given symbol name. Instantiation is done in a
+// different worker thread and has no visible side effect.
+void Resolver::maybePreloadArchiveMember(StringRef sym) {
+ auto it = _archiveMap.find(sym);
+ if (it == _archiveMap.end())
+ return;
+ ArchiveLibraryFile *archive = it->second;
+ archive->preload(_context.getTaskGroup(), sym);
+}
+
// Returns true if at least one of N previous files has created an
// undefined symbol.
bool Resolver::undefinesAdded(int begin, int end) {
@@ -261,6 +275,16 @@ File *Resolver::getFile(int &index, int &groupLevel) {
return cast<FileNode>(inputs[index++].get())->getFile();
}
+// Make a map of Symbol -> ArchiveFile.
+void Resolver::makePreloadArchiveMap() {
+ std::vector<std::unique_ptr<Node>> &nodes = _context.getNodes();
+ for (auto it = nodes.rbegin(), e = nodes.rend(); it != e; ++it)
+ if (auto *fnode = dyn_cast<FileNode>(it->get()))
+ if (auto *archive = dyn_cast<ArchiveLibraryFile>(fnode->getFile()))
+ for (StringRef sym : archive->getDefinedSymbols())
+ _archiveMap[sym] = archive;
+}
+
// Keep adding atoms until _context.getNextFile() returns an error. This
// function is where undefined atoms are resolved.
bool Resolver::resolveUndefines() {
@@ -277,6 +301,7 @@ bool Resolver::resolveUndefines() {
<< ": " << ec.message() << "\n";
return false;
}
+ file->beforeLink();
switch (file->kind()) {
case File::kindObject:
if (groupLevel > 0)
@@ -446,6 +471,7 @@ void Resolver::removeCoalescedAwayAtoms() {
}
bool Resolver::resolve() {
+ makePreloadArchiveMap();
if (!resolveUndefines())
return false;
updateReferences();
diff --git a/lld/lib/ReaderWriter/FileArchive.cpp b/lld/lib/ReaderWriter/FileArchive.cpp
index a344fdf72a7..fe336aa4cea 100644
--- a/lld/lib/ReaderWriter/FileArchive.cpp
+++ b/lld/lib/ReaderWriter/FileArchive.cpp
@@ -17,7 +17,9 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
+#include <future>
#include <memory>
+#include <mutex>
#include <set>
#include <unordered_map>
@@ -57,6 +59,17 @@ public:
return nullptr;
_membersInstantiated.insert(memberStart);
+
+ // Check if a file is preloaded.
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ auto it = _preloaded.find(memberStart);
+ if (it != _preloaded.end()) {
+ std::future<const File *> &future = it->second;
+ return future.get();
+ }
+ }
+
std::unique_ptr<File> result;
if (instantiateMember(ci, result))
return nullptr;
@@ -65,6 +78,37 @@ public:
return result.release();
}
+ // Instantiate a member file containing a given symbol name.
+ void preload(TaskGroup &group, StringRef name) override {
+ auto member = _symbolMemberMap.find(name);
+ if (member == _symbolMemberMap.end())
+ return;
+ Archive::child_iterator ci = member->second;
+
+ // Do nothing if a member is already instantiated.
+ const char *memberStart = ci->getBuffer().data();
+ if (_membersInstantiated.count(memberStart))
+ return;
+
+ std::lock_guard<std::mutex> lock(_mutex);
+ if (_preloaded.find(memberStart) != _preloaded.end())
+ return;
+
+ // Instantiate the member
+ auto *promise = new std::promise<const File *>;
+ _preloaded[memberStart] = promise->get_future();
+ _promises.push_back(std::unique_ptr<std::promise<const File *>>(promise));
+
+ group.spawn([=] {
+ std::unique_ptr<File> result;
+ if (instantiateMember(ci, result)) {
+ promise->set_value(nullptr);
+ return;
+ }
+ promise->set_value(result.release());
+ });
+ }
+
/// \brief parse each member
std::error_code
parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
@@ -117,7 +161,8 @@ public:
}
/// Returns a set of all defined symbols in the archive.
- std::set<StringRef> getDefinedSymbols() const override {
+ std::set<StringRef> getDefinedSymbols() override {
+ parse();
std::set<StringRef> ret;
for (const auto &e : _symbolMemberMap)
ret.insert(e.first);
@@ -225,6 +270,9 @@ private:
atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
bool _logLoading;
mutable std::vector<std::unique_ptr<MemoryBuffer>> _memberBuffers;
+ mutable std::map<const char *, std::future<const File *>> _preloaded;
+ mutable std::vector<std::unique_ptr<std::promise<const File *>>> _promises;
+ mutable std::mutex _mutex;
};
class ArchiveReader : public Reader {
diff --git a/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp b/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp
index adcee29f649..dd261614c1c 100644
--- a/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp
+++ b/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp
@@ -106,6 +106,8 @@ public:
return _absoluteAtoms;
}
+ void beforeLink() override;
+
void addDefinedAtom(AliasAtom *atom) {
atom->setOrdinal(_ordinal++);
_definedAtoms._atoms.push_back(atom);
@@ -382,7 +384,10 @@ std::error_code FileCOFF::doParse() {
// The mapping for /alternatename is in the context object. This helper
// function iterate over defined atoms and create alias atoms if needed.
createAlternateNameAtoms();
+ return std::error_code();
+}
+void FileCOFF::beforeLink() {
// Acquire the mutex to mutate _ctx.
std::lock_guard<std::recursive_mutex> lock(_ctx.getMutex());
@@ -392,10 +397,8 @@ std::error_code FileCOFF::doParse() {
_ctx.setSafeSEH(false);
if (_ctx.deadStrip())
- for (StringRef sym : undefinedSymbols)
- _ctx.addDeadStripRoot(sym);
-
- return std::error_code();
+ for (const UndefinedAtom *undef : undefined())
+ _ctx.addDeadStripRoot(undef->name());
}
/// Iterate over the symbol table to retrieve all symbols.
OpenPOWER on IntegriCloud