X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=include%2Fllvm%2FSupport%2FAllocator.h;h=4da7acefb68c2d5e97afc5cf76499d4840b90daf;hb=159976c271283abdcd18e36f02a51c074ffa2895;hp=e39c5c121f157c9c841d138da15a7d98adb26d70;hpb=9553188fccbf0ae9c5b6bef26d0d2bd5feff8b59;p=oota-llvm.git diff --git a/include/llvm/Support/Allocator.h b/include/llvm/Support/Allocator.h index e39c5c121f1..4da7acefb68 100644 --- a/include/llvm/Support/Allocator.h +++ b/include/llvm/Support/Allocator.h @@ -6,194 +6,426 @@ // 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 #define LLVM_SUPPORT_ALLOCATOR_H +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/AlignOf.h" +#include "llvm/Support/DataTypes.h" #include "llvm/Support/MathExtras.h" -#include "llvm/System/DataTypes.h" +#include "llvm/Support/Memory.h" #include #include -#include #include +#include namespace llvm { +template struct ReferenceAdder { + typedef T &result; +}; +template struct ReferenceAdder { + 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 class AllocatorBase { public: - MallocAllocator() {} - ~MallocAllocator() {} + /// \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) { +#ifdef __clang__ + static_assert(static_cast( + &AllocatorBase::Allocate) != + static_cast( + &DerivedT::Allocate), + "Class derives from AllocatorBase without implementing the " + "core Allocate(size_t, size_t) overload!"); +#endif + return static_cast(this)->Allocate(Size, Alignment); + } - void Reset() {} + /// \brief Deallocate \a Ptr to \a Size bytes of memory allocated by this + /// allocator. + void Deallocate(const void *Ptr) { +#ifdef __clang__ + static_assert(static_cast( + &AllocatorBase::Deallocate) != + static_cast( + &DerivedT::Deallocate), + "Class derives from AllocatorBase without implementing the " + "core Deallocate(void *) overload!"); +#endif + return static_cast(this)->Deallocate(Ptr); + } - void *Allocate(size_t Size, size_t /*Alignment*/) { return malloc(Size); } + // The rest of these methods are helpers that redirect to one of the above + // core methods. - template - T *Allocate() { return static_cast(malloc(sizeof(T))); } + /// \brief Allocate space for one object without constructing it. + template T *Allocate() { + return static_cast(Allocate(sizeof(T), AlignOf::Alignment)); + } - template - T *Allocate(size_t Num) { - return static_cast(malloc(sizeof(T)*Num)); + /// \brief Allocate space for an array of objects without constructing them. + template T *Allocate(size_t Num) { + return static_cast(Allocate(Num * sizeof(T), AlignOf::Alignment)); } - void Deallocate(const void *Ptr) { free(const_cast(Ptr)); } + /// \brief Allocate space for an array of objects with the specified alignment + /// and without constructing them. + template 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(Allocate(Num * EltSize, Alignment)); + } - void PrintStats() const {} -}; + /// \brief Deallocate space for one object without destroying it. + template void Deallocate(T *Ptr) { + Deallocate(static_cast(Ptr)); + } -/// MemSlab - This structure lives at the beginning of every slab allocated by -/// the bump allocator. -class MemSlab { -public: - size_t Size; - MemSlab *NextPtr; + /// \brief Allocate space for an array of objects without constructing them. + template void Deallocate(T *Ptr, size_t /*Num*/) { + Deallocate(static_cast(Ptr)); + } }; -/// SlabAllocator - This class can be used to parameterize the underlying -/// allocation strategy for the bump allocator. In particular, this is used -/// by the JIT to allocate contiguous swathes of executable memory. The -/// interface uses MemSlab's instead of void *'s so that the allocator -/// doesn't have to remember the size of the pointer it allocated. -class SlabAllocator { +class MallocAllocator : public AllocatorBase { public: - virtual ~SlabAllocator(); - virtual MemSlab *Allocate(size_t Size) = 0; - virtual void Deallocate(MemSlab *Slab) = 0; + MallocAllocator() {} + ~MallocAllocator() {} + + void Reset() {} + + void *Allocate(size_t Size, size_t /*Alignment*/) { return malloc(Size); } + + // Pull in base class overloads. + using AllocatorBase::Allocate; + + void Deallocate(const void *Ptr) { free(const_cast(Ptr)); } + + // Pull in base class overloads. + using AllocatorBase::Deallocate; + + void PrintStats() const {} }; /// MallocSlabAllocator - The default slab allocator for the bump allocator /// is an adapter class for MallocAllocator that just forwards the method /// calls and translates the arguments. -class MallocSlabAllocator : public SlabAllocator { +class MallocSlabAllocator { /// Allocator - The underlying allocator that we forward to. /// MallocAllocator Allocator; public: - MallocSlabAllocator() : Allocator() { } - virtual ~MallocSlabAllocator(); - virtual MemSlab *Allocate(size_t Size); - virtual void Deallocate(MemSlab *Slab); + void *Allocate(size_t Size) { return Allocator.Allocate(Size, 0); } + void Deallocate(void *Slab, size_t Size) { Allocator.Deallocate(Slab); } }; -/// BumpPtrAllocator - This allocator is useful for containers that need -/// very simple memory allocation strategies. In particular, this just keeps -/// allocating memory, and never deletes it until the entire block is dead. This -/// makes allocation speedy, but must only be used when the trade-off is ok. -class BumpPtrAllocator { - BumpPtrAllocator(const BumpPtrAllocator &); // do not implement - void operator=(const BumpPtrAllocator &); // do not implement +namespace detail { + +// We call out to an external function to actually print the message as the +// printing code uses Allocator.h in its implementation. +void printBumpPtrAllocatorStats(unsigned NumSlabs, size_t BytesAllocated, + size_t TotalMemory); +} // End namespace detail. + +/// \brief Allocate memory in an ever growing pool, as if by bump-pointer. +/// +/// This isn't strictly a bump-pointer allocator as it uses backing slabs of +/// memory rather than relying on boundless contiguous heap. However, it has +/// bump-pointer semantics in that is a monotonically growing pool of memory +/// where every allocation is found by merely allocating the next N bytes in +/// the slab, or the next N bytes in the next slab. +/// +/// Note that this also has a threshold for forcing allocations above a certain +/// size into their own slab. +/// +/// The BumpPtrAllocatorImpl template defaults to using a MallocSlabAllocator +/// object, which wraps malloc, to allocate memory, but it can be changed to +/// use a custom allocator. +template +class BumpPtrAllocatorImpl + : public AllocatorBase< + BumpPtrAllocatorImpl> { + BumpPtrAllocatorImpl(const BumpPtrAllocatorImpl &) LLVM_DELETED_FUNCTION; + void operator=(const BumpPtrAllocatorImpl &) LLVM_DELETED_FUNCTION; - /// SlabSize - Allocate data into slabs of this size unless we get an - /// allocation above SizeThreshold. - size_t SlabSize; +public: + static_assert(SizeThreshold <= SlabSize, + "The SizeThreshold must be at most the SlabSize to ensure " + "that objects larger than a slab go into their own memory " + "allocation."); - /// SizeThreshold - For any allocation larger than this threshold, we should - /// allocate a separate slab. - size_t SizeThreshold; + BumpPtrAllocatorImpl() + : CurPtr(nullptr), End(nullptr), BytesAllocated(0), Allocator() {} + template + BumpPtrAllocatorImpl(T &&Allocator) + : CurPtr(nullptr), End(nullptr), BytesAllocated(0), + Allocator(std::forward(Allocator)) {} + ~BumpPtrAllocatorImpl() { + DeallocateSlabs(Slabs.begin(), Slabs.end()); + DeallocateCustomSizedSlabs(); + } - /// Allocator - The underlying allocator we use to get slabs of memory. This - /// defaults to MallocSlabAllocator, which wraps malloc, but it could be - /// changed to use a custom allocator. - SlabAllocator &Allocator; + /// \brief Deallocate all but the current slab and reset the current pointer + /// to the beginning of it, freeing all memory allocated so far. + void Reset() { + if (Slabs.empty()) + return; + + // Reset the state. + BytesAllocated = 0; + CurPtr = (char *)Slabs.front(); + End = CurPtr + SlabSize; + + // Deallocate all but the first slab, and all custome sized slabs. + DeallocateSlabs(std::next(Slabs.begin()), Slabs.end()); + Slabs.erase(std::next(Slabs.begin()), Slabs.end()); + DeallocateCustomSizedSlabs(); + CustomSizedSlabs.clear(); + } - /// CurSlab - The slab that we are currently allocating into. - /// - MemSlab *CurSlab; + /// \brief Allocate space at the specified alignment. + void *Allocate(size_t Size, size_t Alignment) { + if (!CurPtr) // Start a new slab if we haven't allocated one already. + StartNewSlab(); + + // Keep track of how many bytes we've allocated. + BytesAllocated += Size; + + // 0-byte alignment means 1-byte alignment. + if (Alignment == 0) + Alignment = 1; + + // Allocate the aligned space, going forwards from CurPtr. + char *Ptr = alignPtr(CurPtr, Alignment); + + // Check if we can hold it. + if (Ptr + Size <= End) { + CurPtr = Ptr + Size; + // Update the allocation point of this memory block in MemorySanitizer. + // Without this, MemorySanitizer messages for values originated from here + // will point to the allocation of the entire slab. + __msan_allocated_memory(Ptr, Size); + return Ptr; + } + + // If Size is really big, allocate a separate slab for it. + size_t PaddedSize = Size + Alignment - 1; + if (PaddedSize > SizeThreshold) { + void *NewSlab = Allocator.Allocate(PaddedSize); + CustomSizedSlabs.push_back(std::make_pair(NewSlab, PaddedSize)); + + Ptr = alignPtr((char *)NewSlab, Alignment); + assert((uintptr_t)Ptr + Size <= (uintptr_t)NewSlab + PaddedSize); + __msan_allocated_memory(Ptr, Size); + return Ptr; + } + + // Otherwise, start a new slab and try again. + StartNewSlab(); + Ptr = alignPtr(CurPtr, Alignment); + CurPtr = Ptr + Size; + assert(CurPtr <= End && "Unable to allocate memory!"); + __msan_allocated_memory(Ptr, Size); + return Ptr; + } - /// CurPtr - The current pointer into the current slab. This points to the - /// next free byte in the slab. - char *CurPtr; + // Pull in base class overloads. + using AllocatorBase::Allocate; - /// End - The end of the current slab. - /// - char *End; + void Deallocate(const void * /*Ptr*/) {} - /// BytesAllocated - This field tracks how many bytes we've allocated, so - /// that we can compute how much space was wasted. - size_t BytesAllocated; + // Pull in base class overloads. + using AllocatorBase::Deallocate; - /// AlignPtr - Align Ptr to Alignment bytes, rounding up. Alignment should - /// be a power of two. This method rounds up, so AlignPtr(7, 4) == 8 and - /// AlignPtr(8, 4) == 8. - static char *AlignPtr(char *Ptr, size_t Alignment); + size_t GetNumSlabs() const { return Slabs.size() + CustomSizedSlabs.size(); } - /// StartNewSlab - Allocate a new slab and move the bump pointers over into - /// the new slab. Modifies CurPtr and End. - void StartNewSlab(); + size_t getTotalMemory() const { + size_t TotalMemory = 0; + for (auto I = Slabs.begin(), E = Slabs.end(); I != E; ++I) + TotalMemory += computeSlabSize(std::distance(Slabs.begin(), I)); + for (auto &PtrAndSize : CustomSizedSlabs) + TotalMemory += PtrAndSize.second; + return TotalMemory; + } - /// DeallocateSlabs - Deallocate all memory slabs after and including this - /// one. - void DeallocateSlabs(MemSlab *Slab); + void PrintStats() const { + detail::printBumpPtrAllocatorStats(Slabs.size(), BytesAllocated, + getTotalMemory()); + } - static MallocSlabAllocator DefaultSlabAllocator; +private: + /// \brief The current pointer into the current slab. + /// + /// This points to the next free byte in the slab. + char *CurPtr; -public: - BumpPtrAllocator(size_t size = 4096, size_t threshold = 4096, - SlabAllocator &allocator = DefaultSlabAllocator); - ~BumpPtrAllocator(); + /// \brief The end of the current slab. + char *End; - /// Reset - Deallocate all but the current slab and reset the current pointer - /// to the beginning of it, freeing all memory allocated so far. - void Reset(); + /// \brief The slabs allocated so far. + SmallVector Slabs; - /// Allocate - Allocate space at the specified alignment. - /// - void *Allocate(size_t Size, size_t Alignment); + /// \brief Custom-sized slabs allocated for too-large allocation requests. + SmallVector, 0> CustomSizedSlabs; - /// Allocate space, but do not construct, one object. + /// \brief How many bytes we've allocated. /// - template - T *Allocate() { - return static_cast(Allocate(sizeof(T),AlignOf::Alignment)); + /// Used so that we can compute how much space was wasted. + size_t BytesAllocated; + + /// \brief The allocator instance we use to get slabs of memory. + AllocatorT Allocator; + + static size_t computeSlabSize(unsigned SlabIdx) { + // Scale the actual allocated slab size based on the number of slabs + // allocated. Every 128 slabs allocated, we double the allocated size to + // reduce allocation frequency, but saturate at multiplying the slab size by + // 2^30. + return SlabSize * ((size_t)1 << std::min(30, SlabIdx / 128)); } - /// Allocate space for an array of objects. This does not construct the - /// objects though. - template - T *Allocate(size_t Num) { - return static_cast(Allocate(Num * sizeof(T), AlignOf::Alignment)); + /// \brief Allocate a new slab and move the bump pointers over into the new + /// slab, modifying CurPtr and End. + void StartNewSlab() { + size_t AllocatedSlabSize = computeSlabSize(Slabs.size()); + + void *NewSlab = Allocator.Allocate(AllocatedSlabSize); + Slabs.push_back(NewSlab); + CurPtr = (char *)(NewSlab); + End = ((char *)NewSlab) + AllocatedSlabSize; } - /// Allocate space for a specific count of elements and with a specified - /// alignment. - template - 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(Allocate(Num * EltSize, Alignment)); + /// \brief Deallocate a sequence of slabs. + void DeallocateSlabs(SmallVectorImpl::iterator I, + SmallVectorImpl::iterator E) { + for (; I != E; ++I) { + size_t AllocatedSlabSize = + computeSlabSize(std::distance(Slabs.begin(), I)); +#ifndef NDEBUG + // Poison the memory so stale pointers crash sooner. Note we must + // preserve the Size and NextPtr fields at the beginning. + sys::Memory::setRangeWritable(*I, AllocatedSlabSize); + memset(*I, 0xCD, AllocatedSlabSize); +#endif + Allocator.Deallocate(*I, AllocatedSlabSize); + } } - void Deallocate(const void * /*Ptr*/) {} + /// \brief Deallocate all memory for custom sized slabs. + void DeallocateCustomSizedSlabs() { + for (auto &PtrAndSize : CustomSizedSlabs) { + void *Ptr = PtrAndSize.first; + size_t Size = PtrAndSize.second; +#ifndef NDEBUG + // Poison the memory so stale pointers crash sooner. Note we must + // preserve the Size and NextPtr fields at the beginning. + sys::Memory::setRangeWritable(Ptr, Size); + memset(Ptr, 0xCD, Size); +#endif + Allocator.Deallocate(Ptr, Size); + } + } - unsigned GetNumSlabs() const; + template friend class SpecificBumpPtrAllocator; +}; - void PrintStats() const; +/// \brief The standard BumpPtrAllocator which just uses the default template +/// paramaters. +typedef BumpPtrAllocatorImpl<> BumpPtrAllocator; + +/// \brief A BumpPtrAllocator that allows only elements of a specific type to be +/// allocated. +/// +/// This allows calling the destructor in DestroyAll() and when the allocator is +/// destroyed. +template class SpecificBumpPtrAllocator { + BumpPtrAllocator Allocator; + +public: + SpecificBumpPtrAllocator() : Allocator() {} + + ~SpecificBumpPtrAllocator() { DestroyAll(); } + + /// Call the destructor of each allocated object and deallocate all but the + /// current slab and reset the current pointer to the beginning of it, freeing + /// all memory allocated so far. + void DestroyAll() { + auto DestroyElements = [](char *Begin, char *End) { + assert(Begin == alignPtr(Begin, alignOf())); + for (char *Ptr = Begin; Ptr + sizeof(T) <= End; Ptr += sizeof(T)) + reinterpret_cast(Ptr)->~T(); + }; + + for (auto I = Allocator.Slabs.begin(), E = Allocator.Slabs.end(); I != E; + ++I) { + size_t AllocatedSlabSize = BumpPtrAllocator::computeSlabSize( + std::distance(Allocator.Slabs.begin(), I)); + char *Begin = alignPtr((char *)*I, alignOf()); + char *End = *I == Allocator.Slabs.back() ? Allocator.CurPtr + : (char *)*I + AllocatedSlabSize; + + DestroyElements(Begin, End); + } + + for (auto &PtrAndSize : Allocator.CustomSizedSlabs) { + void *Ptr = PtrAndSize.first; + size_t Size = PtrAndSize.second; + DestroyElements(alignPtr((char *)Ptr, alignOf()), (char *)Ptr + Size); + } + + Allocator.Reset(); + } + + /// \brief Allocate space for an array of objects without constructing them. + T *Allocate(size_t num = 1) { return Allocator.Allocate(num); } + +private: }; } // end namespace llvm -inline void *operator new(size_t Size, llvm::BumpPtrAllocator &Allocator) { +template +void *operator new(size_t Size, + llvm::BumpPtrAllocatorImpl &Allocator) { struct S { char c; -#ifdef __GNUC__ - char x __attribute__((aligned)); -#else union { double D; long double LD; long long L; void *P; } x; -#endif }; - return Allocator.Allocate(Size, std::min(llvm::NextPowerOf2(Size), - offsetof(S, x))); + return Allocator.Allocate( + Size, std::min((size_t)llvm::NextPowerOf2(Size), offsetof(S, x))); +} + +template +void operator delete( + void *, llvm::BumpPtrAllocatorImpl &) { } #endif // LLVM_SUPPORT_ALLOCATOR_H