JITMemoryManager.cpp [plain text]
#include "llvm/GlobalValue.h"
#include "llvm/ExecutionEngine/JITMemoryManager.h"
#include "llvm/Support/Compiler.h"
#include "llvm/System/Memory.h"
#include <map>
#include <vector>
#include <cassert>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace llvm;
JITMemoryManager::~JITMemoryManager() {}
namespace {
struct FreeRangeHeader;
struct MemoryRangeHeader {
unsigned ThisAllocated : 1;
unsigned PrevAllocated : 1;
uintptr_t BlockSize : (sizeof(intptr_t)*CHAR_BIT - 2);
MemoryRangeHeader &getBlockAfter() const {
return *(MemoryRangeHeader*)((char*)this+BlockSize);
}
FreeRangeHeader *getFreeBlockBefore() const {
if (PrevAllocated) return 0;
intptr_t PrevSize = ((intptr_t *)this)[-1];
return (FreeRangeHeader*)((char*)this-PrevSize);
}
FreeRangeHeader *FreeBlock(FreeRangeHeader *FreeList);
FreeRangeHeader *TrimAllocationToSize(FreeRangeHeader *FreeList,
uint64_t NewSize);
};
struct FreeRangeHeader : public MemoryRangeHeader {
FreeRangeHeader *Prev;
FreeRangeHeader *Next;
static unsigned getMinBlockSize() {
return sizeof(FreeRangeHeader)+sizeof(intptr_t);
}
void SetEndOfBlockSizeMarker() {
void *EndOfBlock = (char*)this + BlockSize;
((intptr_t *)EndOfBlock)[-1] = BlockSize;
}
FreeRangeHeader *RemoveFromFreeList() {
assert(Next->Prev == this && Prev->Next == this && "Freelist broken!");
Next->Prev = Prev;
return Prev->Next = Next;
}
void AddToFreeList(FreeRangeHeader *FreeList) {
Next = FreeList;
Prev = FreeList->Prev;
Prev->Next = this;
Next->Prev = this;
}
void GrowBlock(uintptr_t NewSize);
FreeRangeHeader *AllocateBlock();
};
}
FreeRangeHeader *FreeRangeHeader::AllocateBlock() {
assert(!ThisAllocated && !getBlockAfter().PrevAllocated &&
"Cannot allocate an allocated block!");
ThisAllocated = 1;
getBlockAfter().PrevAllocated = 1;
return RemoveFromFreeList();
}
FreeRangeHeader *MemoryRangeHeader::FreeBlock(FreeRangeHeader *FreeList) {
MemoryRangeHeader *FollowingBlock = &getBlockAfter();
assert(ThisAllocated && "This block is already allocated!");
assert(FollowingBlock->PrevAllocated && "Flags out of sync!");
FreeRangeHeader *FreeListToReturn = FreeList;
if (!FollowingBlock->ThisAllocated) {
FreeRangeHeader &FollowingFreeBlock = *(FreeRangeHeader *)FollowingBlock;
if (&FollowingFreeBlock == FreeList) {
FreeList = FollowingFreeBlock.Next;
FreeListToReturn = 0;
assert(&FollowingFreeBlock != FreeList && "No tombstone block?");
}
FollowingFreeBlock.RemoveFromFreeList();
BlockSize += FollowingFreeBlock.BlockSize;
FollowingBlock = &FollowingFreeBlock.getBlockAfter();
FollowingBlock->PrevAllocated = 1;
}
assert(FollowingBlock->ThisAllocated && "Missed coalescing?");
if (FreeRangeHeader *PrevFreeBlock = getFreeBlockBefore()) {
PrevFreeBlock->GrowBlock(PrevFreeBlock->BlockSize + BlockSize);
return FreeListToReturn ? FreeListToReturn : PrevFreeBlock;
}
FreeRangeHeader &FreeBlock = *(FreeRangeHeader*)this;
FollowingBlock->PrevAllocated = 0;
FreeBlock.ThisAllocated = 0;
FreeBlock.AddToFreeList(FreeList);
FreeBlock.SetEndOfBlockSizeMarker();
return FreeListToReturn ? FreeListToReturn : &FreeBlock;
}
void FreeRangeHeader::GrowBlock(uintptr_t NewSize) {
assert(NewSize > BlockSize && "Not growing block?");
BlockSize = NewSize;
SetEndOfBlockSizeMarker();
getBlockAfter().PrevAllocated = 0;
}
FreeRangeHeader *MemoryRangeHeader::
TrimAllocationToSize(FreeRangeHeader *FreeList, uint64_t NewSize) {
assert(ThisAllocated && getBlockAfter().PrevAllocated &&
"Cannot deallocate part of an allocated block!");
NewSize = std::max<uint64_t>(FreeRangeHeader::getMinBlockSize(), NewSize);
unsigned HeaderAlign = __alignof(FreeRangeHeader);
NewSize = (NewSize+ (HeaderAlign-1)) & ~(HeaderAlign-1);
assert(NewSize <= BlockSize &&
"Allocating more space from this block than exists!");
if (BlockSize <= NewSize+FreeRangeHeader::getMinBlockSize())
return FreeList;
MemoryRangeHeader &FormerNextBlock = getBlockAfter();
BlockSize = NewSize;
FreeRangeHeader &NewNextBlock = (FreeRangeHeader &)getBlockAfter();
NewNextBlock.BlockSize = (char*)&FormerNextBlock - (char*)&NewNextBlock;
NewNextBlock.ThisAllocated = 0;
NewNextBlock.PrevAllocated = 1;
NewNextBlock.SetEndOfBlockSizeMarker();
FormerNextBlock.PrevAllocated = 0;
NewNextBlock.AddToFreeList(FreeList);
return &NewNextBlock;
}
namespace {
class VISIBILITY_HIDDEN DefaultJITMemoryManager : public JITMemoryManager {
std::vector<sys::MemoryBlock> Blocks; FreeRangeHeader *FreeMemoryList;
MemoryRangeHeader *CurBlock;
unsigned char *CurStubPtr, *StubBase;
unsigned char *GOTBase; void *DlsymTable;
sys::MemoryBlock getNewMemoryBlock(unsigned size);
std::map<const Function*, MemoryRangeHeader*> FunctionBlocks;
std::map<const Function*, MemoryRangeHeader*> TableBlocks;
public:
DefaultJITMemoryManager();
~DefaultJITMemoryManager();
void AllocateGOT();
void SetDlsymTable(void *);
unsigned char *allocateStub(const GlobalValue* F, unsigned StubSize,
unsigned Alignment);
unsigned char *startFunctionBody(const Function *F, uintptr_t &ActualSize) {
FreeRangeHeader* candidateBlock = FreeMemoryList;
FreeRangeHeader* head = FreeMemoryList;
FreeRangeHeader* iter = head->Next;
uintptr_t largest = candidateBlock->BlockSize;
while (iter != head) {
if (iter->BlockSize > largest) {
largest = iter->BlockSize;
candidateBlock = iter;
}
iter = iter->Next;
}
CurBlock = candidateBlock;
FreeMemoryList = candidateBlock->AllocateBlock();
ActualSize = CurBlock->BlockSize-sizeof(MemoryRangeHeader);
return (unsigned char *)(CurBlock+1);
}
void endFunctionBody(const Function *F, unsigned char *FunctionStart,
unsigned char *FunctionEnd) {
assert(FunctionEnd > FunctionStart);
assert(FunctionStart == (unsigned char *)(CurBlock+1) &&
"Mismatched function start/end!");
uintptr_t BlockSize = FunctionEnd - (unsigned char *)CurBlock;
FunctionBlocks[F] = CurBlock;
FreeMemoryList =CurBlock->TrimAllocationToSize(FreeMemoryList, BlockSize);
}
unsigned char *allocateSpace(intptr_t Size, unsigned Alignment) {
CurBlock = FreeMemoryList;
FreeMemoryList = FreeMemoryList->AllocateBlock();
unsigned char *result = (unsigned char *)CurBlock+1;
if (Alignment == 0) Alignment = 1;
result = (unsigned char*)(((intptr_t)result+Alignment-1) &
~(intptr_t)(Alignment-1));
uintptr_t BlockSize = result + Size - (unsigned char *)CurBlock;
FreeMemoryList =CurBlock->TrimAllocationToSize(FreeMemoryList, BlockSize);
return result;
}
unsigned char* startExceptionTable(const Function* F,
uintptr_t &ActualSize) {
return startFunctionBody(F, ActualSize);
}
void endExceptionTable(const Function *F, unsigned char *TableStart,
unsigned char *TableEnd,
unsigned char* FrameRegister) {
assert(TableEnd > TableStart);
assert(TableStart == (unsigned char *)(CurBlock+1) &&
"Mismatched table start/end!");
uintptr_t BlockSize = TableEnd - (unsigned char *)CurBlock;
TableBlocks[F] = CurBlock;
FreeMemoryList =CurBlock->TrimAllocationToSize(FreeMemoryList, BlockSize);
}
unsigned char *getGOTBase() const {
return GOTBase;
}
void *getDlsymTable() const {
return DlsymTable;
}
void deallocateMemForFunction(const Function *F) {
std::map<const Function*, MemoryRangeHeader*>::iterator
I = FunctionBlocks.find(F);
if (I == FunctionBlocks.end()) return;
MemoryRangeHeader *MemRange = I->second;
assert(MemRange->ThisAllocated && "Block isn't allocated!");
#ifndef NDEBUG
memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange));
#endif
FreeMemoryList = MemRange->FreeBlock(FreeMemoryList);
FunctionBlocks.erase(I);
I = TableBlocks.find(F);
if (I == TableBlocks.end()) return;
MemRange = I->second;
assert(MemRange->ThisAllocated && "Block isn't allocated!");
#ifndef NDEBUG
memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange));
#endif
FreeMemoryList = MemRange->FreeBlock(FreeMemoryList);
TableBlocks.erase(I);
}
void setMemoryWritable(void)
{
for (unsigned i = 0, e = Blocks.size(); i != e; ++i)
sys::Memory::setWritable(Blocks[i]);
}
void setMemoryExecutable(void)
{
for (unsigned i = 0, e = Blocks.size(); i != e; ++i)
sys::Memory::setExecutable(Blocks[i]);
}
};
}
DefaultJITMemoryManager::DefaultJITMemoryManager() {
#if defined(__APPLE__) && defined(__arm__)
sys::MemoryBlock MemBlock = getNewMemoryBlock(4 << 20);
#else
sys::MemoryBlock MemBlock = getNewMemoryBlock(16 << 20);
#endif
unsigned char *MemBase = static_cast<unsigned char*>(MemBlock.base());
StubBase = MemBase;
CurStubPtr = MemBase + 512*1024;
MemoryRangeHeader *Mem3 = (MemoryRangeHeader*)(MemBase+MemBlock.size())-1;
Mem3->ThisAllocated = 1;
Mem3->PrevAllocated = 0;
Mem3->BlockSize = 0;
FreeRangeHeader *Mem2 =
(FreeRangeHeader *)(((char*)Mem3)-FreeRangeHeader::getMinBlockSize());
Mem2->ThisAllocated = 0;
Mem2->PrevAllocated = 1;
Mem2->BlockSize = FreeRangeHeader::getMinBlockSize();
Mem2->SetEndOfBlockSizeMarker();
Mem2->Prev = Mem2; Mem2->Next = Mem2;
MemoryRangeHeader *Mem1 = (MemoryRangeHeader*)Mem2-1;
Mem1->ThisAllocated = 1;
Mem1->PrevAllocated = 0;
Mem1->BlockSize = (char*)Mem2 - (char*)Mem1;
FreeRangeHeader *Mem0 = (FreeRangeHeader*)CurStubPtr;
Mem0->ThisAllocated = 0;
Mem0->PrevAllocated = 1;
Mem0->BlockSize = (char*)Mem1-(char*)Mem0;
Mem0->SetEndOfBlockSizeMarker();
Mem0->AddToFreeList(Mem2);
FreeMemoryList = Mem0;
GOTBase = NULL;
DlsymTable = NULL;
}
void DefaultJITMemoryManager::AllocateGOT() {
assert(GOTBase == 0 && "Cannot allocate the got multiple times");
GOTBase = new unsigned char[sizeof(void*) * 8192];
HasGOT = true;
}
void DefaultJITMemoryManager::SetDlsymTable(void *ptr) {
DlsymTable = ptr;
}
DefaultJITMemoryManager::~DefaultJITMemoryManager() {
for (unsigned i = 0, e = Blocks.size(); i != e; ++i)
sys::Memory::ReleaseRWX(Blocks[i]);
delete[] GOTBase;
Blocks.clear();
}
unsigned char *DefaultJITMemoryManager::allocateStub(const GlobalValue* F,
unsigned StubSize,
unsigned Alignment) {
CurStubPtr -= StubSize;
CurStubPtr = (unsigned char*)(((intptr_t)CurStubPtr) &
~(intptr_t)(Alignment-1));
if (CurStubPtr < StubBase) {
fprintf(stderr, "JIT ran out of memory for function stubs!\n");
abort();
}
return CurStubPtr;
}
sys::MemoryBlock DefaultJITMemoryManager::getNewMemoryBlock(unsigned size) {
const sys::MemoryBlock *BOld = Blocks.empty() ? 0 : &Blocks.front();
std::string ErrMsg;
sys::MemoryBlock B = sys::Memory::AllocateRWX(size, BOld, &ErrMsg);
if (B.base() == 0) {
fprintf(stderr,
"Allocation failed when allocating new memory in the JIT\n%s\n",
ErrMsg.c_str());
abort();
}
Blocks.push_back(B);
return B;
}
JITMemoryManager *JITMemoryManager::CreateDefaultMemManager() {
return new DefaultJITMemoryManager();
}