CachedInterpreter: Callback Disassembler

This commit is contained in:
mitaclaw 2024-07-26 19:51:33 -07:00
parent 1f30d05027
commit c431cd2e1e
8 changed files with 193 additions and 4 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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<true> :
InterpretAndCheckExceptions<false>,
Write(op.canEndBlock ? CallbackCast(InterpretAndCheckExceptions<true>) :
CallbackCast(InterpretAndCheckExceptions<false>),
operands);
}
else
{
const InterpretOperands operands = {interpreter, Interpreter::GetInterpreterOp(op.inst),
js.compilerPC, op.inst};
Write(op.canEndBlock ? Interpret<true> : Interpret<false>, operands);
Write(op.canEndBlock ? CallbackCast(Interpret<true>) : CallbackCast(Interpret<false>),
operands);
}
if (op.branchIsIdleLoop)

View File

@ -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 <bool profiled>
static s32 EndBlock(PowerPC::PowerPCState& ppc_state, const EndBlockOperands<profiled>& operands);
template <bool profiled>
static s32 EndBlock(std::ostream& stream, const EndBlockOperands<profiled>& operands);
template <bool write_pc>
static s32 Interpret(PowerPC::PowerPCState& ppc_state, const InterpretOperands& operands);
template <bool write_pc>
static s32 Interpret(std::ostream& stream, const InterpretOperands& operands);
template <bool write_pc>
static s32 InterpretAndCheckExceptions(PowerPC::PowerPCState& ppc_state,
const InterpretAndCheckExceptionsOperands& operands);
template <bool write_pc>
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<u8*> m_free_ranges;
CachedInterpreterBlockCache m_block_cache;

View File

@ -4,6 +4,7 @@
#pragma once
#include <cstddef>
#include <iosfwd>
#include <type_traits>
#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 <class Operands>
static consteval Callback<Operands> CallbackCast(Callback<Operands> callback)
{
return callback;
}
template <class Operands>
static AnyCallback AnyCallbackCast(Callback<Operands> 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 <class Operands>
using Disassemble = s32 (*)(std::ostream& stream, const Operands& operands);
using AnyDisassemble = s32 (*)(std::ostream& stream, const void* operands);
template <class Operands>
static AnyDisassemble AnyDisassembleCast(Disassemble<Operands> disassemble)
{
return reinterpret_cast<AnyDisassemble>(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);

View File

@ -0,0 +1,143 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/PowerPC/CachedInterpreter/CachedInterpreter.h"
#include <algorithm>
#include <array>
#include <mutex>
#include <utility>
#include <fmt/format.h>
#include <fmt/ostream.h>
#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 <bool profiled>
s32 CachedInterpreter::EndBlock(std::ostream& stream, const EndBlockOperands<profiled>& operands)
{
fmt::println(stream, "EndBlock<profiled={}>(downcount={}, num_load_stores={}, num_fp_inst={})",
profiled, operands.downcount, operands.num_load_stores, operands.num_fp_inst);
return sizeof(AnyCallback) + sizeof(operands);
}
template <bool write_pc>
s32 CachedInterpreter::Interpret(std::ostream& stream, const InterpretOperands& operands)
{
fmt::println(stream, "Interpret<write_pc={:5}>(current_pc=0x{:08x}, inst=0x{:08x})", write_pc,
operands.current_pc, operands.inst.hex);
return sizeof(AnyCallback) + sizeof(operands);
}
template <bool write_pc>
s32 CachedInterpreter::InterpretAndCheckExceptions(
std::ostream& stream, const InterpretAndCheckExceptionsOperands& operands)
{
fmt::println(stream,
"InterpretAndCheckExceptions<write_pc={:5}>(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<AnyCallback, AnyDisassemble>;
// 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<LookupKV>({
LOOKUP_KV(CachedInterpreter::PoisonCallback),
LOOKUP_KV(CachedInterpreter::StartProfiledBlock),
LOOKUP_KV(CachedInterpreter::EndBlock<false>),
LOOKUP_KV(CachedInterpreter::EndBlock<true>),
LOOKUP_KV(CachedInterpreter::Interpret<false>),
LOOKUP_KV(CachedInterpreter::Interpret<true>),
LOOKUP_KV(CachedInterpreter::InterpretAndCheckExceptions<false>),
LOOKUP_KV(CachedInterpreter::InterpretAndCheckExceptions<true>),
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<const AnyCallback*>(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;
}

View File

@ -1089,6 +1089,7 @@
<ClCompile Include="Core\NetworkCaptureLogger.cpp" />
<ClCompile Include="Core\PatchEngine.cpp" />
<ClCompile Include="Core\PowerPC\BreakPoints.cpp" />
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreter_Disassembler.cpp" />
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreter.cpp" />
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreterBlockCache.cpp" />
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreterEmitter.cpp" />