[inputGraph] Associate Resolve state with appropriate nodes

This associates resolveState to FileNodes. The control node derive
their resolution state from the inputElements that are contained in
it.

This makes --start-group/--end-group to work with ELF linking.

llvm-svn: 192269
This commit is contained in:
Shankar Easwaran 2013-10-09 03:40:29 +00:00
parent a3a542ff21
commit 03f7763d21
18 changed files with 218 additions and 63 deletions

View File

@ -114,10 +114,11 @@ public:
/// to start processing files as part of the inputelement from beginning.
/// reset the next file index to 0 only if the node is an archive library or
/// a shared library
virtual void resetNextFileIndex() {
virtual void resetNextIndex() {
if ((!_isWholeArchive && (_files[0]->kind() == File::kindArchiveLibrary)) ||
(_files[0]->kind() == File::kindSharedLibrary))
_nextFileIndex = 0;
setResolveState(Resolver::StateNoChange);
return;
}
@ -144,7 +145,8 @@ private:
/// \brief Represents a ELF control node
class ELFGroup : public Group {
public:
ELFGroup(ELFLinkingContext &ctx) : Group(), _elfLinkingContext(ctx) {}
ELFGroup(ELFLinkingContext &ctx, int64_t ordinal)
: Group(ordinal), _elfLinkingContext(ctx) {}
static inline bool classof(const InputElement *a) {
return a->kind() == InputElement::Kind::Control;
@ -167,42 +169,6 @@ public:
return error_code::success();
}
/// \brief Return the file that has to be processed by the resolver
/// to resolve atoms. This iterates over all the elements thats part
/// of this node.
virtual ErrorOr<File &> getNextFile() {
// Does the linker need to process the elements again ?
bool again = false;
// If there are no elements, move on to the next input element
if (_elements.size() == 0)
return make_error_code(InputGraphError::no_more_files);
// If we have processed all the elements as part of this node
// check the resolver status for each input element and if the status
// has not changed, move onto the next file.
if (_nextElementIndex == _elements.size()) {
for (auto &elem : _elements) {
if (elem->getResolverState() == Resolver::StateNoChange) {
again = true;
break;
}
}
if (!again)
return make_error_code(InputGraphError::no_more_files);
_nextElementIndex = 0;
// Reset the next file to be processed as part of each element
for (auto &elem : _elements)
elem->resetNextFileIndex();
}
auto file = _elements[_nextElementIndex]->getNextFile();
// Move on to the next element if we have finished processing all
// the files in the input element
if (error_code(file) == InputGraphError::no_more_files)
_nextElementIndex++;
else
return *file;
return getNextFile();
}
private:
const ELFLinkingContext &_elfLinkingContext;
};

View File

@ -173,21 +173,19 @@ public:
/// Get the next file to be processed by the resolver
virtual ErrorOr<File &> getNextFile() = 0;
/// \brief Set the resolver state for the element
virtual void setResolverState(int32_t state) { _resolveState = state; }
/// \brief Set the resolve state for the element
virtual void setResolveState(uint32_t state) = 0;
/// \brief Get the resolver state for the element
virtual int32_t getResolverState() const { return _resolveState; }
/// \brief Get the resolve state for the element
virtual uint32_t getResolveState() const = 0;
/// Process files again.
virtual void resetNextFileIndex() { _nextFileIndex = 0; }
/// \brief Reset the next index
virtual void resetNextIndex() = 0;
protected:
Kind _kind; // The type of the Element
int64_t _ordinal; // The ordinal value
int64_t _weight; // Weight of the file
int32_t _resolveState; // The resolve state
uint32_t _nextFileIndex; // The file that would be processed by the resolver
};
/// \brief The Control class represents a control node in the InputGraph
@ -204,7 +202,8 @@ public:
ControlNode::ControlKind::Simple,
int64_t _ordinal = -1)
: InputElement(InputElement::Kind::Control, _ordinal),
_controlKind(controlKind), _nextElementIndex(0) {}
_controlKind(controlKind), _currentElementIndex(0),
_nextElementIndex(0) {}
virtual ~ControlNode() {}
@ -233,9 +232,20 @@ public:
/// in the InputElement
virtual void assignFileOrdinals(uint64_t &startOrdinal);
virtual void resetNextIndex() {
_currentElementIndex = _nextElementIndex = 0;
for (auto &elem : _elements)
elem->resetNextIndex();
}
virtual uint32_t getResolveState() const;
virtual void setResolveState(uint32_t);
protected:
ControlKind _controlKind;
InputGraph::InputElementVectorT _elements;
uint32_t _currentElementIndex;
uint32_t _nextElementIndex;
};
@ -246,8 +256,7 @@ protected:
/// directly.
class FileNode : public InputElement {
public:
FileNode(StringRef path, int64_t ordinal = -1)
: InputElement(InputElement::Kind::File, ordinal), _path(path) {}
FileNode(StringRef path, int64_t ordinal = -1);
virtual ErrorOr<StringRef> getPath(const LinkingContext &) const {
return _path;
@ -301,13 +310,29 @@ public:
/// in the InputElement
virtual void assignFileOrdinals(uint64_t &startOrdinal);
/// \brief Reset the file index if the resolver needs to process
/// the node again.
virtual void resetNextIndex();
/// \brief Set the resolve state for the FileNode.
virtual void setResolveState(uint32_t resolveState) {
_resolveState = resolveState;
}
/// \brief Retrieve the resolve state of the FileNode.
virtual uint32_t getResolveState() const { return _resolveState; }
protected:
/// \brief Read the file into _buffer.
error_code readFile(const LinkingContext &ctx, raw_ostream &diagnostics);
StringRef _path;
InputGraph::FileVectorT _files;
std::unique_ptr<llvm::MemoryBuffer> _buffer;
StringRef _path; // The path of the Input file
InputGraph::FileVectorT _files; // A vector of lld File objects
std::unique_ptr<llvm::MemoryBuffer> _buffer; // Memory buffer to actual
// contents
uint32_t _resolveState; // The resolve state of the file
uint32_t _nextFileIndex; // The next file that would be processed by the
// resolver
};
/// \brief A Control node which contains a group of InputElements
@ -316,23 +341,26 @@ protected:
/// follow the group
class Group : public ControlNode {
public:
Group() : ControlNode(ControlNode::ControlKind::Group) {}
Group(int64_t ordinal)
: ControlNode(ControlNode::ControlKind::Group, ordinal) {}
static inline bool classof(const InputElement *a) {
return a->kind() == InputElement::Kind::Control;
}
/// \brief Process input element and add it to the group
virtual bool processInputElement(std::unique_ptr<InputElement> element) {
_elements.push_back(std::move(element));
return true;
}
virtual ErrorOr<File &> getNextFile();
};
/// \brief Represents Internal Input files
class SimpleFileNode : public InputElement {
public:
SimpleFileNode(StringRef path, int64_t ordinal = -1)
: InputElement(InputElement::Kind::SimpleFile, ordinal), _path(path) {}
SimpleFileNode(StringRef path, int64_t ordinal = -1);
virtual llvm::ErrorOr<StringRef> path(const LinkingContext &) const {
return _path;
@ -379,22 +407,35 @@ public:
return error_code::success();
}
/// \brief Return the next File thats part of this node to the
/// resolver.
virtual ErrorOr<File &> getNextFile() {
if (_nextFileIndex == _files.size())
return make_error_code(InputGraphError::no_more_files);
return *_files[_nextFileIndex++];
}
/// \brief Set the resolver state.
virtual void setResolveState(uint32_t resolveState) {
_resolveState = resolveState;
}
/// \brief Retrieve the resolve state.
virtual uint32_t getResolveState() const { return _resolveState; }
// Do nothing here.
virtual void resetNextFileIndex() {}
virtual void resetNextIndex() {}
/// \brief Assign File ordinals for files contained
/// in the InputElement
virtual void assignFileOrdinals(uint64_t &startOrdinal);
protected:
StringRef _path;
InputGraph::FileVectorT _files;
StringRef _path; // A string associated with this file.
InputGraph::FileVectorT _files; // Vector of lld::File objects
uint32_t _nextFileIndex; // The next file that would be processed by the
// resolver
uint32_t _resolveState; // The resolve state associated with this Node
};
} // namespace lld

View File

@ -77,7 +77,7 @@ bool LinkingContext::createInternalFiles(
}
void LinkingContext::setResolverState(uint32_t state) {
_currentInputElement->setResolverState(state);
_currentInputElement->setResolveState(state);
}
ErrorOr<File &> LinkingContext::nextFile() {

View File

@ -292,6 +292,7 @@ void Resolver::resolveUndefines() {
ScopedTask task(getDefaultDomain(), "resolveUndefines");
while (ErrorOr<File &> nextFile = _context.nextFile()) {
_context.setResolverState(Resolver::StateNoChange);
if (error_code(nextFile) == InputGraphError::no_more_files)
break;
if (nextFile->kind() == File::kindObject)

View File

@ -254,7 +254,7 @@ bool GnuLdDriver::parse(int argc, const char *argv[],
break;
case OPT_start_group: {
std::unique_ptr<InputElement> controlStart(new ELFGroup(*ctx));
std::unique_ptr<InputElement> controlStart(new ELFGroup(*ctx, index++));
controlNodeStack.push(controlStart.get());
(llvm::dyn_cast<ControlNode>)(controlNodeStack.top())
->processControlEnter();
@ -266,13 +266,13 @@ bool GnuLdDriver::parse(int argc, const char *argv[],
(llvm::dyn_cast<ControlNode>)(controlNodeStack.top())
->processControlExit();
controlNodeStack.pop();
return true;
break;
case OPT_INPUT:
case OPT_l: {
std::unique_ptr<InputElement> inputFile =
std::move(std::unique_ptr<InputElement>(new ELFFileNode(
*ctx, inputArg->getValue(), searchPath, index, isWholeArchive,
*ctx, inputArg->getValue(), searchPath, index++, isWholeArchive,
asNeeded, inputArg->getOption().getID() == OPT_l)));
if (controlNodeStack.empty())
inputGraph->addInputElement(std::move(inputFile));

View File

@ -53,6 +53,7 @@ bool InputGraph::dump(raw_ostream &diagnostics) {
return true;
}
/// \brief Insert element at position
void InputGraph::insertElementsAt(
std::vector<std::unique_ptr<InputElement> > inputElements,
Position position, size_t pos) {
@ -95,7 +96,11 @@ ErrorOr<void> InputGraph::setNextElementIndex(uint32_t index) {
/// is initially set to -1, if the user wants to override its ordinal,
/// let the user do it
InputElement::InputElement(Kind type, int64_t ordinal)
: _kind(type), _ordinal(ordinal), _weight(0),
: _kind(type), _ordinal(ordinal), _weight(0) {}
/// FileNode
FileNode::FileNode(StringRef path, int64_t ordinal)
: InputElement(InputElement::Kind::File, ordinal), _path(path),
_resolveState(Resolver::StateNoChange), _nextFileIndex(0) {}
/// \brief Assign File ordinals for files contained
@ -133,6 +138,14 @@ FileNode::readFile(const LinkingContext &ctx, raw_ostream &diagnostics) {
return error_code::success();
}
// Reset the next file that would be be processed by the resolver.
// Reset the resolve state too.
void FileNode::resetNextIndex() {
_nextFileIndex = 0;
setResolveState(Resolver::StateNoChange);
}
/// ControlNode
/// \brief Assign File ordinals for files contained
/// in the InputElement
@ -141,9 +154,61 @@ void ControlNode::assignFileOrdinals(uint64_t &startOrdinal) {
elem->assignFileOrdinals(startOrdinal);
}
/// \brief Get the resolver State. The return value of the resolve
/// state for a control node is the or'ed value of the resolve states
/// contained in it.
uint32_t ControlNode::getResolveState() const {
uint32_t resolveState = Resolver::StateNoChange;
for (auto &elem : _elements)
resolveState |= elem->getResolveState();
return resolveState;
}
/// \brief Set the resolve state for the current element
/// thats processed by the resolver.
void ControlNode::setResolveState(uint32_t resolveState) {
if (_elements.size() == 0)
return;
_elements[_currentElementIndex]->setResolveState(resolveState);
}
/// SimpleFileNode
SimpleFileNode::SimpleFileNode(StringRef path, int64_t ordinal)
: InputElement(InputElement::Kind::SimpleFile, ordinal), _path(path),
_nextFileIndex(0), _resolveState(Resolver::StateNoChange) {}
/// \brief Assign File ordinals for files contained
/// in the InputElement
void SimpleFileNode::assignFileOrdinals(uint64_t &startOrdinal) {
for (auto &file : _files)
file->setOrdinalAndIncrement(startOrdinal);
}
/// Group
/// \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.size() == 0)
return make_error_code(InputGraphError::no_more_files);
// If we have processed all the elements as part of this node
// check the resolver status for each input element and if the status
// has not changed, move onto the next file.
if (_nextElementIndex == _elements.size()) {
if (getResolveState() == Resolver::StateNoChange)
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 (error_code(file) == InputGraphError::no_more_files)
_nextElementIndex++;
else
return *file;
return getNextFile();
}

View File

@ -0,0 +1,8 @@
int _start() {
return 0;
}
int main() {
fn();
return 0;
}

Binary file not shown.

View File

@ -0,0 +1,4 @@
int fn() {
fn1();
return 0;
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
int fn1() {
fn2();
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
int fn2() {
return 0;
}

Binary file not shown.

View File

@ -0,0 +1,37 @@
cat > 1.c << \!
int _start() {
return 0;
}
int main() {
fn();
return 0;
}
!
cat > fn.c << \!
int fn() {
fn1();
return 0;
}
!
cat > fn2.c << \!
int fn2() {
return 0;
}
!
cat > fn1.c << \!
int fn1() {
fn2();
}
!
gcc -c 1.c fn.c fn2.c fn1.c
ar cr libfn.a fn.o fn2.o
ar cr libfn1.a fn1.o
lld -flavor gnu -target x86_64 1.o libfn.a libfn1.a -o x
lld -flavor gnu -target x86_64 1.o --start-group libfn.a libfn1.a --end-group -o x
lld -flavor gnu -target x86_64 1.o --start-group fn.o fn2.o fn1.o --end-group -o x
lld -flavor gnu -target x86_64 1.o --start-group --whole-archive libfn.a --no-whole-archive libfn1.a --end-group -o x

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,27 @@
# This tests functionality of --start-group, --end-group
# This link should fail with unresolve symbol
RUN: not lld -flavor gnu -target x86_64 %p/Inputs/group/1.o %p/Inputs/group/libfn.a \
RUN: %p/Inputs/group/libfn1.a -o x 2> %t.err
# Test group
RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o --start-group \
RUN: %p/Inputs/group/libfn.a %p/Inputs/group/libfn1.a --end-group -o %t1
# Mix object files in group
RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o --start-group \
RUN: %p/Inputs/group/fn.o %p/Inputs/group/fn2.o \
RUN: %p/Inputs/group/fn1.o --end-group -o %t2
# Mix Whole archive input, the group should not iterate the file libfn.a
RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o --start-group \
RUN: --whole-archive %p/Inputs/group/libfn.a --no-whole-archive %p/Inputs/group/libfn1.a --end-group -o %t3
RUN: FileCheck --check-prefix=UNRESOLVED %s < %t.err
RUN: llvm-nm %t1 | FileCheck -check-prefix=RESOLVEDEXTERNAL %s
RUN: llvm-nm %t2 | FileCheck -check-prefix=RESOLVEDEXTERNAL %s
RUN: llvm-nm %t3 | FileCheck -check-prefix=RESOLVEDEXTERNAL %s
UNRESOLVED: Undefined Symbol: {{[\/0-9A-Za-z_]+}}libfn1.a(fn1.o) : fn2
RESOLVEDEXTERNAL: {{[0-9a-z]+}} T fn2