forked from OSchip/llvm-project
[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:
parent
a3a542ff21
commit
03f7763d21
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ bool LinkingContext::createInternalFiles(
|
|||
}
|
||||
|
||||
void LinkingContext::setResolverState(uint32_t state) {
|
||||
_currentInputElement->setResolverState(state);
|
||||
_currentInputElement->setResolveState(state);
|
||||
}
|
||||
|
||||
ErrorOr<File &> LinkingContext::nextFile() {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
int _start() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
fn();
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,4 @@
|
|||
int fn() {
|
||||
fn1();
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
int fn1() {
|
||||
fn2();
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
int fn2() {
|
||||
return 0;
|
||||
}
|
Binary file not shown.
|
@ -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.
|
@ -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
|
Loading…
Reference in New Issue