llvm.org GIT mirror llvm / 5de5554
ADT: Add AllocatorList, and use it for yaml::Token - Add AllocatorList, a non-intrusive list that owns an LLVM-style allocator and provides a std::list-like interface (trivially built on top of simple_ilist), - add a typedef (and unit tests) for BumpPtrList, and - use BumpPtrList for the list of llvm::yaml::Token (i.e., TokenQueueT). TokenQueueT has no need for the complexity of an intrusive list. The only reason to inherit from ilist was to customize the allocator. TokenQueueT was the only example in-tree of using ilist<> in a truly non-intrusive way. Moreover, this removes the final use of the non-intrusive ilist_traits<>::createNode (after r280573, r281177, and r281181). I have a WIP patch that removes this customization point (and the API that relies on it) that I plan to commit soon. Note: AllocatorList owns the allocator, which limits the viable API (e.g., splicing must be on the same list). For now I've left out any problematic API. It wouldn't be hard to split AllocatorList into two layers: an Impl class that calls DerivedT::getAlloc (via CRTP), and derived classes that handle Allocator ownership/reference/etc semantics; and then implement splice with appropriate assertions; but TBH we should probably just customize the std::list allocators at that point. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@281182 91177308-0d34-0410-b5e6-96231b3b80d8 Duncan P. N. Exon Smith 3 years ago
6 changed file(s) with 486 addition(s) and 18 deletion(s). Raw diff Collapse all Expand all
0 //===- llvm/ADT/AllocatorList.h - Custom allocator list ---------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_ADT_ALLOCATORLIST_H
10 #define LLVM_ADT_ALLOCATORLIST_H
11
12 #include "llvm/ADT/iterator.h"
13 #include "llvm/ADT/simple_ilist.h"
14 #include "llvm/Support/Allocator.h"
15 #include
16
17 namespace llvm {
18
19 /// A linked-list with a custom, local allocator.
20 ///
21 /// Expose a std::list-like interface that owns and uses a custom LLVM-style
22 /// allocator (e.g., BumpPtrAllocator), leveraging \a simple_ilist for the
23 /// implementation details.
24 ///
25 /// Because this list owns the allocator, calling \a splice() with a different
26 /// list isn't generally safe. As such, \a splice has been left out of the
27 /// interface entirely.
28 template class AllocatorList : AllocatorT {
29 struct Node : ilist_node {
30 Node(Node &&) = delete;
31 Node(const Node &) = delete;
32 Node &operator=(Node &&) = delete;
33 Node &operator=(const Node &) = delete;
34
35 Node(T &&V) : V(std::move(V)) {}
36 Node(const T &V) : V(V) {}
37 template Node(Ts &&... Vs) : V(std::forward(Vs)...) {}
38 T V;
39 };
40
41 typedef simple_ilist list_type;
42 list_type List;
43
44 AllocatorT &getAlloc() { return *this; }
45 const AllocatorT &getAlloc() const { return *this; }
46
47 template Node *create(ArgTs &&... Args) {
48 return new (getAlloc()) Node(std::forward(Args)...);
49 }
50
51 struct Cloner {
52 AllocatorList &AL;
53 Cloner(AllocatorList &AL) : AL(AL) {}
54 Node *operator()(const Node &N) const { return AL.create(N.V); }
55 };
56
57 struct Disposer {
58 AllocatorList &AL;
59 Disposer(AllocatorList &AL) : AL(AL) {}
60 void operator()(Node *N) const {
61 N->~Node();
62 AL.getAlloc().Deallocate(N);
63 }
64 };
65
66 public:
67 typedef T value_type;
68 typedef T *pointer;
69 typedef T &reference;
70 typedef const T *const_pointer;
71 typedef const T &const_reference;
72 typedef typename list_type::size_type size_type;
73 typedef typename list_type::difference_type difference_type;
74
75 private:
76 template
77 class IteratorImpl
78 : public iterator_adaptor_base,
79 IteratorBase,
80 std::bidirectional_iterator_tag, ValueT> {
81 template
82 friend class IteratorImpl;
83 friend AllocatorList;
84
85 typedef iterator_adaptor_base,
86 IteratorBase, std::bidirectional_iterator_tag,
87 ValueT>
88 iterator_adaptor_base;
89
90 public:
91 typedef ValueT value_type;
92 typedef ValueT *pointer;
93 typedef ValueT &reference;
94
95 IteratorImpl() = default;
96 IteratorImpl(const IteratorImpl &) = default;
97 IteratorImpl &operator=(const IteratorImpl &) = default;
98 ~IteratorImpl() = default;
99
100 explicit IteratorImpl(const IteratorBase &I) : iterator_adaptor_base(I) {}
101
102 template
103 IteratorImpl(const IteratorImpl &X,
104 typename std::enable_if
105 OtherIteratorBase, IteratorBase>::value>::type * = nullptr)
106 : iterator_adaptor_base(X.wrapped()) {}
107
108 reference operator*() const { return iterator_adaptor_base::wrapped()->V; }
109 pointer operator->() const { return &operator*(); }
110
111 friend bool operator==(const IteratorImpl &L, const IteratorImpl &R) {
112 return L.wrapped() == R.wrapped();
113 }
114 friend bool operator!=(const IteratorImpl &L, const IteratorImpl &R) {
115 return !(L == R);
116 }
117 };
118
119 public:
120 typedef IteratorImpl iterator;
121 typedef IteratorImpl
122 reverse_iterator;
123 typedef IteratorImpl
124 const_iterator;
125 typedef IteratorImpl
126 const_reverse_iterator;
127
128 AllocatorList() = default;
129 AllocatorList(AllocatorList &&X)
130 : AllocatorT(std::move(X.getAlloc())), List(std::move(X.List)) {}
131 AllocatorList(const AllocatorList &X) {
132 List.cloneFrom(X.List, Cloner(*this), Disposer(*this));
133 }
134 AllocatorList &operator=(AllocatorList &&X) {
135 clear(); // Dispose of current nodes explicitly.
136 List = std::move(X.List);
137 getAlloc() = std::move(X.getAlloc());
138 return *this;
139 }
140 AllocatorList &operator=(const AllocatorList &X) {
141 List.cloneFrom(X.List, Cloner(*this), Disposer(*this));
142 return *this;
143 }
144 ~AllocatorList() { clear(); }
145
146 void swap(AllocatorList &RHS) {
147 List.swap(RHS.List);
148 std::swap(getAlloc(), RHS.getAlloc());
149 }
150
151 bool empty() { return List.empty(); }
152 size_t size() { return List.size(); }
153
154 iterator begin() { return iterator(List.begin()); }
155 iterator end() { return iterator(List.end()); }
156 const_iterator begin() const { return const_iterator(List.begin()); }
157 const_iterator end() const { return const_iterator(List.end()); }
158 reverse_iterator rbegin() { return reverse_iterator(List.rbegin()); }
159 reverse_iterator rend() { return reverse_iterator(List.rend()); }
160 const_reverse_iterator rbegin() const {
161 return const_reverse_iterator(List.rbegin());
162 }
163 const_reverse_iterator rend() const {
164 return const_reverse_iterator(List.rend());
165 }
166
167 T &back() { return List.back().V; }
168 T &front() { return List.front().V; }
169 const T &back() const { return List.back().V; }
170 const T &front() const { return List.front().V; }
171
172 template iterator emplace(iterator I, Ts &&... Vs) {
173 return iterator(List.insert(I.wrapped(), *create(std::forward(Vs)...)));
174 }
175
176 iterator insert(iterator I, T &&V) {
177 return iterator(List.insert(I.wrapped(), *create(std::move(V))));
178 }
179 iterator insert(iterator I, const T &V) {
180 return iterator(List.insert(I.wrapped(), *create(V)));
181 }
182
183 template
184 void insert(iterator I, Iterator First, Iterator Last) {
185 for (; First != Last; ++First)
186 List.insert(I.wrapped(), *create(*First));
187 }
188
189 iterator erase(iterator I) {
190 return iterator(List.eraseAndDispose(I.wrapped(), Disposer(*this)));
191 }
192
193 iterator erase(iterator First, iterator Last) {
194 return iterator(
195 List.eraseAndDispose(First.wrapped(), Last.wrapped(), Disposer(*this)));
196 }
197
198 void clear() { List.clearAndDispose(Disposer(*this)); }
199 void pop_back() { List.eraseAndDispose(--List.end(), Disposer(*this)); }
200 void pop_front() { List.eraseAndDispose(List.begin(), Disposer(*this)); }
201 void push_back(T &&V) { insert(end(), std::move(V)); }
202 void push_front(T &&V) { insert(begin(), std::move(V)); }
203 void push_back(const T &V) { insert(end(), V); }
204 void push_front(const T &V) { insert(begin(), V); }
205 template void emplace_back(Ts &&... Vs) {
206 emplace(end(), std::forward(Vs)...);
207 }
208 template void emplace_front(Ts &&... Vs) {
209 emplace(begin(), std::forward(Vs)...);
210 }
211
212 /// Reset the underlying allocator.
213 ///
214 /// \pre \c empty()
215 void resetAlloc() {
216 assert(empty() && "Cannot reset allocator if not empty");
217 getAlloc().Reset();
218 }
219 };
220
221 template using BumpPtrList = AllocatorList;
222
223 } // end namespace llvm
224
225 #endif // LLVM_ADT_ALLOCATORLIST_H
3939 ///
4040 /// \see ilist_noalloc_traits
4141 template struct ilist_alloc_traits {
42 /// Clone a node.
43 ///
44 /// TODO: Remove this and API that relies on it (it's dead code).
4245 static NodeTy *createNode(const NodeTy &V) { return new NodeTy(V); }
4346 static void deleteNode(NodeTy *V) { delete V; }
4447 };
161161 void insert(iterator I, Iterator First, Iterator Last) {
162162 for (; First != Last; ++First)
163163 insert(I, *First);
164 }
165
166 /// Clone another list.
167 template
168 void cloneFrom(const simple_ilist &L2, Cloner clone, Disposer dispose) {
169 clearAndDispose(dispose);
170 for (const_reference V : L2)
171 push_back(*clone(V));
164172 }
165173
166174 /// Remove a node by reference; never deletes.
1616 #include "llvm/ADT/SmallVector.h"
1717 #include "llvm/ADT/StringExtras.h"
1818 #include "llvm/ADT/Twine.h"
19 #include "llvm/ADT/ilist.h"
20 #include "llvm/ADT/ilist_node.h"
19 #include "llvm/ADT/AllocatorList.h"
2120 #include "llvm/Support/ErrorHandling.h"
2221 #include "llvm/Support/MemoryBuffer.h"
2322 #include "llvm/Support/SourceMgr.h"
108107 void AliasNode::anchor() {}
109108
110109 /// Token - A single YAML token.
111 struct Token : ilist_node {
110 struct Token {
112111 enum TokenKind {
113112 TK_Error, // Uninitialized token.
114113 TK_StreamStart,
147146 }
148147 }
149148
150 namespace llvm {
151 template <> struct ilist_alloc_traits {
152 Token *createNode(const Token &V) {
153 return new (Alloc.Allocate()) Token(V);
154 }
155 static void deleteNode(Token *V) { V->~Token(); }
156
157 BumpPtrAllocator Alloc;
158 };
159 } // end namespace llvm
160
161 typedef ilist TokenQueueT;
149 typedef llvm::BumpPtrList TokenQueueT;
162150
163151 namespace {
164152 /// @brief This struct is used to track simple keys.
796784
797785 // There cannot be any referenced Token's if the TokenQueue is empty. So do a
798786 // quick deallocation of them all.
799 if (TokenQueue.empty()) {
800 TokenQueue.Alloc.Reset();
801 }
787 if (TokenQueue.empty())
788 TokenQueue.resetAlloc();
802789
803790 return Ret;
804791 }
0 //===- unittests/ADT/BumpPtrListTest.cpp - BumpPtrList unit tests ---------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/ADT/AllocatorList.h"
10 #include "llvm/ADT/STLExtras.h"
11 #include "gtest/gtest.h"
12
13 using namespace llvm;
14
15 namespace {
16
17 struct CountsDestructors {
18 static unsigned NumCalls;
19 ~CountsDestructors() { ++NumCalls; }
20 };
21 unsigned CountsDestructors::NumCalls = 0;
22
23 struct MoveOnly {
24 int V;
25 explicit MoveOnly(int V) : V(V) {}
26 MoveOnly() = delete;
27 MoveOnly(MoveOnly &&X) { V = X.V; }
28 MoveOnly(const MoveOnly &X) = delete;
29 MoveOnly &operator=(MoveOnly &&X) = delete;
30 MoveOnly &operator=(const MoveOnly &X) = delete;
31 };
32
33 struct EmplaceOnly {
34 int V1, V2;
35 explicit EmplaceOnly(int V1, int V2) : V1(V1), V2(V2) {}
36 EmplaceOnly() = delete;
37 EmplaceOnly(EmplaceOnly &&X) = delete;
38 EmplaceOnly(const EmplaceOnly &X) = delete;
39 EmplaceOnly &operator=(EmplaceOnly &&X) = delete;
40 EmplaceOnly &operator=(const EmplaceOnly &X) = delete;
41 };
42
43 TEST(BumpPtrListTest, DefaultConstructor) {
44 BumpPtrList L;
45 EXPECT_TRUE(L.empty());
46 }
47
48 TEST(BumpPtrListTest, pushPopBack) {
49 // Build a list with push_back.
50 BumpPtrList L;
51 int Ns[] = {1, 3, 9, 5, 7};
52 for (const int N : Ns)
53 L.push_back(N);
54
55 // Use iterators to check contents.
56 auto I = L.begin();
57 for (int N : Ns)
58 EXPECT_EQ(N, *I++);
59 EXPECT_EQ(I, L.end());
60
61 // Unbuild the list with pop_back.
62 for (int N : llvm::reverse(Ns)) {
63 EXPECT_EQ(N, L.back());
64 L.pop_back();
65 }
66 EXPECT_TRUE(L.empty());
67 }
68
69 TEST(BumpPtrListTest, pushPopFront) {
70 // Build a list with push_front.
71 BumpPtrList L;
72 int Ns[] = {1, 3, 9, 5, 7};
73 for (const int N : Ns)
74 L.push_front(N);
75
76 // Use reverse iterators to check contents.
77 auto I = L.rbegin();
78 for (int N : Ns)
79 EXPECT_EQ(N, *I++);
80 EXPECT_EQ(I, L.rend());
81
82 // Unbuild the list with pop_front.
83 for (int N : llvm::reverse(Ns)) {
84 EXPECT_EQ(N, L.front());
85 L.pop_front();
86 }
87 EXPECT_TRUE(L.empty());
88 }
89
90 TEST(BumpPtrListTest, pushBackMoveOnly) {
91 BumpPtrList L;
92 int Ns[] = {1, 3, 9, 5, 7};
93 for (const int N : Ns) {
94 L.push_back(MoveOnly(N));
95 EXPECT_EQ(N, L.back().V);
96 }
97 // Instantiate with MoveOnly.
98 while (!L.empty())
99 L.pop_back();
100 }
101
102 TEST(BumpPtrListTest, pushFrontMoveOnly) {
103 BumpPtrList L;
104 int Ns[] = {1, 3, 9, 5, 7};
105 for (const int N : Ns) {
106 L.push_front(MoveOnly(N));
107 EXPECT_EQ(N, L.front().V);
108 }
109 // Instantiate with MoveOnly.
110 while (!L.empty())
111 L.pop_front();
112 }
113
114 TEST(BumpPtrListTest, emplaceBack) {
115 BumpPtrList L;
116 int N1s[] = {1, 3, 9, 5, 7};
117 int N2s[] = {7, 3, 1, 8, 2};
118 for (int I = 0; I != 5; ++I) {
119 L.emplace_back(N1s[I], N2s[I]);
120 EXPECT_EQ(N1s[I], L.back().V1);
121 EXPECT_EQ(N2s[I], L.back().V2);
122 }
123 // Instantiate with EmplaceOnly.
124 while (!L.empty())
125 L.pop_back();
126 }
127
128 TEST(BumpPtrListTest, emplaceFront) {
129 BumpPtrList L;
130 int N1s[] = {1, 3, 9, 5, 7};
131 int N2s[] = {7, 3, 1, 8, 2};
132 for (int I = 0; I != 5; ++I) {
133 L.emplace_front(N1s[I], N2s[I]);
134 EXPECT_EQ(N1s[I], L.front().V1);
135 EXPECT_EQ(N2s[I], L.front().V2);
136 }
137 // Instantiate with EmplaceOnly.
138 while (!L.empty())
139 L.pop_front();
140 }
141
142 TEST(BumpPtrListTest, swap) {
143 // Build two lists with different lifetimes and swap them.
144 int N1s[] = {1, 3, 5, 7, 9};
145 int N2s[] = {2, 4, 6, 8, 10};
146
147 BumpPtrList L1;
148 L1.insert(L1.end(), std::begin(N1s), std::end(N1s));
149 {
150 BumpPtrList L2;
151 L2.insert(L2.end(), std::begin(N2s), std::end(N2s));
152
153 // Swap the lists.
154 L1.swap(L2);
155
156 // Check L2's contents before it goes out of scope.
157 auto I = L2.begin();
158 for (int N : N1s)
159 EXPECT_EQ(N, *I++);
160 EXPECT_EQ(I, L2.end());
161 }
162
163 // Check L1's contents now that L2 is out of scope (with its allocation
164 // blocks).
165 auto I = L1.begin();
166 for (int N : N2s)
167 EXPECT_EQ(N, *I++);
168 EXPECT_EQ(I, L1.end());
169 }
170
171 TEST(BumpPtrListTest, clear) {
172 CountsDestructors::NumCalls = 0;
173 CountsDestructors N;
174 BumpPtrList L;
175 L.push_back(N);
176 L.push_back(N);
177 L.push_back(N);
178 EXPECT_EQ(3u, L.size());
179 EXPECT_EQ(0u, CountsDestructors::NumCalls);
180 L.pop_back();
181 EXPECT_EQ(1u, CountsDestructors::NumCalls);
182 L.clear();
183 EXPECT_EQ(3u, CountsDestructors::NumCalls);
184 }
185
186 TEST(BumpPtrListTest, move) {
187 BumpPtrList L1, L2;
188 L1.push_back(1);
189 L2.push_back(2);
190 L1 = std::move(L2);
191 EXPECT_EQ(1u, L1.size());
192 EXPECT_EQ(2, L1.front());
193 EXPECT_EQ(0u, L2.size());
194 }
195
196 TEST(BumpPtrListTest, moveCallsDestructors) {
197 CountsDestructors::NumCalls = 0;
198 BumpPtrList L1, L2;
199 L1.emplace_back();
200 EXPECT_EQ(0u, CountsDestructors::NumCalls);
201 L1 = std::move(L2);
202 EXPECT_EQ(1u, CountsDestructors::NumCalls);
203 }
204
205 TEST(BumpPtrListTest, copy) {
206 BumpPtrList L1, L2;
207 L1.push_back(1);
208 L2.push_back(2);
209 L1 = L2;
210 EXPECT_EQ(1u, L1.size());
211 EXPECT_EQ(2, L1.front());
212 EXPECT_EQ(1u, L2.size());
213 EXPECT_EQ(2, L2.front());
214 }
215
216 TEST(BumpPtrListTest, copyCallsDestructors) {
217 CountsDestructors::NumCalls = 0;
218 BumpPtrList L1, L2;
219 L1.emplace_back();
220 EXPECT_EQ(0u, CountsDestructors::NumCalls);
221 L1 = L2;
222 EXPECT_EQ(1u, CountsDestructors::NumCalls);
223 }
224
225 TEST(BumpPtrListTest, resetAlloc) {
226 // Resetting an empty list should work.
227 BumpPtrList L;
228
229 // Resetting an empty list that has allocated should also work.
230 L.resetAlloc();
231 L.push_back(5);
232 L.erase(L.begin());
233 L.resetAlloc();
234
235 // Resetting a non-empty list should crash.
236 L.push_back(5);
237 #if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
238 EXPECT_DEATH(L.resetAlloc(), "Cannot reset allocator if not empty");
239 #endif
240 }
241
242 } // end namespace
88 ArrayRefTest.cpp
99 BitmaskEnumTest.cpp
1010 BitVectorTest.cpp
11 BumpPtrListTest.cpp
1112 DAGDeltaAlgorithmTest.cpp
1213 DeltaAlgorithmTest.cpp
1314 DenseMapTest.cpp