mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 13:27:45 -07:00
CachedInterpreter: Callback Disassembler
This commit is contained in:
parent
1f30d05027
commit
c431cd2e1e
@ -479,6 +479,7 @@ add_library(core
|
|||||||
PatchEngine.h
|
PatchEngine.h
|
||||||
PowerPC/BreakPoints.cpp
|
PowerPC/BreakPoints.cpp
|
||||||
PowerPC/BreakPoints.h
|
PowerPC/BreakPoints.h
|
||||||
|
PowerPC/CachedInterpreter/CachedInterpreter_Disassembler.cpp
|
||||||
PowerPC/CachedInterpreter/CachedInterpreter.cpp
|
PowerPC/CachedInterpreter/CachedInterpreter.cpp
|
||||||
PowerPC/CachedInterpreter/CachedInterpreter.h
|
PowerPC/CachedInterpreter/CachedInterpreter.h
|
||||||
PowerPC/CachedInterpreter/CachedInterpreterBlockCache.cpp
|
PowerPC/CachedInterpreter/CachedInterpreterBlockCache.cpp
|
||||||
|
@ -195,6 +195,11 @@ u32 GetHookByFunctionAddress(PPCSymbolDB& ppc_symbol_db, u32 address)
|
|||||||
return (symbol && symbol->address == address) ? index : 0;
|
return (symbol && symbol->address == address) ? index : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* GetHookNameByIndex(u32 index)
|
||||||
|
{
|
||||||
|
return os_patches[index].name;
|
||||||
|
}
|
||||||
|
|
||||||
HookType GetHookTypeByIndex(u32 index)
|
HookType GetHookTypeByIndex(u32 index)
|
||||||
{
|
{
|
||||||
return os_patches[index].type;
|
return os_patches[index].type;
|
||||||
|
@ -69,6 +69,7 @@ void ExecuteFromJIT(u32 current_pc, u32 hook_index, Core::System& system);
|
|||||||
u32 GetHookByAddress(u32 address);
|
u32 GetHookByAddress(u32 address);
|
||||||
// Returns the HLE hook index if the address matches the function start
|
// Returns the HLE hook index if the address matches the function start
|
||||||
u32 GetHookByFunctionAddress(PPCSymbolDB& ppc_symbol_db, u32 address);
|
u32 GetHookByFunctionAddress(PPCSymbolDB& ppc_symbol_db, u32 address);
|
||||||
|
const char* GetHookNameByIndex(u32 index);
|
||||||
HookType GetHookTypeByIndex(u32 index);
|
HookType GetHookTypeByIndex(u32 index);
|
||||||
HookFlag GetHookFlagsByIndex(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},
|
{interpreter, Interpreter::GetInterpreterOp(op.inst), js.compilerPC, op.inst},
|
||||||
power_pc,
|
power_pc,
|
||||||
js.downcountAmount};
|
js.downcountAmount};
|
||||||
Write(op.canEndBlock ? InterpretAndCheckExceptions<true> :
|
Write(op.canEndBlock ? CallbackCast(InterpretAndCheckExceptions<true>) :
|
||||||
InterpretAndCheckExceptions<false>,
|
CallbackCast(InterpretAndCheckExceptions<false>),
|
||||||
operands);
|
operands);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const InterpretOperands operands = {interpreter, Interpreter::GetInterpreterOp(op.inst),
|
const InterpretOperands operands = {interpreter, Interpreter::GetInterpreterOp(op.inst),
|
||||||
js.compilerPC, 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)
|
if (op.branchIsIdleLoop)
|
||||||
|
@ -46,6 +46,8 @@ public:
|
|||||||
void Jit(u32 address, bool clear_cache_and_retry_on_failure);
|
void Jit(u32 address, bool clear_cache_and_retry_on_failure);
|
||||||
bool DoJit(u32 address, JitBlock* b, u32 nextPC);
|
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; }
|
JitBaseBlockCache* GetBlockCache() override { return &m_block_cache; }
|
||||||
const char* GetName() const override { return "Cached Interpreter"; }
|
const char* GetName() const override { return "Cached Interpreter"; }
|
||||||
const CommonAsmRoutinesBase* GetAsmRoutines() override { return nullptr; }
|
const CommonAsmRoutinesBase* GetAsmRoutines() override { return nullptr; }
|
||||||
@ -74,20 +76,33 @@ private:
|
|||||||
struct CheckIdleOperands;
|
struct CheckIdleOperands;
|
||||||
|
|
||||||
static s32 StartProfiledBlock(PowerPC::PowerPCState& ppc_state,
|
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>
|
template <bool profiled>
|
||||||
static s32 EndBlock(PowerPC::PowerPCState& ppc_state, const EndBlockOperands<profiled>& operands);
|
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>
|
template <bool write_pc>
|
||||||
static s32 Interpret(PowerPC::PowerPCState& ppc_state, const InterpretOperands& operands);
|
static s32 Interpret(PowerPC::PowerPCState& ppc_state, const InterpretOperands& operands);
|
||||||
template <bool write_pc>
|
template <bool write_pc>
|
||||||
|
static s32 Interpret(std::ostream& stream, const InterpretOperands& operands);
|
||||||
|
template <bool write_pc>
|
||||||
static s32 InterpretAndCheckExceptions(PowerPC::PowerPCState& ppc_state,
|
static s32 InterpretAndCheckExceptions(PowerPC::PowerPCState& ppc_state,
|
||||||
const InterpretAndCheckExceptionsOperands& operands);
|
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(PowerPC::PowerPCState& ppc_state, const HLEFunctionOperands& operands);
|
||||||
|
static s32 HLEFunction(std::ostream& stream, const HLEFunctionOperands& operands);
|
||||||
static s32 WriteBrokenBlockNPC(PowerPC::PowerPCState& ppc_state,
|
static s32 WriteBrokenBlockNPC(PowerPC::PowerPCState& ppc_state,
|
||||||
const WriteBrokenBlockNPCOperands& operands);
|
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(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(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(PowerPC::PowerPCState& ppc_state, const CheckIdleOperands& operands);
|
||||||
|
static s32 CheckIdle(std::ostream& stream, const CheckIdleOperands& operands);
|
||||||
|
|
||||||
HyoutaUtilities::RangeSizeSet<u8*> m_free_ranges;
|
HyoutaUtilities::RangeSizeSet<u8*> m_free_ranges;
|
||||||
CachedInterpreterBlockCache m_block_cache;
|
CachedInterpreterBlockCache m_block_cache;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <iosfwd>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "Common/CodeBlock.h"
|
#include "Common/CodeBlock.h"
|
||||||
@ -24,6 +25,11 @@ protected:
|
|||||||
using Callback = s32 (*)(PowerPC::PowerPCState& ppc_state, const Operands& operands);
|
using Callback = s32 (*)(PowerPC::PowerPCState& ppc_state, const Operands& operands);
|
||||||
using AnyCallback = s32 (*)(PowerPC::PowerPCState& ppc_state, const void* 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>
|
template <class Operands>
|
||||||
static AnyCallback AnyCallbackCast(Callback<Operands> callback)
|
static AnyCallback AnyCallbackCast(Callback<Operands> callback)
|
||||||
{
|
{
|
||||||
@ -31,6 +37,21 @@ protected:
|
|||||||
}
|
}
|
||||||
static consteval AnyCallback AnyCallbackCast(AnyCallback callback) { return callback; }
|
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:
|
public:
|
||||||
CachedInterpreterEmitter() = default;
|
CachedInterpreterEmitter() = default;
|
||||||
explicit CachedInterpreterEmitter(u8* begin, u8* end) : m_code(begin), m_code_end(end) {}
|
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(PowerPC::PowerPCState& ppc_state, const void* operands);
|
||||||
|
static s32 PoisonCallback(std::ostream& stream, const void* operands);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Write(AnyCallback callback, const void* operands, std::size_t size);
|
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\NetworkCaptureLogger.cpp" />
|
||||||
<ClCompile Include="Core\PatchEngine.cpp" />
|
<ClCompile Include="Core\PatchEngine.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\BreakPoints.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\CachedInterpreter.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreterBlockCache.cpp" />
|
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreterBlockCache.cpp" />
|
||||||
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreterEmitter.cpp" />
|
<ClCompile Include="Core\PowerPC\CachedInterpreter\CachedInterpreterEmitter.cpp" />
|
||||||
|
Loading…
Reference in New Issue
Block a user