Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Commit f1f27bb

Browse files
committed
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<>. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@280107 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 788dfe5 commit f1f27bb

File tree

9 files changed

+1018
-142
lines changed

9 files changed

+1018
-142
lines changed

include/llvm/ADT/ilist.h

Lines changed: 33 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,8 @@
2424
#ifndef LLVM_ADT_ILIST_H
2525
#define LLVM_ADT_ILIST_H
2626

27-
#include "llvm/ADT/ilist_base.h"
28-
#include "llvm/ADT/ilist_iterator.h"
29-
#include "llvm/ADT/ilist_node.h"
27+
#include "llvm/ADT/simple_ilist.h"
3028
#include "llvm/Support/Compiler.h"
31-
#include <algorithm>
3229
#include <cassert>
3330
#include <cstddef>
3431
#include <iterator>
@@ -119,17 +116,14 @@ struct ilist_traits<const Ty> : public ilist_traits<Ty> {};
119116
/// ilist_sentinel, which holds pointers to the first and last nodes in the
120117
/// list.
121118
template <typename NodeTy, typename Traits = ilist_traits<NodeTy>>
122-
class iplist : public Traits, ilist_base, ilist_node_access {
119+
class iplist : public Traits, simple_ilist<NodeTy> {
123120
// TODO: Drop this assertion and the transitive type traits anytime after
124121
// v4.0 is branched (i.e,. keep them for one release to help out-of-tree code
125122
// update).
126123
static_assert(!ilist_detail::HasObsoleteCustomization<Traits, NodeTy>::value,
127124
"ilist customization points have changed!");
128125

129-
ilist_sentinel<NodeTy> Sentinel;
130-
131-
typedef ilist_node<NodeTy> node_type;
132-
typedef const ilist_node<NodeTy> const_node_type;
126+
typedef simple_ilist<NodeTy> base_list_type;
133127

134128
static bool op_less(NodeTy &L, NodeTy &R) { return L < R; }
135129
static bool op_equal(NodeTy &L, NodeTy &R) { return L == R; }
@@ -139,65 +133,42 @@ class iplist : public Traits, ilist_base, ilist_node_access {
139133
void operator=(const iplist &) = delete;
140134

141135
public:
142-
typedef NodeTy *pointer;
143-
typedef const NodeTy *const_pointer;
144-
typedef NodeTy &reference;
145-
typedef const NodeTy &const_reference;
146-
typedef NodeTy value_type;
147-
typedef ilist_iterator<NodeTy> iterator;
148-
typedef ilist_iterator<const NodeTy> const_iterator;
149-
typedef size_t size_type;
150-
typedef ptrdiff_t difference_type;
151-
typedef ilist_iterator<const NodeTy, true> const_reverse_iterator;
152-
typedef ilist_iterator<NodeTy, true> reverse_iterator;
136+
typedef typename base_list_type::pointer pointer;
137+
typedef typename base_list_type::const_pointer const_pointer;
138+
typedef typename base_list_type::reference reference;
139+
typedef typename base_list_type::const_reference const_reference;
140+
typedef typename base_list_type::value_type value_type;
141+
typedef typename base_list_type::size_type size_type;
142+
typedef typename base_list_type::difference_type difference_type;
143+
typedef typename base_list_type::iterator iterator;
144+
typedef typename base_list_type::const_iterator const_iterator;
145+
typedef typename base_list_type::reverse_iterator reverse_iterator;
146+
typedef
147+
typename base_list_type::const_reverse_iterator const_reverse_iterator;
153148

154149
iplist() = default;
155150
~iplist() { clear(); }
156151

157-
// Iterator creation methods.
158-
iterator begin() { return ++iterator(Sentinel); }
159-
const_iterator begin() const { return ++const_iterator(Sentinel); }
160-
iterator end() { return iterator(Sentinel); }
161-
const_iterator end() const { return const_iterator(Sentinel); }
162-
163-
// reverse iterator creation methods.
164-
reverse_iterator rbegin() { return ++reverse_iterator(Sentinel); }
165-
const_reverse_iterator rbegin() const{ return ++const_reverse_iterator(Sentinel); }
166-
reverse_iterator rend() { return reverse_iterator(Sentinel); }
167-
const_reverse_iterator rend() const { return const_reverse_iterator(Sentinel); }
168-
169152
// Miscellaneous inspection routines.
170153
size_type max_size() const { return size_type(-1); }
171-
bool LLVM_ATTRIBUTE_UNUSED_RESULT empty() const { return Sentinel.empty(); }
172154

173-
// Front and back accessor functions...
174-
reference front() {
175-
assert(!empty() && "Called front() on empty list!");
176-
return *begin();
177-
}
178-
const_reference front() const {
179-
assert(!empty() && "Called front() on empty list!");
180-
return *begin();
181-
}
182-
reference back() {
183-
assert(!empty() && "Called back() on empty list!");
184-
return *--end();
185-
}
186-
const_reference back() const {
187-
assert(!empty() && "Called back() on empty list!");
188-
return *--end();
189-
}
155+
using base_list_type::begin;
156+
using base_list_type::end;
157+
using base_list_type::rbegin;
158+
using base_list_type::rend;
159+
using base_list_type::empty;
160+
using base_list_type::front;
161+
using base_list_type::back;
190162

191163
void swap(iplist &RHS) {
192164
assert(0 && "Swap does not use list traits callback correctly yet!");
193-
std::swap(Sentinel, RHS.Sentinel);
165+
base_list_type::swap(RHS);
194166
}
195167

196168
iterator insert(iterator where, NodeTy *New) {
197-
ilist_base::insertBefore(*where.getNodePtr(), *this->getNodePtr(New));
198-
169+
auto I = base_list_type::insert(where, *New);
199170
this->addNodeToList(New); // Notify traits that we added a node...
200-
return iterator(New);
171+
return I;
201172
}
202173

203174
iterator insert(iterator where, const NodeTy &New) {
@@ -212,9 +183,8 @@ class iplist : public Traits, ilist_base, ilist_node_access {
212183
}
213184

214185
NodeTy *remove(iterator &IT) {
215-
assert(IT != end() && "Cannot remove end of list!");
216-
NodeTy *Node = &*IT++;
217-
ilist_base::remove(*this->getNodePtr(Node));
186+
NodeTy *Node = &*IT;
187+
base_list_type::erase(IT++);
218188
this->removeNodeFromList(Node); // Notify traits that we removed a node...
219189
return Node;
220190
}
@@ -241,7 +211,7 @@ class iplist : public Traits, ilist_base, ilist_node_access {
241211
///
242212
/// This should only be used immediately before freeing nodes in bulk to
243213
/// avoid traversing the list and bringing all the nodes into cache.
244-
void clearAndLeakNodesUnsafely() { Sentinel.reset(); }
214+
void clearAndLeakNodesUnsafely() { base_list_type::clear(); }
245215

246216
private:
247217
// transfer - The heart of the splice function. Move linked list nodes from
@@ -251,8 +221,7 @@ class iplist : public Traits, ilist_base, ilist_node_access {
251221
if (position == last)
252222
return;
253223

254-
ilist_base::transferBefore(*position.getNodePtr(), *first.getNodePtr(),
255-
*last.getNodePtr());
224+
base_list_type::splice(position, L2, first, last);
256225

257226
// Callback. Note that the nodes have moved from before-last to
258227
// before-position.
@@ -265,9 +234,7 @@ class iplist : public Traits, ilist_base, ilist_node_access {
265234
// Functionality derived from other functions defined above...
266235
//
267236

268-
size_type LLVM_ATTRIBUTE_UNUSED_RESULT size() const {
269-
return std::distance(begin(), end());
270-
}
237+
using base_list_type::size;
271238

272239
iterator erase(iterator first, iterator last) {
273240
while (first != last)
@@ -318,48 +285,12 @@ class iplist : public Traits, ilist_base, ilist_node_access {
318285
void merge(iplist &Right, Compare comp) {
319286
if (this == &Right)
320287
return;
321-
iterator First1 = begin(), Last1 = end();
322-
iterator First2 = Right.begin(), Last2 = Right.end();
323-
while (First1 != Last1 && First2 != Last2) {
324-
if (comp(*First2, *First1)) {
325-
iterator Next = First2;
326-
transfer(First1, Right, First2, ++Next);
327-
First2 = Next;
328-
} else {
329-
++First1;
330-
}
331-
}
332-
if (First2 != Last2)
333-
transfer(Last1, Right, First2, Last2);
288+
this->transferNodesFromList(Right, Right.begin(), Right.end());
289+
base_list_type::merge(Right, comp);
334290
}
335291
void merge(iplist &Right) { return merge(Right, op_less); }
336292

337-
template <class Compare>
338-
void sort(Compare comp) {
339-
// The list is empty, vacuously sorted.
340-
if (empty())
341-
return;
342-
// The list has a single element, vacuously sorted.
343-
if (std::next(begin()) == end())
344-
return;
345-
// Find the split point for the list.
346-
iterator Center = begin(), End = begin();
347-
while (End != end() && std::next(End) != end()) {
348-
Center = std::next(Center);
349-
End = std::next(std::next(End));
350-
}
351-
// Split the list into two.
352-
iplist RightHalf;
353-
RightHalf.splice(RightHalf.begin(), *this, Center, end());
354-
355-
// Sort the two sublists.
356-
sort(comp);
357-
RightHalf.sort(comp);
358-
359-
// Merge the two sublists back together.
360-
merge(RightHalf, comp);
361-
}
362-
void sort() { sort(op_less); }
293+
using base_list_type::sort;
363294

364295
/// \brief Get the previous node, or \c nullptr for the list head.
365296
NodeTy *getPrevNode(NodeTy &N) const {

include/llvm/ADT/ilist_base.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,22 @@ class ilist_base {
3939
N.setNext(nullptr);
4040
}
4141

42+
static void removeRangeImpl(ilist_node_base &First, ilist_node_base &Last) {
43+
ilist_node_base *Prev = First.getPrev();
44+
ilist_node_base *Final = Last.getPrev();
45+
Last.setPrev(Prev);
46+
Prev->setNext(&Last);
47+
48+
// Not strictly necessary, but helps catch a class of bugs.
49+
First.setPrev(nullptr);
50+
Final->setNext(nullptr);
51+
}
52+
4253
static void transferBeforeImpl(ilist_node_base &Next, ilist_node_base &First,
4354
ilist_node_base &Last) {
44-
assert(&Next != &Last && "Should be checked by callers");
45-
assert(&First != &Last && "Should be checked by callers");
55+
if (&Next == &Last || &First == &Last)
56+
return;
57+
4658
// Position cannot be contained in the range to be transferred.
4759
assert(&Next != &First &&
4860
// Check for the most common mistake.
@@ -67,6 +79,9 @@ class ilist_base {
6779
}
6880

6981
template <class T> static void remove(T &N) { removeImpl(N); }
82+
template <class T> static void removeRange(T &First, T &Last) {
83+
removeRangeImpl(First, Last);
84+
}
7085

7186
template <class T> static void transferBefore(T &Next, T &First, T &Last) {
7287
transferBeforeImpl(Next, First, Last);

0 commit comments

Comments
 (0)