From: Chris Lattner Date: Fri, 9 May 2003 03:30:07 +0000 (+0000) Subject: Add support for function stubs, which allow calling functions which need to X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=6125fddb52c3d821a4e4c000cbd210428b0009f6;p=oota-llvm.git Add support for function stubs, which allow calling functions which need to have an address available, but have not yet been code generated. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@6059 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/ExecutionEngine/JIT/Callback.cpp b/lib/ExecutionEngine/JIT/Callback.cpp index fc13a10855b..0cb612c77f8 100644 --- a/lib/ExecutionEngine/JIT/Callback.cpp +++ b/lib/ExecutionEngine/JIT/Callback.cpp @@ -1,12 +1,14 @@ //===-- Callback.cpp - Trap handler for function resolution ---------------===// // -// This file defines the SIGSEGV handler which is invoked when a reference to a -// non-codegen'd function is found. +// This file defines the handler which is invoked when a reference to a +// non-codegen'd function is found. This file defines target specific code +// which is used by the JIT. // //===----------------------------------------------------------------------===// #include "VM.h" #include "Support/Statistic.h" +#include "llvm/CodeGen/MachineCodeEmitter.h" #include static VM *TheVM = 0; @@ -21,6 +23,7 @@ void VM::CompilationCallback() { assert(StackPtr[1] == RetAddr && "Could not find return address on the stack!"); + bool isStub = ((unsigned char*)RetAddr)[0] == 0xCD; // Interrupt marker? // The call instruction should have pushed the return value onto the stack... RetAddr -= 4; // Backtrack to the reference itself... @@ -39,6 +42,14 @@ void VM::CompilationCallback() { // the call. *(unsigned*)RetAddr = NewVal-RetAddr-4; + if (isStub) { + // If this is a stub, rewrite the call into an unconditional branch + // instruction so that two return addresses are not pushed onto the stack + // when the requested function finally gets called. This also makes the + // 0xCD byte (interrupt) dead, so the marker doesn't effect anything. + ((unsigned char*)RetAddr)[-1] = 0xE9; + } + // Change the return address to reexecute the call instruction... StackPtr[1] -= 5; #else @@ -46,6 +57,24 @@ void VM::CompilationCallback() { #endif } +/// emitStubForFunction - This virtual method is used by the JIT when it needs +/// to emit the address of a function for a function whose code has not yet +/// been generated. In order to do this, it generates a stub which jumps to +/// the lazy function compiler, which will eventually get fixed to call the +/// function directly. +/// +void *VM::emitStubForFunction(const Function &F) { +#if defined(i386) || defined(__i386__) || defined(__x86__) + MCE->startFunctionStub(F, 6); + MCE->emitByte(0xE8); // Call with 32 bit pc-rel destination... + MCE->emitGlobalAddress((GlobalValue*)&F, true); + MCE->emitByte(0xCD); // Interrupt - Just a marker identifying the stub! + return MCE->finishFunctionStub(F); +#else + abort(); +#endif +} + void VM::registerCallback() { TheVM = this; } diff --git a/lib/ExecutionEngine/JIT/JIT.h b/lib/ExecutionEngine/JIT/JIT.h index 9080b3be045..17c4ddd3dce 100644 --- a/lib/ExecutionEngine/JIT/JIT.h +++ b/lib/ExecutionEngine/JIT/JIT.h @@ -60,6 +60,14 @@ private: void *getPointerToFunction(const Function *F); void registerCallback(); + + /// emitStubForFunction - This method is used by the JIT when it needs to emit + /// the address of a function for a function whose code has not yet been + /// generated. In order to do this, it generates a stub which jumps to the + /// lazy function compiler, which will eventually get fixed to call the + /// function directly. + /// + void *emitStubForFunction(const Function &F); }; #endif diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp index 69e2453090d..f6f6b30f0f2 100644 --- a/lib/ExecutionEngine/JIT/JITEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp @@ -19,8 +19,11 @@ namespace { class Emitter : public MachineCodeEmitter { VM &TheVM; - unsigned char *CurBlock; - unsigned char *CurByte; + 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; @@ -32,6 +35,8 @@ namespace { 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); @@ -52,14 +57,16 @@ MachineCodeEmitter *VM::createEmitter(VM &V) { #include #include -static void *getMemory() { - return mmap(0, 4096*8, PROT_READ|PROT_WRITE|PROT_EXEC, +// 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(); + CurBlock = (unsigned char *)getMemory(8); CurByte = CurBlock; // Start writing at the beginning of the fn. TheVM.addGlobalMapping(F.getFunction(), CurBlock); } @@ -100,6 +107,24 @@ void Emitter::startBasicBlock(MachineBasicBlock &BB) { } +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. +} + +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 } diff --git a/lib/ExecutionEngine/JIT/VM.cpp b/lib/ExecutionEngine/JIT/VM.cpp index 836e00e4fa8..6fd366ede51 100644 --- a/lib/ExecutionEngine/JIT/VM.cpp +++ b/lib/ExecutionEngine/JIT/VM.cpp @@ -83,7 +83,10 @@ void *VM::getPointerToFunction(const Function *F) { static bool isAlreadyCodeGenerating = false; if (isAlreadyCodeGenerating) { - assert(0 && "Recursive function stubs not handled yet!"); + // Generate a function stub instead of reentering... + void *SAddr = emitStubForFunction(*F); + assert(SAddr && "Target machine doesn't support function stub generation!"); + return SAddr; } // FIXME: JIT all of the functions in the module. Eventually this will JIT diff --git a/lib/ExecutionEngine/JIT/VM.h b/lib/ExecutionEngine/JIT/VM.h index 9080b3be045..17c4ddd3dce 100644 --- a/lib/ExecutionEngine/JIT/VM.h +++ b/lib/ExecutionEngine/JIT/VM.h @@ -60,6 +60,14 @@ private: void *getPointerToFunction(const Function *F); void registerCallback(); + + /// emitStubForFunction - This method is used by the JIT when it needs to emit + /// the address of a function for a function whose code has not yet been + /// generated. In order to do this, it generates a stub which jumps to the + /// lazy function compiler, which will eventually get fixed to call the + /// function directly. + /// + void *emitStubForFunction(const Function &F); }; #endif