[Allocator] Factor the Allocate template overloads into a base class
authorChandler Carruth <chandlerc@gmail.com>
Tue, 15 Apr 2014 00:19:41 +0000 (00:19 +0000)
committerChandler Carruth <chandlerc@gmail.com>
Tue, 15 Apr 2014 00:19:41 +0000 (00:19 +0000)
rather than defining them (differently!) in both allocators. This also
serves as a basis for documenting and even enforcing some of the
LLVM-style "allocator" concept methods which must exist with various
signatures.

I plan on extending and changing the signatures of these to further
simplify our allocator model in subsequent commits, so I wanted to
factor things as best as I could first. Notably, I'm working to add the
'Size' to the deallocation method of all allocators. This has several
implications not the least of which are faster deallocation times on
certain allocation libraries (tcmalloc). It also will allow the JIT
allocator to fully model the existing allocation interfaces and allow
sanitizer poisoning of deallocated regions. The list of advantages goes
on. =] But by factoring things first I'll be able to make this easier by
first introducing template helpers for the deallocation path.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@206225 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Support/Allocator.h

index 19ef4e834a539b384ee8808790ae738d92d58175..a28080187b6e88bc0f84c7299b76a04c7e53ae52 100644 (file)
@@ -6,9 +6,16 @@
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
-//
-// This file defines the MallocAllocator and BumpPtrAllocator interfaces.
-//
+/// \file
+///
+/// This file defines the MallocAllocator and BumpPtrAllocator interfaces. Both
+/// of these conform to an LLVM "Allocator" concept which consists of an
+/// Allocate method accepting a size and alignment, and a Deallocate accepting
+/// a pointer and size. Further, the LLVM "Allocator" concept has overloads of
+/// Allocate and Deallocate for setting size and alignment based on the final
+/// type. These overloads are typically provided by a base class template \c
+/// AllocatorBase.
+///
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_SUPPORT_ALLOCATOR_H
@@ -32,7 +39,46 @@ template <typename T> struct ReferenceAdder<T &> {
   typedef T result;
 };
 
-class MallocAllocator {
+/// \brief CRTP base class providing obvious overloads for the core \c
+/// Allocate() methods of LLVM-style allocators.
+///
+/// This base class both documents the full public interface exposed by all
+/// LLVM-style allocators, and redirects all of the overloads to a single core
+/// set of methods which the derived class must define.
+template <typename DerivedT> class AllocatorBase {
+public:
+  /// \brief Allocate \a Size bytes of \a Alignment aligned memory. This method
+  /// must be implemented by \c DerivedT.
+  void *Allocate(size_t Size, size_t Alignment) {
+    static_assert(static_cast<void *(AllocatorBase::*)(size_t, size_t)>(
+                      &AllocatorBase::Allocate) !=
+                      static_cast<void *(DerivedT::*)(size_t, size_t)>(
+                          &DerivedT::Allocate),
+                  "Class derives from AllocatorBase without implementing the "
+                  "core Allocate(size_t, size_t) overload!");
+    return static_cast<DerivedT *>(this)->Allocate(Size, Alignment);
+  }
+
+  /// \brief Allocate space for one object without constructing it.
+  template <typename T> T *Allocate() {
+    return static_cast<T *>(Allocate(sizeof(T), AlignOf<T>::Alignment));
+  }
+
+  /// \brief Allocate space for an array of objects without constructing them.
+  template <typename T> T *Allocate(size_t Num) {
+    return static_cast<T *>(Allocate(Num * sizeof(T), AlignOf<T>::Alignment));
+  }
+
+  /// \brief Allocate space for an array of objects with the specified alignment
+  /// and without constructing them.
+  template <typename T> T *Allocate(size_t Num, size_t Alignment) {
+    // Round EltSize up to the specified alignment.
+    size_t EltSize = (sizeof(T) + Alignment - 1) & (-Alignment);
+    return static_cast<T *>(Allocate(Num * EltSize, Alignment));
+  }
+};
+
+class MallocAllocator : public AllocatorBase<MallocAllocator> {
 public:
   MallocAllocator() {}
   ~MallocAllocator() {}
@@ -41,13 +87,8 @@ public:
 
   void *Allocate(size_t Size, size_t /*Alignment*/) { return malloc(Size); }
 
-  template <typename T> T *Allocate() {
-    return static_cast<T *>(malloc(sizeof(T)));
-  }
-
-  template <typename T> T *Allocate(size_t Num) {
-    return static_cast<T *>(malloc(sizeof(T) * Num));
-  }
+  // Pull in base class overloads.
+  using AllocatorBase<MallocAllocator>::Allocate;
 
   void Deallocate(const void *Ptr) { free(const_cast<void *>(Ptr)); }
 
@@ -91,7 +132,9 @@ void printBumpPtrAllocatorStats(unsigned NumSlabs, size_t BytesAllocated,
 /// use a custom allocator.
 template <typename AllocatorT = MallocSlabAllocator, size_t SlabSize = 4096,
           size_t SizeThreshold = SlabSize>
-class BumpPtrAllocatorImpl {
+class BumpPtrAllocatorImpl
+    : public AllocatorBase<
+          BumpPtrAllocatorImpl<AllocatorT, SlabSize, SizeThreshold>> {
   BumpPtrAllocatorImpl(const BumpPtrAllocatorImpl &) LLVM_DELETED_FUNCTION;
   void operator=(const BumpPtrAllocatorImpl &) LLVM_DELETED_FUNCTION;
 
@@ -176,23 +219,8 @@ public:
     return Ptr;
   }
 
-  /// \brief Allocate space for one object without constructing it.
-  template <typename T> T *Allocate() {
-    return static_cast<T *>(Allocate(sizeof(T), AlignOf<T>::Alignment));
-  }
-
-  /// \brief Allocate space for an array of objects without constructing them.
-  template <typename T> T *Allocate(size_t Num) {
-    return static_cast<T *>(Allocate(Num * sizeof(T), AlignOf<T>::Alignment));
-  }
-
-  /// \brief Allocate space for an array of objects with the specified alignment
-  /// and without constructing them.
-  template <typename T> T *Allocate(size_t Num, size_t Alignment) {
-    // Round EltSize up to the specified alignment.
-    size_t EltSize = (sizeof(T) + Alignment - 1) & (-Alignment);
-    return static_cast<T *>(Allocate(Num * EltSize, Alignment));
-  }
+  // Pull in base class overloads.
+  using AllocatorBase<BumpPtrAllocatorImpl>::Allocate;
 
   void Deallocate(const void * /*Ptr*/) {}