JITs: Revamp LogGeneratedX86

Debug logging of recompiled code is now a feature of all three JITs.
This commit is contained in:
mitaclaw 2024-07-26 18:41:22 -07:00
parent b3c4e7730e
commit 8dd3782803
8 changed files with 90 additions and 52 deletions

View file

@ -653,11 +653,6 @@ PRIVATE
ZLIB::ZLIB
)
if ((DEFINED CMAKE_ANDROID_ARCH_ABI AND CMAKE_ANDROID_ARCH_ABI MATCHES "x86|x86_64") OR
(NOT DEFINED CMAKE_ANDROID_ARCH_ABI AND _M_X86_64))
target_link_libraries(core PRIVATE bdisasm)
endif()
if (APPLE)
target_link_libraries(core
PRIVATE

View file

@ -3,7 +3,15 @@
#include "Core/PowerPC/CachedInterpreter/CachedInterpreter.h"
#include <span>
#include <sstream>
#include <utility>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include "Common/CommonTypes.h"
#include "Common/GekkoDisassembler.h"
#include "Common/Logging/Log.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
@ -289,14 +297,16 @@ void CachedInterpreter::Jit(u32 em_address, bool clear_cache_and_retry_on_failur
b->near_end = GetWritableCodePtr();
b->far_begin = b->far_end = nullptr;
b->codeSize = static_cast<u32>(b->near_end - b->normalEntry);
// 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_code_buffer);
#ifdef JIT_LOG_GENERATED_CODE
LogGeneratedCode();
#endif
return;
}
}
@ -432,3 +442,22 @@ void CachedInterpreter::ClearCache()
RefreshConfig();
JitBase::ClearCache();
}
void CachedInterpreter::LogGeneratedCode() const
{
std::ostringstream stream;
stream << "\nPPC Code Buffer:\n";
for (const PPCAnalyst::CodeOp& op :
std::span{m_code_buffer.data(), code_block.m_num_instructions})
{
fmt::print(stream, "0x{:08x}\t\t{}\n", op.address,
Common::GekkoDisassembler::Disassemble(op.inst.hex, op.address));
}
stream << "\nHost Code:\n";
Disassemble(*js.curBlock, stream);
// TODO C++20: std::ostringstream::view()
DEBUG_LOG_FMT(DYNA_REC, "{}", std::move(stream).str());
}

View file

@ -71,6 +71,8 @@ private:
void FreeRanges();
void ResetFreeMemoryRanges();
void LogGeneratedCode() const;
struct StartProfiledBlockOperands;
template <bool profiled>
struct EndBlockOperands;

View file

@ -4,11 +4,12 @@
#include "Core/PowerPC/Jit64/Jit.h"
#include <map>
#include <span>
#include <sstream>
#include <string>
#include <disasm.h>
#include <fmt/format.h>
#include <fmt/ostream.h>
// for the PROFILER stuff
#ifdef _WIN32
@ -831,6 +832,10 @@ void Jit64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure)
b->far_end = far_end;
blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block, m_code_buffer);
#ifdef JIT_LOG_GENERATED_CODE
LogGeneratedCode();
#endif
return;
}
}
@ -1196,12 +1201,6 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
return false;
}
b->codeSize = static_cast<u32>(GetCodePtr() - b->normalEntry);
#ifdef JIT_LOG_GENERATED_CODE
LogGeneratedX86(code_block.m_num_instructions, m_code_buffer, start, b);
#endif
return true;
}
@ -1328,39 +1327,24 @@ bool Jit64::HandleFunctionHooking(u32 address)
return true;
}
void LogGeneratedX86(size_t size, const PPCAnalyst::CodeBuffer& code_buffer, const u8* normalEntry,
const JitBlock* b)
void Jit64::LogGeneratedCode() const
{
for (size_t i = 0; i < size; i++)
std::ostringstream stream;
stream << "\nPPC Code Buffer:\n";
for (const PPCAnalyst::CodeOp& op :
std::span{m_code_buffer.data(), code_block.m_num_instructions})
{
const PPCAnalyst::CodeOp& op = code_buffer[i];
const std::string disasm = Common::GekkoDisassembler::Disassemble(op.inst.hex, op.address);
DEBUG_LOG_FMT(DYNA_REC, "IR_X86 PPC: {:08x} {}\n", op.address, disasm);
fmt::print(stream, "0x{:08x}\t\t{}\n", op.address,
Common::GekkoDisassembler::Disassemble(op.inst.hex, op.address));
}
disassembler x64disasm;
x64disasm.set_syntax_intel();
const JitBlock* const block = js.curBlock;
stream << "\nHost Near Code:\n";
m_disassembler->Disassemble(block->normalEntry, block->near_end, stream);
stream << "\nHost Far Code:\n";
m_disassembler->Disassemble(block->far_begin, block->far_end, stream);
u64 disasmPtr = reinterpret_cast<u64>(normalEntry);
const u8* end = normalEntry + b->codeSize;
while (reinterpret_cast<u8*>(disasmPtr) < end)
{
char sptr[1000] = "";
disasmPtr += x64disasm.disasm64(disasmPtr, disasmPtr, reinterpret_cast<u8*>(disasmPtr), sptr);
DEBUG_LOG_FMT(DYNA_REC, "IR_X86 x86: {}", sptr);
}
if (b->codeSize <= 250)
{
std::ostringstream ss;
ss << std::hex;
for (u8 i = 0; i <= b->codeSize; i++)
{
ss.width(2);
ss.fill('0');
ss << static_cast<u32>(*(normalEntry + i));
}
DEBUG_LOG_FMT(DYNA_REC, "IR_X86 bin: {}\n\n\n", ss.str());
}
// TODO C++20: std::ostringstream::view()
DEBUG_LOG_FMT(DYNA_REC, "{}", std::move(stream).str());
}

View file

@ -276,6 +276,8 @@ private:
void FreeRanges();
void ResetFreeMemoryRanges();
void LogGeneratedCode() const;
static void ImHere(Jit64& jit);
JitBlockCache blocks{*this};
@ -294,6 +296,3 @@ private:
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,
const JitBlock* b);

View file

@ -5,10 +5,16 @@
#include <cstdio>
#include <optional>
#include <span>
#include <sstream>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include "Common/Arm64Emitter.h"
#include "Common/CommonTypes.h"
#include "Common/EnumUtils.h"
#include "Common/GekkoDisassembler.h"
#include "Common/HostDisassembler.h"
#include "Common/Logging/Log.h"
#include "Common/MathUtil.h"
@ -1020,6 +1026,10 @@ void JitArm64::Jit(u32 em_address, bool clear_cache_and_retry_on_failure)
b->far_end = far_end;
blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block, m_code_buffer);
#ifdef JIT_LOG_GENERATED_CODE
LogGeneratedCode();
#endif
return;
}
}
@ -1380,10 +1390,30 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
return false;
}
b->codeSize = static_cast<u32>(GetCodePtr() - b->normalEntry);
FlushIcache();
m_far_code.FlushIcache();
return true;
}
void JitArm64::LogGeneratedCode() const
{
std::ostringstream stream;
stream << "\nPPC Code Buffer:\n";
for (const PPCAnalyst::CodeOp& op :
std::span{m_code_buffer.data(), code_block.m_num_instructions})
{
fmt::print(stream, "0x{:08x}\t\t{}\n", op.address,
Common::GekkoDisassembler::Disassemble(op.inst.hex, op.address));
}
const JitBlock* const block = js.curBlock;
stream << "\nHost Near Code:\n";
m_disassembler->Disassemble(block->normalEntry, block->near_end, stream);
stream << "\nHost Far Code:\n";
m_disassembler->Disassemble(block->far_begin, block->far_end, stream);
// TODO C++20: std::ostringstream::view()
DEBUG_LOG_FMT(DYNA_REC, "{}", std::move(stream).str());
}

View file

@ -308,6 +308,8 @@ protected:
void IntializeSpeculativeConstants();
void LogGeneratedCode() const;
// AsmRoutines
void GenerateAsm();
void GenerateCommonAsm();

View file

@ -45,9 +45,6 @@ struct JitBlockData
// and valid_block in particular). This is useful because of
// of the way the instruction cache works on PowerPC.
u32 physicalAddress;
// The number of bytes of JIT'ed code contained in this block. Mostly
// useful for logging.
u32 codeSize;
// The number of PPC instructions represented by this block. Mostly
// useful for logging.
u32 originalSize;