Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x64 JIT: Call into MFPs without thunks #12

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 38 additions & 29 deletions src/jit_no_ir.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,32 @@ class EmitX64 {
BlockKey blk_key{};
bool unimplemented = false;

// Emit a call to a class member function, passing "this_object" (+ an adjustment if necessary)
// As the function's "this" pointer. Only works with classes with single, non-virtual
// inheritance, hence the static asserts. Those are all we need though, thankfully.
template <typename T>
void CallMemberFunction(T func, void* this_object) {
void* function_ptr;
uintptr_t this_ptr = reinterpret_cast<uintptr_t>(this_object);

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
static_assert(sizeof(T) == 8, "[x64 JIT] Invalid size for member function pointer");
std::memcpy(&function_ptr, &func, sizeof(T));
#else
static_assert(sizeof(T) == 16, "[x64 JIT] Invalid size for member function pointer");
uint64_t arr[2];
std::memcpy(arr, &func, sizeof(T));
// First 8 bytes correspond to the actual pointer to the function
function_ptr = reinterpret_cast<void*>(arr[0]);
// Next 8 bytes correspond to the "this" pointer adjustment
this_ptr += arr[1];
#endif

// Load the "this" pointer to arg1 and emit a call to the function
c.mov(ABI_PARAM1, this_ptr);
CallFarFunction(c, function_ptr);
}

void Reset() {
// Reset registers
regs.Reset();
Expand Down Expand Up @@ -157,26 +183,20 @@ class EmitX64 {
c.L(dispatcher_start);

// Call LookupBlock. This will compile a new block and increment timers for us
c.mov(ABI_PARAM1, reinterpret_cast<uintptr_t>(this));
CallFarFunction(c, LookupNewBlockThunk);
CallMemberFunction(&EmitX64::LookupNewBlock, this);
c.test(ABI_RETURN, ABI_RETURN);
c.jz(dispatcher_end);
c.mov(ABI_PARAM1, reinterpret_cast<uintptr_t>(&regs));
c.jmp(ABI_RETURN);
c.L(block_exit);
c.mov(ABI_PARAM1, reinterpret_cast<uintptr_t>(this));
CallFarFunction(c, DoInterruptsAndRunDebugThunk);
CallMemberFunction(&EmitX64::DoInterruptsAndRunDebug, this);
c.jmp(dispatcher_start);
c.L(dispatcher_end);
ABI_PopRegistersAndAdjustStack(c, ABI_ALL_CALLEE_SAVED, 8, 16);
c.ret();
c.ready();
}

static BlockFunc LookupNewBlockThunk(void* this_ptr) {
return reinterpret_cast<EmitX64*>(this_ptr)->LookupNewBlock();
}

BlockFunc LookupNewBlock() {
if (cycles_remaining <= 0) {
return nullptr;
Expand Down Expand Up @@ -222,10 +242,6 @@ class EmitX64 {
return current_blk->func;
}

static void DoInterruptsAndRunDebugThunk(void* this_ptr) {
reinterpret_cast<EmitX64*>(this_ptr)->DoInterruptsAndRunDebug();
}

FORCE_INLINE void LookupBlock() {
auto& vec = block_cache[regs.pc];
for (auto& [key, block] : vec) {
Expand Down Expand Up @@ -572,10 +588,6 @@ class EmitX64 {
NOT_IMPLEMENTED();
}

static u16 MemDataReadThunk(void* mem_ptr, u16 address) {
return reinterpret_cast<MemoryInterface*>(mem_ptr)->DataRead(address);
}

void EmitLoadFunctionCall(Reg64 out, Reg64 address) {
// TODO: Non MMIO reads can be performed inside the JIT.
// Push all registers because our JIT assumes everything is non volatile
Expand All @@ -600,8 +612,8 @@ class EmitX64 {
c.and_(rsp, ~0xF);

c.movzx(ABI_PARAM2, address.cvt16());
c.mov(ABI_PARAM1, reinterpret_cast<uintptr_t>(&mem));
CallFarFunction(c, MemDataReadThunk);
c.xor_(ABI_PARAM3.cvt32(), ABI_PARAM3.cvt32()); // bypass_mmio = false
CallMemberFunction(&MemoryInterface::DataRead, &mem);

// Undo anything we did
c.mov(rsp, rbp);
Expand Down Expand Up @@ -690,8 +702,8 @@ class EmitX64 {
} else {
c.mov(ABI_PARAM2, addr & 0xFFFF);
}
c.mov(ABI_PARAM1, reinterpret_cast<uintptr_t>(&mem));
CallFarFunction(c, MemDataReadThunk);
c.xor_(ABI_PARAM3.cvt32(), ABI_PARAM3.cvt32()); // bypass_mmio = false
CallMemberFunction(&MemoryInterface::DataRead, &mem);

// Undo anything we did
c.mov(rsp, rbp);
Expand Down Expand Up @@ -741,10 +753,10 @@ class EmitX64 {
} else {
c.mov(ABI_PARAM2, addr & 0xFFFF);
}
c.mov(ABI_PARAM1, reinterpret_cast<uintptr_t>(&mem));
CallFarFunction(c, MemDataReadThunk);
c.xor_(ABI_PARAM3.cvt32(), ABI_PARAM3.cvt32()); // bypass_mmio = false
CallMemberFunction(&MemoryInterface::DataRead, &mem);

// Undo anything we did
// Undo anything we did
c.mov(rsp, rbp);
c.pop(r15);
c.pop(r14);
Expand Down Expand Up @@ -2979,10 +2991,6 @@ class EmitX64 {
c.rorx(FACTORS, FACTORS, 48);
}

static void MemDataWriteThunk(void* mem_ptr, u16 address, u16 data) {
reinterpret_cast<MemoryInterface*>(mem_ptr)->DataWrite(address, data);
}

void StoreToMemory(MemImm8 addr, Reg64 value) {
StoreToMemory(addr.Unsigned16() + (blk_key.curr.mod1.page << 8), value);
}
Expand Down Expand Up @@ -3018,9 +3026,10 @@ class EmitX64 {
}
c.mov(ABI_PARAM3, value);
c.mov(ABI_PARAM1, reinterpret_cast<uintptr_t>(&mem));
CallFarFunction(c, MemDataWriteThunk);
c.xor_(ABI_PARAM4.cvt32(), ABI_PARAM4.cvt32()); // bypass_mmio = false
CallMemberFunction(&MemoryInterface::DataWrite, &mem);

// Undo anything we did
// Undo anything we did
c.mov(rsp, rbp);
c.pop(r15);
c.pop(r14);
Expand Down