From 0caf3858331855546aa7c9580b54b59ad5ceaa37 Mon Sep 17 00:00:00 2001 From: Davide Italiano Date: Thu, 6 Apr 2017 17:03:04 +0000 Subject: [PATCH] [ADT] Add a generic breadth-first-search graph iterator. This will be used in LCSSA to speed up the canonicalization. Differential Revision: https://reviews.llvm.org/D31694 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@299660 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ADT/BreadthFirstIterator.h | 165 +++++++++++++++++++++ unittests/ADT/BreadthFirstIteratorTest.cpp | 74 +++++++++ unittests/ADT/CMakeLists.txt | 1 + 3 files changed, 240 insertions(+) create mode 100644 include/llvm/ADT/BreadthFirstIterator.h create mode 100644 unittests/ADT/BreadthFirstIteratorTest.cpp diff --git a/include/llvm/ADT/BreadthFirstIterator.h b/include/llvm/ADT/BreadthFirstIterator.h new file mode 100644 index 00000000000..437510457f3 --- /dev/null +++ b/include/llvm/ADT/BreadthFirstIterator.h @@ -0,0 +1,165 @@ +//===- llvm/ADT/BreadthFirstIterator.h - Breadth First iterator -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file builds on the ADT/GraphTraits.h file to build a generic breadth +// first graph iterator. This file exposes the following functions/types: +// +// bf_begin/bf_end/bf_iterator +// * Normal breadth-first iteration - visit a graph level-by-level. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_BREADTHFIRSTITERATOR_H +#define LLVM_ADT_BREADTHFIRSTITERATOR_H + +#include "llvm/ADT/GraphTraits.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/iterator_range.h" +#include +#include +#include +#include + +namespace llvm { + +// bf_iterator_storage - A private class which is used to figure out where to +// store the visited set. We only provide a non-external variant for now. +template class bf_iterator_storage { +public: + SetType Visited; +}; + +// The visited state for the iteration is a simple set. +template +using bf_iterator_default_set = SmallPtrSet; + +// Generic Breadth first search iterator. +template ::NodeRef>, + class GT = GraphTraits> +class bf_iterator + : public std::iterator, + public bf_iterator_storage { + typedef std::iterator super; + + typedef typename GT::NodeRef NodeRef; + typedef typename GT::ChildIteratorType ChildItTy; + + // First element is the node reference, second is the next child to visit. + typedef std::pair> QueueElement; + + // Visit queue - used to maintain BFS ordering. + // Optional<> because we need markers for levels. + std::queue> VisitQueue; + + // Current level. + unsigned Level; + +private: + inline bf_iterator(NodeRef Node) { + this->Visited.insert(Node); + Level = 0; + + // Also, insert a dummy node as marker. + VisitQueue.push(QueueElement(Node, None)); + VisitQueue.push(None); + } + + inline bf_iterator() = default; + + inline void toNext() { + Optional Head = VisitQueue.front(); + QueueElement H = Head.getValue(); + NodeRef Node = H.first; + Optional &ChildIt = H.second; + + if (!ChildIt) + ChildIt.emplace(GT::child_begin(Node)); + while (*ChildIt != GT::child_end(Node)) { + NodeRef Next = *(*ChildIt)++; + + // Already visited? + if (this->Visited.insert(Next).second) + VisitQueue.push(QueueElement(Next, None)); + } + VisitQueue.pop(); + + // Go to the next element skipping markers if needed. + if (!VisitQueue.empty()) { + Head = VisitQueue.front(); + if (Head != None) + return; + Level += 1; + VisitQueue.pop(); + + // Don't push another marker if this is the last + // element. + if (!VisitQueue.empty()) + VisitQueue.push(None); + } + } + +public: + typedef typename super::pointer pointer; + + // Provide static begin and end methods as our public "constructors" + static bf_iterator begin(const GraphT &G) { + return bf_iterator(GT::getEntryNode(G)); + } + + static bf_iterator end(const GraphT &G) { return bf_iterator(); } + + bool operator==(const bf_iterator &RHS) const { + assert(VisitQueue.size() == 0); + return VisitQueue == RHS.VisitQueue; + } + + bool operator!=(const bf_iterator &RHS) const { return !(*this == RHS); } + + const NodeRef &operator*() const { return VisitQueue.front()->first; } + + // This is a nonstandard operator-> that dereferenfces the pointer an extra + // time so that you can actually call methods on the node, because the + // contained type is a pointer. + NodeRef operator->() const { return **this; } + + bf_iterator &operator++() { // Pre-increment + toNext(); + return *this; + } + + bf_iterator operator++(int) { // Post-increment + bf_iterator ItCopy = *this; + ++*this; + return ItCopy; + } + + unsigned getLevel() const { return Level; } +}; + +// Provide global constructors that automatically figure out correct types. +template bf_iterator bf_begin(const T &G) { + return bf_iterator::begin(G); +} + +template bf_iterator bf_end(const T &G) { + return bf_iterator::end(G); +} + +// Provide an accessor method to use them in range-based patterns. +template iterator_range> breadth_first(const T &G) { + return make_range(bf_begin(G), bf_end(G)); +} + +} // end namespace llvm + +#endif // LLVM_ADT_BREADTHFIRSTITERATOR_H diff --git a/unittests/ADT/BreadthFirstIteratorTest.cpp b/unittests/ADT/BreadthFirstIteratorTest.cpp new file mode 100644 index 00000000000..42a07bbe930 --- /dev/null +++ b/unittests/ADT/BreadthFirstIteratorTest.cpp @@ -0,0 +1,74 @@ +//=== llvm/unittest/ADT/BreadthFirstIteratorTest.cpp - BFS iterator tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/BreadthFirstIterator.h" +#include "TestGraph.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace llvm { + +TEST(BreadthFristIteratorTest, Basic) { + typedef bf_iterator> BFIter; + + Graph<4> G; + G.AddEdge(0, 1); + G.AddEdge(0, 2); + G.AddEdge(1, 3); + + auto It = BFIter::begin(G); + auto End = BFIter::end(G); + EXPECT_EQ(It.getLevel(), 0U); + EXPECT_EQ(*It, G.AccessNode(0)); + ++It; + EXPECT_EQ(It.getLevel(), 1U); + EXPECT_EQ(*It, G.AccessNode(1)); + ++It; + EXPECT_EQ(It.getLevel(), 1U); + EXPECT_EQ(*It, G.AccessNode(2)); + ++It; + EXPECT_EQ(It.getLevel(), 2U); + EXPECT_EQ(*It, G.AccessNode(3)); + ++It; + EXPECT_EQ(It, End); +} + +TEST(BreadthFristIteratorTest, Cycle) { + typedef bf_iterator> BFIter; + + Graph<4> G; + G.AddEdge(0, 1); + G.AddEdge(1, 0); + G.AddEdge(1, 2); + G.AddEdge(2, 1); + G.AddEdge(2, 1); + G.AddEdge(2, 3); + G.AddEdge(3, 2); + G.AddEdge(3, 1); + G.AddEdge(3, 0); + + auto It = BFIter::begin(G); + auto End = BFIter::end(G); + EXPECT_EQ(It.getLevel(), 0U); + EXPECT_EQ(*It, G.AccessNode(0)); + ++It; + EXPECT_EQ(It.getLevel(), 1U); + EXPECT_EQ(*It, G.AccessNode(1)); + ++It; + EXPECT_EQ(It.getLevel(), 2U); + EXPECT_EQ(*It, G.AccessNode(2)); + ++It; + EXPECT_EQ(It.getLevel(), 3U); + EXPECT_EQ(*It, G.AccessNode(3)); + ++It; + EXPECT_EQ(It, End); +} + +} // end namespace llvm diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt index 738f6efe92d..fa977ac5d3f 100644 --- a/unittests/ADT/CMakeLists.txt +++ b/unittests/ADT/CMakeLists.txt @@ -9,6 +9,7 @@ set(ADTSources ArrayRefTest.cpp BitmaskEnumTest.cpp BitVectorTest.cpp + BreadthFirstIteratorTest.cpp BumpPtrListTest.cpp DAGDeltaAlgorithmTest.cpp DeltaAlgorithmTest.cpp