X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=lib%2FExecutionEngine%2FJIT%2FJITEmitter.cpp;h=6dd2d56f7f2044a490baca5c3c5f8e62213a3c62;hb=e738656c0b380e5059cb522927aeb49554d01e46;hp=d6f75c08f7643a7275077dd922e6da7a2129302b;hpb=c309a7627ce8849b63f6f297ca319d582f4ae066;p=oota-llvm.git diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp index d6f75c08f76..6dd2d56f7f2 100644 --- a/lib/ExecutionEngine/JIT/JITEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp @@ -5,41 +5,155 @@ // //===----------------------------------------------------------------------===// +#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; - unsigned char *CurBlock; - unsigned char *CurByte; - - std::vector > BBRefs; - std::map BBLocations; + // 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; + + // 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); }; } @@ -47,38 +161,18 @@ MachineCodeEmitter *VM::createEmitter(VM &V) { return new Emitter(V); } - -#define _POSIX_MAPPED_FILES -#include -#include - -static void *getMemory() { - return mmap(0, 4096*8, PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); -} - - void Emitter::startFunction(MachineFunction &F) { - CurBlock = (unsigned char *)getMemory(); - 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"); } @@ -88,67 +182,79 @@ 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; + CurByte = CurBlock = MemMgr.allocateStub(StubSize); } +void *Emitter::finishFunctionStub(const Function &F) { + NumBytes += CurByte-CurBlock; + DEBUG(std::cerr << "Finished CodeGen of [0x" << std::hex + << (unsigned)(intptr_t)CurBlock + << std::dec << "] Function stub for: " << F.getName() + << ": " << CurByte-CurBlock << " bytes of text\n"); + std::swap(CurBlock, SavedCurBlock); + CurByte = SavedCurByte; + return SavedCurBlock; +} 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. -// -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::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); } -// emitAddress - Emit an address in either direct or PCRelative form... +// getConstantPoolEntryAddress - Return the address of the 'ConstantNum' entry +// in the constant pool that was last emitted with the 'emitConstantPool' +// method. // -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; +uint64_t Emitter::getConstantPoolEntryAddress(unsigned ConstantNum) { + assert(ConstantNum < ConstantPoolAddresses.size() && + "Invalid ConstantPoolIndex!"); + return (intptr_t)ConstantPoolAddresses[ConstantNum]; } -void Emitter::emitGlobalAddress(GlobalValue *V, bool isPCRelative) { - if (isPCRelative) { // must be a call, this is a major hack! - // FIXME: Try looking up the function to see if it is already compiled! - TheVM.addFunctionRef(CurByte, cast(V)); - - // Delayed resolution... - emitAddress((void*)VM::CompilationCallback, isPCRelative); - } else { - emitAddress(TheVM.getPointerToGlobal(V), isPCRelative); - } +// getCurrentPCValue - This returns the address that the next emitted byte +// will be output to. +// +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); + } }