Re-commit r225766, r225767, r225769, r225814, r225816, r225829, and r225832.

These changes depended on r225674 and had been rolled back in r225859.
Because r225674 has been re-submitted, it's safe to re-submit them.

llvm-svn: 226132
This commit is contained in:
Rui Ueyama 2015-01-15 06:49:21 +00:00
parent 8afb37e1a1
commit 80c04431ca
14 changed files with 114 additions and 349 deletions

View File

@ -49,18 +49,7 @@ public:
typedef FileVectorT::iterator FileIterT;
/// \brief Initialize the inputgraph
InputGraph() : _nextElementIndex(0), _currentInputElement(nullptr) {}
virtual ~InputGraph();
/// getNextFile returns the next file that needs to be processed by
/// the resolver. When there are no more files to be processed, an
/// nullptr is returned.
File *getNextFile();
/// Adds an observer of getNextFile(). Each time a new file is about to be
/// returned from getNextFile(), registered observers are called with the file
/// being returned.
void registerObserver(std::function<void(File *)>);
InputGraph() : _index(0) {}
/// \brief Adds a node into the InputGraph
void addInputElement(std::unique_ptr<InputElement>);
@ -68,34 +57,16 @@ public:
/// \brief Adds a node at the beginning of the InputGraph
void addInputElementFront(std::unique_ptr<InputElement>);
/// Normalize the InputGraph. It calls getReplacements() on each element.
void normalize();
InputElementVectorT &inputElements() {
return _inputArgs;
}
// Returns the current group size if we are at an --end-group.
// Otherwise returns 0.
int getGroupSize();
void skipGroup();
InputElementVectorT &inputElements() { return _inputArgs; }
// \brief Returns the number of input files.
size_t size() const { return _inputArgs.size(); }
/// \brief Dump the input Graph
bool dump(raw_ostream &diagnostics = llvm::errs());
protected:
// Input arguments
InputElementVectorT _inputArgs;
// Index of the next element to be processed
uint32_t _nextElementIndex;
InputElement *_currentInputElement;
std::vector<std::function<void(File *)>> _observers;
private:
InputElement *getNextInputElement();
size_t _index;
};
/// \brief This describes each element in the InputGraph. The Kind
@ -125,11 +96,6 @@ public:
/// Get the next file to be processed by the resolver
virtual File *getNextFile() = 0;
/// Get the elements that we want to expand with.
virtual bool getReplacements(InputGraph::InputElementVectorT &) {
return false;
}
protected:
Kind _kind; // The type of the Element
};
@ -165,9 +131,13 @@ private:
class FileNode : public InputElement {
public:
FileNode(StringRef path)
: InputElement(InputElement::Kind::File), _path(path), _nextFileIndex(0) {
: InputElement(InputElement::Kind::File), _path(path), _done(false) {
}
FileNode(StringRef path, std::unique_ptr<File> f)
: InputElement(InputElement::Kind::File), _path(path), _file(std::move(f)),
_done(false) {}
virtual ErrorOr<StringRef> getPath(const LinkingContext &) const {
return _path;
}
@ -186,36 +156,33 @@ public:
}
/// \brief Get the list of files
range<InputGraph::FileIterT> files() {
return make_range(_files.begin(), _files.end());
}
File *getFile() { return _file.get(); }
/// \brief add a file to the list of files
virtual void addFiles(InputGraph::FileVectorT files) {
assert(files.size() == 1);
assert(_files.empty());
for (std::unique_ptr<File> &ai : files)
_files.push_back(std::move(ai));
assert(!_file);
_file = std::move(files[0]);
}
bool getReplacements(InputGraph::InputElementVectorT &result) override;
/// \brief Return the next File thats part of this node to the
/// resolver.
File *getNextFile() override {
if (_nextFileIndex == _files.size())
assert(_file);
if (_done)
return nullptr;
return _files[_nextFileIndex++].get();
_done = true;
return _file.get();
}
std::error_code parse(const LinkingContext &, raw_ostream &) override;
protected:
StringRef _path; // The path of the Input file
InputGraph::FileVectorT _files; // A vector of lld File objects
std::unique_ptr<File> _file; // An lld File object
// The next file that would be processed by the resolver
uint32_t _nextFileIndex;
bool _done;
};
/// \brief Represents Internal Input files
@ -223,15 +190,13 @@ class SimpleFileNode : public FileNode {
public:
SimpleFileNode(StringRef path) : FileNode(path) {}
SimpleFileNode(StringRef path, std::unique_ptr<File> f)
: FileNode(path) {
_files.push_back(std::move(f));
}
: FileNode(path, std::move(f)) {}
virtual ~SimpleFileNode() {}
/// \brief add a file to the list of files
virtual void appendInputFile(std::unique_ptr<File> f) {
_files.push_back(std::move(f));
_file = std::move(f);
}
};

View File

@ -55,8 +55,8 @@ public:
private:
typedef std::function<void(StringRef, bool)> UndefCallback;
bool undefinesAdded(int count);
File *nextFile(bool &inGroup);
bool undefinesAdded(int begin, int end);
File *getFile(int &index, int &groupLevel);
/// \brief Add section group/.gnu.linkonce if it does not exist previously.
void maybeAddSectionGroupOrGnuLinkOnce(const DefinedAtom &atom);

View File

@ -21,9 +21,7 @@ namespace lld {
class WrapperNode : public FileNode {
public:
WrapperNode(std::unique_ptr<File> file) : FileNode(file->path()) {
_files.push_back(std::move(file));
}
WrapperNode(std::unique_ptr<File> f) : FileNode(f->path(), std::move(f)) {}
};
}

View File

@ -30,6 +30,28 @@ static const uint8_t DEFAULT_DOS_STUB[128] = {'M', 'Z'};
namespace lld {
namespace pecoff {
class ResolvableSymbols {
public:
void add(File *file);
const std::set<std::string> &defined() {
readAllSymbols();
return _defined;
}
private:
// Files are read lazily, so that it has no runtime overhead if
// no one accesses this class.
void readAllSymbols();
std::set<std::string> _defined;
std::set<File *> _seen;
std::set<File *> _queue;
std::mutex _mutex;
};
} // end namespace pecoff
class PECOFFLinkingContext : public LinkingContext {
public:
PECOFFLinkingContext()
@ -328,6 +350,10 @@ public:
std::recursive_mutex &getMutex() { return _mutex; }
pecoff::ResolvableSymbols *getResolvableSymsFile() {
return &_resolvableSyms;
}
protected:
/// Method to create a internal file for the entry symbol
std::unique_ptr<File> createEntrySymbolFile() const override;
@ -442,6 +468,8 @@ private:
// Name of the temporary file for lib.exe subcommand. For debugging
// only.
std::string _moduleDefinitionFile;
pecoff::ResolvableSymbols _resolvableSyms;
};
} // end namespace lld

View File

@ -13,33 +13,6 @@
using namespace lld;
InputGraph::~InputGraph() { }
File *InputGraph::getNextFile() {
// Try to get the next file of _currentInputElement. If the current input
// element points to an archive file, and there's a file left in the archive,
// it will succeed. If not, try to get the next file in the input graph.
for (;;) {
if (_currentInputElement) {
File *next = _currentInputElement->getNextFile();
if (next) {
for (const std::function<void(File *)> &observer : _observers)
observer(next);
return next;
}
}
InputElement *elt = getNextInputElement();
if (!elt)
return nullptr;
_currentInputElement = elt;
}
}
void InputGraph::registerObserver(std::function<void(File *)> fn) {
_observers.push_back(fn);
}
void InputGraph::addInputElement(std::unique_ptr<InputElement> ie) {
_inputArgs.push_back(std::move(ie));
}
@ -48,63 +21,9 @@ void InputGraph::addInputElementFront(std::unique_ptr<InputElement> ie) {
_inputArgs.insert(_inputArgs.begin(), std::move(ie));
}
bool InputGraph::dump(raw_ostream &diagnostics) {
for (std::unique_ptr<InputElement> &ie : _inputArgs)
if (!ie->dump(diagnostics))
return false;
return true;
}
/// \brief Helper functions for the resolver
InputElement *InputGraph::getNextInputElement() {
if (_nextElementIndex >= _inputArgs.size())
return nullptr;
InputElement *elem = _inputArgs[_nextElementIndex++].get();
if (isa<GroupEnd>(elem))
return getNextInputElement();
return elem;
}
void InputGraph::normalize() {
std::vector<std::unique_ptr<InputElement>> vec;
for (std::unique_ptr<InputElement> &elt : _inputArgs) {
if (elt->getReplacements(vec))
continue;
vec.push_back(std::move(elt));
}
_inputArgs = std::move(vec);
}
// If we are at the end of a group, return its size (which indicates
// how many files we need to go back in the command line).
// Returns 0 if we are not at the end of a group.
int InputGraph::getGroupSize() {
if (_nextElementIndex >= _inputArgs.size())
return 0;
InputElement *elem = _inputArgs[_nextElementIndex].get();
if (const GroupEnd *group = dyn_cast<GroupEnd>(elem))
return group->getSize();
return 0;
}
void InputGraph::skipGroup() {
if (_nextElementIndex >= _inputArgs.size())
return;
if (isa<GroupEnd>(_inputArgs[_nextElementIndex].get()))
_nextElementIndex++;
}
bool FileNode::getReplacements(InputGraph::InputElementVectorT &result) {
if (_files.size() < 2)
return false;
for (std::unique_ptr<File> &file : _files)
result.push_back(llvm::make_unique<SimpleFileNode>(_path, std::move(file)));
return true;
}
std::error_code FileNode::parse(const LinkingContext &, raw_ostream &) {
for (std::unique_ptr<File> &file : _files)
if (std::error_code ec = file->parse())
if (_file)
if (std::error_code ec = _file->parse())
return ec;
return std::error_code();
}

View File

@ -231,52 +231,52 @@ void Resolver::addAtoms(const std::vector<const DefinedAtom *> &newAtoms) {
// Returns true if at least one of N previous files has created an
// undefined symbol.
bool Resolver::undefinesAdded(int n) {
for (size_t i = _fileIndex - n; i < _fileIndex; ++i)
if (_newUndefinesAdded[_files[i]])
return true;
bool Resolver::undefinesAdded(int begin, int end) {
std::vector<std::unique_ptr<InputElement>> &inputs =
_context.getInputGraph().inputElements();
for (int i = begin; i < end; ++i)
if (FileNode *node = dyn_cast<FileNode>(inputs[i].get()))
if (_newUndefinesAdded[node->getFile()])
return true;
return false;
}
File *Resolver::nextFile(bool &inGroup) {
if (size_t groupSize = _context.getInputGraph().getGroupSize()) {
File *Resolver::getFile(int &index, int &groupLevel) {
std::vector<std::unique_ptr<InputElement>> &inputs
= _context.getInputGraph().inputElements();
if ((size_t)index >= inputs.size())
return nullptr;
if (GroupEnd *group = dyn_cast<GroupEnd>(inputs[index].get())) {
// We are at the end of the current group. If one or more new
// undefined atom has been added in the last groupSize files, we
// reiterate over the files.
if (undefinesAdded(groupSize))
_fileIndex -= groupSize;
_context.getInputGraph().skipGroup();
return nextFile(inGroup);
int size = group->getSize();
if (undefinesAdded(index - size, index)) {
index -= size;
++groupLevel;
return getFile(index, groupLevel);
}
++index;
--groupLevel;
return getFile(index, groupLevel);
}
if (_fileIndex < _files.size()) {
// We are still in the current group.
inGroup = true;
return _files[_fileIndex++];
}
// We are not in a group. Get a new file.
File *file = _context.getInputGraph().getNextFile();
if (!file)
return nullptr;
_files.push_back(&*file);
++_fileIndex;
inGroup = false;
return file;
return cast<FileNode>(inputs[index++].get())->getFile();
}
// Keep adding atoms until _context.getNextFile() returns an error. This
// function is where undefined atoms are resolved.
void Resolver::resolveUndefines() {
ScopedTask task(getDefaultDomain(), "resolveUndefines");
int index = 0;
int groupLevel = 0;
for (;;) {
bool inGroup = false;
bool undefAdded = false;
File *file = nextFile(inGroup);
File *file = getFile(index, groupLevel);
if (!file)
return;
switch (file->kind()) {
case File::kindObject:
if (inGroup)
if (groupLevel > 0)
break;
assert(!file->hasOrdinal());
file->setOrdinal(_context.getNextOrdinalAndIncrement());
@ -293,7 +293,7 @@ void Resolver::resolveUndefines() {
handleSharedLibrary(*file);
break;
}
_newUndefinesAdded[&*file] = undefAdded;
_newUndefinesAdded[file] = undefAdded;
}
}

View File

@ -77,7 +77,6 @@ bool Driver::link(LinkingContext &context, raw_ostream &diagnostics) {
InputGraph &inputGraph = context.getInputGraph();
if (!inputGraph.size())
return false;
inputGraph.normalize();
bool fail = false;

View File

@ -689,9 +689,6 @@ bool GnuLdDriver::parse(int argc, const char *argv[],
}
}
if (ctx->outputFileType() == LinkingContext::OutputFileType::YAML)
inputGraph->dump(diagnostics);
// Validate the combination of options used.
if (!ctx->validate(diagnostics))
return false;

View File

@ -1416,6 +1416,7 @@ bool WinLinkDriver::parse(int argc, const char *argv[],
if (isReadingDirectiveSection)
if (file->parse())
return false;
ctx.getResolvableSymsFile()->add(file.get());
ctx.getInputGraph().addInputElement(
std::unique_ptr<InputElement>(new WrapperNode(std::move(file))));
}
@ -1437,6 +1438,7 @@ bool WinLinkDriver::parse(int argc, const char *argv[],
if (isReadingDirectiveSection)
if (file->parse())
return false;
ctx.getResolvableSymsFile()->add(file.get());
ctx.addLibraryFile(
std::unique_ptr<FileNode>(new WrapperNode(std::move(file))));
}

View File

@ -930,9 +930,7 @@ bool MachOLinkingContext::customAtomOrderer(const DefinedAtom *left,
static File *getFirstFile(const std::unique_ptr<InputElement> &elem) {
FileNode *e = dyn_cast<FileNode>(const_cast<InputElement *>(elem.get()));
if (!e || e->files().empty())
return nullptr;
return e->files()[0].get();
return e ? e->getFile() : nullptr;
}
static bool isLibrary(const std::unique_ptr<InputElement> &elem) {

View File

@ -174,45 +174,6 @@ private:
mutable llvm::BumpPtrAllocator _alloc;
};
class ResolvableSymbols {
public:
void add(File *file) {
std::lock_guard<std::mutex> lock(_mutex);
if (_seen.count(file) > 0)
return;
_seen.insert(file);
_queue.insert(file);
}
const std::set<std::string> &defined() {
readAllSymbols();
return _defined;
}
private:
// Files are read lazily, so that it has no runtime overhead if
// no one accesses this class.
void readAllSymbols() {
std::lock_guard<std::mutex> lock(_mutex);
for (File *file : _queue) {
if (auto *archive = dyn_cast<ArchiveLibraryFile>(file)) {
for (const std::string &sym : archive->getDefinedSymbols())
_defined.insert(sym);
continue;
}
for (const DefinedAtom *atom : file->defined())
if (!atom->name().empty())
_defined.insert(atom->name());
}
_queue.clear();
}
std::set<std::string> _defined;
std::set<File *> _seen;
std::set<File *> _queue;
std::mutex _mutex;
};
// A ExportedSymbolRenameFile is a virtual archive file for dllexported symbols.
//
// One usually has to specify the exact symbol name to resolve it. That's true
@ -246,7 +207,7 @@ private:
class ExportedSymbolRenameFile : public impl::VirtualArchiveLibraryFile {
public:
ExportedSymbolRenameFile(const PECOFFLinkingContext &ctx,
std::shared_ptr<ResolvableSymbols> syms)
ResolvableSymbols *syms)
: VirtualArchiveLibraryFile("<export>"), _syms(syms),
_ctx(const_cast<PECOFFLinkingContext *>(&ctx)) {
for (PECOFFLinkingContext::ExportDesc &desc : _ctx->getDllExports())
@ -258,7 +219,7 @@ public:
if (_exportedSyms.count(sym) == 0)
return nullptr;
std::string replace;
if (!findDecoratedSymbol(_ctx, _syms.get(), sym.str(), replace))
if (!findDecoratedSymbol(_ctx, _syms, sym.str(), replace))
return nullptr;
for (ExportDesc &exp : _ctx->getDllExports())
@ -271,7 +232,7 @@ public:
private:
std::set<std::string> _exportedSyms;
std::shared_ptr<ResolvableSymbols> _syms;
ResolvableSymbols *_syms;
mutable llvm::BumpPtrAllocator _alloc;
mutable PECOFFLinkingContext *_ctx;
};
@ -284,7 +245,7 @@ private:
class EntryPointFile : public SimpleFile {
public:
EntryPointFile(const PECOFFLinkingContext &ctx,
std::shared_ptr<ResolvableSymbols> syms)
ResolvableSymbols *syms)
: SimpleFile("<entry>"), _ctx(const_cast<PECOFFLinkingContext *>(&ctx)),
_syms(syms), _firstTime(true) {}
@ -316,7 +277,7 @@ private:
StringRef opt = _ctx->getEntrySymbolName();
if (!opt.empty()) {
std::string mangled;
if (findDecoratedSymbol(_ctx, _syms.get(), opt, mangled))
if (findDecoratedSymbol(_ctx, _syms, opt, mangled))
return mangled;
return _ctx->decorateSymbol(opt);
}
@ -341,7 +302,7 @@ private:
if (_syms->defined().count(sym))
return true;
std::string ignore;
return findDecoratedSymbol(_ctx, _syms.get(), sym, ignore);
return findDecoratedSymbol(_ctx, _syms, sym, ignore);
};
switch (_ctx->getSubsystem()) {
@ -372,7 +333,7 @@ private:
PECOFFLinkingContext *_ctx;
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
std::mutex _mutex;
std::shared_ptr<ResolvableSymbols> _syms;
ResolvableSymbols *_syms;
llvm::BumpPtrAllocator _alloc;
bool _firstTime;
};

View File

@ -118,9 +118,7 @@ bool PECOFFLinkingContext::createImplicitFiles(
llvm::make_unique<pecoff::LocallyImportedSymbolFile>(*this));
getInputGraph().addInputElement(std::move(impFileNode));
std::shared_ptr<pecoff::ResolvableSymbols> syms(
new pecoff::ResolvableSymbols());
getInputGraph().registerObserver([=](File *file) { syms->add(file); });
pecoff::ResolvableSymbols* syms = getResolvableSymsFile();
// Create a file for dllexported symbols.
auto exportNode = llvm::make_unique<SimpleFileNode>("<export>");
@ -331,4 +329,27 @@ void PECOFFLinkingContext::addPasses(PassManager &pm) {
pm.add(std::unique_ptr<Pass>(new pecoff::InferSubsystemPass(*this)));
}
void pecoff::ResolvableSymbols::add(File *file) {
std::lock_guard<std::mutex> lock(_mutex);
if (_seen.count(file) > 0)
return;
_seen.insert(file);
_queue.insert(file);
}
void pecoff::ResolvableSymbols::readAllSymbols() {
std::lock_guard<std::mutex> lock(_mutex);
for (File *file : _queue) {
if (auto *archive = dyn_cast<ArchiveLibraryFile>(file)) {
for (const std::string &sym : archive->getDefinedSymbols())
_defined.insert(sym);
continue;
}
for (const DefinedAtom *atom : file->defined())
if (!atom->name().empty())
_defined.insert(atom->name());
}
_queue.clear();
}
} // end namespace lld

View File

@ -4,7 +4,6 @@ add_lld_unittest(DriverTests
DarwinLdDriverTest.cpp
WinLinkDriverTest.cpp
WinLinkModuleDefTest.cpp
InputGraphTest.cpp
)
target_link_libraries(DriverTests

View File

@ -1,122 +0,0 @@
//===- lld/unittest/InputGraphTest.cpp -----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief InputGraph Tests
///
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include "lld/Core/InputGraph.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/Simple.h"
using namespace lld;
namespace {
class TestLinkingContext : public LinkingContext {
public:
Writer &writer() const override { llvm_unreachable("no writer!"); }
bool validateImpl(raw_ostream &) override { return true; }
};
class TestExpandFileNode : public SimpleFileNode {
public:
TestExpandFileNode(StringRef path) : SimpleFileNode(path) {}
/// Returns the elements replacing this node
bool getReplacements(InputGraph::InputElementVectorT &result) override {
for (std::unique_ptr<InputElement> &elt : _expandElements)
result.push_back(std::move(elt));
return true;
}
void addElement(std::unique_ptr<InputElement> element) {
_expandElements.push_back(std::move(element));
}
private:
InputGraph::InputElementVectorT _expandElements;
};
class InputGraphTest : public testing::Test {
public:
InputGraphTest() {
_ctx.setInputGraph(std::unique_ptr<InputGraph>(new InputGraph()));
_graph = &_ctx.getInputGraph();
}
StringRef getNext() {
File *file = _graph->getNextFile();
EXPECT_TRUE(file);
return file->path();
}
void expectEnd() {
File *file = _graph->getNextFile();
EXPECT_TRUE(file == nullptr);
}
protected:
TestLinkingContext _ctx;
InputGraph *_graph;
};
} // end anonymous namespace
static std::unique_ptr<SimpleFileNode> createFile(StringRef name) {
std::vector<std::unique_ptr<File>> files;
files.push_back(std::unique_ptr<SimpleFile>(new SimpleFile(name)));
std::unique_ptr<SimpleFileNode> file(new SimpleFileNode("filenode"));
file->addFiles(std::move(files));
return file;
}
TEST_F(InputGraphTest, Empty) {
expectEnd();
}
TEST_F(InputGraphTest, File) {
_graph->addInputElement(createFile("file1"));
EXPECT_EQ("file1", getNext());
expectEnd();
}
// Node expansion tests
TEST_F(InputGraphTest, Normalize) {
_graph->addInputElement(createFile("file1"));
std::unique_ptr<TestExpandFileNode> expandFile(
new TestExpandFileNode("node"));
expandFile->addElement(createFile("file2"));
expandFile->addElement(createFile("file3"));
_graph->addInputElement(std::move(expandFile));
_graph->normalize();
EXPECT_EQ("file1", getNext());
EXPECT_EQ("file2", getNext());
EXPECT_EQ("file3", getNext());
expectEnd();
}
TEST_F(InputGraphTest, Observer) {
std::vector<std::string> files;
_graph->registerObserver([&](File *file) { files.push_back(file->path()); });
_graph->addInputElement(createFile("file1"));
_graph->addInputElement(createFile("file2"));
EXPECT_EQ("file1", getNext());
EXPECT_EQ("file2", getNext());
expectEnd();
EXPECT_EQ(2U, files.size());
EXPECT_EQ("file1", files[0]);
EXPECT_EQ("file2", files[1]);
}