mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-13 20:32:21 +00:00
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
This commit is contained in:
parent
70bca51f92
commit
6125fddb52
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user