llvm.org GIT mirror llvm / a66f054
ADT: Never allocate nodes in iplist<> and ilist<> Remove createNode() and any API that depending on it, and add HasCreateNode to the list of checks for HasObsoleteCustomizations. Now an ilist *never* allocates (this was already true for iplist). This factors out all the differences between iplist and ilist. I'll aim to rename both to "owning_ilist" eventually, to call out the interesting (not exactly intrusive) ownership semantics. In the meantime, I've left both names around to reduce code churn. One of the deleted APIs is the ilist copy constructor. I've lifted up and tested iplist::cloneFrom (ala simple_ilist::cloneFrom) as a replacement. Users of ilist<> and iplist<> that want the list to allocate nodes have a few options: - use std::list; - use AllocatorList or BumpPtrList (or build a similarly trivial list); - use cloneFrom (which is explicit at the call site); or - allocate at the call site. See r280573, r281177, r281181, and r281182 for examples of what to do if you're updating out-of-tree code. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@281184 91177308-0d34-0410-b5e6-96231b3b80d8 Duncan P. N. Exon Smith 4 years ago
6 changed file(s) with 76 addition(s) and 110 deletion(s). Raw diff Collapse all Expand all
3232
3333 namespace llvm {
3434
35 /// Use new/delete by default for iplist and ilist.
36 ///
37 /// Specialize this to get different behaviour for allocation-related API. (If
38 /// you really want new/delete, consider just using std::list.)
35 /// Use delete by default for iplist and ilist.
36 ///
37 /// Specialize this to get different behaviour for ownership-related API. (If
38 /// you really want ownership semantics, consider using std::list or building
39 /// something like \a BumpPtrList.)
3940 ///
4041 /// \see ilist_noalloc_traits
4142 template struct ilist_alloc_traits {
42 /// Clone a node.
43 ///
44 /// TODO: Remove this and API that relies on it (it's dead code).
45 static NodeTy *createNode(const NodeTy &V) { return new NodeTy(V); }
4643 static void deleteNode(NodeTy *V) { delete V; }
4744 };
4845
49 /// Custom traits to disable node creation and do nothing on deletion.
46 /// Custom traits to do nothing on deletion.
5047 ///
5148 /// Specialize ilist_alloc_traits to inherit from this to disable the
52 /// non-intrusive parts of iplist and/or ilist. It has no createNode function,
53 /// and deleteNode does nothing.
49 /// non-intrusive deletion in iplist (which implies ownership).
50 ///
51 /// If you want purely intrusive semantics with no callbacks, consider using \a
52 /// simple_ilist instead.
5453 ///
5554 /// \code
5655 /// template <>
138137 static const bool value = sizeof(test(nullptr)) == sizeof(Yes);
139138 };
140139
140 /// Type trait to check for a traits class that has a createNode member.
141 /// Allocation should be managed in a wrapper class, instead of in
142 /// ilist_traits.
143 template struct HasCreateNode {
144 typedef char Yes[1];
145 typedef char No[2];
146 template struct SFINAE {};
147
148 template
149 static Yes &test(U *I, decltype(I->createNode(make())) * = 0);
150 template static No &test(...);
151
152 public:
153 static const bool value = sizeof(test(nullptr)) == sizeof(Yes);
154 };
155
141156 template struct HasObsoleteCustomization {
142 static const bool value =
143 HasGetNext::value || HasCreateSentinel::value;
157 static const bool value = HasGetNext::value ||
158 HasCreateSentinel::value ||
159 HasCreateNode::value;
144160 };
145161
146162 } // end namespace ilist_detail
238254 return insert(++where, New);
239255 }
240256
257 /// Clone another list.
258 template void cloneFrom(const iplist_impl &L2, Cloner clone) {
259 clear();
260 for (const_reference V : L2)
261 push_back(clone(V));
262 }
263
241264 pointer remove(iterator &IT) {
242265 pointer Node = &*IT++;
243266 this->removeNodeFromList(Node); // Notify traits that we removed a node...
393416 iplist &operator=(const iplist &X) = delete;
394417 };
395418
396 /// An intrusive list with ownership and callbacks specified/controlled by
397 /// ilist_traits, with API that is unsafe for polymorphic types.
398 template
399 class ilist : public iplist {
400 typedef iplist base_list_type;
401
402 public:
403 typedef typename base_list_type::size_type size_type;
404 typedef typename base_list_type::iterator iterator;
405 typedef typename base_list_type::value_type value_type;
406 typedef typename base_list_type::const_reference const_reference;
407
408 ilist() {}
409 ilist(const ilist &right) : base_list_type() {
410 insert(this->begin(), right.begin(), right.end());
411 }
412 explicit ilist(size_type count) {
413 insert(this->begin(), count, value_type());
414 }
415 ilist(size_type count, const_reference val) {
416 insert(this->begin(), count, val);
417 }
418 template ilist(InIt first, InIt last) {
419 insert(this->begin(), first, last);
420 }
421
422 ilist(ilist &&X) : base_list_type(std::move(X)) {}
423 ilist &operator=(ilist &&X) {
424 *static_cast(this) = std::move(X);
425 return *this;
426 }
427
428 // bring hidden functions into scope
429 using base_list_type::insert;
430 using base_list_type::push_front;
431 using base_list_type::push_back;
432
433 // Main implementation here - Insert for a node passed by value...
434 iterator insert(iterator where, const_reference val) {
435 return insert(where, this->createNode(val));
436 }
437
438
439 // Front and back inserters...
440 void push_front(const_reference val) { insert(this->begin(), val); }
441 void push_back(const_reference val) { insert(this->end(), val); }
442
443 void insert(iterator where, size_type count, const_reference val) {
444 for (; count != 0; --count) insert(where, val);
445 }
446
447 // Assign special forms...
448 void assign(size_type count, const_reference val) {
449 iterator I = this->begin();
450 for (; I != this->end() && count != 0; ++I, --count)
451 *I = val;
452 if (count != 0)
453 insert(this->end(), val, val);
454 else
455 erase(I, this->end());
456 }
457 template void assign(InIt first1, InIt last1) {
458 iterator first2 = this->begin(), last2 = this->end();
459 for ( ; first1 != last1 && first2 != last2; ++first1, ++first2)
460 *first1 = *first2;
461 if (first2 == last2)
462 erase(first1, last1);
463 else
464 insert(last1, first2, last2);
465 }
466
467
468 // Resize members...
469 void resize(size_type newsize, value_type val) {
470 iterator i = this->begin();
471 size_type len = 0;
472 for ( ; i != this->end() && len < newsize; ++i, ++len) /* empty*/ ;
473
474 if (len == newsize)
475 erase(i, this->end());
476 else // i == end()
477 insert(this->end(), newsize - len, val);
478 }
479 void resize(size_type newsize) { resize(newsize, value_type()); }
480 };
419 template using ilist = iplist;
481420
482421 } // End llvm namespace
483422
5252 instr_iterator Last);
5353
5454 void deleteNode(MachineInstr *MI);
55 // Leave out createNode...
5655 };
5756
5857 class MachineBasicBlock
4949
5050 template <> struct ilist_alloc_traits {
5151 void deleteNode(MachineBasicBlock *MBB);
52 // Disallow createNode...
5352 };
5453
5554 template <> struct ilist_callback_traits {
8484 static void deleteNode(SDNode *) {
8585 llvm_unreachable("ilist_traits shouldn't see a deleteNode call!");
8686 }
87 // Don't implement createNode...
8887 };
8988
9089 /// Keeps track of dbg_value information through SDISel. We do
3232
3333 template <> struct ilist_alloc_traits {
3434 static void deleteNode(MCFragment *V);
35 // Leave out createNode...
3635 };
3736
3837 /// Instances of this class represent a uniqued identifier for a section in the
2727
2828 TEST(IListTest, Basic) {
2929 ilist List;
30 List.push_back(Node(1));
30 List.push_back(new Node(1));
3131 EXPECT_EQ(1, List.back().Value);
3232 EXPECT_EQ(nullptr, List.getPrevNode(List.back()));
3333 EXPECT_EQ(nullptr, List.getNextNode(List.back()));
3434
35 List.push_back(Node(2));
35 List.push_back(new Node(2));
3636 EXPECT_EQ(2, List.back().Value);
3737 EXPECT_EQ(2, List.getNextNode(List.front())->Value);
3838 EXPECT_EQ(1, List.getPrevNode(List.back())->Value);
4343 EXPECT_EQ(1, ConstList.getPrevNode(ConstList.back())->Value);
4444 }
4545
46 TEST(IListTest, cloneFrom) {
47 Node L1Nodes[] = {Node(0), Node(1)};
48 Node L2Nodes[] = {Node(0), Node(1)};
49 ilist L1, L2, L3;
50
51 // Build L1 from L1Nodes.
52 L1.push_back(&L1Nodes[0]);
53 L1.push_back(&L1Nodes[1]);
54
55 // Build L2 from L2Nodes, based on L1 nodes.
56 L2.cloneFrom(L1, [&](const Node &N) { return &L2Nodes[N.Value]; });
57
58 // Add a node to L3 to be deleted, and then rebuild L3 by copying L1.
59 L3.push_back(new Node(7));
60 L3.cloneFrom(L1, [](const Node &N) { return new Node(N); });
61
62 EXPECT_EQ(2u, L1.size());
63 EXPECT_EQ(&L1Nodes[0], &L1.front());
64 EXPECT_EQ(&L1Nodes[1], &L1.back());
65 EXPECT_EQ(2u, L2.size());
66 EXPECT_EQ(&L2Nodes[0], &L2.front());
67 EXPECT_EQ(&L2Nodes[1], &L2.back());
68 EXPECT_EQ(2u, L3.size());
69 EXPECT_EQ(0, L3.front().Value);
70 EXPECT_EQ(1, L3.back().Value);
71
72 // Don't free nodes on the stack.
73 L1.clearAndLeakNodesUnsafely();
74 L2.clearAndLeakNodesUnsafely();
75 }
76
4677 TEST(IListTest, SpliceOne) {
4778 ilist List;
48 List.push_back(1);
79 List.push_back(new Node(1));
4980
5081 // The single-element splice operation supports noops.
5182 List.splice(List.begin(), List, List.begin());
5485 EXPECT_TRUE(std::next(List.begin()) == List.end());
5586
5687 // Altenative noop. Move the first element behind itself.
57 List.push_back(2);
58 List.push_back(3);
88 List.push_back(new Node(2));
89 List.push_back(new Node(3));
5990 List.splice(std::next(List.begin()), List, List.begin());
6091 EXPECT_EQ(3u, List.size());
6192 EXPECT_EQ(1, List.front().Value);
110141 EXPECT_TRUE(E == List.end());
111142
112143 // List with contents.
113 List.push_back(1);
144 List.push_back(new Node(1));
114145 ASSERT_EQ(1u, List.size());
115146 Node *N = &*List.begin();
116147 EXPECT_EQ(1, N->Value);
120151 delete N;
121152
122153 // List is still functional.
123 List.push_back(5);
124 List.push_back(6);
154 List.push_back(new Node(5));
155 List.push_back(new Node(6));
125156 ASSERT_EQ(2u, List.size());
126157 EXPECT_EQ(5, List.front().Value);
127158 EXPECT_EQ(6, List.back().Value);