llvm-project/lld/lib/ReaderWriter/FileArchive.cpp
Rui Ueyama 3a8d7e2f10 [PATCH] Speculatively instantiate archive members
LLD parses archive file index table only at first. When it finds a symbol
it is looking for is defined in a member file in an archive file, it actually
reads the member from the archive file. That's done in the core linker.

That's a single-thread process since the core linker is single threaded.
If your command line contains a few object files and a lot of archive files
(which is quite often the case), LLD hardly utilizes hardware parallelism.

This patch improves parallelism by speculatively instantiating archive
file members. At the beginning of the core linking, we first create a map
containing all symbols defined in all members, and each time we find a
new undefined symbol, we instantiate a member file containing the
symbol (if such file exists). File instantiation is side effect free, so this
should not affect correctness.

This is a quick benchmark result. Time to link self-link LLD executable:

Linux   9.78s -> 8.50s (0.86x)
Windows 6.18s -> 4.51s (0.73x)

http://reviews.llvm.org/D7015

llvm-svn: 226336
2015-01-16 22:44:50 +00:00

308 lines
9.8 KiB
C++

//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/LinkingContext.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ObjectFile.h"
#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>
using llvm::object::Archive;
using llvm::object::ObjectFile;
using llvm::object::SymbolRef;
using llvm::object::symbol_iterator;
using llvm::object::object_error;
namespace lld {
namespace {
/// \brief The FileArchive class represents an Archive Library file
class FileArchive : public lld::ArchiveLibraryFile {
public:
FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry &reg,
StringRef path, bool logLoading)
: ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())),
_registry(reg), _logLoading(logLoading) {}
virtual ~FileArchive() {}
/// \brief Check if any member of the archive contains an Atom with the
/// specified name and return the File object for that member, or nullptr.
const File *find(StringRef name, bool dataSymbolOnly) const override {
auto member = _symbolMemberMap.find(name);
if (member == _symbolMemberMap.end())
return nullptr;
Archive::child_iterator ci = member->second;
// Don't return a member already returned
const char *memberStart = ci->getBuffer().data();
if (_membersInstantiated.count(memberStart))
return nullptr;
if (dataSymbolOnly && !isDataSymbol(ci, name))
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;
// give up the pointer so that this object no longer manages it
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 {
if (std::error_code ec = parse())
return ec;
for (auto mf = _archive->child_begin(), me = _archive->child_end();
mf != me; ++mf) {
std::unique_ptr<File> file;
if (std::error_code ec = instantiateMember(mf, file))
return ec;
result.push_back(std::move(file));
}
return std::error_code();
}
const atom_collection<DefinedAtom> &defined() const override {
return _definedAtoms;
}
const atom_collection<UndefinedAtom> &undefined() const override {
return _undefinedAtoms;
}
const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
return _sharedLibraryAtoms;
}
const atom_collection<AbsoluteAtom> &absolute() const override {
return _absoluteAtoms;
}
std::error_code buildTableOfContents() {
DEBUG_WITH_TYPE("FileArchive", llvm::dbgs()
<< "Table of contents for archive '"
<< _archive->getFileName() << "':\n");
for (auto i = _archive->symbol_begin(), e = _archive->symbol_end();
i != e; ++i) {
StringRef name = i->getName();
ErrorOr<Archive::child_iterator> memberOrErr = i->getMember();
if (std::error_code ec = memberOrErr.getError())
return ec;
Archive::child_iterator member = memberOrErr.get();
DEBUG_WITH_TYPE(
"FileArchive",
llvm::dbgs() << llvm::format("0x%08llX ", member->getBuffer().data())
<< "'" << name << "'\n");
_symbolMemberMap[name] = member;
}
return std::error_code();
}
/// Returns a set of all defined symbols in the archive.
std::set<StringRef> getDefinedSymbols() override {
parse();
std::set<StringRef> ret;
for (const auto &e : _symbolMemberMap)
ret.insert(e.first);
return ret;
}
protected:
std::error_code doParse() override {
// Make Archive object which will be owned by FileArchive object.
std::error_code ec;
_archive.reset(new Archive(_mb->getMemBufferRef(), ec));
if (ec)
return ec;
if ((ec = buildTableOfContents()))
return ec;
return std::error_code();
}
private:
std::error_code
instantiateMember(Archive::child_iterator member,
std::unique_ptr<File> &result) const {
ErrorOr<llvm::MemoryBufferRef> mbOrErr = member->getMemoryBufferRef();
if (std::error_code ec = mbOrErr.getError())
return ec;
llvm::MemoryBufferRef mb = mbOrErr.get();
std::string memberPath = (_archive->getFileName() + "("
+ mb.getBufferIdentifier() + ")").str();
if (_logLoading)
llvm::errs() << memberPath << "\n";
std::unique_ptr<MemoryBuffer> memberMB(MemoryBuffer::getMemBuffer(
mb.getBuffer(), memberPath, false));
std::vector<std::unique_ptr<File>> files;
if (std::error_code ec = _registry.loadFile(std::move(memberMB), files))
return ec;
assert(files.size() == 1);
result = std::move(files[0]);
if (std::error_code ec = result->parse())
return ec;
// The memory buffer is co-owned by the archive file and the children,
// so that the bufffer is deallocated when all the members are destructed.
result->setSharedMemoryBuffer(_mb);
return std::error_code();
}
// Parses the given memory buffer as an object file, and returns true
// code if the given symbol is a data symbol. If the symbol is not a data
// symbol or does not exist, returns false.
bool isDataSymbol(Archive::child_iterator member, StringRef symbol) const {
ErrorOr<llvm::MemoryBufferRef> buf = member->getMemoryBufferRef();
if (buf.getError())
return false;
std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer(
buf.get().getBuffer(), buf.get().getBufferIdentifier(), false));
auto objOrErr(ObjectFile::createObjectFile(mb->getMemBufferRef()));
if (objOrErr.getError())
return false;
std::unique_ptr<ObjectFile> obj = std::move(objOrErr.get());
SymbolRef::Type symtype;
uint32_t symflags;
symbol_iterator ibegin = obj->symbol_begin();
symbol_iterator iend = obj->symbol_end();
StringRef symbolname;
for (symbol_iterator i = ibegin; i != iend; ++i) {
// Get symbol name
if (i->getName(symbolname))
return false;
if (symbolname != symbol)
continue;
// Get symbol flags
symflags = i->getFlags();
if (symflags <= SymbolRef::SF_Undefined)
continue;
// Get Symbol Type
if (i->getType(symtype))
return false;
if (symtype == SymbolRef::ST_Data)
return true;
}
return false;
}
private:
typedef std::unordered_map<StringRef, Archive::child_iterator> MemberMap;
typedef std::set<const char *> InstantiatedSet;
std::shared_ptr<MemoryBuffer> _mb;
const Registry &_registry;
std::unique_ptr<Archive> _archive;
mutable MemberMap _symbolMemberMap;
mutable InstantiatedSet _membersInstantiated;
atom_collection_vector<DefinedAtom> _definedAtoms;
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
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 {
public:
ArchiveReader(bool logLoading) : _logLoading(logLoading) {}
bool canParse(file_magic magic, StringRef,
const MemoryBuffer &) const override {
return (magic == llvm::sys::fs::file_magic::archive);
}
std::error_code
loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry &reg,
std::vector<std::unique_ptr<File>> &result) const override {
StringRef path = mb->getBufferIdentifier();
std::unique_ptr<FileArchive> file(
new FileArchive(std::move(mb), reg, path, _logLoading));
result.push_back(std::move(file));
return std::error_code();
}
private:
bool _logLoading;
};
} // anonymous namespace
void Registry::addSupportArchives(bool logLoading) {
add(std::unique_ptr<Reader>(new ArchiveReader(logLoading)));
}
} // end namespace lld