[llvm] r281182 - ADT: Add AllocatorList, and use it for yaml::Token

Duncan P. N. Exon Smith via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 11 15:40:41 PDT 2016


Author: dexonsmith
Date: Sun Sep 11 17:40:40 2016
New Revision: 281182

URL: http://llvm.org/viewvc/llvm-project?rev=281182&view=rev
Log:
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.

Added:
    llvm/trunk/include/llvm/ADT/AllocatorList.h
    llvm/trunk/unittests/ADT/BumpPtrListTest.cpp
Modified:
    llvm/trunk/include/llvm/ADT/ilist.h
    llvm/trunk/include/llvm/ADT/simple_ilist.h
    llvm/trunk/lib/Support/YAMLParser.cpp
    llvm/trunk/unittests/ADT/CMakeLists.txt

Added: llvm/trunk/include/llvm/ADT/AllocatorList.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/AllocatorList.h?rev=281182&view=auto
==============================================================================
--- llvm/trunk/include/llvm/ADT/AllocatorList.h (added)
+++ llvm/trunk/include/llvm/ADT/AllocatorList.h Sun Sep 11 17:40:40 2016
@@ -0,0 +1,226 @@
+//===- llvm/ADT/AllocatorList.h - Custom allocator 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_ALLOCATORLIST_H
+#define LLVM_ADT_ALLOCATORLIST_H
+
+#include "llvm/ADT/iterator.h"
+#include "llvm/ADT/simple_ilist.h"
+#include "llvm/Support/Allocator.h"
+#include <type_traits>
+
+namespace llvm {
+
+/// A linked-list with a custom, local allocator.
+///
+/// Expose a std::list-like interface that owns and uses a custom LLVM-style
+/// allocator (e.g., BumpPtrAllocator), leveraging \a simple_ilist for the
+/// implementation details.
+///
+/// Because this list owns the allocator, calling \a splice() with a different
+/// list isn't generally safe.  As such, \a splice has been left out of the
+/// interface entirely.
+template <class T, class AllocatorT> class AllocatorList : AllocatorT {
+  struct Node : ilist_node<Node> {
+    Node(Node &&) = delete;
+    Node(const Node &) = delete;
+    Node &operator=(Node &&) = delete;
+    Node &operator=(const Node &) = delete;
+
+    Node(T &&V) : V(std::move(V)) {}
+    Node(const T &V) : V(V) {}
+    template <class... Ts> Node(Ts &&... Vs) : V(std::forward<Ts>(Vs)...) {}
+    T V;
+  };
+
+  typedef simple_ilist<Node> list_type;
+  list_type List;
+
+  AllocatorT &getAlloc() { return *this; }
+  const AllocatorT &getAlloc() const { return *this; }
+
+  template <class... ArgTs> Node *create(ArgTs &&... Args) {
+    return new (getAlloc()) Node(std::forward<ArgTs>(Args)...);
+  }
+
+  struct Cloner {
+    AllocatorList &AL;
+    Cloner(AllocatorList &AL) : AL(AL) {}
+    Node *operator()(const Node &N) const { return AL.create(N.V); }
+  };
+
+  struct Disposer {
+    AllocatorList &AL;
+    Disposer(AllocatorList &AL) : AL(AL) {}
+    void operator()(Node *N) const {
+      N->~Node();
+      AL.getAlloc().Deallocate(N);
+    }
+  };
+
+public:
+  typedef T value_type;
+  typedef T *pointer;
+  typedef T &reference;
+  typedef const T *const_pointer;
+  typedef const T &const_reference;
+  typedef typename list_type::size_type size_type;
+  typedef typename list_type::difference_type difference_type;
+
+private:
+  template <class ValueT, class IteratorBase>
+  class IteratorImpl
+      : public iterator_adaptor_base<IteratorImpl<ValueT, IteratorBase>,
+                                     IteratorBase,
+                                     std::bidirectional_iterator_tag, ValueT> {
+    template <class OtherValueT, class OtherIteratorBase>
+    friend class IteratorImpl;
+    friend AllocatorList;
+
+    typedef iterator_adaptor_base<IteratorImpl<ValueT, IteratorBase>,
+                                  IteratorBase, std::bidirectional_iterator_tag,
+                                  ValueT>
+        iterator_adaptor_base;
+
+  public:
+    typedef ValueT value_type;
+    typedef ValueT *pointer;
+    typedef ValueT &reference;
+
+    IteratorImpl() = default;
+    IteratorImpl(const IteratorImpl &) = default;
+    IteratorImpl &operator=(const IteratorImpl &) = default;
+    ~IteratorImpl() = default;
+
+    explicit IteratorImpl(const IteratorBase &I) : iterator_adaptor_base(I) {}
+
+    template <class OtherValueT, class OtherIteratorBase>
+    IteratorImpl(const IteratorImpl<OtherValueT, OtherIteratorBase> &X,
+                 typename std::enable_if<std::is_convertible<
+                     OtherIteratorBase, IteratorBase>::value>::type * = nullptr)
+        : iterator_adaptor_base(X.wrapped()) {}
+
+    reference operator*() const { return iterator_adaptor_base::wrapped()->V; }
+    pointer operator->() const { return &operator*(); }
+
+    friend bool operator==(const IteratorImpl &L, const IteratorImpl &R) {
+      return L.wrapped() == R.wrapped();
+    }
+    friend bool operator!=(const IteratorImpl &L, const IteratorImpl &R) {
+      return !(L == R);
+    }
+  };
+
+public:
+  typedef IteratorImpl<T, typename list_type::iterator> iterator;
+  typedef IteratorImpl<T, typename list_type::reverse_iterator>
+      reverse_iterator;
+  typedef IteratorImpl<const T, typename list_type::const_iterator>
+      const_iterator;
+  typedef IteratorImpl<const T, typename list_type::const_reverse_iterator>
+      const_reverse_iterator;
+
+  AllocatorList() = default;
+  AllocatorList(AllocatorList &&X)
+      : AllocatorT(std::move(X.getAlloc())), List(std::move(X.List)) {}
+  AllocatorList(const AllocatorList &X) {
+    List.cloneFrom(X.List, Cloner(*this), Disposer(*this));
+  }
+  AllocatorList &operator=(AllocatorList &&X) {
+    clear(); // Dispose of current nodes explicitly.
+    List = std::move(X.List);
+    getAlloc() = std::move(X.getAlloc());
+    return *this;
+  }
+  AllocatorList &operator=(const AllocatorList &X) {
+    List.cloneFrom(X.List, Cloner(*this), Disposer(*this));
+    return *this;
+  }
+  ~AllocatorList() { clear(); }
+
+  void swap(AllocatorList &RHS) {
+    List.swap(RHS.List);
+    std::swap(getAlloc(), RHS.getAlloc());
+  }
+
+  bool empty() { return List.empty(); }
+  size_t size() { return List.size(); }
+
+  iterator begin() { return iterator(List.begin()); }
+  iterator end() { return iterator(List.end()); }
+  const_iterator begin() const { return const_iterator(List.begin()); }
+  const_iterator end() const { return const_iterator(List.end()); }
+  reverse_iterator rbegin() { return reverse_iterator(List.rbegin()); }
+  reverse_iterator rend() { return reverse_iterator(List.rend()); }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(List.rbegin());
+  }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(List.rend());
+  }
+
+  T &back() { return List.back().V; }
+  T &front() { return List.front().V; }
+  const T &back() const { return List.back().V; }
+  const T &front() const { return List.front().V; }
+
+  template <class... Ts> iterator emplace(iterator I, Ts &&... Vs) {
+    return iterator(List.insert(I.wrapped(), *create(std::forward<Ts>(Vs)...)));
+  }
+
+  iterator insert(iterator I, T &&V) {
+    return iterator(List.insert(I.wrapped(), *create(std::move(V))));
+  }
+  iterator insert(iterator I, const T &V) {
+    return iterator(List.insert(I.wrapped(), *create(V)));
+  }
+
+  template <class Iterator>
+  void insert(iterator I, Iterator First, Iterator Last) {
+    for (; First != Last; ++First)
+      List.insert(I.wrapped(), *create(*First));
+  }
+
+  iterator erase(iterator I) {
+    return iterator(List.eraseAndDispose(I.wrapped(), Disposer(*this)));
+  }
+
+  iterator erase(iterator First, iterator Last) {
+    return iterator(
+        List.eraseAndDispose(First.wrapped(), Last.wrapped(), Disposer(*this)));
+  }
+
+  void clear() { List.clearAndDispose(Disposer(*this)); }
+  void pop_back() { List.eraseAndDispose(--List.end(), Disposer(*this)); }
+  void pop_front() { List.eraseAndDispose(List.begin(), Disposer(*this)); }
+  void push_back(T &&V) { insert(end(), std::move(V)); }
+  void push_front(T &&V) { insert(begin(), std::move(V)); }
+  void push_back(const T &V) { insert(end(), V); }
+  void push_front(const T &V) { insert(begin(), V); }
+  template <class... Ts> void emplace_back(Ts &&... Vs) {
+    emplace(end(), std::forward<Ts>(Vs)...);
+  }
+  template <class... Ts> void emplace_front(Ts &&... Vs) {
+    emplace(begin(), std::forward<Ts>(Vs)...);
+  }
+
+  /// Reset the underlying allocator.
+  ///
+  /// \pre \c empty()
+  void resetAlloc() {
+    assert(empty() && "Cannot reset allocator if not empty");
+    getAlloc().Reset();
+  }
+};
+
+template <class T> using BumpPtrList = AllocatorList<T, BumpPtrAllocator>;
+
+} // end namespace llvm
+
+#endif // LLVM_ADT_ALLOCATORLIST_H

Modified: llvm/trunk/include/llvm/ADT/ilist.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/ilist.h?rev=281182&r1=281181&r2=281182&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ADT/ilist.h (original)
+++ llvm/trunk/include/llvm/ADT/ilist.h Sun Sep 11 17:40:40 2016
@@ -40,6 +40,9 @@ namespace llvm {
 ///
 /// \see ilist_noalloc_traits
 template <typename NodeTy> struct ilist_alloc_traits {
+  /// Clone a node.
+  ///
+  /// TODO: Remove this and API that relies on it (it's dead code).
   static NodeTy *createNode(const NodeTy &V) { return new NodeTy(V); }
   static void deleteNode(NodeTy *V) { delete V; }
 };

Modified: llvm/trunk/include/llvm/ADT/simple_ilist.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/simple_ilist.h?rev=281182&r1=281181&r2=281182&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ADT/simple_ilist.h (original)
+++ llvm/trunk/include/llvm/ADT/simple_ilist.h Sun Sep 11 17:40:40 2016
@@ -164,6 +164,14 @@ public:
       insert(I, *First);
   }
 
+  /// Clone another list.
+  template <class Cloner, class Disposer>
+  void cloneFrom(const simple_ilist &L2, Cloner clone, Disposer dispose) {
+    clearAndDispose(dispose);
+    for (const_reference V : L2)
+      push_back(*clone(V));
+  }
+
   /// Remove a node by reference; never deletes.
   ///
   /// \see \a erase() for removing by iterator.

Modified: llvm/trunk/lib/Support/YAMLParser.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/YAMLParser.cpp?rev=281182&r1=281181&r2=281182&view=diff
==============================================================================
--- llvm/trunk/lib/Support/YAMLParser.cpp (original)
+++ llvm/trunk/lib/Support/YAMLParser.cpp Sun Sep 11 17:40:40 2016
@@ -17,8 +17,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Twine.h"
-#include "llvm/ADT/ilist.h"
-#include "llvm/ADT/ilist_node.h"
+#include "llvm/ADT/AllocatorList.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/SourceMgr.h"
@@ -109,7 +108,7 @@ void SequenceNode::anchor() {}
 void AliasNode::anchor() {}
 
 /// Token - A single YAML token.
-struct Token : ilist_node<Token> {
+struct Token {
   enum TokenKind {
     TK_Error, // Uninitialized token.
     TK_StreamStart,
@@ -148,18 +147,7 @@ struct Token : ilist_node<Token> {
 }
 }
 
-namespace llvm {
-template <> struct ilist_alloc_traits<Token> {
-  Token *createNode(const Token &V) {
-    return new (Alloc.Allocate<Token>()) Token(V);
-  }
-  static void deleteNode(Token *V) { V->~Token(); }
-
-  BumpPtrAllocator Alloc;
-};
-} // end namespace llvm
-
-typedef ilist<Token> TokenQueueT;
+typedef llvm::BumpPtrList<Token> TokenQueueT;
 
 namespace {
 /// @brief This struct is used to track simple keys.
@@ -797,9 +785,8 @@ Token Scanner::getNext() {
 
   // There cannot be any referenced Token's if the TokenQueue is empty. So do a
   // quick deallocation of them all.
-  if (TokenQueue.empty()) {
-    TokenQueue.Alloc.Reset();
-  }
+  if (TokenQueue.empty())
+    TokenQueue.resetAlloc();
 
   return Ret;
 }

Added: llvm/trunk/unittests/ADT/BumpPtrListTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/BumpPtrListTest.cpp?rev=281182&view=auto
==============================================================================
--- llvm/trunk/unittests/ADT/BumpPtrListTest.cpp (added)
+++ llvm/trunk/unittests/ADT/BumpPtrListTest.cpp Sun Sep 11 17:40:40 2016
@@ -0,0 +1,243 @@
+//===- unittests/ADT/BumpPtrListTest.cpp - BumpPtrList 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/AllocatorList.h"
+#include "llvm/ADT/STLExtras.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+struct CountsDestructors {
+  static unsigned NumCalls;
+  ~CountsDestructors() { ++NumCalls; }
+};
+unsigned CountsDestructors::NumCalls = 0;
+
+struct MoveOnly {
+  int V;
+  explicit MoveOnly(int V) : V(V) {}
+  MoveOnly() = delete;
+  MoveOnly(MoveOnly &&X) { V = X.V; }
+  MoveOnly(const MoveOnly &X) = delete;
+  MoveOnly &operator=(MoveOnly &&X) = delete;
+  MoveOnly &operator=(const MoveOnly &X) = delete;
+};
+
+struct EmplaceOnly {
+  int V1, V2;
+  explicit EmplaceOnly(int V1, int V2) : V1(V1), V2(V2) {}
+  EmplaceOnly() = delete;
+  EmplaceOnly(EmplaceOnly &&X) = delete;
+  EmplaceOnly(const EmplaceOnly &X) = delete;
+  EmplaceOnly &operator=(EmplaceOnly &&X) = delete;
+  EmplaceOnly &operator=(const EmplaceOnly &X) = delete;
+};
+
+TEST(BumpPtrListTest, DefaultConstructor) {
+  BumpPtrList<int> L;
+  EXPECT_TRUE(L.empty());
+}
+
+TEST(BumpPtrListTest, pushPopBack) {
+  // Build a list with push_back.
+  BumpPtrList<int> L;
+  int Ns[] = {1, 3, 9, 5, 7};
+  for (const int N : Ns)
+    L.push_back(N);
+
+  // Use iterators to check contents.
+  auto I = L.begin();
+  for (int N : Ns)
+    EXPECT_EQ(N, *I++);
+  EXPECT_EQ(I, L.end());
+
+  // Unbuild the list with pop_back.
+  for (int N : llvm::reverse(Ns)) {
+    EXPECT_EQ(N, L.back());
+    L.pop_back();
+  }
+  EXPECT_TRUE(L.empty());
+}
+
+TEST(BumpPtrListTest, pushPopFront) {
+  // Build a list with push_front.
+  BumpPtrList<int> L;
+  int Ns[] = {1, 3, 9, 5, 7};
+  for (const int N : Ns)
+    L.push_front(N);
+
+  // Use reverse iterators to check contents.
+  auto I = L.rbegin();
+  for (int N : Ns)
+    EXPECT_EQ(N, *I++);
+  EXPECT_EQ(I, L.rend());
+
+  // Unbuild the list with pop_front.
+  for (int N : llvm::reverse(Ns)) {
+    EXPECT_EQ(N, L.front());
+    L.pop_front();
+  }
+  EXPECT_TRUE(L.empty());
+}
+
+TEST(BumpPtrListTest, pushBackMoveOnly) {
+  BumpPtrList<MoveOnly> L;
+  int Ns[] = {1, 3, 9, 5, 7};
+  for (const int N : Ns) {
+    L.push_back(MoveOnly(N));
+    EXPECT_EQ(N, L.back().V);
+  }
+  // Instantiate with MoveOnly.
+  while (!L.empty())
+    L.pop_back();
+}
+
+TEST(BumpPtrListTest, pushFrontMoveOnly) {
+  BumpPtrList<MoveOnly> L;
+  int Ns[] = {1, 3, 9, 5, 7};
+  for (const int N : Ns) {
+    L.push_front(MoveOnly(N));
+    EXPECT_EQ(N, L.front().V);
+  }
+  // Instantiate with MoveOnly.
+  while (!L.empty())
+    L.pop_front();
+}
+
+TEST(BumpPtrListTest, emplaceBack) {
+  BumpPtrList<EmplaceOnly> L;
+  int N1s[] = {1, 3, 9, 5, 7};
+  int N2s[] = {7, 3, 1, 8, 2};
+  for (int I = 0; I != 5; ++I) {
+    L.emplace_back(N1s[I], N2s[I]);
+    EXPECT_EQ(N1s[I], L.back().V1);
+    EXPECT_EQ(N2s[I], L.back().V2);
+  }
+  // Instantiate with EmplaceOnly.
+  while (!L.empty())
+    L.pop_back();
+}
+
+TEST(BumpPtrListTest, emplaceFront) {
+  BumpPtrList<EmplaceOnly> L;
+  int N1s[] = {1, 3, 9, 5, 7};
+  int N2s[] = {7, 3, 1, 8, 2};
+  for (int I = 0; I != 5; ++I) {
+    L.emplace_front(N1s[I], N2s[I]);
+    EXPECT_EQ(N1s[I], L.front().V1);
+    EXPECT_EQ(N2s[I], L.front().V2);
+  }
+  // Instantiate with EmplaceOnly.
+  while (!L.empty())
+    L.pop_front();
+}
+
+TEST(BumpPtrListTest, swap) {
+  // Build two lists with different lifetimes and swap them.
+  int N1s[] = {1, 3, 5, 7, 9};
+  int N2s[] = {2, 4, 6, 8, 10};
+
+  BumpPtrList<int> L1;
+  L1.insert(L1.end(), std::begin(N1s), std::end(N1s));
+  {
+    BumpPtrList<int> L2;
+    L2.insert(L2.end(), std::begin(N2s), std::end(N2s));
+
+    // Swap the lists.
+    L1.swap(L2);
+
+    // Check L2's contents before it goes out of scope.
+    auto I = L2.begin();
+    for (int N : N1s)
+      EXPECT_EQ(N, *I++);
+    EXPECT_EQ(I, L2.end());
+  }
+
+  // Check L1's contents now that L2 is out of scope (with its allocation
+  // blocks).
+  auto I = L1.begin();
+  for (int N : N2s)
+    EXPECT_EQ(N, *I++);
+  EXPECT_EQ(I, L1.end());
+}
+
+TEST(BumpPtrListTest, clear) {
+  CountsDestructors::NumCalls = 0;
+  CountsDestructors N;
+  BumpPtrList<CountsDestructors> L;
+  L.push_back(N);
+  L.push_back(N);
+  L.push_back(N);
+  EXPECT_EQ(3u, L.size());
+  EXPECT_EQ(0u, CountsDestructors::NumCalls);
+  L.pop_back();
+  EXPECT_EQ(1u, CountsDestructors::NumCalls);
+  L.clear();
+  EXPECT_EQ(3u, CountsDestructors::NumCalls);
+}
+
+TEST(BumpPtrListTest, move) {
+  BumpPtrList<int> L1, L2;
+  L1.push_back(1);
+  L2.push_back(2);
+  L1 = std::move(L2);
+  EXPECT_EQ(1u, L1.size());
+  EXPECT_EQ(2, L1.front());
+  EXPECT_EQ(0u, L2.size());
+}
+
+TEST(BumpPtrListTest, moveCallsDestructors) {
+  CountsDestructors::NumCalls = 0;
+  BumpPtrList<CountsDestructors> L1, L2;
+  L1.emplace_back();
+  EXPECT_EQ(0u, CountsDestructors::NumCalls);
+  L1 = std::move(L2);
+  EXPECT_EQ(1u, CountsDestructors::NumCalls);
+}
+
+TEST(BumpPtrListTest, copy) {
+  BumpPtrList<int> L1, L2;
+  L1.push_back(1);
+  L2.push_back(2);
+  L1 = L2;
+  EXPECT_EQ(1u, L1.size());
+  EXPECT_EQ(2, L1.front());
+  EXPECT_EQ(1u, L2.size());
+  EXPECT_EQ(2, L2.front());
+}
+
+TEST(BumpPtrListTest, copyCallsDestructors) {
+  CountsDestructors::NumCalls = 0;
+  BumpPtrList<CountsDestructors> L1, L2;
+  L1.emplace_back();
+  EXPECT_EQ(0u, CountsDestructors::NumCalls);
+  L1 = L2;
+  EXPECT_EQ(1u, CountsDestructors::NumCalls);
+}
+
+TEST(BumpPtrListTest, resetAlloc) {
+  // Resetting an empty list should work.
+  BumpPtrList<int> L;
+
+  // Resetting an empty list that has allocated should also work.
+  L.resetAlloc();
+  L.push_back(5);
+  L.erase(L.begin());
+  L.resetAlloc();
+
+  // Resetting a non-empty list should crash.
+  L.push_back(5);
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
+  EXPECT_DEATH(L.resetAlloc(), "Cannot reset allocator if not empty");
+#endif
+}
+
+} // end namespace

Modified: llvm/trunk/unittests/ADT/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/CMakeLists.txt?rev=281182&r1=281181&r2=281182&view=diff
==============================================================================
--- llvm/trunk/unittests/ADT/CMakeLists.txt (original)
+++ llvm/trunk/unittests/ADT/CMakeLists.txt Sun Sep 11 17:40:40 2016
@@ -9,6 +9,7 @@ set(ADTSources
   ArrayRefTest.cpp
   BitmaskEnumTest.cpp
   BitVectorTest.cpp
+  BumpPtrListTest.cpp
   DAGDeltaAlgorithmTest.cpp
   DeltaAlgorithmTest.cpp
   DenseMapTest.cpp




More information about the llvm-commits mailing list