DolphinQt: JIT Widget Refresh

Fulfilling a certain six-year-old todo.
This commit is contained in:
mitaclaw
2024-10-19 02:30:44 -07:00
parent ca9222a16b
commit 9afd09598c
36 changed files with 1640 additions and 390 deletions
+8
View File
@@ -146,6 +146,14 @@ void Host_UpdateDisasmDialog()
{
}
void Host_JitCacheCleared()
{
}
void Host_JitProfileDataWiped()
{
}
void Host_UpdateMainFrame()
{
}
+74 -124
View File
@@ -3,52 +3,43 @@
#include "Common/HostDisassembler.h"
#include <sstream>
#include <span>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <fmt/ranges.h>
#if defined(HAVE_LLVM)
#include <fmt/format.h>
#include <llvm-c/Disassembler.h>
#include <llvm-c/Target.h>
#elif defined(_M_X86_64)
#include <disasm.h> // Bochs
#endif
#include "Common/Assert.h"
#include "Common/VariantUtil.h"
#include "Core/PowerPC/JitInterface.h"
#include "Core/System.h"
#if defined(HAVE_LLVM)
class HostDisassemblerLLVM : public HostDisassembler
class HostDisassemblerLLVM final : public HostDisassembler
{
public:
HostDisassemblerLLVM(const std::string& host_disasm, int inst_size = -1,
const std::string& cpu = "");
~HostDisassemblerLLVM()
{
if (m_can_disasm)
LLVMDisasmDispose(m_llvm_context);
}
explicit HostDisassemblerLLVM(const char* host_disasm, const char* cpu = "",
std::size_t inst_size = 0);
~HostDisassemblerLLVM();
private:
bool m_can_disasm;
LLVMDisasmContextRef m_llvm_context;
int m_instruction_size;
std::size_t m_instruction_size;
std::string DisassembleHostBlock(const u8* code_start, const u32 code_size,
u32* host_instructions_count, u64 starting_pc) override;
std::size_t Disassemble(const u8* begin, const u8* end, std::ostream& stream) override;
};
HostDisassemblerLLVM::HostDisassemblerLLVM(const std::string& host_disasm, int inst_size,
const std::string& cpu)
: m_can_disasm(false), m_instruction_size(inst_size)
HostDisassemblerLLVM::HostDisassemblerLLVM(const char* host_disasm, const char* cpu,
std::size_t inst_size)
: m_instruction_size(inst_size)
{
LLVMInitializeAllTargetInfos();
LLVMInitializeAllTargetMCs();
LLVMInitializeAllDisassemblers();
m_llvm_context =
LLVMCreateDisasmCPU(host_disasm.c_str(), cpu.c_str(), nullptr, 0, nullptr, nullptr);
m_llvm_context = LLVMCreateDisasmCPU(host_disasm, cpu, nullptr, 0, nullptr, nullptr);
// Couldn't create llvm context
if (!m_llvm_context)
@@ -56,153 +47,112 @@ HostDisassemblerLLVM::HostDisassemblerLLVM(const std::string& host_disasm, int i
LLVMSetDisasmOptions(m_llvm_context, LLVMDisassembler_Option_AsmPrinterVariant |
LLVMDisassembler_Option_PrintLatency);
m_can_disasm = true;
}
std::string HostDisassemblerLLVM::DisassembleHostBlock(const u8* code_start, const u32 code_size,
u32* host_instructions_count,
u64 starting_pc)
HostDisassemblerLLVM::~HostDisassemblerLLVM()
{
if (!m_can_disasm)
return "(No LLVM context)";
if (m_llvm_context)
LLVMDisasmDispose(m_llvm_context);
}
u8* disasmPtr = (u8*)code_start;
const u8* end = code_start + code_size;
std::size_t HostDisassemblerLLVM::Disassemble(const u8* begin, const u8* end, std::ostream& stream)
{
std::size_t instruction_count = 0;
if (!m_llvm_context)
return instruction_count;
std::ostringstream x86_disasm;
while ((u8*)disasmPtr < end)
while (begin < end)
{
char inst_disasm[256];
size_t inst_size = LLVMDisasmInstruction(m_llvm_context, disasmPtr, (u64)(end - disasmPtr),
starting_pc, inst_disasm, 256);
x86_disasm << "0x" << std::hex << starting_pc << "\t";
if (!inst_size)
const auto inst_size =
LLVMDisasmInstruction(m_llvm_context, const_cast<u8*>(begin), static_cast<u64>(end - begin),
reinterpret_cast<u64>(begin), inst_disasm, sizeof(inst_disasm));
if (inst_size == 0)
{
x86_disasm << "Invalid inst:";
if (m_instruction_size != -1)
if (m_instruction_size != 0)
{
// If we are on an architecture that has a fixed instruction size
// We can continue onward past this bad instruction.
std::string inst_str;
for (int i = 0; i < m_instruction_size; ++i)
inst_str += fmt::format("{:02x}", disasmPtr[i]);
x86_disasm << inst_str << std::endl;
disasmPtr += m_instruction_size;
// If we are on an architecture that has a fixed instruction
// size, we can continue onward past this bad instruction.
fmt::println(stream, "{}\tInvalid inst: {:02x}", fmt::ptr(begin),
fmt::join(std::span{begin, m_instruction_size}, ""));
begin += m_instruction_size;
}
else
{
// We can't continue if we are on an architecture that has flexible instruction sizes
// Dump the rest of the block instead
std::string code_block;
for (int i = 0; (disasmPtr + i) < end; ++i)
code_block += fmt::format("{:02x}", disasmPtr[i]);
x86_disasm << code_block << std::endl;
// We can't continue if we are on an architecture that has flexible
// instruction sizes. Dump the rest of the block instead.
fmt::println(stream, "{}\tInvalid inst: {:02x}", fmt::ptr(begin),
fmt::join(std::span{begin, end}, ""));
break;
}
}
else
{
x86_disasm << inst_disasm << std::endl;
disasmPtr += inst_size;
starting_pc += inst_size;
fmt::println(stream, "{}{}", fmt::ptr(begin), inst_disasm);
begin += inst_size;
}
(*host_instructions_count)++;
++instruction_count;
}
return x86_disasm.str();
return instruction_count;
}
#elif defined(_M_X86_64)
class HostDisassemblerX86 : public HostDisassembler
class HostDisassemblerBochs final : public HostDisassembler
{
public:
HostDisassemblerX86();
explicit HostDisassemblerBochs();
~HostDisassemblerBochs() = default;
private:
disassembler m_disasm;
std::string DisassembleHostBlock(const u8* code_start, const u32 code_size,
u32* host_instructions_count, u64 starting_pc) override;
std::size_t Disassemble(const u8* begin, const u8* end, std::ostream& stream) override;
};
HostDisassemblerX86::HostDisassemblerX86()
HostDisassemblerBochs::HostDisassemblerBochs()
{
m_disasm.set_syntax_intel();
}
std::string HostDisassemblerX86::DisassembleHostBlock(const u8* code_start, const u32 code_size,
u32* host_instructions_count, u64 starting_pc)
std::size_t HostDisassemblerBochs::Disassemble(const u8* begin, const u8* end, std::ostream& stream)
{
u64 disasmPtr = (u64)code_start;
const u8* end = code_start + code_size;
std::ostringstream x86_disasm;
while ((u8*)disasmPtr < end)
std::size_t instruction_count = 0;
while (begin < end)
{
char inst_disasm[256];
disasmPtr += m_disasm.disasm64(disasmPtr, disasmPtr, (u8*)disasmPtr, inst_disasm);
x86_disasm << inst_disasm << std::endl;
(*host_instructions_count)++;
const auto inst_size =
m_disasm.disasm64(0, reinterpret_cast<bx_address>(begin), begin, inst_disasm);
fmt::println(stream, "{}\t{}", fmt::ptr(begin), inst_disasm);
begin += inst_size;
++instruction_count;
}
return x86_disasm.str();
return instruction_count;
}
#endif
std::unique_ptr<HostDisassembler> GetNewDisassembler(const std::string& arch)
std::unique_ptr<HostDisassembler> HostDisassembler::Factory(Platform arch)
{
switch (arch)
{
#if defined(HAVE_LLVM)
if (arch == "x86")
case Platform::x86_64:
return std::make_unique<HostDisassemblerLLVM>("x86_64-none-unknown");
if (arch == "aarch64")
return std::make_unique<HostDisassemblerLLVM>("aarch64-none-unknown", 4, "cortex-a57");
if (arch == "armv7")
return std::make_unique<HostDisassemblerLLVM>("armv7-none-unknown", 4, "cortex-a15");
case Platform::aarch64:
return std::make_unique<HostDisassemblerLLVM>("aarch64-none-unknown", "cortex-a57", 4);
#elif defined(_M_X86_64)
if (arch == "x86")
return std::make_unique<HostDisassemblerX86>();
case Platform::x86_64:
return std::make_unique<HostDisassemblerBochs>();
#else
case Platform{}: // warning C4065: "switch statement contains 'default' but no 'case' labels"
#endif
return std::make_unique<HostDisassembler>();
default:
return std::make_unique<HostDisassembler>();
}
}
DisassembleResult DisassembleBlock(HostDisassembler* disasm, u32 address)
std::size_t HostDisassembler::Disassemble(const u8* begin, const u8* end, std::ostream& stream)
{
auto res = Core::System::GetInstance().GetJitInterface().GetHostCode(address);
return std::visit(overloaded{[&](JitInterface::GetHostCodeError error) {
DisassembleResult result;
switch (error)
{
case JitInterface::GetHostCodeError::NoJitActive:
result.text = "(No JIT active)";
break;
case JitInterface::GetHostCodeError::NoTranslation:
result.text = "(No translation)";
break;
default:
ASSERT(false);
break;
}
result.entry_address = address;
result.instruction_count = 0;
result.code_size = 0;
return result;
},
[&](JitInterface::GetHostCodeResult host_result) {
DisassembleResult new_result;
u32 instruction_count = 0;
new_result.text = disasm->DisassembleHostBlock(
host_result.code, host_result.code_size, &instruction_count,
(u64)host_result.code);
new_result.entry_address = host_result.entry_address;
new_result.code_size = host_result.code_size;
new_result.instruction_count = instruction_count;
return new_result;
}},
res);
fmt::println(stream, "{}\t{:02x}", fmt::ptr(begin), fmt::join(std::span{begin, end}, ""));
return 0;
}
+12 -16
View File
@@ -3,28 +3,24 @@
#pragma once
#include <cstddef>
#include <iosfwd>
#include <memory>
#include <string>
#include "Common/CommonTypes.h"
class HostDisassembler
{
public:
virtual ~HostDisassembler() {}
virtual std::string DisassembleHostBlock(const u8* code_start, const u32 code_size,
u32* host_instructions_count, u64 starting_pc)
enum class Platform
{
return "(No disassembler)";
}
};
x86_64,
aarch64,
};
struct DisassembleResult
{
std::string text;
u32 entry_address = 0;
u32 instruction_count = 0;
u32 code_size = 0;
};
virtual ~HostDisassembler() = default;
std::unique_ptr<HostDisassembler> GetNewDisassembler(const std::string& arch);
DisassembleResult DisassembleBlock(HostDisassembler* disasm, u32 address);
static std::unique_ptr<HostDisassembler> Factory(Platform arch);
virtual std::size_t Disassemble(const u8* begin, const u8* end, std::ostream& stream);
};
+2
View File
@@ -60,6 +60,8 @@ void Host_PPCSymbolsChanged();
void Host_RefreshDSPDebuggerWindow();
void Host_RequestRenderWindowSize(int width, int height);
void Host_UpdateDisasmDialog();
void Host_JitCacheCleared();
void Host_JitProfileDataWiped();
void Host_UpdateMainFrame();
void Host_UpdateTitle(const std::string& title);
void Host_YieldToUI();
@@ -10,6 +10,7 @@
#include "Core/CoreTiming.h"
#include "Core/HLE/HLE.h"
#include "Core/HW/CPU.h"
#include "Core/Host.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/Interpreter/Interpreter.h"
#include "Core/PowerPC/Jit64Common/Jit64Constants.h"
@@ -290,13 +291,12 @@ void CachedInterpreter::Jit(u32 em_address, bool clear_cache_and_retry_on_failur
b->far_begin = b->far_end = nullptr;
b->codeSize = static_cast<u32>(b->near_end - b->normalEntry);
b->originalSize = code_block.m_num_instructions;
// Mark the memory region that this code block uses in the RangeSizeSet.
if (b->near_begin != b->near_end)
m_free_ranges.erase(b->near_begin, b->near_end);
m_block_cache.FinalizeBlock(*b, jo.enableBlocklink, code_block.m_physical_addresses);
m_block_cache.FinalizeBlock(*b, jo.enableBlocklink, code_block, m_code_buffer);
return;
}
@@ -413,6 +413,18 @@ std::vector<JitBase::MemoryStats> CachedInterpreter::GetMemoryStats() const
return {{"free", m_free_ranges.get_stats()}};
}
std::size_t CachedInterpreter::DisassembleNearCode(const JitBlock& block,
std::ostream& stream) const
{
return Disassemble(block, stream);
}
std::size_t CachedInterpreter::DisassembleFarCode(const JitBlock& block, std::ostream& stream) const
{
stream << "N/A\n";
return 0;
}
void CachedInterpreter::ClearCache()
{
m_block_cache.Clear();
@@ -420,4 +432,5 @@ void CachedInterpreter::ClearCache()
ClearCodeSpace();
ResetFreeMemoryRanges();
RefreshConfig();
Host_JitCacheCleared();
}
@@ -51,6 +51,9 @@ public:
static std::size_t Disassemble(const JitBlock& block, std::ostream& stream);
std::size_t DisassembleNearCode(const JitBlock& block, std::ostream& stream) const override;
std::size_t DisassembleFarCode(const JitBlock& block, std::ostream& stream) const override;
JitBaseBlockCache* GetBlockCache() override { return &m_block_cache; }
const char* GetName() const override { return "Cached Interpreter"; }
const CommonAsmRoutinesBase* GetAsmRoutines() override { return nullptr; }
+41 -3
View File
@@ -9,6 +9,7 @@
#include <disasm.h>
#include <fmt/format.h>
#include <fmt/ostream.h>
// for the PROFILER stuff
#ifdef _WIN32
@@ -18,6 +19,7 @@
#include "Common/CommonTypes.h"
#include "Common/EnumUtils.h"
#include "Common/GekkoDisassembler.h"
#include "Common/HostDisassembler.h"
#include "Common/IOFile.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
@@ -30,6 +32,7 @@
#include "Core/HW/GPFifo.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/Host.h"
#include "Core/MachineContext.h"
#include "Core/PatchEngine.h"
#include "Core/PowerPC/Interpreter/Interpreter.h"
@@ -116,7 +119,9 @@ using namespace PowerPC;
and such, but it's currently limited to integer ops only. This can definitely be made better.
*/
Jit64::Jit64(Core::System& system) : JitBase(system), QuantizedMemoryRoutines(*this)
Jit64::Jit64(Core::System& system)
: JitBase(system), QuantizedMemoryRoutines(*this),
m_disassembler(HostDisassembler::Factory(HostDisassembler::Platform::x86_64))
{
}
@@ -308,6 +313,7 @@ void Jit64::ClearCache()
RefreshConfig();
asm_routines.Regenerate();
ResetFreeMemoryRanges();
Host_JitCacheCleared();
}
void Jit64::FreeRanges()
@@ -826,7 +832,7 @@ void Jit64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure)
b->far_begin = far_start;
b->far_end = far_end;
blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block.m_physical_addresses);
blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block, m_code_buffer);
return;
}
}
@@ -1193,7 +1199,6 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
}
b->codeSize = static_cast<u32>(GetCodePtr() - b->normalEntry);
b->originalSize = code_block.m_num_instructions;
#ifdef JIT_LOG_GENERATED_CODE
LogGeneratedX86(code_block.m_num_instructions, m_code_buffer, start, b);
@@ -1213,6 +1218,39 @@ std::vector<JitBase::MemoryStats> Jit64::GetMemoryStats() const
return {{"near", m_free_ranges_near.get_stats()}, {"far", m_free_ranges_far.get_stats()}};
}
std::size_t Jit64::DisassembleNearCode(const JitBlock& block, std::ostream& stream) const
{
// The last element of the JitBlock::linkData vector is not necessarily the furthest exit.
// See: Jit64::JustWriteExit
const auto iter = std::ranges::max_element(block.linkData, {}, &JitBlock::LinkData::exitPtrs);
// Link data is not guaranteed, e.g. Jit64::WriteRfiExitDestInRSCRATCH
if (iter == block.linkData.end())
return m_disassembler->Disassemble(block.normalEntry, block.near_end, stream);
// A JitBlock's near_end only records where the XEmitter was after DoJit concludes. However, a
// JitBlock's exits will be modified by block linking. If Block A wants to link its final exit
// to the entry_point of Block B, and Block B follows Block A in memory, then the final exit's
// JMP will not have its destination modified but will instead be overwritten by a multibyte NOP.
// Trickily, Block A's near_end does not necessarily equal Block B's entry_point because Block B's
// entry_point is aligned to the next multiple of 4! This means the multibyte NOP may need to
// extend past Block A's near_end, complicating host code disassembly. If the opcode of a JMP
// instruction is found at the final exit, the block will be disassembled like normal. If one
// is not, the exit is assumed to be overwritten, and special action is taken.
const u8* const furthest_exit = iter->exitPtrs;
if (*furthest_exit == 0xE9)
return m_disassembler->Disassemble(block.normalEntry, block.near_end, stream);
const auto inst_count = m_disassembler->Disassemble(block.normalEntry, furthest_exit, stream);
fmt::println(stream, "{}\tmultibyte nop", fmt::ptr(furthest_exit));
return inst_count + 1;
}
std::size_t Jit64::DisassembleFarCode(const JitBlock& block, std::ostream& stream) const
{
return m_disassembler->Disassemble(block.far_begin, block.far_end, stream);
}
BitSet8 Jit64::ComputeStaticGQRs(const PPCAnalyst::CodeBlock& cb) const
{
return cb.m_gqr_used & ~cb.m_gqr_modified;
+5
View File
@@ -34,6 +34,7 @@
#include "Core/PowerPC/JitCommon/JitBase.h"
#include "Core/PowerPC/JitCommon/JitCache.h"
class HostDisassembler;
namespace PPCAnalyst
{
struct CodeBlock;
@@ -68,6 +69,9 @@ public:
void EraseSingleBlock(const JitBlock& block) override;
std::vector<MemoryStats> GetMemoryStats() const override;
std::size_t DisassembleNearCode(const JitBlock& block, std::ostream& stream) const override;
std::size_t DisassembleFarCode(const JitBlock& block, std::ostream& stream) const override;
// Finds a free memory region and sets the near and far code emitters to point at that region.
// Returns false if no free memory region can be found for either of the two.
bool SetEmitterStateToFreeCodeRegion();
@@ -288,6 +292,7 @@ private:
const bool m_im_here_debug = false;
const bool m_im_here_log = false;
std::map<u32, int> m_been_here;
std::unique_ptr<HostDisassembler> m_disassembler;
};
void LogGeneratedX86(size_t size, const PPCAnalyst::CodeBuffer& code_buffer, const u8* normalEntry,
+18 -3
View File
@@ -9,6 +9,7 @@
#include "Common/Arm64Emitter.h"
#include "Common/CommonTypes.h"
#include "Common/EnumUtils.h"
#include "Common/HostDisassembler.h"
#include "Common/Logging/Log.h"
#include "Common/MathUtil.h"
#include "Common/MsgHandler.h"
@@ -22,6 +23,7 @@
#include "Core/HW/GPFifo.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/Host.h"
#include "Core/PatchEngine.h"
#include "Core/PowerPC/Interpreter/Interpreter.h"
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
@@ -39,7 +41,9 @@ constexpr size_t NEAR_CODE_SIZE = 1024 * 1024 * 64;
constexpr size_t FAR_CODE_SIZE = 1024 * 1024 * 64;
constexpr size_t TOTAL_CODE_SIZE = NEAR_CODE_SIZE * 2 + FAR_CODE_SIZE * 2;
JitArm64::JitArm64(Core::System& system) : JitBase(system), m_float_emit(this)
JitArm64::JitArm64(Core::System& system)
: JitBase(system), m_float_emit(this),
m_disassembler(HostDisassembler::Factory(HostDisassembler::Platform::aarch64))
{
}
@@ -186,6 +190,8 @@ void JitArm64::GenerateAsmAndResetFreeMemoryRanges()
ResetFreeMemoryRanges(routines_near_end - routines_near_start,
routines_far_end - routines_far_start);
Host_JitCacheCleared();
}
void JitArm64::FreeRanges()
@@ -1014,7 +1020,7 @@ void JitArm64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure)
b->far_begin = far_start;
b->far_end = far_end;
blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block.m_physical_addresses);
blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block, m_code_buffer);
return;
}
}
@@ -1048,6 +1054,16 @@ std::vector<JitBase::MemoryStats> JitArm64::GetMemoryStats() const
{"far_1", m_free_ranges_far_1.get_stats()}};
}
std::size_t JitArm64::DisassembleNearCode(const JitBlock& block, std::ostream& stream) const
{
return m_disassembler->Disassemble(block.normalEntry, block.near_end, stream);
}
std::size_t JitArm64::DisassembleFarCode(const JitBlock& block, std::ostream& stream) const
{
return m_disassembler->Disassemble(block.far_begin, block.far_end, stream);
}
std::optional<size_t> JitArm64::SetEmitterStateToFreeCodeRegion()
{
// Find some large free memory blocks and set code emitters to point at them. If we can't find
@@ -1366,7 +1382,6 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
}
b->codeSize = static_cast<u32>(GetCodePtr() - b->normalEntry);
b->originalSize = code_block.m_num_instructions;
FlushIcache();
m_far_code.FlushIcache();
+7
View File
@@ -20,6 +20,8 @@
#include "Core/PowerPC/JitCommon/JitBase.h"
#include "Core/PowerPC/PPCAnalyst.h"
class HostDisassembler;
class JitArm64 : public JitBase, public Arm64Gen::ARM64CodeBlock, public CommonAsmRoutinesBase
{
public:
@@ -51,6 +53,9 @@ public:
void EraseSingleBlock(const JitBlock& block) override;
std::vector<MemoryStats> GetMemoryStats() const override;
std::size_t DisassembleNearCode(const JitBlock& block, std::ostream& stream) const override;
std::size_t DisassembleFarCode(const JitBlock& block, std::ostream& stream) const override;
const char* GetName() const override { return "JITARM64"; }
// OPCODES
@@ -413,4 +418,6 @@ protected:
HyoutaUtilities::RangeSizeSet<u8*> m_free_ranges_near_1;
HyoutaUtilities::RangeSizeSet<u8*> m_free_ranges_far_0;
HyoutaUtilities::RangeSizeSet<u8*> m_free_ranges_far_1;
std::unique_ptr<HostDisassembler> m_disassembler;
};
@@ -5,6 +5,7 @@
#include <array>
#include <cstddef>
#include <iosfwd>
#include <map>
#include <string_view>
#include <unordered_set>
@@ -202,6 +203,9 @@ public:
using MemoryStats = std::pair<std::string_view, std::pair<std::size_t, double>>;
virtual std::vector<MemoryStats> GetMemoryStats() const = 0;
virtual std::size_t DisassembleNearCode(const JitBlock& block, std::ostream& stream) const = 0;
virtual std::size_t DisassembleFarCode(const JitBlock& block, std::ostream& stream) const = 0;
virtual const CommonAsmRoutinesBase* GetAsmRoutines() = 0;
virtual bool HandleFault(uintptr_t access_address, SContext* ctx) = 0;
@@ -8,13 +8,16 @@
#include <cstring>
#include <functional>
#include <map>
#include <ranges>
#include <set>
#include <span>
#include <utility>
#include "Common/CommonTypes.h"
#include "Common/JitRegister.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/Host.h"
#include "Core/PowerPC/JitCommon/JitBase.h"
#include "Core/PowerPC/MMU.h"
#include "Core/PowerPC/PPCSymbolDB.h"
@@ -124,6 +127,7 @@ void JitBaseBlockCache::WipeBlockProfilingData(const Core::CPUThreadGuard&)
if (JitBlock::ProfileData* const profile_data = kv.second.profile_data.get())
*profile_data = {};
}
Host_JitProfileDataWiped();
}
JitBlock* JitBaseBlockCache::AllocateBlock(u32 em_address)
@@ -139,7 +143,8 @@ JitBlock* JitBaseBlockCache::AllocateBlock(u32 em_address)
}
void JitBaseBlockCache::FinalizeBlock(JitBlock& block, bool block_link,
const std::set<u32>& physical_addresses)
const PPCAnalyst::CodeBlock& code_block,
const PPCAnalyst::CodeBuffer& code_buffer)
{
size_t index = FastLookupIndexForAddress(block.effectiveAddress, block.feature_flags);
if (m_entry_points_ptr)
@@ -153,7 +158,18 @@ void JitBaseBlockCache::FinalizeBlock(JitBlock& block, bool block_link,
}
block.fast_block_map_index = index;
block.physical_addresses = physical_addresses;
block.physical_addresses = code_block.m_physical_addresses;
block.originalSize = code_block.m_num_instructions;
if (m_jit.IsDebuggingEnabled())
{
// TODO C++23: Can do this all in one statement with `std::vector::assign_range`.
const std::ranges::transform_view original_buffer_transform_view{
std::span{code_buffer.data(), block.originalSize},
[](const PPCAnalyst::CodeOp& op) { return std::make_pair(op.address, op.inst); }};
block.original_buffer.assign(original_buffer_transform_view.begin(),
original_buffer_transform_view.end());
}
for (u32 addr : block.physical_addresses)
{
@@ -175,13 +191,14 @@ void JitBaseBlockCache::FinalizeBlock(JitBlock& block, bool block_link,
if (Common::JitRegister::IsEnabled() &&
(symbol = m_jit.m_ppc_symbol_db.GetSymbolFromAddr(block.effectiveAddress)) != nullptr)
{
Common::JitRegister::Register(block.normalEntry, block.codeSize, "JIT_PPC_{}_{:08x}",
symbol->function_name.c_str(), block.physicalAddress);
Common::JitRegister::Register(block.normalEntry, block.near_end - block.normalEntry,
"JIT_PPC_{}_{:08x}", symbol->function_name,
block.physicalAddress);
}
else
{
Common::JitRegister::Register(block.normalEntry, block.codeSize, "JIT_PPC_{:08x}",
block.physicalAddress);
Common::JitRegister::Register(block.normalEntry, block.near_end - block.normalEntry,
"JIT_PPC_{:08x}", block.physicalAddress);
}
}
@@ -19,6 +19,7 @@
#include "Common/CommonTypes.h"
#include "Core/HW/Memmap.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/PPCAnalyst.h"
class JitBase;
@@ -105,6 +106,10 @@ struct JitBlock : public JitBlockData
// This set stores all physical addresses of all occupied instructions.
std::set<u32> physical_addresses;
// This is only available when debugging is enabled. It is a trimmed-down copy of the
// PPCAnalyst::CodeBuffer used to recompile this block, including repeat instructions.
std::vector<std::pair<u32, UGeckoInstruction>> original_buffer;
std::unique_ptr<ProfileData> profile_data;
};
@@ -162,9 +167,11 @@ public:
JitBlock** GetFastBlockMapFallback();
void RunOnBlocks(const Core::CPUThreadGuard& guard, std::function<void(const JitBlock&)> f) const;
void WipeBlockProfilingData(const Core::CPUThreadGuard& guard);
std::size_t GetBlockCount() const { return block_map.size(); }
JitBlock* AllocateBlock(u32 em_address);
void FinalizeBlock(JitBlock& block, bool block_link, const std::set<u32>& physical_addresses);
void FinalizeBlock(JitBlock& block, bool block_link, const PPCAnalyst::CodeBlock& code_block,
const PPCAnalyst::CodeBuffer& code_buffer);
// Look for the block in the slow but accurate way.
// This function shall be used if FastLookupIndexForAddress() failed.
+24 -38
View File
@@ -188,46 +188,18 @@ void JitInterface::WipeBlockProfilingData(const Core::CPUThreadGuard& guard)
m_jit->GetBlockCache()->WipeBlockProfilingData(guard);
}
std::variant<JitInterface::GetHostCodeError, JitInterface::GetHostCodeResult>
JitInterface::GetHostCode(u32 address) const
void JitInterface::RunOnBlocks(const Core::CPUThreadGuard& guard,
std::function<void(const JitBlock&)> f) const
{
if (!m_jit)
{
return GetHostCodeError::NoJitActive;
}
if (m_jit)
m_jit->GetBlockCache()->RunOnBlocks(guard, std::move(f));
}
auto& ppc_state = m_system.GetPPCState();
JitBlock* block =
m_jit->GetBlockCache()->GetBlockFromStartAddress(address, ppc_state.feature_flags);
if (!block)
{
for (int i = 0; i < 500; i++)
{
block = m_jit->GetBlockCache()->GetBlockFromStartAddress(address - 4 * i,
ppc_state.feature_flags);
if (block)
break;
}
if (block)
{
if (!(block->effectiveAddress <= address &&
block->originalSize + block->effectiveAddress >= address))
block = nullptr;
}
// Do not merge this "if" with the above - block changes inside it.
if (!block)
{
return GetHostCodeError::NoTranslation;
}
}
GetHostCodeResult result;
result.code = block->normalEntry;
result.code_size = block->codeSize;
result.entry_address = block->effectiveAddress;
return result;
std::size_t JitInterface::GetBlockCount() const
{
if (m_jit)
return m_jit->GetBlockCache()->GetBlockCount();
return 0;
}
bool JitInterface::HandleFault(uintptr_t access_address, SContext* ctx)
@@ -276,6 +248,20 @@ std::vector<JitBase::MemoryStats> JitInterface::GetMemoryStats() const
return {};
}
std::size_t JitInterface::DisassembleNearCode(const JitBlock& block, std::ostream& stream) const
{
if (m_jit)
return m_jit->DisassembleNearCode(block, stream);
return 0;
}
std::size_t JitInterface::DisassembleFarCode(const JitBlock& block, std::ostream& stream) const
{
if (m_jit)
return m_jit->DisassembleFarCode(block, stream);
return 0;
}
void JitInterface::InvalidateICache(u32 address, u32 size, bool forced)
{
if (m_jit)
+7 -16
View File
@@ -6,11 +6,10 @@
#include <cstddef>
#include <cstdio>
#include <functional>
#include <iosfwd>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include <vector>
#include "Common/CommonTypes.h"
@@ -46,23 +45,11 @@ public:
CPUCoreBase* InitJitCore(PowerPC::CPUCore core);
CPUCoreBase* GetCore() const;
// Debugging
enum class GetHostCodeError
{
NoJitActive,
NoTranslation,
};
struct GetHostCodeResult
{
const u8* code;
u32 code_size;
u32 entry_address;
};
void UpdateMembase();
void JitBlockLogDump(const Core::CPUThreadGuard& guard, std::FILE* file) const;
void WipeBlockProfilingData(const Core::CPUThreadGuard& guard);
std::variant<GetHostCodeError, GetHostCodeResult> GetHostCode(u32 address) const;
void RunOnBlocks(const Core::CPUThreadGuard& guard, std::function<void(const JitBlock&)> f) const;
std::size_t GetBlockCount() const;
// Memory Utilities
bool HandleFault(uintptr_t access_address, SContext* ctx);
@@ -85,6 +72,10 @@ public:
using MemoryStats = std::pair<std::string_view, std::pair<std::size_t, double>>;
std::vector<MemoryStats> GetMemoryStats() const;
// Disassemble the recompiled code from a JIT block. Returns the disassembled instruction count.
std::size_t DisassembleNearCode(const JitBlock& block, std::ostream& stream) const;
std::size_t DisassembleFarCode(const JitBlock& block, std::ostream& stream) const;
// If "forced" is true, a recompile is being requested on code that hasn't been modified.
void InvalidateICache(u32 address, u32 size, bool forced);
void InvalidateICacheLine(u32 address);
+8
View File
@@ -86,6 +86,14 @@ void Host_UpdateDisasmDialog()
{
}
void Host_JitCacheCleared()
{
}
void Host_JitProfileDataWiped()
{
}
void Host_UpdateMainFrame()
{
s_update_main_frame_event.Set();
+3
View File
@@ -222,6 +222,8 @@ add_executable(dolphin-emu
Debugger/CodeWidget.h
Debugger/GekkoSyntaxHighlight.cpp
Debugger/GekkoSyntaxHighlight.h
Debugger/JitBlockTableModel.cpp
Debugger/JitBlockTableModel.h
Debugger/JITWidget.cpp
Debugger/JITWidget.h
Debugger/MemoryViewWidget.cpp
@@ -297,6 +299,7 @@ add_executable(dolphin-emu
QtUtils/BlockUserInputFilter.h
QtUtils/ClearLayoutRecursively.cpp
QtUtils/ClearLayoutRecursively.h
QtUtils/ClickableStatusBar.h
QtUtils/DolphinFileDialog.cpp
QtUtils/DolphinFileDialog.h
QtUtils/DoubleClickEventFilter.cpp
@@ -881,7 +881,7 @@ void CodeViewWidget::OnPPCComparison()
{
const u32 addr = GetContextAddress();
emit RequestPPCComparison(addr);
emit RequestPPCComparison(addr, m_system.GetPPCState().msr.IR);
}
void CodeViewWidget::OnAddFunction()
@@ -55,7 +55,7 @@ public:
u32 AddressForRow(int row) const;
signals:
void RequestPPCComparison(u32 addr);
void RequestPPCComparison(u32 address, bool translate_address);
void ShowMemory(u32 address);
void UpdateCodeWidget();
@@ -210,6 +210,11 @@ void CodeWidget::OnBranchWatchDialog()
m_branch_watch_dialog->activateWindow();
}
void CodeWidget::OnSetCodeAddress(u32 address)
{
SetAddress(address, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate);
}
void CodeWidget::OnPPCSymbolsChanged()
{
UpdateSymbols();

Some files were not shown because too many files have changed in this diff Show More