diff options
-rw-r--r-- | include/llvm/ExecutionEngine/JITMemoryManager.h | 69 | ||||
-rw-r--r-- | include/llvm/Support/Allocator.h | 97 | ||||
-rw-r--r-- | include/llvm/System/Memory.h | 9 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JITEmitter.cpp | 51 | ||||
-rw-r--r-- | lib/ExecutionEngine/JIT/JITMemoryManager.cpp | 364 | ||||
-rw-r--r-- | lib/Support/Allocator.cpp | 236 | ||||
-rw-r--r-- | lib/System/Unix/Memory.inc | 7 | ||||
-rw-r--r-- | lib/System/Win32/Memory.inc | 7 | ||||
-rw-r--r-- | tools/lli/lli.cpp | 3 | ||||
-rw-r--r-- | unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp | 276 | ||||
-rw-r--r-- | unittests/Support/AllocatorTest.cpp | 61 |
11 files changed, 234 insertions, 946 deletions
diff --git a/include/llvm/ExecutionEngine/JITMemoryManager.h b/include/llvm/ExecutionEngine/JITMemoryManager.h index 4539011..02ec1c3 100644 --- a/include/llvm/ExecutionEngine/JITMemoryManager.h +++ b/include/llvm/ExecutionEngine/JITMemoryManager.h @@ -15,12 +15,9 @@ #define LLVM_EXECUTION_ENGINE_JIT_MEMMANAGER_H #include "llvm/Support/DataTypes.h" -#include <string> namespace llvm { - class Function; - class GlobalValue; /// JITMemoryManager - This interface is used by the JIT to allocate and manage /// memory for the code generated by the JIT. This can be reimplemented by @@ -91,19 +88,16 @@ public: //===--------------------------------------------------------------------===// // Main Allocation Functions //===--------------------------------------------------------------------===// - - /// startFunctionBody - When we start JITing a function, the JIT calls this + + /// startFunctionBody - When we start JITing a function, the JIT calls this /// method to allocate a block of free RWX memory, which returns a pointer to - /// it. If the JIT wants to request a block of memory of at least a certain - /// size, it passes that value as ActualSize, and this method returns a block - /// with at least that much space. If the JIT doesn't know ahead of time how - /// much space it will need to emit the function, it passes 0 for the - /// ActualSize. In either case, this method is required to pass back the size - /// of the allocated block through ActualSize. The JIT will be careful to - /// not write more than the returned ActualSize bytes of memory. - virtual uint8_t *startFunctionBody(const Function *F, + /// it. The JIT doesn't know ahead of time how much space it will need to + /// emit the function, so it doesn't pass in the size. Instead, this method + /// is required to pass back a "valid size". The JIT will be careful to not + /// write more than the returned ActualSize bytes of memory. + virtual uint8_t *startFunctionBody(const Function *F, uintptr_t &ActualSize) = 0; - + /// allocateStub - This method is called by the JIT to allocate space for a /// function stub (used to handle limited branch displacements) while it is /// JIT compiling a function. For example, if foo calls bar, and if bar @@ -124,12 +118,10 @@ public: virtual void endFunctionBody(const Function *F, uint8_t *FunctionStart, uint8_t *FunctionEnd) = 0; - /// allocateSpace - Allocate a memory block of the given size. This method - /// cannot be called between calls to startFunctionBody and endFunctionBody. + /// allocateSpace - Allocate a memory block of the given size. virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) = 0; /// allocateGlobal - Allocate memory for a global. - /// virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) = 0; /// deallocateMemForFunction - Free JIT memory for the specified function. @@ -145,49 +137,6 @@ public: /// the exception table. virtual void endExceptionTable(const Function *F, uint8_t *TableStart, uint8_t *TableEnd, uint8_t* FrameRegister) = 0; - - /// CheckInvariants - For testing only. Return true if all internal - /// invariants are preserved, or return false and set ErrorStr to a helpful - /// error message. - virtual bool CheckInvariants(std::string &ErrorStr) { - return true; - } - - /// GetDefaultCodeSlabSize - For testing only. Returns DefaultCodeSlabSize - /// from DefaultJITMemoryManager. - virtual size_t GetDefaultCodeSlabSize() { - return 0; - } - - /// GetDefaultDataSlabSize - For testing only. Returns DefaultCodeSlabSize - /// from DefaultJITMemoryManager. - virtual size_t GetDefaultDataSlabSize() { - return 0; - } - - /// GetDefaultStubSlabSize - For testing only. Returns DefaultCodeSlabSize - /// from DefaultJITMemoryManager. - virtual size_t GetDefaultStubSlabSize() { - return 0; - } - - /// GetNumCodeSlabs - For testing only. Returns the number of MemoryBlocks - /// allocated for code. - virtual unsigned GetNumCodeSlabs() { - return 0; - } - - /// GetNumDataSlabs - For testing only. Returns the number of MemoryBlocks - /// allocated for data. - virtual unsigned GetNumDataSlabs() { - return 0; - } - - /// GetNumStubSlabs - For testing only. Returns the number of MemoryBlocks - /// allocated for function stubs. - virtual unsigned GetNumStubSlabs() { - return 0; - } }; } // end namespace llvm. diff --git a/include/llvm/Support/Allocator.h b/include/llvm/Support/Allocator.h index 4c84878..c0414f9 100644 --- a/include/llvm/Support/Allocator.h +++ b/include/llvm/Support/Allocator.h @@ -15,8 +15,6 @@ #define LLVM_SUPPORT_ALLOCATOR_H #include "llvm/Support/AlignOf.h" -#include "llvm/Support/DataTypes.h" -#include <cassert> #include <cstdlib> namespace llvm { @@ -43,104 +41,21 @@ public: void PrintStats() const {} }; -/// MemSlab - This structure lives at the beginning of every slab allocated by -/// the bump allocator. -class MemSlab { -public: - size_t Size; - MemSlab *NextPtr; -}; - -/// 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 { -public: - virtual ~SlabAllocator(); - virtual MemSlab *Allocate(size_t Size) = 0; - virtual void Deallocate(MemSlab *Slab) = 0; -}; - -/// 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 { - /// 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); -}; - -/// BumpPtrAllocator - This allocator is useful for containers that need -/// very simple memory allocation strategies. In particular, this just keeps +/// 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 - /// SlabSize - Allocate data into slabs of this size unless we get an - /// allocation above SizeThreshold. - size_t SlabSize; - - /// SizeThreshold - For any allocation larger than this threshold, we should - /// allocate a separate slab. - size_t SizeThreshold; - - /// 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; - - /// CurSlab - The slab that we are currently allocating into. - /// - MemSlab *CurSlab; - - /// CurPtr - The current pointer into the current slab. This points to the - /// next free byte in the slab. - char *CurPtr; - - /// End - The end of the current slab. - /// - char *End; - - /// BytesAllocated - This field tracks how many bytes we've allocated, so - /// that we can compute how much space was wasted. - size_t BytesAllocated; - - /// 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); - - /// StartNewSlab - Allocate a new slab and move the bump pointers over into - /// the new slab. Modifies CurPtr and End. - void StartNewSlab(); - - /// DeallocateSlabs - Deallocate all memory slabs after and including this - /// one. - void DeallocateSlabs(MemSlab *Slab); - - static MallocSlabAllocator DefaultSlabAllocator; - + void *TheMemory; public: - BumpPtrAllocator(size_t size = 4096, size_t threshold = 4096, - SlabAllocator &allocator = DefaultSlabAllocator); + BumpPtrAllocator(); ~BumpPtrAllocator(); - /// 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(); - /// Allocate - Allocate space at the specified alignment. - /// void *Allocate(size_t Size, size_t Alignment); /// Allocate space, but do not construct, one object. @@ -168,11 +83,9 @@ public: void Deallocate(const void * /*Ptr*/) {} - unsigned GetNumSlabs() const; - void PrintStats() const; }; } // end namespace llvm -#endif // LLVM_SUPPORT_ALLOCATOR_H +#endif diff --git a/include/llvm/System/Memory.h b/include/llvm/System/Memory.h index d6300db..136dc8a 100644 --- a/include/llvm/System/Memory.h +++ b/include/llvm/System/Memory.h @@ -14,7 +14,6 @@ #ifndef LLVM_SYSTEM_MEMORY_H #define LLVM_SYSTEM_MEMORY_H -#include "llvm/Support/DataTypes.h" #include <string> namespace llvm { @@ -27,13 +26,11 @@ namespace sys { /// @brief Memory block abstraction. class MemoryBlock { public: - MemoryBlock() { } - MemoryBlock(void *addr, size_t size) : Address(addr), Size(size) { } void *base() const { return Address; } - size_t size() const { return Size; } + unsigned size() const { return Size; } private: void *Address; ///< Address of first byte of memory area - size_t Size; ///< Size, in bytes of the memory area + unsigned Size; ///< Size, in bytes of the memory area friend class Memory; }; @@ -53,7 +50,7 @@ namespace sys { /// a null memory block and fills in *ErrMsg. /// /// @brief Allocate Read/Write/Execute memory. - static MemoryBlock AllocateRWX(size_t NumBytes, + static MemoryBlock AllocateRWX(unsigned NumBytes, const MemoryBlock *NearBlock, std::string *ErrMsg = 0); diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp index 246a0ce..ce52273 100644 --- a/lib/ExecutionEngine/JIT/JITEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp @@ -51,7 +51,6 @@ using namespace llvm; STATISTIC(NumBytes, "Number of bytes of machine code compiled"); STATISTIC(NumRelos, "Number of relocations applied"); -STATISTIC(NumRetries, "Number of retries with more memory"); static JIT *TheJIT = 0; @@ -426,12 +425,6 @@ namespace { // save BufferBegin/BufferEnd/CurBufferPtr here. uint8_t *SavedBufferBegin, *SavedBufferEnd, *SavedCurBufferPtr; - // When reattempting to JIT a function after running out of space, we store - // the estimated size of the function we're trying to JIT here, so we can - // ask the memory manager for at least this much space. When we - // successfully emit the function, we reset this back to zero. - uintptr_t SizeEstimate; - /// Relocations - These are the relocations that the function needs, as /// emitted. std::vector<MachineRelocation> Relocations; @@ -503,8 +496,7 @@ namespace { DebugLocTuple PrevDLT; public: - JITEmitter(JIT &jit, JITMemoryManager *JMM) - : SizeEstimate(0), Resolver(jit), CurFn(0) { + JITEmitter(JIT &jit, JITMemoryManager *JMM) : Resolver(jit), CurFn(0) { MemMgr = JMM ? JMM : JITMemoryManager::CreateDefaultMemManager(); if (jit.getJITInfo().needsGOT()) { MemMgr->AllocateGOT(); @@ -569,14 +561,9 @@ namespace { return MBBLocations[MBB->getNumber()]; } - /// retryWithMoreMemory - Log a retry and deallocate all memory for the - /// given function. Increase the minimum allocation size so that we get - /// more memory next time. - void retryWithMoreMemory(MachineFunction &F); - /// deallocateMemForFunction - Deallocate all memory for the specified /// function body. - void deallocateMemForFunction(const Function *F); + void deallocateMemForFunction(Function *F); /// AddStubToCurrentFunction - Mark the current function being JIT'd as /// using the stub at the specified address. Allows @@ -938,9 +925,6 @@ void JITEmitter::startFunction(MachineFunction &F) { // previously allocated. ActualSize += GetSizeOfGlobalsInBytes(F); DOUT << "JIT: ActualSize after globals " << ActualSize << "\n"; - } else if (SizeEstimate > 0) { - // SizeEstimate will be non-zero on reallocation attempts. - ActualSize = SizeEstimate; } BufferBegin = CurBufferPtr = MemMgr->startFunctionBody(F.getFunction(), @@ -965,15 +949,12 @@ void JITEmitter::startFunction(MachineFunction &F) { bool JITEmitter::finishFunction(MachineFunction &F) { if (CurBufferPtr == BufferEnd) { - // We must call endFunctionBody before retrying, because - // deallocateMemForFunction requires it. - MemMgr->endFunctionBody(F.getFunction(), BufferBegin, CurBufferPtr); - retryWithMoreMemory(F); - return true; + // FIXME: Allocate more space, then try again. + llvm_report_error("JIT: Ran out of space for generated machine code!"); } - + emitJumpTableInfo(F.getJumpTableInfo()); - + // FnStart is the start of the text, not the start of the constant pool and // other per-function data. uint8_t *FnStart = @@ -1064,12 +1045,8 @@ bool JITEmitter::finishFunction(MachineFunction &F) { MemMgr->endFunctionBody(F.getFunction(), BufferBegin, CurBufferPtr); if (CurBufferPtr == BufferEnd) { - retryWithMoreMemory(F); - return true; - } else { - // Now that we've succeeded in emitting the function, reset the - // SizeEstimate back down to zero. - SizeEstimate = 0; + // FIXME: Allocate more space, then try again. + llvm_report_error("JIT: Ran out of space for generated machine code!"); } BufferBegin = CurBufferPtr = 0; @@ -1154,19 +1131,9 @@ bool JITEmitter::finishFunction(MachineFunction &F) { return false; } -void JITEmitter::retryWithMoreMemory(MachineFunction &F) { - DOUT << "JIT: Ran out of space for native code. Reattempting.\n"; - Relocations.clear(); // Clear the old relocations or we'll reapply them. - ConstPoolAddresses.clear(); - ++NumRetries; - deallocateMemForFunction(F.getFunction()); - // Try again with at least twice as much free space. - SizeEstimate = (uintptr_t)(2 * (BufferEnd - BufferBegin)); -} - /// deallocateMemForFunction - Deallocate all memory for the specified /// function body. Also drop any references the function has to stubs. -void JITEmitter::deallocateMemForFunction(const Function *F) { +void JITEmitter::deallocateMemForFunction(Function *F) { MemMgr->deallocateMemForFunction(F); // If the function did not reference any stubs, return. diff --git a/lib/ExecutionEngine/JIT/JITMemoryManager.cpp b/lib/ExecutionEngine/JIT/JITMemoryManager.cpp index 2d64fcf..253c001 100644 --- a/lib/ExecutionEngine/JIT/JITMemoryManager.cpp +++ b/lib/ExecutionEngine/JIT/JITMemoryManager.cpp @@ -11,16 +11,10 @@ // //===----------------------------------------------------------------------===// -#define DEBUG_TYPE "jit" -#include "llvm/ExecutionEngine/JITMemoryManager.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ADT/Statistic.h" #include "llvm/GlobalValue.h" -#include "llvm/Support/Allocator.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" #include "llvm/Support/Compiler.h" -#include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/System/Memory.h" #include <map> #include <vector> @@ -31,7 +25,6 @@ #include <cstring> using namespace llvm; -STATISTIC(NumSlabs, "Number of slabs of memory allocated by the JIT"); JITMemoryManager::~JITMemoryManager() {} @@ -148,7 +141,7 @@ FreeRangeHeader *FreeRangeHeader::AllocateBlock() { /// FreeRangeHeader to allocate from. FreeRangeHeader *MemoryRangeHeader::FreeBlock(FreeRangeHeader *FreeList) { MemoryRangeHeader *FollowingBlock = &getBlockAfter(); - assert(ThisAllocated && "This block is already free!"); + assert(ThisAllocated && "This block is already allocated!"); assert(FollowingBlock->PrevAllocated && "Flags out of sync!"); FreeRangeHeader *FreeListToReturn = FreeList; @@ -251,157 +244,70 @@ TrimAllocationToSize(FreeRangeHeader *FreeList, uint64_t NewSize) { // Memory Block Implementation. //===----------------------------------------------------------------------===// -namespace { - - class DefaultJITMemoryManager; - - class JITSlabAllocator : public SlabAllocator { - DefaultJITMemoryManager &JMM; - public: - JITSlabAllocator(DefaultJITMemoryManager &jmm) : JMM(jmm) { } - virtual ~JITSlabAllocator() { } - virtual MemSlab *Allocate(size_t Size); - virtual void Deallocate(MemSlab *Slab); - }; - +namespace { /// DefaultJITMemoryManager - Manage memory for the JIT code generation. /// This splits a large block of MAP_NORESERVE'd memory into two /// sections, one for function stubs, one for the functions themselves. We /// have to do this because we may need to emit a function stub while in the /// middle of emitting a function, and we don't know how large the function we /// are emitting is. - class DefaultJITMemoryManager : public JITMemoryManager { - - // Whether to poison freed memory. - bool PoisonMemory; - - /// LastSlab - This points to the last slab allocated and is used as the - /// NearBlock parameter to AllocateRWX so that we can attempt to lay out all - /// stubs, data, and code contiguously in memory. In general, however, this - /// is not possible because the NearBlock parameter is ignored on Windows - /// platforms and even on Unix it works on a best-effort pasis. - sys::MemoryBlock LastSlab; - - // Memory slabs allocated by the JIT. We refer to them as slabs so we don't - // confuse them with the blocks of memory descibed above. - std::vector<sys::MemoryBlock> CodeSlabs; - JITSlabAllocator BumpSlabAllocator; - BumpPtrAllocator StubAllocator; - BumpPtrAllocator DataAllocator; - - // Circular list of free blocks. - FreeRangeHeader *FreeMemoryList; + class VISIBILITY_HIDDEN DefaultJITMemoryManager : public JITMemoryManager { + bool PoisonMemory; // Whether to poison freed memory. + std::vector<sys::MemoryBlock> Blocks; // Memory blocks allocated by the JIT + FreeRangeHeader *FreeMemoryList; // Circular list of free blocks. + // When emitting code into a memory block, this is the block. MemoryRangeHeader *CurBlock; - + + uint8_t *CurStubPtr, *StubBase; + uint8_t *CurGlobalPtr, *GlobalEnd; uint8_t *GOTBase; // Target Specific reserved memory void *DlsymTable; // Stub external symbol information + // Centralize memory block allocation. + sys::MemoryBlock getNewMemoryBlock(unsigned size); + std::map<const Function*, MemoryRangeHeader*> FunctionBlocks; std::map<const Function*, MemoryRangeHeader*> TableBlocks; public: DefaultJITMemoryManager(); ~DefaultJITMemoryManager(); - /// allocateNewSlab - Allocates a new MemoryBlock and remembers it as the - /// last slab it allocated, so that subsequent allocations follow it. - sys::MemoryBlock allocateNewSlab(size_t size); - - /// DefaultCodeSlabSize - When we have to go map more memory, we allocate at - /// least this much unless more is requested. - static const size_t DefaultCodeSlabSize; - - /// DefaultSlabSize - Allocate data into slabs of this size unless we get - /// an allocation above SizeThreshold. - static const size_t DefaultSlabSize; - - /// DefaultSizeThreshold - For any allocation larger than this threshold, we - /// should allocate a separate slab. - static const size_t DefaultSizeThreshold; - void AllocateGOT(); void SetDlsymTable(void *); - - // Testing methods. - virtual bool CheckInvariants(std::string &ErrorStr); - size_t GetDefaultCodeSlabSize() { return DefaultCodeSlabSize; } - size_t GetDefaultDataSlabSize() { return DefaultSlabSize; } - size_t GetDefaultStubSlabSize() { return DefaultSlabSize; } - unsigned GetNumCodeSlabs() { return CodeSlabs.size(); } - unsigned GetNumDataSlabs() { return DataAllocator.GetNumSlabs(); } - unsigned GetNumStubSlabs() { return StubAllocator.GetNumSlabs(); } - + + uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, + unsigned Alignment); + /// startFunctionBody - When a function starts, allocate a block of free /// executable memory, returning a pointer to it and its actual size. uint8_t *startFunctionBody(const Function *F, uintptr_t &ActualSize) { - + FreeRangeHeader* candidateBlock = FreeMemoryList; FreeRangeHeader* head = FreeMemoryList; FreeRangeHeader* iter = head->Next; uintptr_t largest = candidateBlock->BlockSize; - + // Search for the largest free block while (iter != head) { - if (iter->BlockSize > largest) { - largest = iter->BlockSize; - candidateBlock = iter; - } - iter = iter->Next; + if (iter->BlockSize > largest) { + largest = iter->BlockSize; + candidateBlock = iter; + } + iter = iter->Next; } - - // If this block isn't big enough for the allocation desired, allocate - // another block of memory and add it to the free list. - if (largest - sizeof(MemoryRangeHeader) < ActualSize) { - DOUT << "JIT: Allocating another slab of memory for function."; - candidateBlock = allocateNewCodeSlab((size_t)ActualSize); - } - + // Select this candidate block for allocation CurBlock = candidateBlock; // Allocate the entire memory block. FreeMemoryList = candidateBlock->AllocateBlock(); - ActualSize = CurBlock->BlockSize - sizeof(MemoryRangeHeader); - return (uint8_t *)(CurBlock + 1); - } - - /// allocateNewCodeSlab - Helper method to allocate a new slab of code - /// memory from the OS and add it to the free list. Returns the new - /// FreeRangeHeader at the base of the slab. - FreeRangeHeader *allocateNewCodeSlab(size_t MinSize) { - // If the user needs at least MinSize free memory, then we account for - // two MemoryRangeHeaders: the one in the user's block, and the one at the - // end of the slab. - size_t PaddedMin = MinSize + 2 * sizeof(MemoryRangeHeader); - size_t SlabSize = std::max(DefaultCodeSlabSize, PaddedMin); - sys::MemoryBlock B = allocateNewSlab(SlabSize); - CodeSlabs.push_back(B); - char *MemBase = (char*)(B.base()); - - // Put a tiny allocated block at the end of the memory chunk, so when - // FreeBlock calls getBlockAfter it doesn't fall off the end. - MemoryRangeHeader *EndBlock = - (MemoryRangeHeader*)(MemBase + B.size()) - 1; - EndBlock->ThisAllocated = 1; - EndBlock->PrevAllocated = 0; - EndBlock->BlockSize = sizeof(MemoryRangeHeader); - - // Start out with a vast new block of free memory. - FreeRangeHeader *NewBlock = (FreeRangeHeader*)MemBase; - NewBlock->ThisAllocated = 0; - // Make sure getFreeBlockBefore doesn't look into unmapped memory. - NewBlock->PrevAllocated = 1; - NewBlock->BlockSize = (uintptr_t)EndBlock - (uintptr_t)NewBlock; - NewBlock->SetEndOfBlockSizeMarker(); - NewBlock->AddToFreeList(FreeMemoryList); - - assert(NewBlock->BlockSize - sizeof(MemoryRangeHeader) >= MinSize && - "The block was too small!"); - return NewBlock; + ActualSize = CurBlock->BlockSize-sizeof(MemoryRangeHeader); + return (uint8_t *)(CurBlock+1); } - + /// endFunctionBody - The function F is now allocated, and takes the memory /// in the range [FunctionStart,FunctionEnd). void endFunctionBody(const Function *F, uint8_t *FunctionStart, @@ -417,8 +323,7 @@ namespace { FreeMemoryList =CurBlock->TrimAllocationToSize(FreeMemoryList, BlockSize); } - /// allocateSpace - Allocate a memory block of the given size. This method - /// cannot be called between calls to startFunctionBody and endFunctionBody. + /// allocateSpace - Allocate a memory block of the given size. uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) { CurBlock = FreeMemoryList; FreeMemoryList = FreeMemoryList->AllocateBlock(); @@ -435,15 +340,27 @@ namespace { return result; } - /// allocateStub - Allocate memory for a function stub. - uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, - unsigned Alignment) { - return (uint8_t*)StubAllocator.Allocate(StubSize, Alignment); - } - - /// allocateGlobal - Allocate memory for a global. + /// allocateGlobal - Allocate memory for a global. Unlike allocateSpace, + /// this method does not touch the current block and can be called at any + /// time. uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) { - return (uint8_t*)DataAllocator.Allocate(Size, Alignment); + uint8_t *Result = CurGlobalPtr; + + // Align the pointer. + if (Alignment == 0) Alignment = 1; + Result = (uint8_t*)(((uintptr_t)Result + Alignment-1) & + ~(uintptr_t)(Alignment-1)); + + // Move the current global pointer forward. + CurGlobalPtr += Result - CurGlobalPtr + Size; + + // Check for overflow. + if (CurGlobalPtr > GlobalEnd) { + // FIXME: Allocate more memory. + llvm_report_error("JIT ran out of memory for globals!"); + } + + return Result; } /// startExceptionTable - Use startFunctionBody to allocate memory for the @@ -520,15 +437,15 @@ namespace { /// the code pages may need permissions changed. void setMemoryWritable(void) { - for (unsigned i = 0, e = CodeSlabs.size(); i != e; ++i) - sys::Memory::setWritable(CodeSlabs[i]); + for (unsigned i = 0, e = Blocks.size(); i != e; ++i) + sys::Memory::setWritable(Blocks[i]); } /// setMemoryExecutable - When code generation is done and we're ready to /// start execution, the code pages may need permissions changed. void setMemoryExecutable(void) { - for (unsigned i = 0, e = CodeSlabs.size(); i != e; ++i) - sys::Memory::setExecutable(CodeSlabs[i]); + for (unsigned i = 0, e = Blocks.size(); i != e; ++i) + sys::Memory::setExecutable(Blocks[i]); } /// setPoisonMemory - Controls whether we write garbage over freed memory. @@ -539,35 +456,28 @@ namespace { }; } -MemSlab *JITSlabAllocator::Allocate(size_t Size) { - sys::MemoryBlock B = JMM.allocateNewSlab(Size); - MemSlab *Slab = (MemSlab*)B.base(); - Slab->Size = B.size(); - Slab->NextPtr = 0; - return Slab; -} - -void JITSlabAllocator::Deallocate(MemSlab *Slab) { - sys::MemoryBlock B(Slab, Slab->Size); - sys::Memory::ReleaseRWX(B); -} - -DefaultJITMemoryManager::DefaultJITMemoryManager() - : LastSlab(0, 0), - BumpSlabAllocator(*this), - StubAllocator(DefaultSlabSize, DefaultSizeThreshold, BumpSlabAllocator), - DataAllocator(DefaultSlabSize, DefaultSizeThreshold, BumpSlabAllocator) { - +DefaultJITMemoryManager::DefaultJITMemoryManager() { #ifdef NDEBUG + PoisonMemory = true; +#else PoisonMemory = false; +#endif + + // Allocate a 16M block of memory for functions. +#if defined(__APPLE__) && defined(__arm__) + sys::MemoryBlock MemBlock = getNewMemoryBlock(4 << 20); #else - PoisonMemory = true; + sys::MemoryBlock MemBlock = getNewMemoryBlock(16 << 20); #endif - // Allocate space for code. - sys::MemoryBlock MemBlock = allocateNewSlab(DefaultCodeSlabSize); - CodeSlabs.push_back(MemBlock); - uint8_t *MemBase = (uint8_t*)MemBlock.base(); + uint8_t *MemBase = static_cast<uint8_t*>(MemBlock.base()); + + // Allocate stubs backwards to the base, globals forward from the stubs, and + // functions forward after globals. + StubBase = MemBase; + CurStubPtr = MemBase + 512*1024; // Use 512k for stubs, working backwards. + CurGlobalPtr = CurStubPtr; // Use 2M for globals, working forwards. + GlobalEnd = CurGlobalPtr + 2*1024*1024; // We set up the memory chunk with 4 mem regions, like this: // [ START @@ -584,7 +494,7 @@ DefaultJITMemoryManager::DefaultJITMemoryManager() MemoryRangeHeader *Mem3 = (MemoryRangeHeader*)(MemBase+MemBlock.size())-1; Mem3->ThisAllocated = 1; Mem3->PrevAllocated = 0; - Mem3->BlockSize = sizeof(MemoryRangeHeader); + Mem3->BlockSize = 0; /// Add a tiny free region so that the free list always has one entry. FreeRangeHeader *Mem2 = @@ -600,12 +510,12 @@ DefaultJITMemoryManager::DefaultJITMemoryManager() MemoryRangeHeader *Mem1 = (MemoryRangeHeader*)Mem2-1; Mem1->ThisAllocated = 1; Mem1->PrevAllocated = 0; - Mem1->BlockSize = sizeof(MemoryRangeHeader); + Mem1->BlockSize = (char*)Mem2 - (char*)Mem1; // Add a FreeRangeHeader to the start of the function body region, indicating // that the space is free. Mark the previous block allocated so we never look // at it. - FreeRangeHeader *Mem0 = (FreeRangeHeader*)MemBase; + FreeRangeHeader *Mem0 = (FreeRangeHeader*)GlobalEnd; Mem0->ThisAllocated = 0; Mem0->PrevAllocated = 1; Mem0->BlockSize = (char*)Mem1-(char*)Mem0; @@ -630,124 +540,40 @@ void DefaultJITMemoryManager::SetDlsymTable(void *ptr) { } DefaultJITMemoryManager::~DefaultJITMemoryManager() { - for (unsigned i = 0, e = CodeSlabs.size(); i != e; ++i) - sys::Memory::ReleaseRWX(CodeSlabs[i]); - + for (unsigned i = 0, e = Blocks.size(); i != e; ++i) + sys::Memory::ReleaseRWX(Blocks[i]); + delete[] GOTBase; + Blocks.clear(); } -sys::MemoryBlock DefaultJITMemoryManager::allocateNewSlab(size_t size) { +uint8_t *DefaultJITMemoryManager::allocateStub(const GlobalValue* F, + unsigned StubSize, + unsigned Alignment) { + CurStubPtr -= StubSize; + CurStubPtr = (uint8_t*)(((intptr_t)CurStubPtr) & + ~(intptr_t)(Alignment-1)); + if (CurStubPtr < StubBase) { + // FIXME: allocate a new block + llvm_report_error("JIT ran out of memory for function stubs!"); + } + return CurStubPtr; +} + +sys::MemoryBlock DefaultJITMemoryManager::getNewMemoryBlock(unsigned size) { // Allocate a new block close to the last one. + const sys::MemoryBlock *BOld = Blocks.empty() ? 0 : &Blocks.back(); std::string ErrMsg; - sys::MemoryBlock *LastSlabPtr = LastSlab.base() ? &LastSlab : 0; - sys::MemoryBlock B = sys::Memory::AllocateRWX(size, LastSlabPtr, &ErrMsg); + sys::MemoryBlock B = sys::Memory::AllocateRWX(size, BOld, &ErrMsg); if (B.base() == 0) { llvm_report_error("Allocation failed when allocating new memory in the" " JIT\n" + ErrMsg); } - LastSlab = B; - ++NumSlabs; + Blocks.push_back(B); return B; } -/// CheckInvariants - For testing only. Return "" if all internal invariants -/// are preserved, and a helpful error message otherwise. For free and -/// allocated blocks, make sure that adding BlockSize gives a valid block. -/// For free blocks, make sure they're in the free list and that their end of -/// block size marker is correct. This function should return an error before -/// accessing bad memory. This function is defined here instead of in -/// JITMemoryManagerTest.cpp so that we don't have to expose all of the -/// implementation details of DefaultJITMemoryManager. -bool DefaultJITMemoryManager::CheckInvariants(std::string &ErrorStr) { - raw_string_ostream Err(ErrorStr); - - // Construct a the set of FreeRangeHeader pointers so we can query it - // efficiently. - llvm::SmallPtrSet<MemoryRangeHeader*, 16> FreeHdrSet; - FreeRangeHeader* FreeHead = FreeMemoryList; - FreeRangeHeader* FreeRange = FreeHead; - - do { - // Check that the free range pointer is in the blocks we've allocated. - bool Found = false; - for (std::vector<sys::MemoryBlock>::iterator I = CodeSlabs.begin(), - E = CodeSlabs.end(); I != E && !Found; ++I) { - char *Start = (char*)I->base(); - char *End = Start + I->size(); - Found = (Start <= (char*)FreeRange && (char*)FreeRange < End); - } - if (!Found) { - Err << "Corrupt free list; points to " << FreeRange; - return false; - } - - if (FreeRange->Next->Prev != FreeRange) { - Err << "Next and Prev pointers do not match."; - return false; - } - - // Otherwise, add it to the set. - FreeHdrSet.insert(FreeRange); - FreeRange = FreeRange->Next; - } while (FreeRange != FreeHead); - - // Go over each block, and look at each MemoryRangeHeader. - for (std::vector<sys::MemoryBlock>::iterator I = CodeSlabs.begin(), - E = CodeSlabs.end(); I != E; ++I) { - char *Start = (char*)I->base(); - char *End = Start + I->size(); - - // Check each memory range. - for (MemoryRangeHeader *Hdr = (MemoryRangeHeader*)Start, *LastHdr = NULL; - Start <= (char*)Hdr && (char*)Hdr < End; - Hdr = &Hdr->getBlockAfter()) { - if (Hdr->ThisAllocated == 0) { - // Check that this range is in the free list. - if (!FreeHdrSet.count(Hdr)) { - Err << "Found free header at " << Hdr << " that is not in free list."; - return false; - } - - // Now make sure the size marker at the end of the block is correct. - uintptr_t *Marker = ((uintptr_t*)&Hdr->getBlockAfter()) - 1; - if (!(Start <= (char*)Marker && (char*)Marker < End)) { - Err << "Block size in header points out of current MemoryBlock."; - return false; - } - if (Hdr->BlockSize != *Marker) { - Err << "End of block size marker (" << *Marker << ") " - << "and BlockSize (" << Hdr->BlockSize << ") don't match."; - return false; - } - } - - if (LastHdr && LastHdr->ThisAllocated != Hdr->PrevAllocated) { - Err << "Hdr->PrevAllocated (" << Hdr->PrevAllocated << ") != " - << "LastHdr->ThisAllocated (" << LastHdr->ThisAllocated << ")"; - return false; - } else if (!LastHdr && !Hdr->PrevAllocated) { - Err << "The first header should have PrevAllocated true."; - return false; - } - - // Remember the last header. - LastHdr = Hdr; - } - } - - // All invariants are preserved. - return true; -} JITMemoryManager *JITMemoryManager::CreateDefaultMemManager() { return new DefaultJITMemoryManager(); } - -// Allocate memory for code in 512K slabs. -const size_t DefaultJITMemoryManager::DefaultCodeSlabSize = 512 * 1024; - -// Allocate globals and stubs in slabs of 64K. (probably 16 pages) -const size_t DefaultJITMemoryManager::DefaultSlabSize = 64 * 1024; - -// Waste at most 16K at the end of each bump slab. (probably 4 pages) -const size_t DefaultJITMemoryManager::DefaultSizeThreshold = 16 * 1024; diff --git a/lib/Support/Allocator.cpp b/lib/Support/Allocator.cpp index e302f0b..db0d8f3 100644 --- a/lib/Support/Allocator.cpp +++ b/lib/Support/Allocator.cpp @@ -15,155 +15,127 @@ #include "llvm/Support/Recycler.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/Streams.h" -#include <cstring> +#include <ostream> +using namespace llvm; -namespace llvm { - -BumpPtrAllocator::BumpPtrAllocator(size_t size, size_t threshold, - SlabAllocator &allocator) - : SlabSize(size), SizeThreshold(threshold), Allocator(allocator), - CurSlab(0), BytesAllocated(0) { - StartNewSlab(); -} - -BumpPtrAllocator::~BumpPtrAllocator() { - DeallocateSlabs(CurSlab); -} - -/// 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. -char *BumpPtrAllocator::AlignPtr(char *Ptr, size_t Alignment) { - assert(Alignment && (Alignment & (Alignment - 1)) == 0 && - "Alignment is not a power of two!"); - - // Do the alignment. - return (char*)(((uintptr_t)Ptr + Alignment - 1) & - ~(uintptr_t)(Alignment - 1)); -} - -/// StartNewSlab - Allocate a new slab and move the bump pointers over into -/// the new slab. Modifies CurPtr and End. -void BumpPtrAllocator::StartNewSlab() { - MemSlab *NewSlab = Allocator.Allocate(SlabSize); - NewSlab->NextPtr = CurSlab; - CurSlab = NewSlab; - CurPtr = (char*)(CurSlab + 1); - End = CurPtr + CurSlab->Size; -} +//===----------------------------------------------------------------------===// +// MemRegion class implementation +//===----------------------------------------------------------------------===// -/// DeallocateSlabs - Deallocate all memory slabs after and including this -/// one. -void BumpPtrAllocator::DeallocateSlabs(MemSlab *Slab) { - while (Slab) { - MemSlab *NextSlab = Slab->NextPtr; -#ifndef NDEBUG - // Poison the memory so stale pointers crash sooner. Note we must - // preserve the Size and NextPtr fields at the beginning. - memset(Slab + 1, 0xCD, Slab->Size - sizeof(MemSlab)); -#endif - Allocator.Deallocate(Slab); - Slab = NextSlab; +namespace { +/// MemRegion - This is one chunk of the BumpPtrAllocator. +class MemRegion { + unsigned RegionSize; + MemRegion *Next; + char *NextPtr; +public: + void Init(unsigned size, unsigned Alignment, MemRegion *next) { + RegionSize = size; + Next = next; + NextPtr = (char*)(this+1); + + // Align NextPtr. + NextPtr = (char*)((intptr_t)(NextPtr+Alignment-1) & + ~(intptr_t)(Alignment-1)); } -} - -/// Reset - Deallocate all but the current slab and reset the current pointer -/// to the beginning of it, freeing all memory allocated so far. -void BumpPtrAllocator::Reset() { - DeallocateSlabs(CurSlab->NextPtr); - CurSlab->NextPtr = 0; - CurPtr = (char*)(CurSlab + 1); - End = CurPtr + CurSlab->Size; -} - -/// Allocate - Allocate space at the specified alignment. -/// -void *BumpPtrAllocator::Allocate(size_t Size, size_t Alignment) { - // 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; - return Ptr; + + const MemRegion *getNext() const { return Next; } + unsigned getNumBytesAllocated() const { + return NextPtr-(const char*)this; } - - // If Size is really big, allocate a separate slab for it. - if (Size > SizeThreshold) { - size_t PaddedSize = Size + sizeof(MemSlab) + Alignment - 1; - MemSlab *NewSlab = Allocator.Allocate(PaddedSize); - - // Put the new slab after the current slab, since we are not allocating - // into it. - NewSlab->NextPtr = CurSlab->NextPtr; - CurSlab->NextPtr = NewSlab; - - Ptr = AlignPtr((char*)(NewSlab + 1), Alignment); - assert((uintptr_t)Ptr + Size < (uintptr_t)NewSlab + NewSlab->Size); - return Ptr; + + /// Allocate - Allocate and return at least the specified number of bytes. + /// + void *Allocate(size_t AllocSize, size_t Alignment, MemRegion **RegPtr) { + + char* Result = (char*) (((uintptr_t) (NextPtr+Alignment-1)) + & ~((uintptr_t) Alignment-1)); + + // Speculate the new value of NextPtr. + char* NextPtrTmp = Result + AllocSize; + + // If we are still within the current region, return Result. + if (unsigned (NextPtrTmp - (char*) this) <= RegionSize) { + NextPtr = NextPtrTmp; + return Result; + } + + // Otherwise, we have to allocate a new chunk. Create one twice as big as + // this one. + MemRegion *NewRegion = (MemRegion *)malloc(RegionSize*2); + NewRegion->Init(RegionSize*2, Alignment, this); + + // Update the current "first region" pointer to point to the new region. + *RegPtr = NewRegion; + + // Try allocating from it now. + return NewRegion->Allocate(AllocSize, Alignment, RegPtr); } - - // Otherwise, start a new slab and try again. - StartNewSlab(); - Ptr = AlignPtr(CurPtr, Alignment); - CurPtr = Ptr + Size; - assert(CurPtr < End && "Unable to allocate memory!"); - return Ptr; -} - -unsigned BumpPtrAllocator::GetNumSlabs() const { - unsigned NumSlabs = 0; - for (MemSlab *Slab = CurSlab; Slab != 0; Slab = Slab->NextPtr) { - ++NumSlabs; + + /// Deallocate - Recursively release all memory for this and its next regions + /// to the system. + void Deallocate() { + MemRegion *next = Next; + free(this); + if (next) + next->Deallocate(); } - return NumSlabs; -} -void BumpPtrAllocator::PrintStats() const { - unsigned NumSlabs = 0; - size_t TotalMemory = 0; - for (MemSlab *Slab = CurSlab; Slab != 0; Slab = Slab->NextPtr) { - TotalMemory += Slab->Size; - ++NumSlabs; + /// DeallocateAllButLast - Recursively release all memory for this and its + /// next regions to the system stopping at the last region in the list. + /// Returns the pointer to the last region. + MemRegion *DeallocateAllButLast() { + MemRegion *next = Next; + if (!next) + return this; + free(this); + return next->DeallocateAllButLast(); } - - cerr << "\nNumber of memory regions: " << NumSlabs << '\n' - << "Bytes used: " << BytesAllocated << '\n' - << "Bytes allocated: " << TotalMemory << '\n' - << "Bytes wasted: " << (TotalMemory - BytesAllocated) - << " (includes alignment, etc)\n"; +}; } -MallocSlabAllocator BumpPtrAllocator::DefaultSlabAllocator = - MallocSlabAllocator(); +//===----------------------------------------------------------------------===// +// BumpPtrAllocator class implementation +//===----------------------------------------------------------------------===// -SlabAllocator::~SlabAllocator() { } +BumpPtrAllocator::BumpPtrAllocator() { + TheMemory = malloc(4096); + ((MemRegion*)TheMemory)->Init(4096, 1, 0); +} -MallocSlabAllocator::~MallocSlabAllocator() { } +BumpPtrAllocator::~BumpPtrAllocator() { + ((MemRegion*)TheMemory)->Deallocate(); +} -MemSlab *MallocSlabAllocator::Allocate(size_t Size) { - MemSlab *Slab = (MemSlab*)Allocator.Allocate(Size, 0); - Slab->Size = Size; - Slab->NextPtr = 0; - return Slab; +void BumpPtrAllocator::Reset() { + MemRegion *MRP = (MemRegion*)TheMemory; + MRP = MRP->DeallocateAllButLast(); + MRP->Init(4096, 1, 0); + TheMemory = MRP; } -void MallocSlabAllocator::Deallocate(MemSlab *Slab) { - Allocator.Deallocate(Slab); +void *BumpPtrAllocator::Allocate(size_t Size, size_t Align) { + MemRegion *MRP = (MemRegion*)TheMemory; + void *Ptr = MRP->Allocate(Size, Align, &MRP); + TheMemory = MRP; + return Ptr; } -void PrintRecyclerStats(size_t Size, - size_t Align, - size_t FreeListSize) { - cerr << "Recycler element size: " << Size << '\n' - << "Recycler element alignment: " << Align << '\n' - << "Number of elements free for recycling: " << FreeListSize << '\n'; +void BumpPtrAllocator::PrintStats() const { + unsigned BytesUsed = 0; + unsigned NumRegions = 0; + const MemRegion *R = (MemRegion*)TheMemory; + for (; R; R = R->getNext(), ++NumRegions) + BytesUsed += R->getNumBytesAllocated(); + + cerr << "\nNumber of memory regions: " << NumRegions << "\n"; + cerr << "Bytes allocated: " << BytesUsed << "\n"; } +void llvm::PrintRecyclerStats(size_t Size, + size_t Align, + size_t FreeListSize) { + cerr << "Recycler element size: " << Size << '\n'; + cerr << "Recycler element alignment: " << Align << '\n'; + cerr << "Number of elements free for recycling: " << FreeListSize << '\n'; } diff --git a/lib/System/Unix/Memory.inc b/lib/System/Unix/Memory.inc index a80f56f..b7a7013 100644 --- a/lib/System/Unix/Memory.inc +++ b/lib/System/Unix/Memory.inc @@ -12,7 +12,6 @@ //===----------------------------------------------------------------------===// #include "Unix.h" -#include "llvm/Support/DataTypes.h" #include "llvm/System/Process.h" #ifdef HAVE_SYS_MMAN_H @@ -29,12 +28,12 @@ /// is very OS specific. /// llvm::sys::MemoryBlock -llvm::sys::Memory::AllocateRWX(size_t NumBytes, const MemoryBlock* NearBlock, +llvm::sys::Memory::AllocateRWX(unsigned NumBytes, const MemoryBlock* NearBlock, std::string *ErrMsg) { if (NumBytes == 0) return MemoryBlock(); - size_t pageSize = Process::GetPageSize(); - size_t NumPages = (NumBytes+pageSize-1)/pageSize; + unsigned pageSize = Process::GetPageSize(); + unsigned NumPages = (NumBytes+pageSize-1)/pageSize; int fd = -1; #ifdef NEED_DEV_ZERO_FOR_MMAP diff --git a/lib/System/Win32/Memory.inc b/lib/System/Win32/Memory.inc index 7611ecd..5e5cf7a 100644 --- a/lib/System/Win32/Memory.inc +++ b/lib/System/Win32/Memory.inc @@ -13,7 +13,6 @@ //===----------------------------------------------------------------------===// #include "Win32.h" -#include "llvm/Support/DataTypes.h" #include "llvm/System/Process.h" namespace llvm { @@ -24,13 +23,13 @@ using namespace sys; //=== and must not be UNIX code //===----------------------------------------------------------------------===// -MemoryBlock Memory::AllocateRWX(size_t NumBytes, +MemoryBlock Memory::AllocateRWX(unsigned NumBytes, const MemoryBlock *NearBlock, std::string *ErrMsg) { if (NumBytes == 0) return MemoryBlock(); - static const size_t pageSize = Process::GetPageSize(); - size_t NumPages = (NumBytes+pageSize-1)/pageSize; + static const long pageSize = Process::GetPageSize(); + unsigned NumPages = (NumBytes+pageSize-1)/pageSize; //FIXME: support NearBlock if ever needed on Win64. diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index f9d86d7..0337703 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -136,6 +136,9 @@ int main(int argc, char **argv, char * const *envp) { builder.setEngineKind(ForceInterpreter ? EngineKind::Interpreter : EngineKind::JIT); + // FIXME: Don't allocate GVs with code once the JIT because smarter about + // memory management. + builder.setAllocateGVsWithCode(true); // If we are supposed to override the target triple, do so now. if (!TargetTriple.empty()) diff --git a/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp b/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp index f9b3a03..e69de29 100644 --- a/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp +++ b/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp @@ -1,276 +0,0 @@ -//===- JITMemoryManagerTest.cpp - Unit tests for the JIT memory manager ---===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "gtest/gtest.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/ExecutionEngine/JITMemoryManager.h" -#include "llvm/DerivedTypes.h" -#include "llvm/Function.h" -#include "llvm/GlobalValue.h" - -using namespace llvm; - -namespace { - -Function *makeFakeFunction() { - std::vector<const Type*> params; - const FunctionType *FTy = FunctionType::get(Type::VoidTy, params, false); - return Function::Create(FTy, GlobalValue::ExternalLinkage); -} - -// Allocate three simple functions that fit in the initial slab. This exercises -// the code in the case that we don't have to allocate more memory to store the -// function bodies. -TEST(JITMemoryManagerTest, NoAllocations) { - OwningPtr<JITMemoryManager> MemMgr( - JITMemoryManager::CreateDefaultMemManager()); - uintptr_t size; - uint8_t *start; - std::string Error; - - // Allocate the functions. - OwningPtr<Function> F1(makeFakeFunction()); - size = 1024; - start = MemMgr->startFunctionBody(F1.get(), size); - memset(start, 0xFF, 1024); - MemMgr->endFunctionBody(F1.get(), start, start + 1024); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - - OwningPtr<Function> F2(makeFakeFunction()); - size = 1024; - start = MemMgr->startFunctionBody(F2.get(), size); - memset(start, 0xFF, 1024); - MemMgr->endFunctionBody(F2.get(), start, start + 1024); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - - OwningPtr<Function> F3(makeFakeFunction()); - size = 1024; - start = MemMgr->startFunctionBody(F3.get(), size); - memset(start, 0xFF, 1024); - MemMgr->endFunctionBody(F3.get(), start, start + 1024); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - - // Deallocate them out of order, in case that matters. - MemMgr->deallocateMemForFunction(F2.get()); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - MemMgr->deallocateMemForFunction(F1.get()); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - MemMgr->deallocateMemForFunction(F3.get()); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; -} - -// Make three large functions that take up most of the space in the slab. Then -// try allocating three smaller functions that don't require additional slabs. -TEST(JITMemoryManagerTest, TestCodeAllocation) { - OwningPtr<JITMemoryManager> MemMgr( - JITMemoryManager::CreateDefaultMemManager()); - uintptr_t size; - uint8_t *start; - std::string Error; - - // Big functions are a little less than the largest block size. - const uintptr_t smallFuncSize = 1024; - const uintptr_t bigFuncSize = (MemMgr->GetDefaultCodeSlabSize() - - smallFuncSize * 2); - - // Allocate big functions - OwningPtr<Function> F1(makeFakeFunction()); - size = bigFuncSize; - start = MemMgr->startFunctionBody(F1.get(), size); - ASSERT_LE(bigFuncSize, size); - memset(start, 0xFF, bigFuncSize); - MemMgr->endFunctionBody(F1.get(), start, start + bigFuncSize); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - - OwningPtr<Function> F2(makeFakeFunction()); - size = bigFuncSize; - start = MemMgr->startFunctionBody(F2.get(), size); - ASSERT_LE(bigFuncSize, size); - memset(start, 0xFF, bigFuncSize); - MemMgr->endFunctionBody(F2.get(), start, start + bigFuncSize); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - - OwningPtr<Function> F3(makeFakeFunction()); - size = bigFuncSize; - start = MemMgr->startFunctionBody(F3.get(), size); - ASSERT_LE(bigFuncSize, size); - memset(start, 0xFF, bigFuncSize); - MemMgr->endFunctionBody(F3.get(), start, start + bigFuncSize); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - - // Check that each large function took it's own slab. - EXPECT_EQ(3U, MemMgr->GetNumCodeSlabs()); - - // Allocate small functions - OwningPtr<Function> F4(makeFakeFunction()); - size = smallFuncSize; - start = MemMgr->startFunctionBody(F4.get(), size); - ASSERT_LE(smallFuncSize, size); - memset(start, 0xFF, smallFuncSize); - MemMgr->endFunctionBody(F4.get(), start, start + smallFuncSize); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - - OwningPtr<Function> F5(makeFakeFunction()); - size = smallFuncSize; - start = MemMgr->startFunctionBody(F5.get(), size); - ASSERT_LE(smallFuncSize, size); - memset(start, 0xFF, smallFuncSize); - MemMgr->endFunctionBody(F5.get(), start, start + smallFuncSize); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - - OwningPtr<Function> F6(makeFakeFunction()); - size = smallFuncSize; - start = MemMgr->startFunctionBody(F6.get(), size); - ASSERT_LE(smallFuncSize, size); - memset(start, 0xFF, smallFuncSize); - MemMgr->endFunctionBody(F6.get(), start, start + smallFuncSize); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - - // Check that the small functions didn't allocate any new slabs. - EXPECT_EQ(3U, MemMgr->GetNumCodeSlabs()); - - // Deallocate them out of order, in case that matters. - MemMgr->deallocateMemForFunction(F2.get()); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - MemMgr->deallocateMemForFunction(F1.get()); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - MemMgr->deallocateMemForFunction(F4.get()); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - MemMgr->deallocateMemForFunction(F3.get()); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - MemMgr->deallocateMemForFunction(F5.get()); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; - MemMgr->deallocateMemForFunction(F6.get()); - EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; -} - -// Allocate five global ints of varying widths and alignment, and check their -// alignment and overlap. -TEST(JITMemoryManagerTest, TestSmallGlobalInts) { - OwningPtr<JITMemoryManager> MemMgr( - JITMemoryManager::CreateDefaultMemManager()); - uint8_t *a = (uint8_t *)MemMgr->allocateGlobal(8, 0); - uint16_t *b = (uint16_t*)MemMgr->allocateGlobal(16, 2); - uint32_t *c = (uint32_t*)MemMgr->allocateGlobal(32, 4); - uint64_t *d = (uint64_t*)MemMgr->allocateGlobal(64, 8); - - // Check the alignment. - EXPECT_EQ(0U, ((uintptr_t)b) & 0x1); - EXPECT_EQ(0U, ((uintptr_t)c) & 0x3); - EXPECT_EQ(0U, ((uintptr_t)d) & 0x7); - - // Initialize them each one at a time and make sure they don't overlap. - *a = 0xff; - *b = 0U; - *c = 0U; - *d = 0U; - EXPECT_EQ(0xffU, *a); - EXPECT_EQ(0U, *b); - EXPECT_EQ(0U, *c); - EXPECT_EQ(0U, *d); - *a = 0U; - *b = 0xffffU; - EXPECT_EQ(0U, *a); - EXPECT_EQ(0xffffU, *b); - EXPECT_EQ(0U, *c); - EXPECT_EQ(0U, *d); - *b = 0U; - *c = 0xffffffffU; - EXPECT_EQ(0U, *a); - EXPECT_EQ(0U, *b); - EXPECT_EQ(0xffffffffU, *c); - EXPECT_EQ(0U, *d); - *c = 0U; - *d = 0xffffffffffffffffU; - EXPECT_EQ(0U, *a); - EXPECT_EQ(0U, *b); - EXPECT_EQ(0U, *c); - EXPECT_EQ(0xffffffffffffffffU, *d); - - // Make sure we didn't allocate any extra slabs for this tiny amount of data. - EXPECT_EQ(1U, MemMgr->GetNumDataSlabs()); -} - -// Allocate a small global, a big global, and a third global, and make sure we -// only use two slabs for that. -TEST(JITMemoryManagerTest, TestLargeGlobalArray) { - OwningPtr<JITMemoryManager> MemMgr( - JITMemoryManager::CreateDefaultMemManager()); - size_t Size = 4 * MemMgr->GetDefaultDataSlabSize(); - uint64_t *a = (uint64_t*)MemMgr->allocateGlobal(64, 8); - uint8_t *g = MemMgr->allocateGlobal(Size, 8); - uint64_t *b = (uint64_t*)MemMgr->allocateGlobal(64, 8); - - // Check the alignment. - EXPECT_EQ(0U, ((uintptr_t)a) & 0x7); - EXPECT_EQ(0U, ((uintptr_t)g) & 0x7); - EXPECT_EQ(0U, ((uintptr_t)b) & 0x7); - - // Initialize them to make sure we don't segfault and make sure they don't - // overlap. - memset(a, 0x1, 8); - memset(g, 0x2, Size); - memset(b, 0x3, 8); - EXPECT_EQ(0x0101010101010101U, *a); - // Just check the edges. - EXPECT_EQ(0x02U, g[0]); - EXPECT_EQ(0x02U, g[Size - 1]); - EXPECT_EQ(0x0303030303030303U, *b); - - // Check the number of slabs. - EXPECT_EQ(2U, MemMgr->GetNumDataSlabs()); -} - -// Allocate lots of medium globals so that we can test moving the bump allocator -// to a new slab. -TEST(JITMemoryManagerTest, TestManyGlobals) { - OwningPtr<JITMemoryManager> MemMgr( - JITMemoryManager::CreateDefaultMemManager()); - size_t SlabSize = MemMgr->GetDefaultDataSlabSize(); - size_t Size = 128; - int Iters = (SlabSize / Size) + 1; - - // We should start with one slab. - EXPECT_EQ(1U, MemMgr->GetNumDataSlabs()); - - // After allocating a bunch of globals, we should have two. - for (int I = 0; I < Iters; ++I) - MemMgr->allocateGlobal(Size, 8); - EXPECT_EQ(2U, MemMgr->GetNumDataSlabs()); - - // And after much more, we should have three. - for (int I = 0; I < Iters; ++I) - MemMgr->allocateGlobal(Size, 8); - EXPECT_EQ(3U, MemMgr->GetNumDataSlabs()); -} - -// Allocate lots of function stubs so that we can test moving the stub bump -// allocator to a new slab. -TEST(JITMemoryManagerTest, TestManyStubs) { - OwningPtr<JITMemoryManager> MemMgr( - JITMemoryManager::CreateDefaultMemManager()); - size_t SlabSize = MemMgr->GetDefaultStubSlabSize(); - size_t Size = 128; - int Iters = (SlabSize / Size) + 1; - - // We should start with one slab. - EXPECT_EQ(1U, MemMgr->GetNumStubSlabs()); - - // After allocating a bunch of stubs, we should have two. - for (int I = 0; I < Iters; ++I) - MemMgr->allocateStub(NULL, Size, 8); - EXPECT_EQ(2U, MemMgr->GetNumStubSlabs()); - - // And after much more, we should have three. - for (int I = 0; I < Iters; ++I) - MemMgr->allocateStub(NULL, Size, 8); - EXPECT_EQ(3U, MemMgr->GetNumStubSlabs()); -} - -} diff --git a/unittests/Support/AllocatorTest.cpp b/unittests/Support/AllocatorTest.cpp index 139270b..e69de29 100644 --- a/unittests/Support/AllocatorTest.cpp +++ b/unittests/Support/AllocatorTest.cpp @@ -1,61 +0,0 @@ -//===- llvm/unittest/Support/AllocatorTest.cpp - BumpPtrAllocator tests ---===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Support/Allocator.h" - -#include "gtest/gtest.h" - -using namespace llvm; - -namespace { - -TEST(AllocatorTest, Basics) { - BumpPtrAllocator Alloc; - int *a = (int*)Alloc.Allocate(sizeof(int), 0); - int *b = (int*)Alloc.Allocate(sizeof(int) * 10, 0); - int *c = (int*)Alloc.Allocate(sizeof(int), 0); - *a = 1; - b[0] = 2; - b[9] = 2; - *c = 3; - EXPECT_EQ(1, *a); - EXPECT_EQ(2, b[0]); - EXPECT_EQ(2, b[9]); - EXPECT_EQ(3, *c); - EXPECT_EQ(1U, Alloc.GetNumSlabs()); -} - -// Allocate enough bytes to create three slabs. -TEST(AllocatorTest, ThreeSlabs) { - BumpPtrAllocator Alloc(4096, 4096); - Alloc.Allocate(3000, 0); - EXPECT_EQ(1U, Alloc.GetNumSlabs()); - Alloc.Allocate(3000, 0); - EXPECT_EQ(2U, Alloc.GetNumSlabs()); - Alloc.Allocate(3000, 0); - EXPECT_EQ(3U, Alloc.GetNumSlabs()); -} - -// Allocate enough bytes to create two slabs, reset the allocator, and do it -// again. -TEST(AllocatorTest, TestReset) { - BumpPtrAllocator Alloc(4096, 4096); - Alloc.Allocate(3000, 0); - EXPECT_EQ(1U, Alloc.GetNumSlabs()); - Alloc.Allocate(3000, 0); - EXPECT_EQ(2U, Alloc.GetNumSlabs()); - Alloc.Reset(); - EXPECT_EQ(1U, Alloc.GetNumSlabs()); - Alloc.Allocate(3000, 0); - EXPECT_EQ(1U, Alloc.GetNumSlabs()); - Alloc.Allocate(3000, 0); - EXPECT_EQ(2U, Alloc.GetNumSlabs()); -} - -} // anonymous namespace |