mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 17:32:36 +00:00
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
This commit is contained in:
parent
35c1fea391
commit
9200890ea3
@ -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 {
|
||||
|
@ -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);
|
||||
|
269
include/llvm/ADT/simple_ilist.h
Normal file
269
include/llvm/ADT/simple_ilist.h
Normal file
@ -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
|
@ -44,6 +44,7 @@ set(ADTSources
|
||||
ScopeExitTest.cpp
|
||||
SequenceTest.cpp
|
||||
SetVectorTest.cpp
|
||||
SimpleIListTest.cpp
|
||||
SmallPtrSetTest.cpp
|
||||
SmallStringTest.cpp
|
||||
SmallVectorTest.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;
|
||||
|
||||
|
@ -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
|
||||
|
586
unittests/ADT/SimpleIListTest.cpp
Normal file
586
unittests/ADT/SimpleIListTest.cpp
Normal file
@ -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
|
@ -20,6 +20,7 @@ set(IRSources
|
||||
LegacyPassManagerTest.cpp
|
||||
MDBuilderTest.cpp
|
||||
MetadataTest.cpp
|
||||
ModuleTest.cpp
|
||||
PassManagerTest.cpp
|
||||
PatternMatch.cpp
|
||||
TypeBuilderTest.cpp
|
||||
|
48
unittests/IR/ModuleTest.cpp
Normal file
48
unittests/IR/ModuleTest.cpp
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user