Author: lattner Date: Wed Dec 5 17:39:57 2007 New Revision: 44640 URL: http://llvm.org/viewvc/llvm-project?rev=44640&view=rev Log: split the JIT memory management code out from the main JIT logic into its own JITMemoryManager interface. There is no functionality change with this patch.
Added: llvm/trunk/include/llvm/ExecutionEngine/JITMemoryManager.h llvm/trunk/lib/ExecutionEngine/JIT/JITMemoryManager.cpp Modified: llvm/trunk/include/llvm/ExecutionEngine/JIT.h llvm/trunk/lib/ExecutionEngine/JIT/JITEmitter.cpp Modified: llvm/trunk/include/llvm/ExecutionEngine/JIT.h URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/JIT.h?rev=44640&r1=44639&r2=44640&view=diff ============================================================================== --- llvm/trunk/include/llvm/ExecutionEngine/JIT.h (original) +++ llvm/trunk/include/llvm/ExecutionEngine/JIT.h Wed Dec 5 17:39:57 2007 @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef EXECUTION_ENGINE_JIT_H -#define EXECUTION_ENGINE_JIT_H +#ifndef LLVM_EXECUTION_ENGINE_JIT_H +#define LLVM_EXECUTION_ENGINE_JIT_H #include "llvm/ExecutionEngine/ExecutionEngine.h" #include <cstdlib> Added: llvm/trunk/include/llvm/ExecutionEngine/JITMemoryManager.h URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/JITMemoryManager.h?rev=44640&view=auto ============================================================================== --- llvm/trunk/include/llvm/ExecutionEngine/JITMemoryManager.h (added) +++ llvm/trunk/include/llvm/ExecutionEngine/JITMemoryManager.h Wed Dec 5 17:39:57 2007 @@ -0,0 +1,96 @@ +//===-- JITMemoryManager.h - Interface JIT uses to Allocate Mem -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the JITMemoryManagerInterface +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTION_ENGINE_JIT_H +#define LLVM_EXECUTION_ENGINE_JIT_H + +#include "llvm/Support/DataTypes.h" + +namespace llvm { + class Function; + +/// 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 +/// clients that have a strong desire to control how the layout of JIT'd memory +/// works. +class JITMemoryManager { +protected: + bool HasGOT; +public: + JITMemoryManager() : HasGOT(false) {} + virtual ~JITMemoryManager(); + + /// CreateDefaultMemManager - This is used to create the default + /// JIT Memory Manager if the client does not provide one to the JIT. + static JITMemoryManager *CreateDefaultMemManager(); + + //===--------------------------------------------------------------------===// + // Global Offset Table Management + //===--------------------------------------------------------------------===// + + /// AllocateGOT - If the current table requires a Global Offset Table, this + /// method is invoked to allocate it. This method is required to set HasGOT + /// to true. + virtual void AllocateGOT() = 0; + + /// isManagingGOT - Return true if the AllocateGOT method is called. + /// + bool isManagingGOT() const { + return HasGOT; + } + + /// getGOTBase - If this is managing a Global Offset Table, this method should + /// return a pointer to its base. + virtual unsigned char *getGOTBase() const = 0; + + //===--------------------------------------------------------------------===// + // Main Allocation Functions + //===--------------------------------------------------------------------===// + + /// 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. 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 unsigned char *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 + /// either needs to be lazily compiled or is a native function that exists too + /// far away from the call site to work, this method will be used to make a + /// thunk for it. The stub should be "close" to the current function body, + /// but should not be included in the 'actualsize' returned by + /// startFunctionBody. + virtual unsigned char *allocateStub(unsigned StubSize, unsigned Alignment) =0; + + + /// endFunctionBody - This method is called when the JIT is done codegen'ing + /// the specified function. At this point we know the size of the JIT + /// compiled function. This passes in FunctionStart (which was returned by + /// the startFunctionBody method) and FunctionEnd which is a pointer to the + /// actual end of the function. This method should mark the space allocated + /// and remember where it is in case the client wants to deallocate it. + virtual void endFunctionBody(const Function *F, unsigned char *FunctionStart, + unsigned char *FunctionEnd) = 0; + + /// deallocateMemForFunction - Free JIT memory for the specified function. + /// This is never called when the JIT is currently emitting a function. + virtual void deallocateMemForFunction(const Function *F) = 0; +}; + +} // end namespace llvm. + +#endif Modified: llvm/trunk/lib/ExecutionEngine/JIT/JITEmitter.cpp URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/JIT/JITEmitter.cpp?rev=44640&r1=44639&r2=44640&view=diff ============================================================================== --- llvm/trunk/lib/ExecutionEngine/JIT/JITEmitter.cpp (original) +++ llvm/trunk/lib/ExecutionEngine/JIT/JITEmitter.cpp Wed Dec 5 17:39:57 2007 @@ -22,7 +22,7 @@ #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineRelocation.h" -#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" #include "llvm/Target/TargetData.h" #include "llvm/Target/TargetJITInfo.h" #include "llvm/Target/TargetMachine.h" @@ -30,7 +30,6 @@ #include "llvm/Support/MutexGuard.h" #include "llvm/System/Disassembler.h" #include "llvm/ADT/Statistic.h" -#include "llvm/System/Memory.h" #include <algorithm> using namespace llvm; @@ -38,395 +37,6 @@ STATISTIC(NumRelos, "Number of relocations applied"); static JIT *TheJIT = 0; -//===----------------------------------------------------------------------===// -// JITMemoryManager code. -// -namespace { - /// MemoryRangeHeader - For a range of memory, this is the header that we put - /// on the block of memory. It is carefully crafted to be one word of memory. - /// Allocated blocks have just this header, free'd blocks have FreeRangeHeader - /// which starts with this. - struct FreeRangeHeader; - struct MemoryRangeHeader { - /// ThisAllocated - This is true if this block is currently allocated. If - /// not, this can be converted to a FreeRangeHeader. - unsigned ThisAllocated : 1; - - /// PrevAllocated - Keep track of whether the block immediately before us is - /// allocated. If not, the word immediately before this header is the size - /// of the previous block. - unsigned PrevAllocated : 1; - - /// BlockSize - This is the size in bytes of this memory block, - /// including this header. - uintptr_t BlockSize : (sizeof(intptr_t)*8 - 2); - - - /// getBlockAfter - Return the memory block immediately after this one. - /// - MemoryRangeHeader &getBlockAfter() const { - return *(MemoryRangeHeader*)((char*)this+BlockSize); - } - - /// getFreeBlockBefore - If the block before this one is free, return it, - /// otherwise return null. - FreeRangeHeader *getFreeBlockBefore() const { - if (PrevAllocated) return 0; - intptr_t PrevSize = ((intptr_t *)this)[-1]; - return (FreeRangeHeader*)((char*)this-PrevSize); - } - - /// FreeBlock - Turn an allocated block into a free block, adjusting - /// bits in the object headers, and adding an end of region memory block. - FreeRangeHeader *FreeBlock(FreeRangeHeader *FreeList); - - /// TrimAllocationToSize - If this allocated block is significantly larger - /// than NewSize, split it into two pieces (where the former is NewSize - /// bytes, including the header), and add the new block to the free list. - FreeRangeHeader *TrimAllocationToSize(FreeRangeHeader *FreeList, - uint64_t NewSize); - }; - - /// FreeRangeHeader - For a memory block that isn't already allocated, this - /// keeps track of the current block and has a pointer to the next free block. - /// Free blocks are kept on a circularly linked list. - struct FreeRangeHeader : public MemoryRangeHeader { - FreeRangeHeader *Prev; - FreeRangeHeader *Next; - - /// getMinBlockSize - Get the minimum size for a memory block. Blocks - /// smaller than this size cannot be created. - static unsigned getMinBlockSize() { - return sizeof(FreeRangeHeader)+sizeof(intptr_t); - } - - /// SetEndOfBlockSizeMarker - The word at the end of every free block is - /// known to be the size of the free block. Set it for this block. - 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; - } - - /// GrowBlock - The block after this block just got deallocated. Merge it - /// into the current block. - void GrowBlock(uintptr_t NewSize); - - /// AllocateBlock - Mark this entire block allocated, updating freelists - /// etc. This returns a pointer to the circular free-list. - FreeRangeHeader *AllocateBlock(); - }; -} - - -/// AllocateBlock - Mark this entire block allocated, updating freelists -/// etc. This returns a pointer to the circular free-list. -FreeRangeHeader *FreeRangeHeader::AllocateBlock() { - assert(!ThisAllocated && !getBlockAfter().PrevAllocated && - "Cannot allocate an allocated block!"); - // Mark this block allocated. - ThisAllocated = 1; - getBlockAfter().PrevAllocated = 1; - - // Remove it from the free list. - return RemoveFromFreeList(); -} - -/// FreeBlock - Turn an allocated block into a free block, adjusting -/// bits in the object headers, and adding an end of region memory block. -/// If possible, coalesce this block with neighboring blocks. Return the -/// FreeRangeHeader to allocate from. -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 the block after this one is free, merge it into this block. - if (!FollowingBlock->ThisAllocated) { - FreeRangeHeader &FollowingFreeBlock = *(FreeRangeHeader *)FollowingBlock; - // "FreeList" always needs to be a valid free block. If we're about to - // coalesce with it, update our notion of what the free list is. - if (&FollowingFreeBlock == FreeList) { - FreeList = FollowingFreeBlock.Next; - FreeListToReturn = 0; - assert(&FollowingFreeBlock != FreeList && "No tombstone block?"); - } - FollowingFreeBlock.RemoveFromFreeList(); - - // Include the following block into this one. - BlockSize += FollowingFreeBlock.BlockSize; - FollowingBlock = &FollowingFreeBlock.getBlockAfter(); - - // Tell the block after the block we are coalescing that this block is - // allocated. - FollowingBlock->PrevAllocated = 1; - } - - assert(FollowingBlock->ThisAllocated && "Missed coalescing?"); - - if (FreeRangeHeader *PrevFreeBlock = getFreeBlockBefore()) { - PrevFreeBlock->GrowBlock(PrevFreeBlock->BlockSize + BlockSize); - return FreeListToReturn ? FreeListToReturn : PrevFreeBlock; - } - - // Otherwise, mark this block free. - FreeRangeHeader &FreeBlock = *(FreeRangeHeader*)this; - FollowingBlock->PrevAllocated = 0; - FreeBlock.ThisAllocated = 0; - - // Link this into the linked list of free blocks. - FreeBlock.AddToFreeList(FreeList); - - // Add a marker at the end of the block, indicating the size of this free - // block. - FreeBlock.SetEndOfBlockSizeMarker(); - return FreeListToReturn ? FreeListToReturn : &FreeBlock; -} - -/// GrowBlock - The block after this block just got deallocated. Merge it -/// into the current block. -void FreeRangeHeader::GrowBlock(uintptr_t NewSize) { - assert(NewSize > BlockSize && "Not growing block?"); - BlockSize = NewSize; - SetEndOfBlockSizeMarker(); - getBlockAfter().PrevAllocated = 0; -} - -/// TrimAllocationToSize - If this allocated block is significantly larger -/// than NewSize, split it into two pieces (where the former is NewSize -/// bytes, including the header), and add the new block to the free list. -FreeRangeHeader *MemoryRangeHeader:: -TrimAllocationToSize(FreeRangeHeader *FreeList, uint64_t NewSize) { - assert(ThisAllocated && getBlockAfter().PrevAllocated && - "Cannot deallocate part of an allocated block!"); - - // Round up size for alignment of header. - unsigned HeaderAlign = __alignof(FreeRangeHeader); - NewSize = (NewSize+ (HeaderAlign-1)) & ~(HeaderAlign-1); - - // Size is now the size of the block we will remove from the start of the - // current block. - assert(NewSize <= BlockSize && - "Allocating more space from this block than exists!"); - - // If splitting this block will cause the remainder to be too small, do not - // split the block. - if (BlockSize <= NewSize+FreeRangeHeader::getMinBlockSize()) - return FreeList; - - // Otherwise, we splice the required number of bytes out of this block, form - // a new block immediately after it, then mark this block allocated. - MemoryRangeHeader &FormerNextBlock = getBlockAfter(); - - // Change the size of this block. - BlockSize = NewSize; - - // Get the new block we just sliced out and turn it into a free block. - 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 { - /// JITMemoryManager - Manage memory for the JIT code generation in a logical, - /// sane way. 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. This never bothers to release the memory, because when - /// we are ready to destroy the JIT, the program exits. - class JITMemoryManager { - 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; - - unsigned char *CurStubPtr, *StubBase; - unsigned char *GOTBase; // Target Specific reserved memory - - // Centralize memory block allocation. - sys::MemoryBlock getNewMemoryBlock(unsigned size); - - std::map<const Function*, MemoryRangeHeader*> FunctionBlocks; - public: - JITMemoryManager(bool useGOT); - ~JITMemoryManager(); - - inline unsigned char *allocateStub(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. - unsigned char *startFunctionBody(uintptr_t &ActualSize) { - CurBlock = FreeMemoryList; - - // Allocate the entire memory block. - FreeMemoryList = FreeMemoryList->AllocateBlock(); - ActualSize = CurBlock->BlockSize-sizeof(MemoryRangeHeader); - return (unsigned char *)(CurBlock+1); - } - - /// endFunctionBody - The function F is now allocated, and takes the memory - /// in the range [FunctionStart,FunctionEnd). - 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; - - // Release the memory at the end of this block that isn't needed. - FreeMemoryList =CurBlock->TrimAllocationToSize(FreeMemoryList, BlockSize); - } - - unsigned char *getGOTBase() const { - return GOTBase; - } - bool isManagingGOT() const { - return GOTBase != NULL; - } - - /// deallocateMemForFunction - Deallocate all memory for the specified - /// function body. - void deallocateMemForFunction(const Function *F) { - std::map<const Function*, MemoryRangeHeader*>::iterator - I = FunctionBlocks.find(F); - if (I == FunctionBlocks.end()) return; - - // Find the block that is allocated for this function. - MemoryRangeHeader *MemRange = I->second; - assert(MemRange->ThisAllocated && "Block isn't allocated!"); - - // Fill the buffer with garbage! - DEBUG(memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange))); - - // Free the memory. - FreeMemoryList = MemRange->FreeBlock(FreeMemoryList); - - // Finally, remove this entry from FunctionBlocks. - FunctionBlocks.erase(I); - } - }; -} - -JITMemoryManager::JITMemoryManager(bool useGOT) { - // Allocate a 16M block of memory for functions. - sys::MemoryBlock MemBlock = getNewMemoryBlock(16 << 20); - - unsigned char *MemBase = reinterpret_cast<unsigned char*>(MemBlock.base()); - - // Allocate stubs backwards from the base, allocate functions forward - // from the base. - StubBase = MemBase; - CurStubPtr = MemBase + 512*1024; // Use 512k for stubs, working backwards. - - // We set up the memory chunk with 4 mem regions, like this: - // [ START - // [ Free #0 ] -> Large space to allocate functions from. - // [ Allocated #1 ] -> Tiny space to separate regions. - // [ Free #2 ] -> Tiny space so there is always at least 1 free block. - // [ Allocated #3 ] -> Tiny space to prevent looking past end of block. - // END ] - // - // The last three blocks are never deallocated or touched. - - // Add MemoryRangeHeader to the end of the memory region, indicating that - // the space after the block of memory is allocated. This is block #3. - MemoryRangeHeader *Mem3 = (MemoryRangeHeader*)(MemBase+MemBlock.size())-1; - Mem3->ThisAllocated = 1; - Mem3->PrevAllocated = 0; - Mem3->BlockSize = 0; - - /// Add a tiny free region so that the free list always has one entry. - FreeRangeHeader *Mem2 = - (FreeRangeHeader *)(((char*)Mem3)-FreeRangeHeader::getMinBlockSize()); - Mem2->ThisAllocated = 0; - Mem2->PrevAllocated = 1; - Mem2->BlockSize = FreeRangeHeader::getMinBlockSize(); - Mem2->SetEndOfBlockSizeMarker(); - Mem2->Prev = Mem2; // Mem2 *is* the free list for now. - Mem2->Next = Mem2; - - /// Add a tiny allocated region so that Mem2 is never coalesced away. - MemoryRangeHeader *Mem1 = (MemoryRangeHeader*)Mem2-1; - Mem1->ThisAllocated = 1; - Mem1->PrevAllocated = 0; - 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*)CurStubPtr; - Mem0->ThisAllocated = 0; - Mem0->PrevAllocated = 1; - Mem0->BlockSize = (char*)Mem1-(char*)Mem0; - Mem0->SetEndOfBlockSizeMarker(); - Mem0->AddToFreeList(Mem2); - - // Start out with the freelist pointing to Mem0. - FreeMemoryList = Mem0; - - // Allocate the GOT. - GOTBase = NULL; - if (useGOT) GOTBase = new unsigned char[sizeof(void*) * 8192]; -} - -JITMemoryManager::~JITMemoryManager() { - for (unsigned i = 0, e = Blocks.size(); i != e; ++i) - sys::Memory::ReleaseRWX(Blocks[i]); - - delete[] GOTBase; - Blocks.clear(); -} - -unsigned char *JITMemoryManager::allocateStub(unsigned StubSize, - unsigned Alignment) { - CurStubPtr -= StubSize; - CurStubPtr = (unsigned char*)(((intptr_t)CurStubPtr) & - ~(intptr_t)(Alignment-1)); - if (CurStubPtr < StubBase) { - // FIXME: allocate a new block - cerr << "JIT ran out of memory for function stubs!\n"; - abort(); - } - return CurStubPtr; -} - -sys::MemoryBlock JITMemoryManager::getNewMemoryBlock(unsigned size) { - // Allocate a new block close to the last one. - 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) { - cerr << "Allocation failed when allocating new memory in the JIT\n"; - cerr << ErrMsg << "\n"; - abort(); - } - Blocks.push_back(B); - return B; -} //===----------------------------------------------------------------------===// // JIT lazy compilation code. @@ -504,9 +114,9 @@ } /// getGOTIndexForAddress - Return a new or existing index in the GOT for - /// and address. This function only manages slots, it does not manage the + /// an address. This function only manages slots, it does not manage the /// contents of the slots or the memory associated with the GOT. - unsigned getGOTIndexForAddr(void* addr); + unsigned getGOTIndexForAddr(void *addr); /// JITCompilerFn - This function is called to resolve a stub to a compiled /// address. If the LLVM Function corresponding to the stub has not yet @@ -597,7 +207,6 @@ revGOTMap[addr] = idx; DOUT << "Adding GOT entry " << idx << " for addr " << addr << "\n"; - // ((void**)MemMgr.getGOTBase())[idx] = addr; } return idx; } @@ -669,7 +278,7 @@ /// JITEmitter - The JIT implementation of the MachineCodeEmitter, which is /// used to output functions to memory for execution. class JITEmitter : public MachineCodeEmitter { - JITMemoryManager MemMgr; + JITMemoryManager *MemMgr; // When outputting a function stub in the context of some other function, we // save BufferBegin/BufferEnd/CurBufferPtr here. @@ -703,9 +312,15 @@ /// Resolver - This contains info about the currently resolved functions. JITResolver Resolver; public: - JITEmitter(JIT &jit) - : MemMgr(jit.getJITInfo().needsGOT()), Resolver(jit) { - if (MemMgr.isManagingGOT()) DOUT << "JIT is managing a GOT\n"; + JITEmitter(JIT &jit) : Resolver(jit) { + MemMgr = JITMemoryManager::CreateDefaultMemManager(); + if (jit.getJITInfo().needsGOT()) { + MemMgr->AllocateGOT(); + DOUT << "JIT is managing a GOT\n"; + } + } + ~JITEmitter() { + delete MemMgr; } JITResolver &getJITResolver() { return Resolver; } @@ -742,7 +357,7 @@ /// deallocateMemForFunction - Deallocate all memory for the specified /// function body. void deallocateMemForFunction(Function *F) { - MemMgr.deallocateMemForFunction(F); + MemMgr->deallocateMemForFunction(F); } private: void *getPointerToGlobal(GlobalValue *GV, void *Reference, bool NoNeedStub); @@ -783,7 +398,8 @@ void JITEmitter::startFunction(MachineFunction &F) { uintptr_t ActualSize; - BufferBegin = CurBufferPtr = MemMgr.startFunctionBody(ActualSize); + BufferBegin = CurBufferPtr = MemMgr->startFunctionBody(F.getFunction(), + ActualSize); BufferEnd = BufferBegin+ActualSize; // Ensure the constant pool/jump table info is at least 4-byte aligned. @@ -814,7 +430,7 @@ (unsigned char *)TheJIT->getPointerToGlobalIfAvailable(F.getFunction()); unsigned char *FnEnd = CurBufferPtr; - MemMgr.endFunctionBody(F.getFunction(), BufferBegin, FnEnd); + MemMgr->endFunctionBody(F.getFunction(), BufferBegin, FnEnd); NumBytes += FnEnd-FnStart; if (!Relocations.empty()) { @@ -847,29 +463,29 @@ // if we are managing the GOT and the relocation wants an index, // give it one - if (MemMgr.isManagingGOT() && MR.isGOTRelative()) { + if (MR.isGOTRelative() && MemMgr->isManagingGOT()) { unsigned idx = Resolver.getGOTIndexForAddr(ResultPtr); MR.setGOTIndex(idx); - if (((void**)MemMgr.getGOTBase())[idx] != ResultPtr) { + if (((void**)MemMgr->getGOTBase())[idx] != ResultPtr) { DOUT << "GOT was out of date for " << ResultPtr - << " pointing at " << ((void**)MemMgr.getGOTBase())[idx] + << " pointing at " << ((void**)MemMgr->getGOTBase())[idx] << "\n"; - ((void**)MemMgr.getGOTBase())[idx] = ResultPtr; + ((void**)MemMgr->getGOTBase())[idx] = ResultPtr; } } } TheJIT->getJITInfo().relocate(BufferBegin, &Relocations[0], - Relocations.size(), MemMgr.getGOTBase()); + Relocations.size(), MemMgr->getGOTBase()); } // Update the GOT entry for F to point to the new code. - if (MemMgr.isManagingGOT()) { + if (MemMgr->isManagingGOT()) { unsigned idx = Resolver.getGOTIndexForAddr((void*)BufferBegin); - if (((void**)MemMgr.getGOTBase())[idx] != (void*)BufferBegin) { + if (((void**)MemMgr->getGOTBase())[idx] != (void*)BufferBegin) { DOUT << "GOT was out of date for " << (void*)BufferBegin - << " pointing at " << ((void**)MemMgr.getGOTBase())[idx] << "\n"; - ((void**)MemMgr.getGOTBase())[idx] = (void*)BufferBegin; + << " pointing at " << ((void**)MemMgr->getGOTBase())[idx] << "\n"; + ((void**)MemMgr->getGOTBase())[idx] = (void*)BufferBegin; } } @@ -976,7 +592,7 @@ SavedBufferEnd = BufferEnd; SavedCurBufferPtr = CurBufferPtr; - BufferBegin = CurBufferPtr = MemMgr.allocateStub(StubSize, Alignment); + BufferBegin = CurBufferPtr = MemMgr->allocateStub(StubSize, Alignment); BufferEnd = BufferBegin+StubSize+1; } Added: llvm/trunk/lib/ExecutionEngine/JIT/JITMemoryManager.cpp URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/JIT/JITMemoryManager.cpp?rev=44640&view=auto ============================================================================== --- llvm/trunk/lib/ExecutionEngine/JIT/JITMemoryManager.cpp (added) +++ llvm/trunk/lib/ExecutionEngine/JIT/JITMemoryManager.cpp Wed Dec 5 17:39:57 2007 @@ -0,0 +1,427 @@ +//===-- JITMemoryManager.cpp - Memory Allocator for JIT'd code ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the DefaultJITMemoryManager class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/Support/Compiler.h" +#include "llvm/System/Memory.h" +#include <map> +#include <vector> +using namespace llvm; + + +JITMemoryManager::~JITMemoryManager() {} + +//===----------------------------------------------------------------------===// +// Memory Block Implementation. +//===----------------------------------------------------------------------===// + +namespace { + /// MemoryRangeHeader - For a range of memory, this is the header that we put + /// on the block of memory. It is carefully crafted to be one word of memory. + /// Allocated blocks have just this header, free'd blocks have FreeRangeHeader + /// which starts with this. + struct FreeRangeHeader; + struct MemoryRangeHeader { + /// ThisAllocated - This is true if this block is currently allocated. If + /// not, this can be converted to a FreeRangeHeader. + unsigned ThisAllocated : 1; + + /// PrevAllocated - Keep track of whether the block immediately before us is + /// allocated. If not, the word immediately before this header is the size + /// of the previous block. + unsigned PrevAllocated : 1; + + /// BlockSize - This is the size in bytes of this memory block, + /// including this header. + uintptr_t BlockSize : (sizeof(intptr_t)*8 - 2); + + + /// getBlockAfter - Return the memory block immediately after this one. + /// + MemoryRangeHeader &getBlockAfter() const { + return *(MemoryRangeHeader*)((char*)this+BlockSize); + } + + /// getFreeBlockBefore - If the block before this one is free, return it, + /// otherwise return null. + FreeRangeHeader *getFreeBlockBefore() const { + if (PrevAllocated) return 0; + intptr_t PrevSize = ((intptr_t *)this)[-1]; + return (FreeRangeHeader*)((char*)this-PrevSize); + } + + /// FreeBlock - Turn an allocated block into a free block, adjusting + /// bits in the object headers, and adding an end of region memory block. + FreeRangeHeader *FreeBlock(FreeRangeHeader *FreeList); + + /// TrimAllocationToSize - If this allocated block is significantly larger + /// than NewSize, split it into two pieces (where the former is NewSize + /// bytes, including the header), and add the new block to the free list. + FreeRangeHeader *TrimAllocationToSize(FreeRangeHeader *FreeList, + uint64_t NewSize); + }; + + /// FreeRangeHeader - For a memory block that isn't already allocated, this + /// keeps track of the current block and has a pointer to the next free block. + /// Free blocks are kept on a circularly linked list. + struct FreeRangeHeader : public MemoryRangeHeader { + FreeRangeHeader *Prev; + FreeRangeHeader *Next; + + /// getMinBlockSize - Get the minimum size for a memory block. Blocks + /// smaller than this size cannot be created. + static unsigned getMinBlockSize() { + return sizeof(FreeRangeHeader)+sizeof(intptr_t); + } + + /// SetEndOfBlockSizeMarker - The word at the end of every free block is + /// known to be the size of the free block. Set it for this block. + 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; + } + + /// GrowBlock - The block after this block just got deallocated. Merge it + /// into the current block. + void GrowBlock(uintptr_t NewSize); + + /// AllocateBlock - Mark this entire block allocated, updating freelists + /// etc. This returns a pointer to the circular free-list. + FreeRangeHeader *AllocateBlock(); + }; +} + + +/// AllocateBlock - Mark this entire block allocated, updating freelists +/// etc. This returns a pointer to the circular free-list. +FreeRangeHeader *FreeRangeHeader::AllocateBlock() { + assert(!ThisAllocated && !getBlockAfter().PrevAllocated && + "Cannot allocate an allocated block!"); + // Mark this block allocated. + ThisAllocated = 1; + getBlockAfter().PrevAllocated = 1; + + // Remove it from the free list. + return RemoveFromFreeList(); +} + +/// FreeBlock - Turn an allocated block into a free block, adjusting +/// bits in the object headers, and adding an end of region memory block. +/// If possible, coalesce this block with neighboring blocks. Return the +/// FreeRangeHeader to allocate from. +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 the block after this one is free, merge it into this block. + if (!FollowingBlock->ThisAllocated) { + FreeRangeHeader &FollowingFreeBlock = *(FreeRangeHeader *)FollowingBlock; + // "FreeList" always needs to be a valid free block. If we're about to + // coalesce with it, update our notion of what the free list is. + if (&FollowingFreeBlock == FreeList) { + FreeList = FollowingFreeBlock.Next; + FreeListToReturn = 0; + assert(&FollowingFreeBlock != FreeList && "No tombstone block?"); + } + FollowingFreeBlock.RemoveFromFreeList(); + + // Include the following block into this one. + BlockSize += FollowingFreeBlock.BlockSize; + FollowingBlock = &FollowingFreeBlock.getBlockAfter(); + + // Tell the block after the block we are coalescing that this block is + // allocated. + FollowingBlock->PrevAllocated = 1; + } + + assert(FollowingBlock->ThisAllocated && "Missed coalescing?"); + + if (FreeRangeHeader *PrevFreeBlock = getFreeBlockBefore()) { + PrevFreeBlock->GrowBlock(PrevFreeBlock->BlockSize + BlockSize); + return FreeListToReturn ? FreeListToReturn : PrevFreeBlock; + } + + // Otherwise, mark this block free. + FreeRangeHeader &FreeBlock = *(FreeRangeHeader*)this; + FollowingBlock->PrevAllocated = 0; + FreeBlock.ThisAllocated = 0; + + // Link this into the linked list of free blocks. + FreeBlock.AddToFreeList(FreeList); + + // Add a marker at the end of the block, indicating the size of this free + // block. + FreeBlock.SetEndOfBlockSizeMarker(); + return FreeListToReturn ? FreeListToReturn : &FreeBlock; +} + +/// GrowBlock - The block after this block just got deallocated. Merge it +/// into the current block. +void FreeRangeHeader::GrowBlock(uintptr_t NewSize) { + assert(NewSize > BlockSize && "Not growing block?"); + BlockSize = NewSize; + SetEndOfBlockSizeMarker(); + getBlockAfter().PrevAllocated = 0; +} + +/// TrimAllocationToSize - If this allocated block is significantly larger +/// than NewSize, split it into two pieces (where the former is NewSize +/// bytes, including the header), and add the new block to the free list. +FreeRangeHeader *MemoryRangeHeader:: +TrimAllocationToSize(FreeRangeHeader *FreeList, uint64_t NewSize) { + assert(ThisAllocated && getBlockAfter().PrevAllocated && + "Cannot deallocate part of an allocated block!"); + + // Round up size for alignment of header. + unsigned HeaderAlign = __alignof(FreeRangeHeader); + NewSize = (NewSize+ (HeaderAlign-1)) & ~(HeaderAlign-1); + + // Size is now the size of the block we will remove from the start of the + // current block. + assert(NewSize <= BlockSize && + "Allocating more space from this block than exists!"); + + // If splitting this block will cause the remainder to be too small, do not + // split the block. + if (BlockSize <= NewSize+FreeRangeHeader::getMinBlockSize()) + return FreeList; + + // Otherwise, we splice the required number of bytes out of this block, form + // a new block immediately after it, then mark this block allocated. + MemoryRangeHeader &FormerNextBlock = getBlockAfter(); + + // Change the size of this block. + BlockSize = NewSize; + + // Get the new block we just sliced out and turn it into a free block. + 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; +} + +//===----------------------------------------------------------------------===// +// Memory Block Implementation. +//===----------------------------------------------------------------------===// + +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 VISIBILITY_HIDDEN DefaultJITMemoryManager : public JITMemoryManager { + 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; + + unsigned char *CurStubPtr, *StubBase; + unsigned char *GOTBase; // Target Specific reserved memory + + // Centralize memory block allocation. + sys::MemoryBlock getNewMemoryBlock(unsigned size); + + std::map<const Function*, MemoryRangeHeader*> FunctionBlocks; + public: + DefaultJITMemoryManager(); + ~DefaultJITMemoryManager(); + + void AllocateGOT(); + + unsigned char *allocateStub(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. + unsigned char *startFunctionBody(const Function *F, uintptr_t &ActualSize) { + CurBlock = FreeMemoryList; + + // Allocate the entire memory block. + FreeMemoryList = FreeMemoryList->AllocateBlock(); + ActualSize = CurBlock->BlockSize-sizeof(MemoryRangeHeader); + return (unsigned char *)(CurBlock+1); + } + + /// endFunctionBody - The function F is now allocated, and takes the memory + /// in the range [FunctionStart,FunctionEnd). + 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; + + // Release the memory at the end of this block that isn't needed. + FreeMemoryList =CurBlock->TrimAllocationToSize(FreeMemoryList, BlockSize); + } + + unsigned char *getGOTBase() const { + return GOTBase; + } + + /// deallocateMemForFunction - Deallocate all memory for the specified + /// function body. + void deallocateMemForFunction(const Function *F) { + std::map<const Function*, MemoryRangeHeader*>::iterator + I = FunctionBlocks.find(F); + if (I == FunctionBlocks.end()) return; + + // Find the block that is allocated for this function. + MemoryRangeHeader *MemRange = I->second; + assert(MemRange->ThisAllocated && "Block isn't allocated!"); + + // Fill the buffer with garbage! +#ifndef NDEBUG + memset(MemRange+1, 0xCD, MemRange->BlockSize-sizeof(*MemRange)); +#endif + + // Free the memory. + FreeMemoryList = MemRange->FreeBlock(FreeMemoryList); + + // Finally, remove this entry from FunctionBlocks. + FunctionBlocks.erase(I); + } + }; +} + +DefaultJITMemoryManager::DefaultJITMemoryManager() { + // Allocate a 16M block of memory for functions. + sys::MemoryBlock MemBlock = getNewMemoryBlock(16 << 20); + + unsigned char *MemBase = reinterpret_cast<unsigned char*>(MemBlock.base()); + + // Allocate stubs backwards from the base, allocate functions forward + // from the base. + StubBase = MemBase; + CurStubPtr = MemBase + 512*1024; // Use 512k for stubs, working backwards. + + // We set up the memory chunk with 4 mem regions, like this: + // [ START + // [ Free #0 ] -> Large space to allocate functions from. + // [ Allocated #1 ] -> Tiny space to separate regions. + // [ Free #2 ] -> Tiny space so there is always at least 1 free block. + // [ Allocated #3 ] -> Tiny space to prevent looking past end of block. + // END ] + // + // The last three blocks are never deallocated or touched. + + // Add MemoryRangeHeader to the end of the memory region, indicating that + // the space after the block of memory is allocated. This is block #3. + MemoryRangeHeader *Mem3 = (MemoryRangeHeader*)(MemBase+MemBlock.size())-1; + Mem3->ThisAllocated = 1; + Mem3->PrevAllocated = 0; + Mem3->BlockSize = 0; + + /// Add a tiny free region so that the free list always has one entry. + FreeRangeHeader *Mem2 = + (FreeRangeHeader *)(((char*)Mem3)-FreeRangeHeader::getMinBlockSize()); + Mem2->ThisAllocated = 0; + Mem2->PrevAllocated = 1; + Mem2->BlockSize = FreeRangeHeader::getMinBlockSize(); + Mem2->SetEndOfBlockSizeMarker(); + Mem2->Prev = Mem2; // Mem2 *is* the free list for now. + Mem2->Next = Mem2; + + /// Add a tiny allocated region so that Mem2 is never coalesced away. + MemoryRangeHeader *Mem1 = (MemoryRangeHeader*)Mem2-1; + Mem1->ThisAllocated = 1; + Mem1->PrevAllocated = 0; + 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*)CurStubPtr; + Mem0->ThisAllocated = 0; + Mem0->PrevAllocated = 1; + Mem0->BlockSize = (char*)Mem1-(char*)Mem0; + Mem0->SetEndOfBlockSizeMarker(); + Mem0->AddToFreeList(Mem2); + + // Start out with the freelist pointing to Mem0. + FreeMemoryList = Mem0; + + GOTBase = NULL; +} + +void DefaultJITMemoryManager::AllocateGOT() { + assert(GOTBase == 0 && "Cannot allocate the got multiple times"); + GOTBase = new unsigned char[sizeof(void*) * 8192]; + HasGOT = true; +} + + +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(unsigned StubSize, + unsigned Alignment) { + CurStubPtr -= StubSize; + CurStubPtr = (unsigned char*)(((intptr_t)CurStubPtr) & + ~(intptr_t)(Alignment-1)); + if (CurStubPtr < StubBase) { + // FIXME: allocate a new block + fprintf(stderr, "JIT ran out of memory for function stubs!\n"); + abort(); + } + 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.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(); +} _______________________________________________ llvm-commits mailing list llvm-commits@cs.uiuc.edu http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits