diff --git a/lld/include/lld/Core/InputGraph.h b/lld/include/lld/Core/InputGraph.h index ad934dc3d5cd..df1d727a02db 100644 --- a/lld/include/lld/Core/InputGraph.h +++ b/lld/include/lld/Core/InputGraph.h @@ -67,6 +67,9 @@ public: /// \brief Set Ordinals for all the InputElements that form the InputGraph virtual bool assignOrdinals(); + /// Normalize the InputGraph. + virtual void normalize(); + /// Destructor virtual ~InputGraph() {} @@ -131,6 +134,13 @@ public: File // Represents a type associated with File Nodes }; + /// How does the inputGraph expand the InputElement + enum class ExpandType : uint8_t { + None, // Do nothing(Default) + ReplaceAndExpand, // Replace current node and expand + ExpandOnly // Expand the current node + }; + /// \brief Initialize the Input Element, The ordinal value of an input Element /// is initially set to -1, if the user wants to override its ordinal, /// let the user do it @@ -175,6 +185,16 @@ public: /// \brief Reset the next index virtual void resetNextIndex() = 0; + /// Normalize functions + + /// \brief How do we want to expand the current node ? + virtual ExpandType expandType() const { return ExpandType::None; } + + /// \brief Get the elements that we want to expand with. + virtual range expandElements() { + llvm_unreachable("no element to expand"); + } + protected: Kind _kind; // The type of the Element int64_t _ordinal; // The ordinal value diff --git a/lld/lib/Core/InputGraph.cpp b/lld/lib/Core/InputGraph.cpp index 920c7bddc44f..92bf8bed5855 100644 --- a/lld/lib/Core/InputGraph.cpp +++ b/lld/lib/Core/InputGraph.cpp @@ -86,6 +86,38 @@ error_code InputGraph::setNextElementIndex(uint32_t index) { return error_code::success(); } +// Normalize the InputGraph. +void InputGraph::normalize() { + auto iterb = _inputArgs.begin(); + auto itere = _inputArgs.end(); + auto currentIter = _inputArgs.begin(); + bool replaceCurrentNode = false; + bool expand = false; + + std::vector > _workInputArgs; + while (iterb != itere) { + replaceCurrentNode = false; + expand = false; + InputElement::ExpandType expandType = (*iterb)->expandType(); + if (expandType == InputElement::ExpandType::ReplaceAndExpand) { + replaceCurrentNode = true; + expand = true; + } else if (expandType == InputElement::ExpandType::ExpandOnly) { + replaceCurrentNode = false; + expand = true; + } + currentIter = iterb++; + if (expand) + _workInputArgs.insert( + _workInputArgs.end(), + std::make_move_iterator((*currentIter)->expandElements().begin()), + std::make_move_iterator((*currentIter)->expandElements().end())); + if (!replaceCurrentNode) + _workInputArgs.push_back(std::move(*currentIter)); + } + _inputArgs = std::move(_workInputArgs); +} + /// InputElement /// \brief Initialize the Input Element, The ordinal value of an input Element diff --git a/lld/unittests/DriverTests/InputGraphTest.cpp b/lld/unittests/DriverTests/InputGraphTest.cpp index 08d52fe1567a..6c0dbdbda0b4 100644 --- a/lld/unittests/DriverTests/InputGraphTest.cpp +++ b/lld/unittests/DriverTests/InputGraphTest.cpp @@ -78,6 +78,44 @@ public: } }; +class MyExpandFileNode : public FileNode { +public: + MyExpandFileNode(StringRef path, int64_t ordinal, ExpandType expandType) + : FileNode(path, ordinal), _expandType(expandType) {} + + bool validate() { return true; } + + bool dump(raw_ostream &) { return true; } + + virtual error_code parse(const LinkingContext &, raw_ostream &) { + return error_code::success(); + } + + virtual ErrorOr getNextFile() { + if (_nextFileIndex == _files.size()) + return make_error_code(InputGraphError::no_more_files); + return *_files[_nextFileIndex++]; + } + + /// \brief How do we want to expand the current node ? + virtual ExpandType expandType() const { return _expandType; } + + /// \brief Get the elements that we want to expand with. + virtual range expandElements() { + return make_range(_expandElements.begin(), _expandElements.end()); + } + + /// Process the input Elemenet + virtual bool addElement(std::unique_ptr element) { + _expandElements.push_back(std::move(element)); + return true; + } + +private: + InputGraph::InputElementVectorT _expandElements; + ExpandType _expandType; +}; + class MyObjFile : public SimpleFile { public: MyObjFile(LinkingContext &context, StringRef path) @@ -362,4 +400,154 @@ TEST_F(InputGraphTest, AddNodeWithGroupIteration) { EXPECT_NE(InputGraphError::no_more_files, error_code(objfile)); EXPECT_EQ("group_objfile2", (*objfile).path()); } + +// Node expansion tests. +TEST_F(InputGraphTest, ExpandInputGraphNode) { + std::unique_ptr myfile(new MyFileNode("multi_files1", 0)); + std::vector > objfiles; + std::unique_ptr obj1(new MyObjFile(_context, "objfile1")); + std::unique_ptr obj2(new MyObjFile(_context, "objfile2")); + objfiles.push_back(std::move(obj1)); + objfiles.push_back(std::move(obj2)); + myfile->addFiles(std::move(objfiles)); + EXPECT_EQ(true, inputGraph().addInputElement(std::move(myfile))); + objfiles.clear(); + + std::unique_ptr expandFile(new MyExpandFileNode( + "expand_node", 1, InputElement::ExpandType::ExpandOnly)); + + std::unique_ptr filenode1(new MyFileNode("expand_file1", 2)); + std::unique_ptr obj3(new MyObjFile(_context, "objfile3")); + objfiles.push_back(std::move(obj3)); + filenode1->addFiles(std::move(objfiles)); + expandFile->addElement(std::move(filenode1)); + objfiles.clear(); + + std::unique_ptr filenode2(new MyFileNode("expand_file2", 3)); + std::unique_ptr obj4(new MyObjFile(_context, "objfile4")); + objfiles.push_back(std::move(obj4)); + filenode2->addFiles(std::move(objfiles)); + expandFile->addElement(std::move(filenode2)); + objfiles.clear(); + + // Add expand file to InputGraph + EXPECT_EQ(true, inputGraph().addInputElement(std::move(expandFile))); + + std::unique_ptr filenode3(new MyFileNode("obj_after_expand", 4)); + std::unique_ptr obj5(new MyObjFile(_context, "objfile5")); + std::unique_ptr obj6(new MyObjFile(_context, "objfile6")); + objfiles.push_back(std::move(obj5)); + objfiles.push_back(std::move(obj6)); + filenode3->addFiles(std::move(objfiles)); + + // Add an extra obj after the expand node + EXPECT_EQ(true, inputGraph().addInputElement(std::move(filenode3))); + + inputGraph().normalize(); + + ErrorOr nextElement = inputGraph().getNextInputElement(); + EXPECT_NE(InputGraphError::no_more_elements, error_code(nextElement)); + EXPECT_EQ(InputElement::Kind::File, (*nextElement)->kind()); + FileNode *fileNode = llvm::dyn_cast(*nextElement); + EXPECT_EQ("multi_files1", (*fileNode).getUserPath()); + + nextElement = inputGraph().getNextInputElement(); + EXPECT_NE(InputGraphError::no_more_elements, error_code(nextElement)); + EXPECT_EQ(InputElement::Kind::File, (*nextElement)->kind()); + fileNode = llvm::dyn_cast(*nextElement); + EXPECT_EQ("expand_file1", (*fileNode).getUserPath()); + + nextElement = inputGraph().getNextInputElement(); + EXPECT_NE(InputGraphError::no_more_elements, error_code(nextElement)); + EXPECT_EQ(InputElement::Kind::File, (*nextElement)->kind()); + fileNode = llvm::dyn_cast(*nextElement); + EXPECT_EQ("expand_file2", (*fileNode).getUserPath()); + + nextElement = inputGraph().getNextInputElement(); + EXPECT_NE(InputGraphError::no_more_elements, error_code(nextElement)); + EXPECT_EQ(InputElement::Kind::File, (*nextElement)->kind()); + fileNode = llvm::dyn_cast(*nextElement); + EXPECT_EQ("expand_node", (*fileNode).getUserPath()); + + nextElement = inputGraph().getNextInputElement(); + EXPECT_NE(InputGraphError::no_more_elements, error_code(nextElement)); + EXPECT_EQ(InputElement::Kind::File, (*nextElement)->kind()); + fileNode = llvm::dyn_cast(*nextElement); + EXPECT_EQ("obj_after_expand", (*fileNode).getUserPath()); + + nextElement = inputGraph().getNextInputElement(); + EXPECT_EQ(InputGraphError::no_more_elements, error_code(nextElement)); +} + +// Node expansion tests. +TEST_F(InputGraphTest, ExpandAndReplaceInputGraphNode) { + std::unique_ptr myfile(new MyFileNode("multi_files1", 0)); + std::vector > objfiles; + std::unique_ptr obj1(new MyObjFile(_context, "objfile1")); + std::unique_ptr obj2(new MyObjFile(_context, "objfile2")); + objfiles.push_back(std::move(obj1)); + objfiles.push_back(std::move(obj2)); + myfile->addFiles(std::move(objfiles)); + EXPECT_EQ(true, inputGraph().addInputElement(std::move(myfile))); + objfiles.clear(); + + std::unique_ptr expandFile(new MyExpandFileNode( + "expand_node", 1, InputElement::ExpandType::ReplaceAndExpand)); + + std::unique_ptr filenode1(new MyFileNode("expand_file1", 2)); + std::unique_ptr obj3(new MyObjFile(_context, "objfile3")); + objfiles.push_back(std::move(obj3)); + filenode1->addFiles(std::move(objfiles)); + expandFile->addElement(std::move(filenode1)); + objfiles.clear(); + + std::unique_ptr filenode2(new MyFileNode("expand_file2", 3)); + std::unique_ptr obj4(new MyObjFile(_context, "objfile4")); + objfiles.push_back(std::move(obj4)); + filenode2->addFiles(std::move(objfiles)); + expandFile->addElement(std::move(filenode2)); + objfiles.clear(); + + // Add expand file to InputGraph + EXPECT_EQ(true, inputGraph().addInputElement(std::move(expandFile))); + + std::unique_ptr filenode3(new MyFileNode("obj_after_expand", 4)); + std::unique_ptr obj5(new MyObjFile(_context, "objfile5")); + std::unique_ptr obj6(new MyObjFile(_context, "objfile6")); + objfiles.push_back(std::move(obj5)); + objfiles.push_back(std::move(obj6)); + filenode3->addFiles(std::move(objfiles)); + + // Add an extra obj after the expand node + EXPECT_EQ(true, inputGraph().addInputElement(std::move(filenode3))); + + inputGraph().normalize(); + + ErrorOr nextElement = inputGraph().getNextInputElement(); + EXPECT_NE(InputGraphError::no_more_elements, error_code(nextElement)); + EXPECT_EQ(InputElement::Kind::File, (*nextElement)->kind()); + FileNode *fileNode = llvm::dyn_cast(*nextElement); + EXPECT_EQ("multi_files1", (*fileNode).getUserPath()); + + nextElement = inputGraph().getNextInputElement(); + EXPECT_NE(InputGraphError::no_more_elements, error_code(nextElement)); + EXPECT_EQ(InputElement::Kind::File, (*nextElement)->kind()); + fileNode = llvm::dyn_cast(*nextElement); + EXPECT_EQ("expand_file1", (*fileNode).getUserPath()); + + nextElement = inputGraph().getNextInputElement(); + EXPECT_NE(InputGraphError::no_more_elements, error_code(nextElement)); + EXPECT_EQ(InputElement::Kind::File, (*nextElement)->kind()); + fileNode = llvm::dyn_cast(*nextElement); + EXPECT_EQ("expand_file2", (*fileNode).getUserPath()); + + nextElement = inputGraph().getNextInputElement(); + EXPECT_NE(InputGraphError::no_more_elements, error_code(nextElement)); + EXPECT_EQ(InputElement::Kind::File, (*nextElement)->kind()); + fileNode = llvm::dyn_cast(*nextElement); + EXPECT_EQ("obj_after_expand", (*fileNode).getUserPath()); + + nextElement = inputGraph().getNextInputElement(); + EXPECT_EQ(InputGraphError::no_more_elements, error_code(nextElement)); +} }