diff options
author | Duncan P. N. Exon Smith <dexonsmith@apple.com> | 2016-08-30 16:23:55 +0000 |
---|---|---|
committer | Duncan P. N. Exon Smith <dexonsmith@apple.com> | 2016-08-30 16:23:55 +0000 |
commit | ac79897019554441b5b58794549bbae85affac67 (patch) | |
tree | ceaf4f3e6c5348387fcee6aa1e61ba80379e3506 | |
parent | c213685f69b1c7a1c0882b9b3533b6f3111d8092 (diff) | |
download | bcm5719-llvm-ac79897019554441b5b58794549bbae85affac67.tar.gz bcm5719-llvm-ac79897019554441b5b58794549bbae85affac67.zip |
ADT: Split out simple_ilist, a simple intrusive list
Split out a new, low-level intrusive list type with clear semantics.
Unlike iplist (and ilist), all operations on simple_ilist are intrusive,
and simple_ilist never takes ownership of its nodes. This enables an
intuitive API that has the right defaults for intrusive lists.
- insert() takes references (not pointers!) to nodes (in iplist/ilist,
passing a reference will cause the node to be copied).
- erase() takes only iterators (like std::list), and does not destroy
the nodes.
- remove() takes only references and has the same behaviour as erase().
- clear() does not destroy the nodes.
- The destructor does not destroy the nodes.
- New API {erase,remove,clear}AndDispose() take an extra Disposer
functor for callsites that want to call some disposal routine (e.g.,
std::default_delete).
This list is not currently configurable, and has no callbacks.
The initial motivation was to fix iplist<>::sort to work correctly (even
with callbacks in ilist_traits<>). iplist<> uses simple_ilist<>::sort
directly. The new test in unittests/IR/ModuleTest.cpp crashes without
this commit.
Fixing sort() via a low-level layer provided a good opportunity to:
- Unit test the low-level functionality thoroughly.
- Modernize the API, largely inspired by other intrusive list
implementations.
Here's a sketch of a longer-term plan:
- Create BumpPtrList<>, a non-intrusive list implemented using
simple_ilist<>, and use it for the Token list in
lib/Support/YAMLParser.cpp. This will factor out the only real use of
createNode().
- Evolve the iplist<> and ilist<> APIs in the direction of
simple_ilist<>, making allocation/deallocation explicit at call sites
(similar to simple_ilist<>::eraseAndDispose()).
- Factor out remaining calls to createNode() and deleteNode() and remove
the customization from ilist_traits<>.
- Transition uses of iplist<>/ilist<> that don't need callbacks over to
simple_ilist<>.
llvm-svn: 280107
-rw-r--r-- | llvm/include/llvm/ADT/ilist.h | 135 | ||||
-rw-r--r-- | llvm/include/llvm/ADT/ilist_base.h | 19 | ||||
-rw-r--r-- | llvm/include/llvm/ADT/simple_ilist.h | 269 | ||||
-rw-r--r-- | llvm/unittests/ADT/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/unittests/ADT/IListBaseTest.cpp | 40 | ||||
-rw-r--r-- | llvm/unittests/ADT/IListIteratorTest.cpp | 61 | ||||
-rw-r--r-- | llvm/unittests/ADT/SimpleIListTest.cpp | 586 | ||||
-rw-r--r-- | llvm/unittests/IR/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/unittests/IR/ModuleTest.cpp | 48 |
9 files changed, 1018 insertions, 142 deletions
diff --git a/llvm/include/llvm/ADT/ilist.h b/llvm/include/llvm/ADT/ilist.h index 5c031a832f7..137d4ec4146 100644 --- a/llvm/include/llvm/ADT/ilist.h +++ b/llvm/include/llvm/ADT/ilist.h @@ -24,11 +24,8 @@ #ifndef LLVM_ADT_ILIST_H #define LLVM_ADT_ILIST_H -#include "llvm/ADT/ilist_base.h" -#include "llvm/ADT/ilist_iterator.h" -#include "llvm/ADT/ilist_node.h" +#include "llvm/ADT/simple_ilist.h" #include "llvm/Support/Compiler.h" -#include <algorithm> #include <cassert> #include <cstddef> #include <iterator> @@ -119,17 +116,14 @@ struct ilist_traits<const Ty> : public ilist_traits<Ty> {}; /// ilist_sentinel, which holds pointers to the first and last nodes in the /// list. template <typename NodeTy, typename Traits = ilist_traits<NodeTy>> -class iplist : public Traits, ilist_base, ilist_node_access { +class iplist : public Traits, simple_ilist<NodeTy> { // TODO: Drop this assertion and the transitive type traits anytime after // v4.0 is branched (i.e,. keep them for one release to help out-of-tree code // update). static_assert(!ilist_detail::HasObsoleteCustomization<Traits, NodeTy>::value, "ilist customization points have changed!"); - ilist_sentinel<NodeTy> Sentinel; - - typedef ilist_node<NodeTy> node_type; - typedef const ilist_node<NodeTy> const_node_type; + typedef simple_ilist<NodeTy> base_list_type; static bool op_less(NodeTy &L, NodeTy &R) { return L < R; } static bool op_equal(NodeTy &L, NodeTy &R) { return L == R; } @@ -139,65 +133,42 @@ class iplist : public Traits, ilist_base, ilist_node_access { void operator=(const iplist &) = delete; public: - typedef NodeTy *pointer; - typedef const NodeTy *const_pointer; - typedef NodeTy &reference; - typedef const NodeTy &const_reference; - typedef NodeTy value_type; - typedef ilist_iterator<NodeTy> iterator; - typedef ilist_iterator<const NodeTy> const_iterator; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - typedef ilist_iterator<const NodeTy, true> const_reverse_iterator; - typedef ilist_iterator<NodeTy, true> reverse_iterator; + typedef typename base_list_type::pointer pointer; + typedef typename base_list_type::const_pointer const_pointer; + typedef typename base_list_type::reference reference; + typedef typename base_list_type::const_reference const_reference; + typedef typename base_list_type::value_type value_type; + typedef typename base_list_type::size_type size_type; + typedef typename base_list_type::difference_type difference_type; + typedef typename base_list_type::iterator iterator; + typedef typename base_list_type::const_iterator const_iterator; + typedef typename base_list_type::reverse_iterator reverse_iterator; + typedef + typename base_list_type::const_reverse_iterator const_reverse_iterator; iplist() = default; ~iplist() { clear(); } - // Iterator creation methods. - iterator begin() { return ++iterator(Sentinel); } - const_iterator begin() const { return ++const_iterator(Sentinel); } - iterator end() { return iterator(Sentinel); } - const_iterator end() const { return const_iterator(Sentinel); } - - // reverse iterator creation methods. - reverse_iterator rbegin() { return ++reverse_iterator(Sentinel); } - const_reverse_iterator rbegin() const{ return ++const_reverse_iterator(Sentinel); } - reverse_iterator rend() { return reverse_iterator(Sentinel); } - const_reverse_iterator rend() const { return const_reverse_iterator(Sentinel); } - // Miscellaneous inspection routines. size_type max_size() const { return size_type(-1); } - bool LLVM_ATTRIBUTE_UNUSED_RESULT empty() const { return Sentinel.empty(); } - // Front and back accessor functions... - reference front() { - assert(!empty() && "Called front() on empty list!"); - return *begin(); - } - const_reference front() const { - assert(!empty() && "Called front() on empty list!"); - return *begin(); - } - reference back() { - assert(!empty() && "Called back() on empty list!"); - return *--end(); - } - const_reference back() const { - assert(!empty() && "Called back() on empty list!"); - return *--end(); - } + using base_list_type::begin; + using base_list_type::end; + using base_list_type::rbegin; + using base_list_type::rend; + using base_list_type::empty; + using base_list_type::front; + using base_list_type::back; void swap(iplist &RHS) { assert(0 && "Swap does not use list traits callback correctly yet!"); - std::swap(Sentinel, RHS.Sentinel); + base_list_type::swap(RHS); } iterator insert(iterator where, NodeTy *New) { - ilist_base::insertBefore(*where.getNodePtr(), *this->getNodePtr(New)); - + auto I = base_list_type::insert(where, *New); this->addNodeToList(New); // Notify traits that we added a node... - return iterator(New); + return I; } iterator insert(iterator where, const NodeTy &New) { @@ -212,9 +183,8 @@ public: } NodeTy *remove(iterator &IT) { - assert(IT != end() && "Cannot remove end of list!"); - NodeTy *Node = &*IT++; - ilist_base::remove(*this->getNodePtr(Node)); + NodeTy *Node = &*IT; + base_list_type::erase(IT++); this->removeNodeFromList(Node); // Notify traits that we removed a node... return Node; } @@ -241,7 +211,7 @@ public: /// /// This should only be used immediately before freeing nodes in bulk to /// avoid traversing the list and bringing all the nodes into cache. - void clearAndLeakNodesUnsafely() { Sentinel.reset(); } + void clearAndLeakNodesUnsafely() { base_list_type::clear(); } private: // transfer - The heart of the splice function. Move linked list nodes from @@ -251,8 +221,7 @@ private: if (position == last) return; - ilist_base::transferBefore(*position.getNodePtr(), *first.getNodePtr(), - *last.getNodePtr()); + base_list_type::splice(position, L2, first, last); // Callback. Note that the nodes have moved from before-last to // before-position. @@ -265,9 +234,7 @@ public: // Functionality derived from other functions defined above... // - size_type LLVM_ATTRIBUTE_UNUSED_RESULT size() const { - return std::distance(begin(), end()); - } + using base_list_type::size; iterator erase(iterator first, iterator last) { while (first != last) @@ -318,48 +285,12 @@ public: void merge(iplist &Right, Compare comp) { if (this == &Right) return; - iterator First1 = begin(), Last1 = end(); - iterator First2 = Right.begin(), Last2 = Right.end(); - while (First1 != Last1 && First2 != Last2) { - if (comp(*First2, *First1)) { - iterator Next = First2; - transfer(First1, Right, First2, ++Next); - First2 = Next; - } else { - ++First1; - } - } - if (First2 != Last2) - transfer(Last1, Right, First2, Last2); + this->transferNodesFromList(Right, Right.begin(), Right.end()); + base_list_type::merge(Right, comp); } void merge(iplist &Right) { return merge(Right, op_less); } - template <class Compare> - void sort(Compare comp) { - // The list is empty, vacuously sorted. - if (empty()) - return; - // The list has a single element, vacuously sorted. - if (std::next(begin()) == end()) - return; - // Find the split point for the list. - iterator Center = begin(), End = begin(); - while (End != end() && std::next(End) != end()) { - Center = std::next(Center); - End = std::next(std::next(End)); - } - // Split the list into two. - iplist RightHalf; - RightHalf.splice(RightHalf.begin(), *this, Center, end()); - - // Sort the two sublists. - sort(comp); - RightHalf.sort(comp); - - // Merge the two sublists back together. - merge(RightHalf, comp); - } - void sort() { sort(op_less); } + using base_list_type::sort; /// \brief Get the previous node, or \c nullptr for the list head. NodeTy *getPrevNode(NodeTy &N) const { diff --git a/llvm/include/llvm/ADT/ilist_base.h b/llvm/include/llvm/ADT/ilist_base.h index 27a39bedef0..a41696d1f71 100644 --- a/llvm/include/llvm/ADT/ilist_base.h +++ b/llvm/include/llvm/ADT/ilist_base.h @@ -39,10 +39,22 @@ public: N.setNext(nullptr); } + static void removeRangeImpl(ilist_node_base &First, ilist_node_base &Last) { + ilist_node_base *Prev = First.getPrev(); + ilist_node_base *Final = Last.getPrev(); + Last.setPrev(Prev); + Prev->setNext(&Last); + + // Not strictly necessary, but helps catch a class of bugs. + First.setPrev(nullptr); + Final->setNext(nullptr); + } + static void transferBeforeImpl(ilist_node_base &Next, ilist_node_base &First, ilist_node_base &Last) { - assert(&Next != &Last && "Should be checked by callers"); - assert(&First != &Last && "Should be checked by callers"); + if (&Next == &Last || &First == &Last) + return; + // Position cannot be contained in the range to be transferred. assert(&Next != &First && // Check for the most common mistake. @@ -67,6 +79,9 @@ public: } template <class T> static void remove(T &N) { removeImpl(N); } + template <class T> static void removeRange(T &First, T &Last) { + removeRangeImpl(First, Last); + } template <class T> static void transferBefore(T &Next, T &First, T &Last) { transferBeforeImpl(Next, First, Last); diff --git a/llvm/include/llvm/ADT/simple_ilist.h b/llvm/include/llvm/ADT/simple_ilist.h new file mode 100644 index 00000000000..f9983d8ed4f --- /dev/null +++ b/llvm/include/llvm/ADT/simple_ilist.h @@ -0,0 +1,269 @@ +//===- llvm/ADT/simple_ilist.h - Simple Intrusive List ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_SIMPLE_ILIST_H +#define LLVM_ADT_SIMPLE_ILIST_H + +#include "llvm/ADT/ilist_base.h" +#include "llvm/ADT/ilist_iterator.h" +#include "llvm/ADT/ilist_node.h" +#include <algorithm> +#include <cassert> +#include <cstddef> + +namespace llvm { + +/// A simple intrusive list implementation. +/// +/// This is a simple intrusive list for a \c T that inherits from \c +/// ilist_node<T>. The list never takes ownership of anything inserted in it. +/// +/// Unlike \a iplist<T> and \a ilist<T>, \a simple_ilist<T> never allocates or +/// deletes values, and has no callback traits. +/// +/// The API for adding nodes include \a push_front(), \a push_back(), and \a +/// insert(). These all take values by reference (not by pointer), except for +/// the range version of \a insert(). +/// +/// There are three sets of API for discarding nodes from the list: \a +/// remove(), which takes a reference to the node to remove, \a erase(), which +/// takes an iterator or iterator range and returns the next one, and \a +/// clear(), which empties out the container. All three are constant time +/// operations. None of these deletes any nodes; in particular, if there is a +/// single node in the list, then these have identical semantics: +/// \li \c L.remove(L.front()); +/// \li \c L.erase(L.begin()); +/// \li \c L.clear(); +/// +/// As a convenience for callers, there are parallel APIs that take a \c +/// Disposer (such as \c std::default_delete<T>): \a removeAndDispose(), \a +/// eraseAndDispose(), and \a clearAndDispose(). These have different names +/// because the extra semantic is otherwise non-obvious. They are equivalent +/// to calling \a std::for_each() on the range to be discarded. +template <typename T> class simple_ilist : ilist_base, ilist_node_access { + ilist_sentinel<T> Sentinel; + +public: + typedef T value_type; + typedef T *pointer; + typedef T &reference; + typedef const T *const_pointer; + typedef const T &const_reference; + typedef ilist_iterator<T> iterator; + typedef ilist_iterator<const T> const_iterator; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef ilist_iterator<const T, true> const_reverse_iterator; + typedef ilist_iterator<T, true> reverse_iterator; + + simple_ilist() = default; + ~simple_ilist() = default; + + // No copy constructors. + simple_ilist(const simple_ilist &) = delete; + simple_ilist &operator=(const simple_ilist &) = delete; + + // Move constructors. + simple_ilist(simple_ilist &&X) { splice(end(), X); } + simple_ilist &operator=(simple_ilist &&X) { + clear(); + splice(end(), X); + return *this; + } + + iterator begin() { return ++iterator(Sentinel); } + const_iterator begin() const { return ++const_iterator(Sentinel); } + iterator end() { return iterator(Sentinel); } + const_iterator end() const { return const_iterator(Sentinel); } + reverse_iterator rbegin() { return ++reverse_iterator(Sentinel); } + const_reverse_iterator rbegin() const { + return ++const_reverse_iterator(Sentinel); + } + reverse_iterator rend() { return reverse_iterator(Sentinel); } + const_reverse_iterator rend() const { + return const_reverse_iterator(Sentinel); + } + + /// Check if the list is empty in constant time. + bool LLVM_ATTRIBUTE_UNUSED_RESULT empty() const { return Sentinel.empty(); } + + /// Calculate the size of the list in linear time. + size_type LLVM_ATTRIBUTE_UNUSED_RESULT size() const { + return std::distance(begin(), end()); + } + + reference front() { return *begin(); } + const_reference front() const { return *begin(); } + reference back() { return *rbegin(); } + const_reference back() const { return *rbegin(); } + + /// Insert a node at the front; never copies. + void push_front(reference Node) { insert(begin(), Node); } + + /// Insert a node at the back; never copies. + void push_back(reference Node) { insert(end(), Node); } + + /// Remove the node at the front; never deletes. + void pop_front() { erase(begin()); } + + /// Remove the node at the back; never deletes. + void pop_back() { erase(--end()); } + + /// Swap with another list in place using std::swap. + void swap(simple_ilist &X) { std::swap(*this, X); } + + /// Insert a node by reference; never copies. + iterator insert(iterator I, reference Node) { + ilist_base::insertBefore(*I.getNodePtr(), *this->getNodePtr(&Node)); + return iterator(&Node); + } + + /// Insert a range of nodes; never copies. + template <class Iterator> + void insert(iterator I, Iterator First, Iterator Last) { + for (; First != Last; ++First) + insert(I, *First); + } + + /// Remove a node by reference; never deletes. + /// + /// \see \a erase() for removing by iterator. + /// \see \a removeAndDispose() if the node should be deleted. + void remove(reference N) { ilist_base::remove(*this->getNodePtr(&N)); } + + /// Remove a node by reference and dispose of it. + template <class Disposer> + void removeAndDispose(reference N, Disposer dispose) { + remove(N); + dispose(&N); + } + + /// Remove a node by iterator; never deletes. + /// + /// \see \a remove() for removing by reference. + /// \see \a eraseAndDispose() it the node should be deleted. + iterator erase(iterator I) { + assert(I != end() && "Cannot remove end of list!"); + remove(*I++); + return I; + } + + /// Remove a range of nodes; never deletes. + /// + /// \see \a eraseAndDispose() if the nodes should be deleted. + iterator erase(iterator First, iterator Last) { + ilist_base::removeRange(*First.getNodePtr(), *Last.getNodePtr()); + return Last; + } + + /// Remove a node by iterator and dispose of it. + template <class Disposer> + iterator eraseAndDispose(iterator I, Disposer dispose) { + auto Next = std::next(I); + erase(I); + dispose(&*I); + return Next; + } + + /// Remove a range of nodes and dispose of them. + template <class Disposer> + iterator eraseAndDispose(iterator First, iterator Last, Disposer dispose) { + while (First != Last) + First = eraseAndDispose(First, dispose); + return Last; + } + + /// Clear the list; never deletes. + /// + /// \see \a clearAndDispose() if the nodes should be deleted. + void clear() { Sentinel.reset(); } + + /// Clear the list and dispose of the nodes. + template <class Disposer> void clearAndDispose(Disposer dispose) { + eraseAndDispose(begin(), end(), dispose); + } + + /// Splice in another list. + void splice(iterator I, simple_ilist &L2) { + splice(I, L2, L2.begin(), L2.end()); + } + + /// Splice in a node from another list. + void splice(iterator I, simple_ilist &L2, iterator Node) { + splice(I, L2, Node, std::next(Node)); + } + + /// Splice in a range of nodes from another list. + void splice(iterator I, simple_ilist &, iterator First, iterator Last) { + ilist_base::transferBefore(*I.getNodePtr(), *First.getNodePtr(), + *Last.getNodePtr()); + } + + /// Merge in another list. + /// + /// \pre \c this and \p RHS are sorted. + ///@{ + void merge(simple_ilist &RHS) { merge(RHS, std::less<T>()); } + template <class Compare> void merge(simple_ilist &RHS, Compare comp); + ///@} + + /// Sort the list. + ///@{ + void sort() { sort(std::less<T>()); } + template <class Compare> void sort(Compare comp); + ///@} +}; + +template <class T> +template <class Compare> +void simple_ilist<T>::merge(simple_ilist<T> &RHS, Compare comp) { + if (this == &RHS || RHS.empty()) + return; + iterator LI = begin(), LE = end(); + iterator RI = RHS.begin(), RE = RHS.end(); + while (LI != LE) { + if (comp(*RI, *LI)) { + // Transfer a run of at least size 1 from RHS to LHS. + iterator RunStart = RI++; + RI = std::find_if(RI, RE, [&](reference RV) { return !comp(RV, *LI); }); + splice(LI, RHS, RunStart, RI); + if (RI == RE) + return; + } + ++LI; + } + // Transfer the remaining RHS nodes once LHS is finished. + splice(LE, RHS, RI, RE); +} + +template <class T> +template <class Compare> +void simple_ilist<T>::sort(Compare comp) { + // Vacuously sorted. + if (empty() || std::next(begin()) == end()) + return; + + // Split the list in the middle. + iterator Center = begin(), End = begin(); + while (End != end() && ++End != end()) { + ++Center; + ++End; + } + simple_ilist<T> RHS; + RHS.splice(RHS.end(), *this, Center, end()); + + // Sort the sublists and merge back together. + sort(comp); + RHS.sort(comp); + merge(RHS, comp); +} + +} // end namespace llvm + +#endif // LLVM_ADT_SIMPLE_ILIST_H diff --git a/llvm/unittests/ADT/CMakeLists.txt b/llvm/unittests/ADT/CMakeLists.txt index 82df2c97bd5..a041896b6bd 100644 --- a/llvm/unittests/ADT/CMakeLists.txt +++ b/llvm/unittests/ADT/CMakeLists.txt @@ -44,6 +44,7 @@ set(ADTSources ScopeExitTest.cpp SequenceTest.cpp SetVectorTest.cpp + SimpleIListTest.cpp SmallPtrSetTest.cpp SmallStringTest.cpp SmallVectorTest.cpp diff --git a/llvm/unittests/ADT/IListBaseTest.cpp b/llvm/unittests/ADT/IListBaseTest.cpp index 19223327785..34a8e926345 100644 --- a/llvm/unittests/ADT/IListBaseTest.cpp +++ b/llvm/unittests/ADT/IListBaseTest.cpp @@ -63,6 +63,46 @@ TEST(IListBaseTest, removeImpl) { EXPECT_EQ(nullptr, B.getNext()); } +TEST(IListBaseTest, removeRangeImpl) { + ilist_node_base S, A, B, C, D; + + // [S] <-> A <-> B <-> C <-> D <-> [S] + S.setPrev(&S); + S.setNext(&S); + ilist_base::insertBeforeImpl(S, A); + ilist_base::insertBeforeImpl(S, B); + ilist_base::insertBeforeImpl(S, C); + ilist_base::insertBeforeImpl(S, D); + + // [S] <-> A <-> D <-> [S] + ilist_base::removeRangeImpl(B, D); + EXPECT_EQ(&D, S.getPrev()); + EXPECT_EQ(&A, D.getPrev()); + EXPECT_EQ(&S, A.getPrev()); + EXPECT_EQ(&A, S.getNext()); + EXPECT_EQ(&D, A.getNext()); + EXPECT_EQ(&S, D.getNext()); + EXPECT_EQ(nullptr, B.getPrev()); + EXPECT_EQ(nullptr, C.getNext()); +} + +TEST(IListBaseTest, removeRangeImplAllButSentinel) { + ilist_node_base S, A, B; + + // [S] <-> A <-> B <-> [S] + S.setPrev(&S); + S.setNext(&S); + ilist_base::insertBeforeImpl(S, A); + ilist_base::insertBeforeImpl(S, B); + + // [S] <-> [S] + ilist_base::removeRangeImpl(A, S); + EXPECT_EQ(&S, S.getPrev()); + EXPECT_EQ(&S, S.getNext()); + EXPECT_EQ(nullptr, A.getPrev()); + EXPECT_EQ(nullptr, B.getNext()); +} + TEST(IListBaseTest, transferBeforeImpl) { ilist_node_base S1, S2, A, B, C, D, E; diff --git a/llvm/unittests/ADT/IListIteratorTest.cpp b/llvm/unittests/ADT/IListIteratorTest.cpp index 1a87a270a67..ddcab781b9b 100644 --- a/llvm/unittests/ADT/IListIteratorTest.cpp +++ b/llvm/unittests/ADT/IListIteratorTest.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/ilist.h" +#include "llvm/ADT/simple_ilist.h" #include "gtest/gtest.h" using namespace llvm; @@ -17,10 +17,10 @@ namespace { struct Node : ilist_node<Node> {}; TEST(IListIteratorTest, DefaultConstructor) { - iplist<Node>::iterator I; - iplist<Node>::reverse_iterator RI; - iplist<Node>::const_iterator CI; - iplist<Node>::const_reverse_iterator CRI; + simple_ilist<Node>::iterator I; + simple_ilist<Node>::reverse_iterator RI; + simple_ilist<Node>::const_iterator CI; + simple_ilist<Node>::const_reverse_iterator CRI; EXPECT_EQ(nullptr, I.getNodePtr()); EXPECT_EQ(nullptr, CI.getNodePtr()); EXPECT_EQ(nullptr, RI.getNodePtr()); @@ -38,7 +38,7 @@ TEST(IListIteratorTest, DefaultConstructor) { } TEST(IListIteratorTest, Empty) { - iplist<Node> L; + simple_ilist<Node> L; // Check iterators of L. EXPECT_EQ(L.begin(), L.end()); @@ -49,21 +49,18 @@ TEST(IListIteratorTest, Empty) { EXPECT_EQ(L.rend(), L.end().getReverse()); // Iterators shouldn't match default constructors. - iplist<Node>::iterator I; - iplist<Node>::reverse_iterator RI; + simple_ilist<Node>::iterator I; + simple_ilist<Node>::reverse_iterator RI; EXPECT_NE(I, L.begin()); EXPECT_NE(I, L.end()); EXPECT_NE(RI, L.rbegin()); EXPECT_NE(RI, L.rend()); - - // Don't delete nodes. - L.clearAndLeakNodesUnsafely(); } TEST(IListIteratorTest, OneNodeList) { - iplist<Node> L; + simple_ilist<Node> L; Node A; - L.insert(L.end(), &A); + L.insert(L.end(), A); // Check address of reference. EXPECT_EQ(&A, &*L.begin()); @@ -81,16 +78,13 @@ TEST(IListIteratorTest, OneNodeList) { // Check conversions. EXPECT_EQ(L.rbegin(), L.begin().getReverse()); EXPECT_EQ(L.begin(), L.rbegin().getReverse()); - - // Don't delete nodes. - L.clearAndLeakNodesUnsafely(); } TEST(IListIteratorTest, TwoNodeList) { - iplist<Node> L; + simple_ilist<Node> L; Node A, B; - L.insert(L.end(), &A); - L.insert(L.end(), &B); + L.insert(L.end(), A); + L.insert(L.end(), B); // Check order. EXPECT_EQ(&A, &*L.begin()); @@ -105,45 +99,36 @@ TEST(IListIteratorTest, TwoNodeList) { EXPECT_EQ(L.rbegin(), (++L.begin()).getReverse()); EXPECT_EQ(++L.begin(), L.rbegin().getReverse()); EXPECT_EQ(L.begin(), (++L.rbegin()).getReverse()); - - // Don't delete nodes. - L.clearAndLeakNodesUnsafely(); } TEST(IListIteratorTest, CheckEraseForward) { - iplist<Node> L; + simple_ilist<Node> L; Node A, B; - L.insert(L.end(), &A); - L.insert(L.end(), &B); + L.insert(L.end(), A); + L.insert(L.end(), B); // Erase nodes. auto I = L.begin(); EXPECT_EQ(&A, &*I); - EXPECT_EQ(&A, L.remove(I++)); + L.remove(*I++); EXPECT_EQ(&B, &*I); - EXPECT_EQ(&B, L.remove(I++)); + L.remove(*I++); EXPECT_EQ(L.end(), I); - - // Don't delete nodes. - L.clearAndLeakNodesUnsafely(); } TEST(IListIteratorTest, CheckEraseReverse) { - iplist<Node> L; + simple_ilist<Node> L; Node A, B; - L.insert(L.end(), &A); - L.insert(L.end(), &B); + L.insert(L.end(), A); + L.insert(L.end(), B); // Erase nodes. auto RI = L.rbegin(); EXPECT_EQ(&B, &*RI); - EXPECT_EQ(&B, L.remove(&*RI++)); + L.remove(*RI++); EXPECT_EQ(&A, &*RI); - EXPECT_EQ(&A, L.remove(&*RI++)); + L.remove(*RI++); EXPECT_EQ(L.rend(), RI); - - // Don't delete nodes. - L.clearAndLeakNodesUnsafely(); } } // end namespace diff --git a/llvm/unittests/ADT/SimpleIListTest.cpp b/llvm/unittests/ADT/SimpleIListTest.cpp new file mode 100644 index 00000000000..f252080c782 --- /dev/null +++ b/llvm/unittests/ADT/SimpleIListTest.cpp @@ -0,0 +1,586 @@ +//===- unittests/ADT/SimpleIListTest.cpp - simple_ilist unit 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/simple_ilist.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +struct Node : ilist_node<Node> {}; +bool operator<(const Node &L, const Node &R) { return &L < &R; } +bool makeFalse(const Node &, const Node &) { return false; } + +struct deleteNode : std::default_delete<Node> {}; +void doNothing(Node *) {} + +TEST(SimpleIListTest, DefaultConstructor) { + simple_ilist<Node> L; + EXPECT_EQ(L.begin(), L.end()); + EXPECT_TRUE(L.empty()); + EXPECT_EQ(0u, L.size()); +} + +TEST(SimpleIListTest, pushPopFront) { + simple_ilist<Node> L; + Node A, B; + L.push_front(B); + L.push_front(A); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &L.back()); + EXPECT_FALSE(L.empty()); + EXPECT_EQ(2u, L.size()); + + // Pop front and check the new front. + L.pop_front(); + EXPECT_EQ(&B, &L.front()); + + // Pop to empty. + L.pop_front(); + EXPECT_TRUE(L.empty()); +} + +TEST(SimpleIListTest, pushPopBack) { + simple_ilist<Node> L; + Node A, B; + L.push_back(A); + L.push_back(B); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &L.back()); + EXPECT_FALSE(L.empty()); + EXPECT_EQ(2u, L.size()); + + // Pop back and check the new front. + L.pop_back(); + EXPECT_EQ(&A, &L.back()); + + // Pop to empty. + L.pop_back(); + EXPECT_TRUE(L.empty()); +} + +TEST(SimpleIListTest, swap) { + simple_ilist<Node> L1, L2; + Node A, B; + L1.push_back(A); + L1.push_back(B); + L1.swap(L2); + EXPECT_TRUE(L1.empty()); + EXPECT_EQ(0u, L1.size()); + EXPECT_EQ(&A, &L2.front()); + EXPECT_EQ(&B, &L2.back()); + EXPECT_FALSE(L2.empty()); + EXPECT_EQ(2u, L2.size()); +} + +TEST(SimpleIListTest, insertEraseAtEnd) { + simple_ilist<Node> L; + Node A, B; + L.insert(L.end(), A); + L.insert(L.end(), B); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &L.back()); + EXPECT_FALSE(L.empty()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, insertAtBegin) { + simple_ilist<Node> L; + Node A, B; + L.insert(L.begin(), B); + L.insert(L.begin(), A); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &L.back()); + EXPECT_FALSE(L.empty()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, remove) { + simple_ilist<Node> L; + Node A, B, C; + L.push_back(A); + L.push_back(B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + L.remove(B); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); + + L.remove(A); + EXPECT_EQ(&C, &L.front()); + EXPECT_EQ(1u, L.size()); + + L.remove(C); + EXPECT_TRUE(L.empty()); +} + +TEST(SimpleIListTest, removeAndDispose) { + simple_ilist<Node> L; + Node A, C; + Node *B = new Node; + L.push_back(A); + L.push_back(*B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + L.removeAndDispose(*B, deleteNode()); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, removeAndDisposeNullDeleter) { + simple_ilist<Node> L; + Node A, B, C; + L.push_back(A); + L.push_back(B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + L.removeAndDispose(B, doNothing); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, erase) { + simple_ilist<Node> L; + Node A, B, C; + L.push_back(A); + L.push_back(B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + EXPECT_EQ(C.getIterator(), L.erase(B.getIterator())); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, eraseAndDispose) { + simple_ilist<Node> L; + Node A, C; + Node *B = new Node; + L.push_back(A); + L.push_back(*B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + L.eraseAndDispose(B->getIterator(), deleteNode()); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, eraseAndDisposeNullDeleter) { + simple_ilist<Node> L; + Node A, B, C; + L.push_back(A); + L.push_back(B); + L.push_back(C); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&B, &*++L.begin()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(3u, L.size()); + + L.eraseAndDispose(B.getIterator(), doNothing); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&C, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, eraseRange) { + simple_ilist<Node> L; + Node A, B, C, D, E; + L.push_back(A); + L.push_back(B); + L.push_back(C); + L.push_back(D); + L.push_back(E); + auto I = L.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(&B, &*I++); + EXPECT_EQ(&C, &*I++); + EXPECT_EQ(&D, &*I++); + EXPECT_EQ(&E, &*I++); + EXPECT_EQ(L.end(), I); + EXPECT_EQ(5u, L.size()); + + // Erase a range. + EXPECT_EQ(E.getIterator(), L.erase(B.getIterator(), E.getIterator())); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&E, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, eraseAndDisposeRange) { + simple_ilist<Node> L; + Node A, *B = new Node, *C = new Node, *D = new Node, E; + L.push_back(A); + L.push_back(*B); + L.push_back(*C); + L.push_back(*D); + L.push_back(E); + auto I = L.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(B, &*I++); + EXPECT_EQ(C, &*I++); + EXPECT_EQ(D, &*I++); + EXPECT_EQ(&E, &*I++); + EXPECT_EQ(L.end(), I); + EXPECT_EQ(5u, L.size()); + + // Erase a range. + EXPECT_EQ(E.getIterator(), + L.eraseAndDispose(B->getIterator(), E.getIterator(), deleteNode())); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&E, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, eraseAndDisposeRangeNullDeleter) { + simple_ilist<Node> L; + Node A, B, C, D, E; + L.push_back(A); + L.push_back(B); + L.push_back(C); + L.push_back(D); + L.push_back(E); + auto I = L.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(&B, &*I++); + EXPECT_EQ(&C, &*I++); + EXPECT_EQ(&D, &*I++); + EXPECT_EQ(&E, &*I++); + EXPECT_EQ(L.end(), I); + EXPECT_EQ(5u, L.size()); + + // Erase a range. + EXPECT_EQ(E.getIterator(), + L.eraseAndDispose(B.getIterator(), E.getIterator(), doNothing)); + EXPECT_EQ(&A, &L.front()); + EXPECT_EQ(&E, &L.back()); + EXPECT_EQ(2u, L.size()); +} + +TEST(SimpleIListTest, clear) { + simple_ilist<Node> L; + Node A, B; + L.push_back(A); + L.push_back(B); + L.clear(); + EXPECT_TRUE(L.empty()); + EXPECT_EQ(0u, L.size()); +} + +TEST(SimpleIListTest, clearAndDispose) { + simple_ilist<Node> L; + Node *A = new Node; + Node *B = new Node; + L.push_back(*A); + L.push_back(*B); + L.clearAndDispose(deleteNode()); + EXPECT_TRUE(L.empty()); + EXPECT_EQ(0u, L.size()); +} + +TEST(SimpleIListTest, clearAndDisposeNullDeleter) { + simple_ilist<Node> L; + Node A, B; + L.push_back(A); + L.push_back(B); + L.clearAndDispose(doNothing); + EXPECT_TRUE(L.empty()); + EXPECT_EQ(0u, L.size()); +} + +TEST(SimpleIListTest, spliceList) { + simple_ilist<Node> L1, L2; + Node A, B, C, D; + + // [A, D]. + L1.push_back(A); + L1.push_back(D); + + // [B, C]. + L2.push_back(B); + L2.push_back(C); + + // Splice in L2, giving [A, B, C, D]. + L1.splice(--L1.end(), L2); + EXPECT_TRUE(L2.empty()); + EXPECT_EQ(4u, L1.size()); + auto I = L1.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(&B, &*I++); + EXPECT_EQ(&C, &*I++); + EXPECT_EQ(&D, &*I++); + EXPECT_EQ(L1.end(), I); +} + +TEST(SimpleIListTest, spliceSingle) { + simple_ilist<Node> L1, L2; + Node A, B, C, D, E; + + // [A, C]. + L1.push_back(A); + L1.push_back(C); + + // [D, B, E]. + L2.push_back(D); + L2.push_back(B); + L2.push_back(E); + + // Splice B from L2 to L1, giving [A, B, C] and [D, E]. + L1.splice(--L1.end(), L2, ++L2.begin()); + auto I = L1.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(&B, &*I++); + EXPECT_EQ(&C, &*I++); + EXPECT_EQ(L1.end(), I); + + I = L2.begin(); + EXPECT_EQ(&D, &*I++); + EXPECT_EQ(&E, &*I++); + EXPECT_EQ(L2.end(), I); +} + +TEST(SimpleIListTest, spliceRange) { + simple_ilist<Node> L1, L2; + Node A, B, C, D, E, F; + + // [A, D]. + L1.push_back(A); + L1.push_back(D); + + // [E, B, C, F]. + L2.push_back(E); + L2.push_back(B); + L2.push_back(C); + L2.push_back(F); + + // Splice B from L2 to L1, giving [A, B, C, D] and [E, F]. + L1.splice(--L1.end(), L2, ++L2.begin(), --L2.end()); + auto I = L1.begin(); + EXPECT_EQ(&A, &*I++); + EXPECT_EQ(&B, &*I++); + EXPECT_EQ(&C, &*I++); + EXPECT_EQ(&D, &*I++); + EXPECT_EQ(L1.end(), I); + + I = L2.begin(); + EXPECT_EQ(&E, &*I++); + EXPECT_EQ(&F, &*I++); + EXPECT_EQ(L2.end(), I); +} + +TEST(SimpleIListTest, merge) { + for (bool IsL1LHS : {false, true}) { + simple_ilist<Node> L1, L2; + Node Ns[10]; + + // Fill L1. + L1.push_back(Ns[0]); + L1.push_back(Ns[3]); + L1.push_back(Ns[4]); + L1.push_back(Ns[8]); + + // Fill L2. + L2.push_back(Ns[1]); + L2.push_back(Ns[2]); + L2.push_back(Ns[5]); + L2.push_back(Ns[6]); + L2.push_back(Ns[7]); + L2.push_back(Ns[9]); + + // Check setup. + EXPECT_EQ(4u, L1.size()); + EXPECT_EQ(6u, L2.size()); + EXPECT_TRUE(std::is_sorted(L1.begin(), L1.end())); + EXPECT_TRUE(std::is_sorted(L2.begin(), L2.end())); + + // Merge. + auto &LHS = IsL1LHS ? L1 : L2; + auto &RHS = IsL1LHS ? L2 : L1; + LHS.merge(RHS); + EXPECT_TRUE(RHS.empty()); + EXPECT_FALSE(LHS.empty()); + EXPECT_TRUE(std::is_sorted(LHS.begin(), LHS.end())); + auto I = LHS.begin(); + for (Node &N : Ns) + EXPECT_EQ(&N, &*I++); + EXPECT_EQ(LHS.end(), I); + } +} + +TEST(SimpleIListTest, mergeIsStable) { + simple_ilist<Node> L1, L2; + Node Ns[5]; + + auto setup = [&]() { + EXPECT_TRUE(L1.empty()); + EXPECT_TRUE(L2.empty()); + + // Fill L1. + L1.push_back(Ns[0]); + L1.push_back(Ns[3]); + L1.push_back(Ns[4]); + + // Fill L2. + L2.push_back(Ns[1]); + L2.push_back(Ns[2]); + + // Check setup. + EXPECT_EQ(3u, L1.size()); + EXPECT_EQ(2u, L2.size()); + EXPECT_TRUE(std::is_sorted(L1.begin(), L1.end(), makeFalse)); + EXPECT_TRUE(std::is_sorted(L2.begin(), L2.end(), makeFalse)); + }; + + // Merge. Should be stable. + setup(); + L1.merge(L2, makeFalse); + EXPECT_TRUE(L2.empty()); + EXPECT_FALSE(L1.empty()); + EXPECT_TRUE(std::is_sorted(L1.begin(), L1.end(), makeFalse)); + auto I = L1.begin(); + EXPECT_EQ(&Ns[0], &*I++); + EXPECT_EQ(&Ns[3], &*I++); + EXPECT_EQ(&Ns[4], &*I++); + EXPECT_EQ(&Ns[1], &*I++); + EXPECT_EQ(&Ns[2], &*I++); + EXPECT_EQ(L1.end(), I); + + // Merge the other way. Should be stable. + L1.clear(); + setup(); + L2.merge(L1, makeFalse); + EXPECT_TRUE(L1.empty()); + EXPECT_FALSE(L2.empty()); + EXPECT_TRUE(std::is_sorted(L2.begin(), L2.end(), makeFalse)); + I = L2.begin(); + EXPECT_EQ(&Ns[1], &*I++); + EXPECT_EQ(&Ns[2], &*I++); + EXPECT_EQ(&Ns[0], &*I++); + EXPECT_EQ(&Ns[3], &*I++); + EXPECT_EQ(&Ns[4], &*I++); + EXPECT_EQ(L2.end(), I); +} + +TEST(SimpleIListTest, mergeEmpty) { + for (bool IsL1LHS : {false, true}) { + simple_ilist<Node> L1, L2; + Node Ns[4]; + + // Fill L1. + L1.push_back(Ns[0]); + L1.push_back(Ns[1]); + L1.push_back(Ns[2]); + L1.push_back(Ns[3]); + + // Check setup. + EXPECT_EQ(4u, L1.size()); + EXPECT_TRUE(L2.empty()); + EXPECT_TRUE(std::is_sorted(L1.begin(), L1.end())); + + // Merge. + auto &LHS = IsL1LHS ? L1 : L2; + auto &RHS = IsL1LHS ? L2 : L1; + LHS.merge(RHS); + EXPECT_TRUE(RHS.empty()); + EXPECT_FALSE(LHS.empty()); + EXPECT_TRUE(std::is_sorted(LHS.begin(), LHS.end())); + auto I = LHS.begin(); + for (Node &N : Ns) + EXPECT_EQ(&N, &*I++); + EXPECT_EQ(LHS.end(), I); + } +} + +TEST(SimpleIListTest, mergeBothEmpty) { + simple_ilist<Node> L1, L2; + L1.merge(L2); + EXPECT_TRUE(L1.empty()); + EXPECT_TRUE(L2.empty()); +} + +TEST(SimpleIListTest, sort) { + simple_ilist<Node> L; + Node Ns[10]; + + // Fill L. + for (int I : {3, 4, 0, 8, 1, 2, 6, 7, 9, 5}) + L.push_back(Ns[I]); + + // Check setup. + EXPECT_EQ(10u, L.size()); + EXPECT_FALSE(std::is_sorted(L.begin(), L.end())); + + // Sort. + L.sort(); + EXPECT_TRUE(std::is_sorted(L.begin(), L.end())); + auto I = L.begin(); + for (Node &N : Ns) + EXPECT_EQ(&N, &*I++); + EXPECT_EQ(L.end(), I); +} + +TEST(SimpleIListTest, sortIsStable) { + simple_ilist<Node> L; + Node Ns[10]; + + // Compare such that nodes are partitioned but not fully sorted. + auto partition = [&](const Node &N) { return &N >= &Ns[5]; }; + auto compare = [&](const Node &L, const Node &R) { + return partition(L) < partition(R); + }; + + // Fill L. + for (int I : {3, 4, 7, 8, 1, 2, 6, 0, 9, 5}) + L.push_back(Ns[I]); + + // Check setup. + EXPECT_EQ(10u, L.size()); + EXPECT_FALSE(std::is_sorted(L.begin(), L.end(), compare)); + + // Sort. + L.sort(compare); + EXPECT_TRUE(std::is_sorted(L.begin(), L.end(), compare)); + auto I = L.begin(); + for (int O : {3, 4, 1, 2, 0}) + EXPECT_EQ(&Ns[O], &*I++); + for (int O : {7, 8, 6, 9, 5}) + EXPECT_EQ(&Ns[O], &*I++); + EXPECT_EQ(L.end(), I); +} + +TEST(SimpleIListTest, sortEmpty) { + simple_ilist<Node> L; + L.sort(); +} + +} // end namespace diff --git a/llvm/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt index 2baa4370c70..750f638c7a4 100644 --- a/llvm/unittests/IR/CMakeLists.txt +++ b/llvm/unittests/IR/CMakeLists.txt @@ -20,6 +20,7 @@ set(IRSources LegacyPassManagerTest.cpp MDBuilderTest.cpp MetadataTest.cpp + ModuleTest.cpp PassManagerTest.cpp PatternMatch.cpp TypeBuilderTest.cpp diff --git a/llvm/unittests/IR/ModuleTest.cpp b/llvm/unittests/IR/ModuleTest.cpp new file mode 100644 index 00000000000..ef1652b3eb2 --- /dev/null +++ b/llvm/unittests/IR/ModuleTest.cpp @@ -0,0 +1,48 @@ +//===- unittests/IR/ModuleTest.cpp - Module unit tests --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Module.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +bool sortByName(const GlobalVariable &L, const GlobalVariable &R) { + return L.getName() < R.getName(); +} + +bool sortByNameReverse(const GlobalVariable &L, const GlobalVariable &R) { + return sortByName(R, L); +} + +TEST(ModuleTest, sortGlobalsByName) { + LLVMContext Context; + for (auto compare : {sortByName, sortByNameReverse}) { + Module M("M", Context); + Type *T = Type::getInt8Ty(Context); + GlobalValue::LinkageTypes L = GlobalValue::ExternalLinkage; + (void)new GlobalVariable(M, T, false, L, nullptr, "A"); + (void)new GlobalVariable(M, T, false, L, nullptr, "F"); + (void)new GlobalVariable(M, T, false, L, nullptr, "G"); + (void)new GlobalVariable(M, T, false, L, nullptr, "E"); + (void)new GlobalVariable(M, T, false, L, nullptr, "B"); + (void)new GlobalVariable(M, T, false, L, nullptr, "H"); + (void)new GlobalVariable(M, T, false, L, nullptr, "C"); + (void)new GlobalVariable(M, T, false, L, nullptr, "D"); + + // Sort the globals by name. + EXPECT_FALSE(std::is_sorted(M.global_begin(), M.global_end(), compare)); + M.getGlobalList().sort(compare); + EXPECT_TRUE(std::is_sorted(M.global_begin(), M.global_end(), compare)); + } +} + +} // end namespace |