X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FExecutionEngine%2FJIT%2FJITEmitter.cpp;h=6dd2d56f7f2044a490baca5c3c5f8e62213a3c62;hb=e738656c0b380e5059cb522927aeb49554d01e46;hp=e6fe09b135e8b4c991be72a94c46860ba0687701;hpb=abb027cf412944db4d27579ba3ae00717d23c25e;p=oota-llvm.git diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp index e6fe09b135e..6dd2d56f7f2 100644 --- a/lib/ExecutionEngine/JIT/JITEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp @@ -5,87 +5,174 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "jit" +#ifndef _POSIX_MAPPED_FILES +#define _POSIX_MAPPED_FILES +#endif #include "VM.h" #include "llvm/CodeGen/MachineCodeEmitter.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/Target/TargetData.h" -#include "llvm/Function.h" +#include "llvm/Module.h" +#include "Support/Debug.h" #include "Support/Statistic.h" +#include "Config/unistd.h" +#include "Config/sys/mman.h" namespace { - Statistic<> NumBytes("jello", "Number of bytes of machine code compiled"); + Statistic<> NumBytes("jit", "Number of bytes of machine code compiled"); + VM *TheVM = 0; + + /// 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 { + unsigned char *MemBase; // Base of block of memory, start of stub mem + unsigned char *FunctionBase; // Start of the function body area + unsigned char *CurStubPtr, *CurFunctionPtr; + public: + JITMemoryManager(); + + inline unsigned char *allocateStub(unsigned StubSize); + inline unsigned char *startFunctionBody(); + inline void endFunctionBody(unsigned char *FunctionEnd); + }; +} + +// getMemory - Return a pointer to the specified number of bytes, which is +// mapped as executable readable and writable. +static void *getMemory(unsigned NumBytes) { + if (NumBytes == 0) return 0; + static const long pageSize = sysconf(_SC_PAGESIZE); + unsigned NumPages = (NumBytes+pageSize-1)/pageSize; + +#if defined(i386) || defined(__i386__) || defined(__x86__) + /* Linux and *BSD tend to have these flags named differently. */ +#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) +# define MAP_ANONYMOUS MAP_ANON +#endif /* defined(MAP_ANON) && !defined(MAP_ANONYMOUS) */ +#elif defined(sparc) || defined(__sparc__) || defined(__sparcv9) +/* nothing */ +#else + std::cerr << "This architecture is not supported by the JIT!\n"; + abort(); +#endif + +#if defined(__linux__) +#define fd 0 +#else +#define fd -1 +#endif + + unsigned mmapFlags = MAP_PRIVATE|MAP_ANONYMOUS; +#ifdef MAP_NORESERVE + mmapFlags |= MAP_NORESERVE; +#endif + + void *pa = mmap(0, pageSize*NumPages, PROT_READ|PROT_WRITE|PROT_EXEC, + mmapFlags, fd, 0); + if (pa == MAP_FAILED) { + perror("mmap"); + abort(); + } + return pa; +} + +JITMemoryManager::JITMemoryManager() { + // Allocate a 16M block of memory... + MemBase = (unsigned char*)getMemory(16 << 20); + FunctionBase = MemBase + 512*1024; // Use 512k for stubs + + // Allocate stubs backwards from the function base, allocate functions forward + // from the function base. + CurStubPtr = CurFunctionPtr = FunctionBase; +} + +unsigned char *JITMemoryManager::allocateStub(unsigned StubSize) { + CurStubPtr -= StubSize; + if (CurStubPtr < MemBase) { + std::cerr << "JIT ran out of memory for function stubs!\n"; + abort(); + } + return CurStubPtr; +} + +unsigned char *JITMemoryManager::startFunctionBody() { + // Round up to an even multiple of 4 bytes, this should eventually be target + // specific. + return (unsigned char*)(((intptr_t)CurFunctionPtr + 3) & ~3); +} + +void JITMemoryManager::endFunctionBody(unsigned char *FunctionEnd) { + assert(FunctionEnd > CurFunctionPtr); + CurFunctionPtr = FunctionEnd; +} + + +namespace { + /// Emitter - The JIT implementation of the MachineCodeEmitter, which is used + /// to output functions to memory for execution. class Emitter : public MachineCodeEmitter { - VM &TheVM; + JITMemoryManager MemMgr; + // CurBlock - The start of the current block of memory. CurByte - The + // current byte being emitted to. unsigned char *CurBlock, *CurByte; // When outputting a function stub in the context of some other function, we // save CurBlock and CurByte here. unsigned char *SavedCurBlock, *SavedCurByte; - - std::vector > BBRefs; - std::map BBLocations; + + // ConstantPoolAddresses - Contains the location for each entry in the + // constant pool. std::vector ConstantPoolAddresses; public: - Emitter(VM &vm) : TheVM(vm) {} + Emitter(VM &vm) { TheVM = &vm; } virtual void startFunction(MachineFunction &F); virtual void finishFunction(MachineFunction &F); virtual void emitConstantPool(MachineConstantPool *MCP); - virtual void startBasicBlock(MachineBasicBlock &BB); virtual void startFunctionStub(const Function &F, unsigned StubSize); virtual void* finishFunctionStub(const Function &F); virtual void emitByte(unsigned char B); - virtual void emitPCRelativeDisp(Value *V); - virtual void emitGlobalAddress(GlobalValue *V, bool isPCRelative); - virtual void emitGlobalAddress(const std::string &Name, bool isPCRelative); - virtual void emitFunctionConstantValueAddress(unsigned ConstantNum, - int Offset); - private: - void emitAddress(void *Addr, bool isPCRelative); + virtual void emitWord(unsigned W); + + virtual uint64_t getGlobalValueAddress(GlobalValue *V); + virtual uint64_t getGlobalValueAddress(const std::string &Name); + virtual uint64_t getConstantPoolEntryAddress(unsigned Entry); + virtual uint64_t getCurrentPCValue(); + + // forceCompilationOf - Force the compilation of the specified function, and + // return its address, because we REALLY need the address now. + // + // FIXME: This is JIT specific! + // + virtual uint64_t forceCompilationOf(Function *F); }; } -MachineCodeEmitter *VM::createX86Emitter(VM &V) { +MachineCodeEmitter *VM::createEmitter(VM &V) { return new Emitter(V); } - -#define _POSIX_MAPPED_FILES -#include -#include - -// FIXME: This should be rewritten to support a real memory manager for -// executable memory pages! -static void *getMemory(unsigned NumPages) { - return mmap(0, 4096*NumPages, PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); -} - - void Emitter::startFunction(MachineFunction &F) { - CurBlock = (unsigned char *)getMemory(8); - CurByte = CurBlock; // Start writing at the beginning of the fn. - TheVM.addGlobalMapping(F.getFunction(), CurBlock); + CurByte = CurBlock = MemMgr.startFunctionBody(); + TheVM->addGlobalMapping(F.getFunction(), CurBlock); } void Emitter::finishFunction(MachineFunction &F) { + MemMgr.endFunctionBody(CurByte); ConstantPoolAddresses.clear(); - for (unsigned i = 0, e = BBRefs.size(); i != e; ++i) { - unsigned Location = BBLocations[BBRefs[i].first]; - unsigned *Ref = BBRefs[i].second; - *Ref = Location-(unsigned)(intptr_t)Ref-4; - } - BBRefs.clear(); - BBLocations.clear(); - NumBytes += CurByte-CurBlock; - DEBUG(std::cerr << "Finished CodeGen of [0x" << std::hex - << (unsigned)(intptr_t)CurBlock - << std::dec << "] Function: " << F.getFunction()->getName() + DEBUG(std::cerr << "Finished CodeGen of [" << (void*)CurBlock + << "] Function: " << F.getFunction()->getName() << ": " << CurByte-CurBlock << " bytes of text\n"); } @@ -95,23 +182,15 @@ void Emitter::emitConstantPool(MachineConstantPool *MCP) { // For now we just allocate some memory on the heap, this can be // dramatically improved. const Type *Ty = ((Value*)Constants[i])->getType(); - void *Addr = malloc(TheVM.getTargetData().getTypeSize(Ty)); - TheVM.InitializeMemory(Constants[i], Addr); + void *Addr = malloc(TheVM->getTargetData().getTypeSize(Ty)); + TheVM->InitializeMemory(Constants[i], Addr); ConstantPoolAddresses.push_back(Addr); } } - -void Emitter::startBasicBlock(MachineBasicBlock &BB) { - BBLocations[BB.getBasicBlock()] = (unsigned)(intptr_t)CurByte; -} - - void Emitter::startFunctionStub(const Function &F, unsigned StubSize) { SavedCurBlock = CurBlock; SavedCurByte = CurByte; - // FIXME: this is a huge waste of memory. - CurBlock = (unsigned char *)getMemory((StubSize+4095)/4096); - CurByte = CurBlock; // Start writing at the beginning of the fn. + CurByte = CurBlock = MemMgr.allocateStub(StubSize); } void *Emitter::finishFunctionStub(const Function &F) { @@ -129,55 +208,53 @@ void Emitter::emitByte(unsigned char B) { *CurByte++ = B; // Write the byte to memory } +void Emitter::emitWord(unsigned W) { + // This won't work if the endianness of the host and target don't agree! (For + // a JIT this can't happen though. :) + *(unsigned*)CurByte = W; + CurByte += sizeof(unsigned); +} -// emitPCRelativeDisp - For functions, just output a displacement that will -// cause a reference to the zero page, which will cause a seg-fault, causing -// things to get resolved on demand. Keep track of these markers. -// -// For basic block references, keep track of where the references are so they -// may be patched up when the basic block is defined. +uint64_t Emitter::getGlobalValueAddress(GlobalValue *V) { + // Try looking up the function to see if it is already compiled, if not return + // 0. + return (intptr_t)TheVM->getPointerToGlobalIfAvailable(V); +} +uint64_t Emitter::getGlobalValueAddress(const std::string &Name) { + return (intptr_t)TheVM->getPointerToNamedFunction(Name); +} + +// getConstantPoolEntryAddress - Return the address of the 'ConstantNum' entry +// in the constant pool that was last emitted with the 'emitConstantPool' +// method. // -void Emitter::emitPCRelativeDisp(Value *V) { - BasicBlock *BB = cast(V); // Keep track of reference... - BBRefs.push_back(std::make_pair(BB, (unsigned*)CurByte)); - CurByte += 4; +uint64_t Emitter::getConstantPoolEntryAddress(unsigned ConstantNum) { + assert(ConstantNum < ConstantPoolAddresses.size() && + "Invalid ConstantPoolIndex!"); + return (intptr_t)ConstantPoolAddresses[ConstantNum]; } -// emitAddress - Emit an address in either direct or PCRelative form... +// getCurrentPCValue - This returns the address that the next emitted byte +// will be output to. // -void Emitter::emitAddress(void *Addr, bool isPCRelative) { - if (isPCRelative) { - *(intptr_t*)CurByte = (intptr_t)Addr - (intptr_t)CurByte-4; - } else { - *(void**)CurByte = Addr; - } - CurByte += 4; -} - -void Emitter::emitGlobalAddress(GlobalValue *V, bool isPCRelative) { - if (isPCRelative) { // must be a call, this is a major hack! - // Try looking up the function to see if it is already compiled! - if (void *Addr = TheVM.getPointerToGlobalIfAvailable(V)) { - emitAddress(Addr, isPCRelative); - } else { // Function has not yet been code generated! - TheVM.addFunctionRef(CurByte, cast(V)); - - // Delayed resolution... - emitAddress((void*)VM::CompilationCallback, isPCRelative); - } - } else { - emitAddress(TheVM.getPointerToGlobal(V), isPCRelative); - } +uint64_t Emitter::getCurrentPCValue() { + return (intptr_t)CurByte; } -void Emitter::emitGlobalAddress(const std::string &Name, bool isPCRelative) { - emitAddress(TheVM.getPointerToNamedFunction(Name), isPCRelative); +uint64_t Emitter::forceCompilationOf(Function *F) { + return (intptr_t)TheVM->getPointerToFunction(F); } -void Emitter::emitFunctionConstantValueAddress(unsigned ConstantNum, - int Offset) { - assert(ConstantNum < ConstantPoolAddresses.size() && - "Invalid ConstantPoolIndex!"); - *(void**)CurByte = (char*)ConstantPoolAddresses[ConstantNum]+Offset; - CurByte += 4; +// getPointerToNamedFunction - This function is used as a global wrapper to +// VM::getPointerToNamedFunction for the purpose of resolving symbols when +// bugpoint is debugging the JIT. In that scenario, we are loading an .so and +// need to resolve function(s) that are being mis-codegenerated, so we need to +// resolve their addresses at runtime, and this is the way to do it. +extern "C" { + void *getPointerToNamedFunction(const char *Name) { + Module &M = TheVM->getModule(); + if (Function *F = M.getNamedFunction(Name)) + return TheVM->getPointerToFunction(F); + return TheVM->getPointerToNamedFunction(Name); + } }