From 92db67450f41aacd436f681d5601dc23a6916c50 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:11:01 +0200 Subject: [PATCH 1/2] Add MFP devirtualization --- src/jit_no_ir.h | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/jit_no_ir.h b/src/jit_no_ir.h index ef811ec..3ff268d 100644 --- a/src/jit_no_ir.h +++ b/src/jit_no_ir.h @@ -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 + void CallMemberFunction(T func, void* this_object) { + void* function_ptr; + uintptr_t this_ptr = reinterpret_cast(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(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(); @@ -157,15 +183,13 @@ 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(this)); - CallFarFunction(c, LookupNewBlockThunk); + CallMemberFunction(&EmitX64::LookupNewBlock, this); c.test(ABI_RETURN, ABI_RETURN); c.jz(dispatcher_end); c.mov(ABI_PARAM1, reinterpret_cast(®s)); c.jmp(ABI_RETURN); c.L(block_exit); - c.mov(ABI_PARAM1, reinterpret_cast(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); @@ -173,10 +197,6 @@ class EmitX64 { c.ready(); } - static BlockFunc LookupNewBlockThunk(void* this_ptr) { - return reinterpret_cast(this_ptr)->LookupNewBlock(); - } - BlockFunc LookupNewBlock() { if (cycles_remaining <= 0) { return nullptr; @@ -222,10 +242,6 @@ class EmitX64 { return current_blk->func; } - static void DoInterruptsAndRunDebugThunk(void* this_ptr) { - reinterpret_cast(this_ptr)->DoInterruptsAndRunDebug(); - } - FORCE_INLINE void LookupBlock() { auto& vec = block_cache[regs.pc]; for (auto& [key, block] : vec) { From d6f15e83c532ef8ea2a6127a92cbf4b30944aa3f Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:23:34 +0200 Subject: [PATCH 2/2] Devirtualize memory interface FPs too --- src/jit_no_ir.h | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/jit_no_ir.h b/src/jit_no_ir.h index 3ff268d..6daf9de 100644 --- a/src/jit_no_ir.h +++ b/src/jit_no_ir.h @@ -588,10 +588,6 @@ class EmitX64 { NOT_IMPLEMENTED(); } - static u16 MemDataReadThunk(void* mem_ptr, u16 address) { - return reinterpret_cast(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 @@ -616,8 +612,8 @@ class EmitX64 { c.and_(rsp, ~0xF); c.movzx(ABI_PARAM2, address.cvt16()); - c.mov(ABI_PARAM1, reinterpret_cast(&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); @@ -706,8 +702,8 @@ class EmitX64 { } else { c.mov(ABI_PARAM2, addr & 0xFFFF); } - c.mov(ABI_PARAM1, reinterpret_cast(&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); @@ -757,10 +753,10 @@ class EmitX64 { } else { c.mov(ABI_PARAM2, addr & 0xFFFF); } - c.mov(ABI_PARAM1, reinterpret_cast(&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); @@ -2995,10 +2991,6 @@ class EmitX64 { c.rorx(FACTORS, FACTORS, 48); } - static void MemDataWriteThunk(void* mem_ptr, u16 address, u16 data) { - reinterpret_cast(mem_ptr)->DataWrite(address, data); - } - void StoreToMemory(MemImm8 addr, Reg64 value) { StoreToMemory(addr.Unsigned16() + (blk_key.curr.mod1.page << 8), value); } @@ -3034,9 +3026,10 @@ class EmitX64 { } c.mov(ABI_PARAM3, value); c.mov(ABI_PARAM1, reinterpret_cast(&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);