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/x64_emitter.h>
|
||||
#include <alloy/backend/x64/x64_function.h>
|
||||
#include <alloy/backend/x64/lowering/lowering_table.h>
|
||||
#include <alloy/runtime/symbol_info.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?
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
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.
|
||||
void CallThunk(void* raw_context, uint8_t* membase,
|
||||
FunctionInfo* symbol_info) {
|
||||
void* ResolveFunctionSymbol(void* raw_context, FunctionInfo* symbol_info) {
|
||||
// TODO(benvanik): generate this thunk at runtime? or a shim?
|
||||
auto thread_state = *((ThreadState**)raw_context);
|
||||
|
||||
Function* fn = NULL;
|
||||
thread_state->runtime()->ResolveFunction(symbol_info->address(), &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) {
|
||||
e.mov(e.r8, (uint64_t)symbol_info);
|
||||
e.mov(e.rax, (uint64_t)CallThunk);
|
||||
// If we are an extern function, we can directly insert a call.
|
||||
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) {
|
||||
// TODO(benvanik): adjust stack?
|
||||
e.add(e.rsp, 0x40);
|
||||
e.jmp(e.rax);
|
||||
} else {
|
||||
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.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) {
|
||||
Reg64 r;
|
||||
e.BeginOp(target, r, 0);
|
||||
if (r != e.r8) {
|
||||
e.mov(e.r8, r);
|
||||
if (r != e.rdx) {
|
||||
e.mov(e.rdx, 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) {
|
||||
// TODO(benvanik): adjust stack?
|
||||
e.add(e.rsp, 0x40);
|
||||
e.jmp(e.rax);
|
||||
} else {
|
||||
e.sub(e.rsp, 0x20);
|
||||
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.
|
||||
auto str = (const char*)i->src1.offset;
|
||||
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.call(e.rax);
|
||||
e.mov(e.rdx, e.qword[e.rsp + 8]);
|
||||
e.mov(e.rcx, e.qword[e.rsp + 0]);
|
||||
e.mov(e.rdx, e.qword[e.rcx + 8]); // membase
|
||||
i = e.Advance(i);
|
||||
return true;
|
||||
});
|
||||
|
|
|
@ -128,7 +128,6 @@ int X64Emitter::Emit(HIRBuilder* builder) {
|
|||
const bool emit_prolog = true;
|
||||
const size_t stack_size = 64;
|
||||
if (emit_prolog) {
|
||||
mov(qword[rsp + 16], rdx);
|
||||
mov(qword[rsp + 8], rcx);
|
||||
sub(rsp, stack_size);
|
||||
mov(qword[rsp + 8 * 0], rbx);
|
||||
|
@ -138,6 +137,10 @@ int X64Emitter::Emit(HIRBuilder* builder) {
|
|||
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();
|
||||
|
||||
// Body.
|
||||
|
|
|
@ -25,6 +25,9 @@ public:
|
|||
X64Function(runtime::FunctionInfo* symbol_info);
|
||||
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);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -67,6 +67,8 @@ typedef struct XECACHEALIGN64 PPCContext_s {
|
|||
// Must be stored at 0x0 for now.
|
||||
// TODO(benvanik): find a nice way to describe this to the JIT.
|
||||
runtime::ThreadState* thread_state;
|
||||
// TODO(benvanik): this is getting nasty. Must be here.
|
||||
uint8_t* membase;
|
||||
|
||||
// Most frequently used registers first.
|
||||
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
|
||||
// current runtime and its data.
|
||||
uint8_t* membase;
|
||||
runtime::Runtime* runtime;
|
||||
volatile int suspend_flag;
|
||||
|
||||
|
|
Loading…
Reference in New Issue