Fixing tail calls in the jit.

This commit is contained in:
Ben Vanik 2014-02-06 21:53:31 -08:00
parent 0d88e83daa
commit 5309356908
6 changed files with 47 additions and 6 deletions

View File

@ -245,17 +245,30 @@ void IssueCall(X64Emitter& e, FunctionInfo* symbol_info, uint32_t flags) {
// Actually jump/call to rax. // Actually jump/call to rax.
if (flags & CALL_TAIL) { if (flags & CALL_TAIL) {
// Pass the callers return address over.
e.mov(e.rdx, e.qword[e.rsp + StackLayout::GUEST_RET_ADDR]);
e.add(e.rsp, (uint32_t)e.stack_size()); e.add(e.rsp, (uint32_t)e.stack_size());
e.jmp(e.rax); e.jmp(e.rax);
} else { } else {
// Return address is from the previous SET_RETURN_ADDRESS.
e.mov(e.rdx, e.qword[e.rsp + StackLayout::GUEST_CALL_RET_ADDR]);
e.call(e.rax); e.call(e.rax);
} }
} }
void IssueCallIndirect(X64Emitter& e, Value* target, uint32_t flags) { void IssueCallIndirect(X64Emitter& e, Value* target, uint32_t flags) {
// Resolve address to the function to call and store in rax.
// TODO(benvanik): caching/etc. For now this makes debugging easier.
Reg64 r; Reg64 r;
e.BeginOp(target, r, 0); e.BeginOp(target, r, 0);
// Check if return.
if (flags & CALL_POSSIBLE_RETURN) {
e.cmp(r.cvt32(), e.dword[e.rsp + StackLayout::GUEST_RET_ADDR]);
e.je("epilog", CodeGenerator::T_NEAR);
}
// Resolve address to the function to call and store in rax.
// TODO(benvanik): caching/etc. For now this makes debugging easier.
if (r != e.rdx) { if (r != e.rdx) {
e.mov(e.rdx, r); e.mov(e.rdx, r);
} }
@ -264,9 +277,15 @@ void IssueCallIndirect(X64Emitter& e, Value* target, uint32_t flags) {
// Actually jump/call to rax. // Actually jump/call to rax.
if (flags & CALL_TAIL) { if (flags & CALL_TAIL) {
// Pass the callers return address over.
e.mov(e.rdx, e.qword[e.rsp + StackLayout::GUEST_RET_ADDR]);
e.add(e.rsp, (uint32_t)e.stack_size()); e.add(e.rsp, (uint32_t)e.stack_size());
e.jmp(e.rax); e.jmp(e.rax);
} else { } else {
// Return address is from the previous SET_RETURN_ADDRESS.
e.mov(e.rdx, e.qword[e.rsp + StackLayout::GUEST_CALL_RET_ADDR]);
e.call(e.rax); e.call(e.rax);
} }
} }
@ -434,6 +453,14 @@ table->AddSequence(OPCODE_RETURN_TRUE, [](X64Emitter& e, Instr*& i) {
return true; return true;
}); });
table->AddSequence(OPCODE_SET_RETURN_ADDRESS, [](X64Emitter& e, Instr*& i) {
XEASSERT(i->src1.value->IsConstant());
e.mov(e.qword[e.rsp + StackLayout::GUEST_CALL_RET_ADDR],
i->src1.value->AsUint64());
i = e.Advance(i);
return true;
});
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Branches // Branches
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------

View File

@ -29,10 +29,12 @@ namespace lowering {
#define DFLUSH() #define DFLUSH()
#define DPRINT #define DPRINT
#define TARGET_THREAD 1
#define IFLUSH() fflush(stdout) #define IFLUSH() fflush(stdout)
#define IPRINT if (thread_state->thread_id() == 1) printf #define IPRINT if (thread_state->thread_id() == TARGET_THREAD) printf
#define DFLUSH() fflush(stdout) #define DFLUSH() fflush(stdout)
#define DPRINT DFLUSH(); if (thread_state->thread_id() == 1) printf #define DPRINT DFLUSH(); if (thread_state->thread_id() == TARGET_THREAD) printf
void TraceString(void* raw_context, const char* str) { void TraceString(void* raw_context, const char* str) {

View File

@ -158,6 +158,10 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
if (emit_prolog) { if (emit_prolog) {
sub(rsp, (uint32_t)stack_size); sub(rsp, (uint32_t)stack_size);
mov(qword[rsp + StackLayout::GUEST_RCX_HOME], rcx); mov(qword[rsp + StackLayout::GUEST_RCX_HOME], rcx);
mov(qword[rsp + StackLayout::GUEST_RET_ADDR], rdx);
mov(qword[rsp + StackLayout::GUEST_CALL_RET_ADDR], 0);
// ReloadRDX:
mov(rdx, qword[rcx + 8]); // membase
} }
auto lowering_table = backend_->lowering_table(); auto lowering_table = backend_->lowering_table();

View File

@ -48,6 +48,6 @@ int X64Function::CallImpl(ThreadState* thread_state, uint64_t return_address) {
thunk( thunk(
machine_code_, machine_code_,
thread_state->raw_context(), thread_state->raw_context(),
thread_state->memory()->membase()); (void*)return_address);
return 0; return 0;
} }

View File

@ -34,6 +34,7 @@ HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
const size_t stack_size = StackLayout::THUNK_STACK_SIZE; const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
// rsp + 0 = return address // rsp + 0 = return address
mov(qword[rsp + 8 * 3], r8);
mov(qword[rsp + 8 * 2], rdx); mov(qword[rsp + 8 * 2], rdx);
mov(qword[rsp + 8 * 1], rcx); mov(qword[rsp + 8 * 1], rcx);
sub(rsp, stack_size); sub(rsp, stack_size);
@ -88,6 +89,7 @@ HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
add(rsp, stack_size); add(rsp, stack_size);
mov(rcx, qword[rsp + 8 * 1]); mov(rcx, qword[rsp + 8 * 1]);
mov(rdx, qword[rsp + 8 * 2]); mov(rdx, qword[rsp + 8 * 2]);
mov(r8, qword[rsp + 8 * 3]);
ret(); ret();
void* fn = Emplace(stack_size); void* fn = Emplace(stack_size);

View File

@ -104,6 +104,10 @@ namespace x64 {
* +------------------+ * +------------------+
* | rcx / context | rsp + 64 * | rcx / context | rsp + 64
* +------------------+ * +------------------+
* | guest ret addr | rsp + 72
* +------------------+
* | call ret addr | rsp + 80
* +------------------+
* ... locals ... * ... locals ...
* +------------------+ * +------------------+
* | (return address) | * | (return address) |
@ -115,8 +119,10 @@ class StackLayout {
public: public:
const static size_t THUNK_STACK_SIZE = 120; const static size_t THUNK_STACK_SIZE = 120;
const static size_t GUEST_STACK_SIZE = 72; const static size_t GUEST_STACK_SIZE = 88;
const static size_t GUEST_RCX_HOME = 64; const static size_t GUEST_RCX_HOME = 64;
const static size_t GUEST_RET_ADDR = 72;
const static size_t GUEST_CALL_RET_ADDR = 80;
}; };