diff --git a/include/llvm/ADT/ilist.h b/include/llvm/ADT/ilist.h index 36650d43750..20cdae32554 100644 --- a/include/llvm/ADT/ilist.h +++ b/include/llvm/ADT/ilist.h @@ -465,6 +465,17 @@ public: return where; } + /// Remove all nodes from the list like clear(), but do not call + /// removeNodeFromList() or deleteNode(). + /// + /// 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() { + if (Head) { + Head = getTail(); + this->setPrev(Head, Head); + } + } private: // transfer - The heart of the splice function. Move linked list nodes from diff --git a/unittests/ADT/ilistTest.cpp b/unittests/ADT/ilistTest.cpp index 07bd5ec6019..0c0cd0fd56f 100644 --- a/unittests/ADT/ilistTest.cpp +++ b/unittests/ADT/ilistTest.cpp @@ -22,6 +22,7 @@ struct Node : ilist_node { Node() {} Node(int _Value) : Value(_Value) {} + ~Node() { Value = -1; } }; TEST(ilistTest, Basic) { @@ -62,4 +63,36 @@ TEST(ilistTest, SpliceOne) { EXPECT_EQ(3, List.back().Value); } +TEST(ilistTest, UnsafeClear) { + ilist List; + + // Before even allocating a sentinel. + List.clearAndLeakNodesUnsafely(); + EXPECT_EQ(0u, List.size()); + + // Empty list with sentinel. + ilist::iterator E = List.end(); + List.clearAndLeakNodesUnsafely(); + EXPECT_EQ(0u, List.size()); + // The sentinel shouldn't change. + EXPECT_TRUE(E == List.end()); + + // List with contents. + List.push_back(1); + ASSERT_EQ(1u, List.size()); + Node *N = List.begin(); + EXPECT_EQ(1, N->Value); + List.clearAndLeakNodesUnsafely(); + EXPECT_EQ(0u, List.size()); + ASSERT_EQ(1, N->Value); + delete N; + + // List is still functional. + List.push_back(5); + List.push_back(6); + ASSERT_EQ(2u, List.size()); + EXPECT_EQ(5, List.front().Value); + EXPECT_EQ(6, List.back().Value); +} + }