From c431cd2e1e3873fc4a7686b55155e719e50cd1f4 Mon Sep 17 00:00:00 2001 From: mitaclaw <140017135+mitaclaw@users.noreply.github.com> Date: Fri, 26 Jul 2024 19:51:33 -0700 Subject: [PATCH] CachedInterpreter: Callback Disassembler --- Source/Core/Core/CMakeLists.txt | 1 + Source/Core/Core/HLE/HLE.cpp | 5 + Source/Core/Core/HLE/HLE.h | 1 + .../CachedInterpreter/CachedInterpreter.cpp | 7 +- .../CachedInterpreter/CachedInterpreter.h | 17 ++- .../CachedInterpreterEmitter.h | 22 +++ .../CachedInterpreter_Disassembler.cpp | 143 ++++++++++++++++++ Source/Core/DolphinLib.props | 1 + 8 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter_Disassembler.cpp diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 6b0022d63c..65ac044e45 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -479,6 +479,7 @@ add_library(core PatchEngine.h PowerPC/BreakPoints.cpp PowerPC/BreakPoints.h + PowerPC/CachedInterpreter/CachedInterpreter_Disassembler.cpp PowerPC/CachedInterpreter/CachedInterpreter.cpp PowerPC/CachedInterpreter/CachedInterpreter.h PowerPC/CachedInterpreter/CachedInterpreterBlockCache.cpp diff --git a/Source/Core/Core/HLE/HLE.cpp b/Source/Core/Core/HLE/HLE.cpp index ff9db8fc75..0fafedfaa8 100644 --- a/Source/Core/Core/HLE/HLE.cpp +++ b/Source/Core/Core/HLE/HLE.cpp @@ -195,6 +195,11 @@ u32 GetHookByFunctionAddress(PPCSymbolDB& ppc_symbol_db, u32 address) return (symbol && symbol->address == address) ? index : 0; } +const char* GetHookNameByIndex(u32 index) +{ + return os_patches[index].name; +} + HookType GetHookTypeByIndex(u32 index) { return os_patches[index].type; diff --git a/Source/Core/Core/HLE/HLE.h b/Source/Core/Core/HLE/HLE.h index e273e8b4d7..e3ffc3d03c 100644 --- a/Source/Core/Core/HLE/HLE.h +++ b/Source/Core/Core/HLE/HLE.h @@ -69,6 +69,7 @@ void ExecuteFromJIT(u32 current_pc, u32 hook_index, Core::System& system); u32 GetHookByAddress(u32 address); // Returns the HLE hook index if the address matches the function start u32 GetHookByFunctionAddress(PPCSymbolDB& ppc_symbol_db, u32 address); +const char* GetHookNameByIndex(u32 index); HookType GetHookTypeByIndex(u32 index); HookFlag GetHookFlagsByIndex(u32 index); diff --git a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp index 594f560f10..81e4b435fa 100644 --- a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp +++ b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.cpp @@ -370,15 +370,16 @@ bool CachedInterpreter::DoJit(u32 em_address, JitBlock* b, u32 nextPC) {interpreter, Interpreter::GetInterpreterOp(op.inst), js.compilerPC, op.inst}, power_pc, js.downcountAmount}; - Write(op.canEndBlock ? InterpretAndCheckExceptions : - InterpretAndCheckExceptions, + Write(op.canEndBlock ? CallbackCast(InterpretAndCheckExceptions) : + CallbackCast(InterpretAndCheckExceptions), operands); } else { const InterpretOperands operands = {interpreter, Interpreter::GetInterpreterOp(op.inst), js.compilerPC, op.inst}; - Write(op.canEndBlock ? Interpret : Interpret, operands); + Write(op.canEndBlock ? CallbackCast(Interpret) : CallbackCast(Interpret), + operands); } if (op.branchIsIdleLoop) diff --git a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.h b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.h index 92da1cd473..b7fa0d664c 100644 --- a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.h +++ b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter.h @@ -46,6 +46,8 @@ public: void Jit(u32 address, bool clear_cache_and_retry_on_failure); bool DoJit(u32 address, JitBlock* b, u32 nextPC); + static std::size_t Disassemble(const JitBlock& block, std::ostream& stream); + JitBaseBlockCache* GetBlockCache() override { return &m_block_cache; } const char* GetName() const override { return "Cached Interpreter"; } const CommonAsmRoutinesBase* GetAsmRoutines() override { return nullptr; } @@ -74,20 +76,33 @@ private: struct CheckIdleOperands; static s32 StartProfiledBlock(PowerPC::PowerPCState& ppc_state, - const StartProfiledBlockOperands& profile_data); + const StartProfiledBlockOperands& operands); + static s32 StartProfiledBlock(std::ostream& stream, const StartProfiledBlockOperands& operands); template static s32 EndBlock(PowerPC::PowerPCState& ppc_state, const EndBlockOperands& operands); + template + static s32 EndBlock(std::ostream& stream, const EndBlockOperands& operands); template static s32 Interpret(PowerPC::PowerPCState& ppc_state, const InterpretOperands& operands); template + static s32 Interpret(std::ostream& stream, const InterpretOperands& operands); + template static s32 InterpretAndCheckExceptions(PowerPC::PowerPCState& ppc_state, const InterpretAndCheckExceptionsOperands& operands); + template + static s32 InterpretAndCheckExceptions(std::ostream& stream, + const InterpretAndCheckExceptionsOperands& operands); static s32 HLEFunction(PowerPC::PowerPCState& ppc_state, const HLEFunctionOperands& operands); + static s32 HLEFunction(std::ostream& stream, const HLEFunctionOperands& operands); static s32 WriteBrokenBlockNPC(PowerPC::PowerPCState& ppc_state, const WriteBrokenBlockNPCOperands& operands); + static s32 WriteBrokenBlockNPC(std::ostream& stream, const WriteBrokenBlockNPCOperands& operands); static s32 CheckFPU(PowerPC::PowerPCState& ppc_state, const CheckHaltOperands& operands); + static s32 CheckFPU(std::ostream& stream, const CheckHaltOperands& operands); static s32 CheckBreakpoint(PowerPC::PowerPCState& ppc_state, const CheckHaltOperands& operands); + static s32 CheckBreakpoint(std::ostream& stream, const CheckHaltOperands& operands); static s32 CheckIdle(PowerPC::PowerPCState& ppc_state, const CheckIdleOperands& operands); + static s32 CheckIdle(std::ostream& stream, const CheckIdleOperands& operands); HyoutaUtilities::RangeSizeSet m_free_ranges; CachedInterpreterBlockCache m_block_cache; diff --git a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreterEmitter.h b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreterEmitter.h index db1a22c413..7b1554ffc1 100644 --- a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreterEmitter.h +++ b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreterEmitter.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include "Common/CodeBlock.h" @@ -24,6 +25,11 @@ protected: using Callback = s32 (*)(PowerPC::PowerPCState& ppc_state, const Operands& operands); using AnyCallback = s32 (*)(PowerPC::PowerPCState& ppc_state, const void* operands); + template + static consteval Callback CallbackCast(Callback callback) + { + return callback; + } template static AnyCallback AnyCallbackCast(Callback callback) { @@ -31,6 +37,21 @@ protected: } static consteval AnyCallback AnyCallbackCast(AnyCallback callback) { return callback; } + // Disassemble callbacks will always return the distance to the next callback. + template + using Disassemble = s32 (*)(std::ostream& stream, const Operands& operands); + using AnyDisassemble = s32 (*)(std::ostream& stream, const void* operands); + + template + static AnyDisassemble AnyDisassembleCast(Disassemble disassemble) + { + return reinterpret_cast(disassemble); + } + static consteval AnyDisassemble AnyDisassembleCast(AnyDisassemble disassemble) + { + return disassemble; + } + public: CachedInterpreterEmitter() = default; explicit CachedInterpreterEmitter(u8* begin, u8* end) : m_code(begin), m_code_end(end) {} @@ -63,6 +84,7 @@ public: } static s32 PoisonCallback(PowerPC::PowerPCState& ppc_state, const void* operands); + static s32 PoisonCallback(std::ostream& stream, const void* operands); private: void Write(AnyCallback callback, const void* operands, std::size_t size); diff --git a/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter_Disassembler.cpp b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter_Disassembler.cpp new file mode 100644 index 0000000000..725d124958 --- /dev/null +++ b/Source/Core/Core/PowerPC/CachedInterpreter/CachedInterpreter_Disassembler.cpp @@ -0,0 +1,143 @@ +// Copyright 2024 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Core/PowerPC/CachedInterpreter/CachedInterpreter.h" + +#include +#include +#include +#include + +#include +#include + +#include "Core/HLE/HLE.h" + +s32 CachedInterpreterEmitter::PoisonCallback(std::ostream& stream, const void* operands) +{ + stream << "PoisonCallback()\n"; + return sizeof(AnyCallback); +} + +s32 CachedInterpreter::StartProfiledBlock(std::ostream& stream, + const StartProfiledBlockOperands& operands) +{ + stream << "StartProfiledBlock()\n"; + return sizeof(AnyCallback) + sizeof(operands); +} + +template +s32 CachedInterpreter::EndBlock(std::ostream& stream, const EndBlockOperands& operands) +{ + fmt::println(stream, "EndBlock(downcount={}, num_load_stores={}, num_fp_inst={})", + profiled, operands.downcount, operands.num_load_stores, operands.num_fp_inst); + return sizeof(AnyCallback) + sizeof(operands); +} + +template +s32 CachedInterpreter::Interpret(std::ostream& stream, const InterpretOperands& operands) +{ + fmt::println(stream, "Interpret(current_pc=0x{:08x}, inst=0x{:08x})", write_pc, + operands.current_pc, operands.inst.hex); + return sizeof(AnyCallback) + sizeof(operands); +} + +template +s32 CachedInterpreter::InterpretAndCheckExceptions( + std::ostream& stream, const InterpretAndCheckExceptionsOperands& operands) +{ + fmt::println(stream, + "InterpretAndCheckExceptions(current_pc=0x{:08x}, inst=0x{:08x}, " + "downcount={})", + write_pc, operands.current_pc, operands.inst.hex, operands.downcount); + return sizeof(AnyCallback) + sizeof(operands); +} + +s32 CachedInterpreter::HLEFunction(std::ostream& stream, const HLEFunctionOperands& operands) +{ + const auto& [system, current_pc, hook_index] = operands; + fmt::println(stream, "HLEFunction(current_pc=0x{:08x}, hook_index={}) [\"{}\"]", current_pc, + hook_index, HLE::GetHookNameByIndex(hook_index)); + return sizeof(AnyCallback) + sizeof(operands); +} + +s32 CachedInterpreter::WriteBrokenBlockNPC(std::ostream& stream, + const WriteBrokenBlockNPCOperands& operands) +{ + const auto& [current_pc] = operands; + fmt::println(stream, "WriteBrokenBlockNPC(current_pc=0x{:08x})", current_pc); + return sizeof(AnyCallback) + sizeof(operands); +} + +s32 CachedInterpreter::CheckFPU(std::ostream& stream, const CheckHaltOperands& operands) +{ + const auto& [power_pc, current_pc, downcount] = operands; + fmt::println(stream, "CheckFPU(current_pc=0x{:08x}, downcount={})", current_pc, downcount); + return sizeof(AnyCallback) + sizeof(operands); +} + +s32 CachedInterpreter::CheckBreakpoint(std::ostream& stream, const CheckHaltOperands& operands) +{ + const auto& [power_pc, current_pc, downcount] = operands; + fmt::println(stream, "CheckBreakpoint(current_pc=0x{:08x}, downcount={})", current_pc, downcount); + return sizeof(AnyCallback) + sizeof(operands); +} + +s32 CachedInterpreter::CheckIdle(std::ostream& stream, const CheckIdleOperands& operands) +{ + const auto& [core_timing, idle_pc] = operands; + fmt::println(stream, "CheckIdle(idle_pc=0x{:08x})", idle_pc); + return sizeof(AnyCallback) + sizeof(operands); +} + +static std::once_flag s_sorted_lookup_flag; + +std::size_t CachedInterpreter::Disassemble(const JitBlock& block, std::ostream& stream) +{ + using LookupKV = std::pair; + + // clang-format off +#define LOOKUP_KV(...) {AnyCallbackCast(__VA_ARGS__), AnyDisassembleCast(__VA_ARGS__)} + // clang-format on + + // Function addresses aren't known at compile-time, so this array is sorted at run-time. + static auto sorted_lookup = std::to_array({ + LOOKUP_KV(CachedInterpreter::PoisonCallback), + LOOKUP_KV(CachedInterpreter::StartProfiledBlock), + LOOKUP_KV(CachedInterpreter::EndBlock), + LOOKUP_KV(CachedInterpreter::EndBlock), + LOOKUP_KV(CachedInterpreter::Interpret), + LOOKUP_KV(CachedInterpreter::Interpret), + LOOKUP_KV(CachedInterpreter::InterpretAndCheckExceptions), + LOOKUP_KV(CachedInterpreter::InterpretAndCheckExceptions), + LOOKUP_KV(CachedInterpreter::HLEFunction), + LOOKUP_KV(CachedInterpreter::WriteBrokenBlockNPC), + LOOKUP_KV(CachedInterpreter::CheckFPU), + LOOKUP_KV(CachedInterpreter::CheckBreakpoint), + LOOKUP_KV(CachedInterpreter::CheckIdle), + }); + +#undef LOOKUP_KV + + std::call_once(s_sorted_lookup_flag, []() { + const auto end = std::ranges::sort(sorted_lookup, {}, &LookupKV::first); + ASSERT_MSG(DYNA_REC, std::ranges::adjacent_find(sorted_lookup, {}, &LookupKV::first) == end, + "Sorted lookup should not contain duplicate keys."); + }); + + std::size_t instruction_count = 0; + for (const u8* normal_entry = block.normalEntry; normal_entry != block.near_end; + ++instruction_count) + { + const auto callback = *reinterpret_cast(normal_entry); + const auto kv = std::ranges::lower_bound(sorted_lookup, callback, {}, &LookupKV::first); + if (kv != sorted_lookup.end() && kv->first == callback) + { + normal_entry += kv->second(stream, normal_entry + sizeof(AnyCallback)); + continue; + } + stream << "UNKNOWN OR ILLEGAL CALLBACK\n"; + break; + } + return instruction_count; +} diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 5f1c59ac4d..206901f7f0 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -1089,6 +1089,7 @@ +