aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorChris Lattner <sabre@nondot.org>2003-05-09 03:30:07 +0000
committerChris Lattner <sabre@nondot.org>2003-05-09 03:30:07 +0000
commit6125fddb52c3d821a4e4c000cbd210428b0009f6 (patch)
tree273ddc62df183c90ced316ce76b88dcdd3d2898b /lib
parent70bca51f92871c7f9d3eac9ab68292c149fab53c (diff)
downloadexternal_llvm-6125fddb52c3d821a4e4c000cbd210428b0009f6.zip
external_llvm-6125fddb52c3d821a4e4c000cbd210428b0009f6.tar.gz
external_llvm-6125fddb52c3d821a4e4c000cbd210428b0009f6.tar.bz2
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
Diffstat (limited to 'lib')
-rw-r--r--lib/ExecutionEngine/JIT/Callback.cpp33
-rw-r--r--lib/ExecutionEngine/JIT/JIT.h8
-rw-r--r--lib/ExecutionEngine/JIT/JITEmitter.cpp35
-rw-r--r--lib/ExecutionEngine/JIT/VM.cpp5
-rw-r--r--lib/ExecutionEngine/JIT/VM.h8
5 files changed, 81 insertions, 8 deletions
diff --git a/lib/ExecutionEngine/JIT/Callback.cpp b/lib/ExecutionEngine/JIT/Callback.cpp
index fc13a10..0cb612c 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 <iostream>
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 9080b3b..17c4ddd 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 69e2453..f6f6b30 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<std::pair<BasicBlock*, unsigned *> > BBRefs;
std::map<BasicBlock*, unsigned> 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 <unistd.h>
#include <sys/mman.h>
-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 836e00e..6fd366e 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 9080b3b..17c4ddd 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