Make it possible to add observers to an Input Graph, so that files returned from an Input Graph can be examined before they are passed to Resolver. To implement some PE/COFF features we need to know all the symbols that *can* be solved, including ones in archive files that are not yet to be read. Currently, Resolver only maintains a set of symbols that are already read. It has no knowledge on symbols in skipped files in an archive file. There are many ways to implement that. I chose to apply the observer pattern here because it seems most non-intrusive. We don't want to mess up Resolver with architecture specific features. Even in PE/COFF, the feature that needs this mechanism is minor. So I chose not to modify Resolver, but add a hook to Input Graph. Differential Revision: http://reviews.llvm.org/D3735 llvm-svn: 208753
125 lines
4.1 KiB
C++
125 lines
4.1 KiB
C++
//===- lib/Core/InputGraph.cpp --------------------------------------------===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lld/Core/InputGraph.h"
|
|
|
|
#include "lld/Core/Resolver.h"
|
|
|
|
#include <memory>
|
|
|
|
using namespace lld;
|
|
|
|
ErrorOr<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) {
|
|
ErrorOr<File &> next = _currentInputElement->getNextFile();
|
|
if (next.getError() != InputGraphError::no_more_files) {
|
|
for (llvm::function_ref<void(File *)> observer : _observers)
|
|
observer(&next.get());
|
|
return std::move(next);
|
|
}
|
|
}
|
|
|
|
ErrorOr<InputElement *> elt = getNextInputElement();
|
|
if (elt.getError() == InputGraphError::no_more_elements || *elt == nullptr)
|
|
return make_error_code(InputGraphError::no_more_files);
|
|
_currentInputElement = *elt;
|
|
}
|
|
}
|
|
|
|
void InputGraph::notifyProgress() { _currentInputElement->notifyProgress(); }
|
|
|
|
void InputGraph::registerObserver(llvm::function_ref<void(File *)> fn) {
|
|
_observers.push_back(fn);
|
|
}
|
|
|
|
void InputGraph::addInputElement(std::unique_ptr<InputElement> ie) {
|
|
_inputArgs.push_back(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 Insert element at position
|
|
void InputGraph::insertElementAt(std::unique_ptr<InputElement> element,
|
|
Position position) {
|
|
if (position == InputGraph::Position::BEGIN) {
|
|
_inputArgs.insert(_inputArgs.begin(), std::move(element));
|
|
return;
|
|
}
|
|
assert(position == InputGraph::Position::END);
|
|
_inputArgs.push_back(std::move(element));
|
|
}
|
|
|
|
/// \brief Helper functions for the resolver
|
|
ErrorOr<InputElement *> InputGraph::getNextInputElement() {
|
|
if (_nextElementIndex >= _inputArgs.size())
|
|
return make_error_code(InputGraphError::no_more_elements);
|
|
return _inputArgs[_nextElementIndex++].get();
|
|
}
|
|
|
|
void InputGraph::normalize() {
|
|
for (std::unique_ptr<InputElement> &elt : _inputArgs)
|
|
elt->expand();
|
|
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);
|
|
}
|
|
|
|
/// \brief Read the file into _buffer.
|
|
error_code FileNode::getBuffer(StringRef filePath) {
|
|
// Create a memory buffer
|
|
std::unique_ptr<MemoryBuffer> mb;
|
|
if (error_code ec = MemoryBuffer::getFileOrSTDIN(filePath, mb))
|
|
return ec;
|
|
_buffer = std::move(mb);
|
|
return error_code::success();
|
|
}
|
|
|
|
/// \brief Return the next file that need to be processed by the resolver.
|
|
/// This also processes input elements depending on the resolve status
|
|
/// of the input elements contained in the group.
|
|
ErrorOr<File &> Group::getNextFile() {
|
|
// If there are no elements, move on to the next input element
|
|
if (_elements.empty())
|
|
return make_error_code(InputGraphError::no_more_files);
|
|
|
|
for (;;) {
|
|
// If we have processed all the elements, and have made no progress on
|
|
// linking, we cannot resolve any symbol from this group. Continue to the
|
|
// next one by returning no_more_files.
|
|
if (_nextElementIndex == _elements.size()) {
|
|
if (!_madeProgress)
|
|
return make_error_code(InputGraphError::no_more_files);
|
|
resetNextIndex();
|
|
}
|
|
|
|
_currentElementIndex = _nextElementIndex;
|
|
auto file = _elements[_nextElementIndex]->getNextFile();
|
|
// Move on to the next element if we have finished processing all
|
|
// the files in the input element
|
|
if (file.getError() == InputGraphError::no_more_files) {
|
|
_nextElementIndex++;
|
|
continue;
|
|
}
|
|
return *file;
|
|
}
|
|
}
|