mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
DolphinQt: JIT Widget Refresh
Fulfilling a certain six-year-old todo.
This commit is contained in:
@@ -146,6 +146,14 @@ void Host_UpdateDisasmDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_JitCacheCleared()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_JitProfileDataWiped()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_UpdateMainFrame()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -86,6 +86,14 @@ void Host_UpdateDisasmDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_JitCacheCleared()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_JitProfileDataWiped()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_UpdateMainFrame()
|
||||
{
|
||||
s_update_main_frame_event.Set();
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user