mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-01-12 17:32:19 +00:00
Reverting r76825 and r76828, since they caused clang runtime errors and some build failure involving memset.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@76838 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
54e650f2c7
commit
4bf370698a
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// 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 = false;
|
||||
#else
|
||||
PoisonMemory = true;
|
||||
#else
|
||||
PoisonMemory = false;
|
||||
#endif
|
||||
|
||||
// Allocate space for code.
|
||||
sys::MemoryBlock MemBlock = allocateNewSlab(DefaultCodeSlabSize);
|
||||
CodeSlabs.push_back(MemBlock);
|
||||
uint8_t *MemBase = (uint8_t*)MemBlock.base();
|
||||
// Allocate a 16M block of memory for functions.
|
||||
#if defined(__APPLE__) && defined(__arm__)
|
||||
sys::MemoryBlock MemBlock = getNewMemoryBlock(4 << 20);
|
||||
#else
|
||||
sys::MemoryBlock MemBlock = getNewMemoryBlock(16 << 20);
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
@ -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 {
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MemRegion class implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
BumpPtrAllocator::BumpPtrAllocator(size_t size, size_t threshold,
|
||||
SlabAllocator &allocator)
|
||||
: SlabSize(size), SizeThreshold(threshold), Allocator(allocator),
|
||||
CurSlab(0), BytesAllocated(0) {
|
||||
StartNewSlab();
|
||||
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));
|
||||
}
|
||||
|
||||
const MemRegion *getNext() const { return Next; }
|
||||
unsigned getNumBytesAllocated() const {
|
||||
return NextPtr-(const char*)this;
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// 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();
|
||||
}
|
||||
|
||||
/// 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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// BumpPtrAllocator class implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
BumpPtrAllocator::BumpPtrAllocator() {
|
||||
TheMemory = malloc(4096);
|
||||
((MemRegion*)TheMemory)->Init(4096, 1, 0);
|
||||
}
|
||||
|
||||
BumpPtrAllocator::~BumpPtrAllocator() {
|
||||
DeallocateSlabs(CurSlab);
|
||||
((MemRegion*)TheMemory)->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.
|
||||
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;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
MemRegion *MRP = (MemRegion*)TheMemory;
|
||||
MRP = MRP->DeallocateAllButLast();
|
||||
MRP->Init(4096, 1, 0);
|
||||
TheMemory = MRP;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Otherwise, start a new slab and try again.
|
||||
StartNewSlab();
|
||||
Ptr = AlignPtr(CurPtr, Alignment);
|
||||
CurPtr = Ptr + Size;
|
||||
assert(CurPtr < End && "Unable to allocate memory!");
|
||||
void *BumpPtrAllocator::Allocate(size_t Size, size_t Align) {
|
||||
MemRegion *MRP = (MemRegion*)TheMemory;
|
||||
void *Ptr = MRP->Allocate(Size, Align, &MRP);
|
||||
TheMemory = MRP;
|
||||
return Ptr;
|
||||
}
|
||||
|
||||
unsigned BumpPtrAllocator::GetNumSlabs() const {
|
||||
unsigned NumSlabs = 0;
|
||||
for (MemSlab *Slab = CurSlab; Slab != 0; Slab = Slab->NextPtr) {
|
||||
++NumSlabs;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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: " << NumSlabs << '\n'
|
||||
<< "Bytes used: " << BytesAllocated << '\n'
|
||||
<< "Bytes allocated: " << TotalMemory << '\n'
|
||||
<< "Bytes wasted: " << (TotalMemory - BytesAllocated)
|
||||
<< " (includes alignment, etc)\n";
|
||||
}
|
||||
|
||||
MallocSlabAllocator BumpPtrAllocator::DefaultSlabAllocator =
|
||||
MallocSlabAllocator();
|
||||
|
||||
SlabAllocator::~SlabAllocator() { }
|
||||
|
||||
MallocSlabAllocator::~MallocSlabAllocator() { }
|
||||
|
||||
MemSlab *MallocSlabAllocator::Allocate(size_t Size) {
|
||||
MemSlab *Slab = (MemSlab*)Allocator.Allocate(Size, 0);
|
||||
Slab->Size = Size;
|
||||
Slab->NextPtr = 0;
|
||||
return Slab;
|
||||
}
|
||||
|
||||
void MallocSlabAllocator::Deallocate(MemSlab *Slab) {
|
||||
Allocator.Deallocate(Slab);
|
||||
}
|
||||
|
||||
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';
|
||||
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';
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user