Fixing tail calls.
This commit is contained in:
parent
aadf92e4ea
commit
bbf3b4bdab
|
@ -10,6 +10,7 @@
|
||||||
#include <alloy/backend/x64/lowering/lowering_sequences.h>
|
#include <alloy/backend/x64/lowering/lowering_sequences.h>
|
||||||
|
|
||||||
#include <alloy/backend/x64/x64_emitter.h>
|
#include <alloy/backend/x64/x64_emitter.h>
|
||||||
|
#include <alloy/backend/x64/x64_function.h>
|
||||||
#include <alloy/backend/x64/lowering/lowering_table.h>
|
#include <alloy/backend/x64/lowering/lowering_table.h>
|
||||||
#include <alloy/runtime/symbol_info.h>
|
#include <alloy/runtime/symbol_info.h>
|
||||||
#include <alloy/runtime/runtime.h>
|
#include <alloy/runtime/runtime.h>
|
||||||
|
@ -35,7 +36,7 @@ void Dummy() {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintString(void* raw_context, uint8_t* membase, const char* str) {
|
void PrintString(void* raw_context, const char* str) {
|
||||||
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
fprintf(stdout, "XE[t] :%d: %s\n", thread_state->GetThreadID(), str);
|
fprintf(stdout, "XE[t] :%d: %s\n", thread_state->GetThreadID(), str);
|
||||||
|
@ -43,48 +44,74 @@ void PrintString(void* raw_context, uint8_t* membase, const char* str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(benvanik): fancy stuff.
|
// TODO(benvanik): fancy stuff.
|
||||||
void CallThunk(void* raw_context, uint8_t* membase,
|
void* ResolveFunctionSymbol(void* raw_context, FunctionInfo* symbol_info) {
|
||||||
FunctionInfo* symbol_info) {
|
|
||||||
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
|
|
||||||
Function* fn = NULL;
|
Function* fn = NULL;
|
||||||
thread_state->runtime()->ResolveFunction(symbol_info->address(), &fn);
|
thread_state->runtime()->ResolveFunction(symbol_info->address(), &fn);
|
||||||
XEASSERTNOTNULL(fn);
|
XEASSERTNOTNULL(fn);
|
||||||
fn->Call(thread_state);
|
XEASSERT(fn->type() == Function::USER_FUNCTION);
|
||||||
|
auto x64_fn = (X64Function*)fn;
|
||||||
|
return x64_fn->machine_code();
|
||||||
|
}
|
||||||
|
void* ResolveFunctionAddress(void* raw_context, uint64_t target_address) {
|
||||||
|
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
||||||
|
auto thread_state = *((ThreadState**)raw_context);
|
||||||
|
|
||||||
|
Function* fn = NULL;
|
||||||
|
thread_state->runtime()->ResolveFunction(target_address, &fn);
|
||||||
|
XEASSERTNOTNULL(fn);
|
||||||
|
XEASSERTALWAYS();
|
||||||
|
//fn->Call(thread_state);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
void IssueCall(X64Emitter& e, FunctionInfo* symbol_info, uint32_t flags) {
|
void IssueCall(X64Emitter& e, FunctionInfo* symbol_info, uint32_t flags) {
|
||||||
e.mov(e.r8, (uint64_t)symbol_info);
|
// If we are an extern function, we can directly insert a call.
|
||||||
e.mov(e.rax, (uint64_t)CallThunk);
|
auto fn = symbol_info->function();
|
||||||
|
if (fn && fn->type() == Function::EXTERN_FUNCTION) {
|
||||||
|
auto extern_fn = (ExternFunction*)fn;
|
||||||
|
e.mov(e.rdx, (uint64_t)extern_fn->arg0());
|
||||||
|
e.mov(e.r8, (uint64_t)extern_fn->arg1());
|
||||||
|
e.mov(e.rax, (uint64_t)extern_fn->handler());
|
||||||
|
} else {
|
||||||
|
// Generic call, resolve address.
|
||||||
|
// TODO(benvanik): caching/etc. For now this makes debugging easier.
|
||||||
|
e.mov(e.rdx, (uint64_t)symbol_info);
|
||||||
|
e.mov(e.rax, (uint64_t)ResolveFunctionSymbol);
|
||||||
|
e.call(e.rax);
|
||||||
|
e.mov(e.rcx, e.qword[e.rsp + 0]);
|
||||||
|
e.mov(e.rdx, e.qword[e.rcx + 8]); // membase
|
||||||
|
}
|
||||||
if (flags & CALL_TAIL) {
|
if (flags & CALL_TAIL) {
|
||||||
|
// TODO(benvanik): adjust stack?
|
||||||
|
e.add(e.rsp, 0x40);
|
||||||
e.jmp(e.rax);
|
e.jmp(e.rax);
|
||||||
} else {
|
} else {
|
||||||
e.call(e.rax);
|
e.call(e.rax);
|
||||||
e.mov(e.rdx, e.qword[e.rsp + 8]);
|
|
||||||
e.mov(e.rcx, e.qword[e.rsp + 0]);
|
e.mov(e.rcx, e.qword[e.rsp + 0]);
|
||||||
|
e.mov(e.rdx, e.qword[e.rcx + 8]); // membase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IndirectCallThunk(void* raw_context, uint8_t* membase,
|
|
||||||
uint64_t target_address) {
|
|
||||||
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
|
||||||
auto thread_state = *((ThreadState**)raw_context);
|
|
||||||
XEASSERTALWAYS();
|
|
||||||
}
|
|
||||||
void IssueCallIndirect(X64Emitter& e, Value* target, uint32_t flags) {
|
void IssueCallIndirect(X64Emitter& e, Value* target, uint32_t flags) {
|
||||||
Reg64 r;
|
Reg64 r;
|
||||||
e.BeginOp(target, r, 0);
|
e.BeginOp(target, r, 0);
|
||||||
if (r != e.r8) {
|
if (r != e.rdx) {
|
||||||
e.mov(e.r8, r);
|
e.mov(e.rdx, r);
|
||||||
}
|
}
|
||||||
e.EndOp(r);
|
e.EndOp(r);
|
||||||
e.mov(e.rax, (uint64_t)IndirectCallThunk);
|
e.mov(e.rax, (uint64_t)ResolveFunctionAddress);
|
||||||
|
e.call(e.rax);
|
||||||
|
e.mov(e.rcx, e.qword[e.rsp + 0]);
|
||||||
|
e.mov(e.rdx, e.qword[e.rcx + 8]); // membase
|
||||||
if (flags & CALL_TAIL) {
|
if (flags & CALL_TAIL) {
|
||||||
|
// TODO(benvanik): adjust stack?
|
||||||
|
e.add(e.rsp, 0x40);
|
||||||
e.jmp(e.rax);
|
e.jmp(e.rax);
|
||||||
} else {
|
} else {
|
||||||
e.sub(e.rsp, 0x20);
|
|
||||||
e.call(e.rax);
|
e.call(e.rax);
|
||||||
e.add(e.rsp, 0x20);
|
e.mov(e.rcx, e.qword[e.rsp + 0]);
|
||||||
|
e.mov(e.rdx, e.qword[e.rcx + 8]); // membase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,11 +541,11 @@ void alloy::backend::x64::lowering::RegisterSequences(LoweringTable* table) {
|
||||||
// TODO(benvanik): pass through.
|
// TODO(benvanik): pass through.
|
||||||
auto str = (const char*)i->src1.offset;
|
auto str = (const char*)i->src1.offset;
|
||||||
auto str_copy = xestrdupa(str);
|
auto str_copy = xestrdupa(str);
|
||||||
e.mov(e.r8, (uint64_t)str_copy);
|
e.mov(e.rdx, (uint64_t)str_copy);
|
||||||
e.mov(e.rax, (uint64_t)PrintString);
|
e.mov(e.rax, (uint64_t)PrintString);
|
||||||
e.call(e.rax);
|
e.call(e.rax);
|
||||||
e.mov(e.rdx, e.qword[e.rsp + 8]);
|
|
||||||
e.mov(e.rcx, e.qword[e.rsp + 0]);
|
e.mov(e.rcx, e.qword[e.rsp + 0]);
|
||||||
|
e.mov(e.rdx, e.qword[e.rcx + 8]); // membase
|
||||||
i = e.Advance(i);
|
i = e.Advance(i);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -128,7 +128,6 @@ int X64Emitter::Emit(HIRBuilder* builder) {
|
||||||
const bool emit_prolog = true;
|
const bool emit_prolog = true;
|
||||||
const size_t stack_size = 64;
|
const size_t stack_size = 64;
|
||||||
if (emit_prolog) {
|
if (emit_prolog) {
|
||||||
mov(qword[rsp + 16], rdx);
|
|
||||||
mov(qword[rsp + 8], rcx);
|
mov(qword[rsp + 8], rcx);
|
||||||
sub(rsp, stack_size);
|
sub(rsp, stack_size);
|
||||||
mov(qword[rsp + 8 * 0], rbx);
|
mov(qword[rsp + 8 * 0], rbx);
|
||||||
|
@ -138,6 +137,10 @@ int X64Emitter::Emit(HIRBuilder* builder) {
|
||||||
mov(qword[rsp + 8 * 4], r15);
|
mov(qword[rsp + 8 * 4], r15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// membase stays in rdx. If we evict it (like on function calls) we
|
||||||
|
// must put it back.
|
||||||
|
mov(rdx, qword[rcx + 8]);
|
||||||
|
|
||||||
auto lowering_table = backend_->lowering_table();
|
auto lowering_table = backend_->lowering_table();
|
||||||
|
|
||||||
// Body.
|
// Body.
|
||||||
|
|
|
@ -25,6 +25,9 @@ public:
|
||||||
X64Function(runtime::FunctionInfo* symbol_info);
|
X64Function(runtime::FunctionInfo* symbol_info);
|
||||||
virtual ~X64Function();
|
virtual ~X64Function();
|
||||||
|
|
||||||
|
void* machine_code() const { return machine_code_; }
|
||||||
|
size_t code_size() const { return code_size_; }
|
||||||
|
|
||||||
void Setup(void* machine_code, size_t code_size);
|
void Setup(void* machine_code, size_t code_size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -67,6 +67,8 @@ typedef struct XECACHEALIGN64 PPCContext_s {
|
||||||
// Must be stored at 0x0 for now.
|
// Must be stored at 0x0 for now.
|
||||||
// TODO(benvanik): find a nice way to describe this to the JIT.
|
// TODO(benvanik): find a nice way to describe this to the JIT.
|
||||||
runtime::ThreadState* thread_state;
|
runtime::ThreadState* thread_state;
|
||||||
|
// TODO(benvanik): this is getting nasty. Must be here.
|
||||||
|
uint8_t* membase;
|
||||||
|
|
||||||
// Most frequently used registers first.
|
// Most frequently used registers first.
|
||||||
uint64_t r[32]; // General purpose registers
|
uint64_t r[32]; // General purpose registers
|
||||||
|
@ -196,7 +198,6 @@ typedef struct XECACHEALIGN64 PPCContext_s {
|
||||||
|
|
||||||
// Runtime-specific data pointer. Used on callbacks to get access to the
|
// Runtime-specific data pointer. Used on callbacks to get access to the
|
||||||
// current runtime and its data.
|
// current runtime and its data.
|
||||||
uint8_t* membase;
|
|
||||||
runtime::Runtime* runtime;
|
runtime::Runtime* runtime;
|
||||||
volatile int suspend_flag;
|
volatile int suspend_flag;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue