CachedInterpreter: Callback Disassembler
This commit is contained in:
parent
1f30d05027
commit
c431cd2e1e
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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" />
|
||||
|
|
Loading…
Reference in New Issue