diff --git a/src/alloy/backend/x64/lowering/lowering_sequences.cc b/src/alloy/backend/x64/lowering/lowering_sequences.cc index 9e9bebdfd..a5c41f5a0 100644 --- a/src/alloy/backend/x64/lowering/lowering_sequences.cc +++ b/src/alloy/backend/x64/lowering/lowering_sequences.cc @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -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; }); diff --git a/src/alloy/backend/x64/x64_emitter.cc b/src/alloy/backend/x64/x64_emitter.cc index 2e17e1543..9b68e16be 100644 --- a/src/alloy/backend/x64/x64_emitter.cc +++ b/src/alloy/backend/x64/x64_emitter.cc @@ -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. diff --git a/src/alloy/backend/x64/x64_function.h b/src/alloy/backend/x64/x64_function.h index cf06bb6c4..e879a72c7 100644 --- a/src/alloy/backend/x64/x64_function.h +++ b/src/alloy/backend/x64/x64_function.h @@ -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: diff --git a/src/alloy/frontend/ppc/ppc_context.h b/src/alloy/frontend/ppc/ppc_context.h index 92d6d2877..5bc5f159e 100644 --- a/src/alloy/frontend/ppc/ppc_context.h +++ b/src/alloy/frontend/ppc/ppc_context.h @@ -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;