CPU/CodeCache: Pack state in struct

pull/3731/head
Stenzek 5 months ago
parent df64109df6
commit f9254e23f0
No known key found for this signature in database

@ -52,6 +52,23 @@ static constexpr u32 RECOMPILE_FRAMES_FOR_INTERPRETER_FALLBACK = 15;
static constexpr u32 INVALIDATE_COUNT_FOR_MANUAL_PROTECTION = 4;
static constexpr u32 INVALIDATE_FRAMES_FOR_MANUAL_PROTECTION = 60;
#if defined(CPU_ARCH_ARM32)
// Use a smaller code buffer size on AArch32 to have a better chance of being in range.
static constexpr u32 RECOMPILER_CODE_CACHE_SIZE = 16 * 1024 * 1024;
static constexpr u32 RECOMPILER_FAR_CODE_CACHE_SIZE = 4 * 1024 * 1024;
#else
static constexpr u32 RECOMPILER_CODE_CACHE_SIZE = 48 * 1024 * 1024;
static constexpr u32 RECOMPILER_FAR_CODE_CACHE_SIZE = 16 * 1024 * 1024;
#endif
// On Linux ARM32/ARM64, we use a dedicated section in the ELF for storing code. This is because without
// ASLR, or on certain ASLR offsets, the sbrk() heap ends up immediately following the text/data sections,
// which means there isn't a large enough gap to fit within range on ARM32. Also enable it for Android,
// because MAP_FIXED_NOREPLACE may not exist on older kernels.
#if (defined(__linux__) && (defined(CPU_ARCH_ARM32) || defined(CPU_ARCH_ARM64))) || defined(__ANDROID__)
#define USE_CODE_BUFFER_SECTION 1
#endif
static void AllocateLUTs();
static void DeallocateLUTs();
static void ResetCodeLUT();
@ -78,18 +95,6 @@ static Block* CreateCachedInterpreterBlock(u32 pc);
template<PGXPMode pgxp_mode>
[[noreturn]] static void ExecuteCachedInterpreterImpl();
// Fast map provides lookup from PC to function
// Function pointers are offset so that you don't need to subtract
CodeLUTArray g_code_lut;
static BlockLUTArray s_block_lut;
static std::unique_ptr<const void*[]> s_lut_code_pointers;
static std::unique_ptr<Block*[]> s_lut_block_pointers;
static PageProtectionArray s_page_protection = {};
static std::vector<Block*> s_blocks;
// for compiling - reuse to avoid allocations
static BlockInstructionList s_block_instructions;
static void BacklinkBlocks(u32 pc, const void* dst);
static void UnlinkBlockExits(Block* block);
static void ResetCodeBuffer();
@ -100,16 +105,44 @@ static PageFaultHandler::HandlerResult HandleFastmemException(void* exception_pc
static void BackpatchLoadStore(void* host_pc, const LoadstoreBackpatchInfo& info);
static void RemoveBackpatchInfoForRange(const void* host_code, u32 size);
static BlockLinkMap s_block_links;
static std::map<const void*, LoadstoreBackpatchInfo> s_fastmem_backpatch_info;
static std::unordered_set<u32> s_fastmem_faulting_pcs;
namespace {
struct Locals
{
std::unique_ptr<const void*[]> lut_code_pointers;
std::unique_ptr<Block*[]> lut_block_pointers;
std::vector<Block*> blocks;
BlockLinkMap block_links;
std::map<const void*, LoadstoreBackpatchInfo> fastmem_backpatch_info;
std::unordered_set<u32> fastmem_faulting_pcs;
// for compiling - reuse to avoid allocations
BlockInstructionList block_instructions;
u8* code_ptr = nullptr;
u8* free_code_ptr = nullptr;
u32 code_size = 0;
u32 code_used = 0;
u8* far_code_ptr = nullptr;
u8* free_far_code_ptr = nullptr;
u32 far_code_size = 0;
u32 far_code_used = 0;
#ifndef USE_CODE_BUFFER_SECTION
u8* code_buffer_ptr = nullptr;
#endif
#ifdef DUMP_CODE_SIZE_STATS
u32 total_instructions_compiled = 0;
u32 total_host_instructions_emitted = 0;
u32 total_host_code_used_by_instructions = 0;
#endif
};
} // namespace
NORETURN_FUNCTION_POINTER void (*g_enter_recompiler)();
const void* g_compile_or_revalidate_block;
const void* g_run_events_and_dispatch;
const void* g_dispatcher;
const void* g_interpret_block;
const void* g_discard_and_recompile_block;
ALIGN_TO_CACHE_LINE static Locals s_locals;
RecompilerFunctions g_recompiler_functions;
#ifdef ENABLE_RECOMPILER_PROFILING
@ -117,45 +150,18 @@ PerfScope MIPSPerfScope("MIPS");
#endif
#if defined(CPU_ARCH_ARM32)
// Use a smaller code buffer size on AArch32 to have a better chance of being in range.
static constexpr u32 RECOMPILER_CODE_CACHE_SIZE = 16 * 1024 * 1024;
static constexpr u32 RECOMPILER_FAR_CODE_CACHE_SIZE = 4 * 1024 * 1024;
#else
static constexpr u32 RECOMPILER_CODE_CACHE_SIZE = 48 * 1024 * 1024;
static constexpr u32 RECOMPILER_FAR_CODE_CACHE_SIZE = 16 * 1024 * 1024;
#endif
// Fast map provides lookup from PC to function
// Function pointers are offset so that you don't need to subtract
ALIGN_TO_CACHE_LINE CodeLUTArray g_code_lut;
ALIGN_TO_CACHE_LINE static BlockLUTArray s_block_lut;
ALIGN_TO_CACHE_LINE static PageProtectionArray s_page_protection = {};
// On Linux ARM32/ARM64, we use a dedicated section in the ELF for storing code. This is because without
// ASLR, or on certain ASLR offsets, the sbrk() heap ends up immediately following the text/data sections,
// which means there isn't a large enough gap to fit within range on ARM32. Also enable it for Android,
// because MAP_FIXED_NOREPLACE may not exist on older kernels.
#if (defined(__linux__) && (defined(CPU_ARCH_ARM32) || defined(CPU_ARCH_ARM64))) || defined(__ANDROID__)
#define USE_CODE_BUFFER_SECTION 1
#ifdef __clang__
#ifdef USE_CODE_BUFFER_SECTION
#pragma clang section bss = ".jitstorage"
__attribute__((aligned(MAX_HOST_PAGE_SIZE))) static u8 s_code_buffer_ptr[RECOMPILER_CODE_CACHE_SIZE];
__attribute__((aligned(MAX_HOST_PAGE_SIZE))) static u8 s_code_buffer_storage[RECOMPILER_CODE_CACHE_SIZE];
#pragma clang section bss = ""
#endif
#else
static u8* s_code_buffer_ptr = nullptr;
#endif
static u8* s_code_ptr = nullptr;
static u8* s_free_code_ptr = nullptr;
static u32 s_code_size = 0;
static u32 s_code_used = 0;
static u8* s_far_code_ptr = nullptr;
static u8* s_free_far_code_ptr = nullptr;
static u32 s_far_code_size = 0;
static u32 s_far_code_used = 0;
#ifdef DUMP_CODE_SIZE_STATS
static u32 s_total_instructions_compiled = 0;
static u32 s_total_host_instructions_emitted = 0;
static u32 s_total_host_code_used_by_instructions = 0;
#endif
} // namespace CPU::CodeCache
bool CPU::CodeCache::IsUsingRecompiler()
@ -172,14 +178,15 @@ bool CPU::CodeCache::ProcessStartup(Error* error)
{
#ifdef USE_CODE_BUFFER_SECTION
const u8* module_base = static_cast<const u8*>(MemMap::GetBaseAddress());
INFO_LOG("Using JIT buffer section of size {} at {} (0x{:X} bytes / {} MB away)", sizeof(s_code_buffer_ptr),
static_cast<void*>(s_code_buffer_ptr), std::abs(static_cast<ptrdiff_t>(s_code_buffer_ptr - module_base)),
(std::abs(static_cast<ptrdiff_t>(s_code_buffer_ptr - module_base)) + (1024 * 1024 - 1)) / (1024 * 1024));
INFO_LOG("Using JIT buffer section of size {} at {} (0x{:X} bytes / {} MB away)", sizeof(s_code_buffer_storage),
static_cast<void*>(s_code_buffer_storage),
std::abs(static_cast<ptrdiff_t>(s_code_buffer_storage - module_base)),
(std::abs(static_cast<ptrdiff_t>(s_code_buffer_storage - module_base)) + (1024 * 1024 - 1)) / (1024 * 1024));
const bool code_buffer_allocated =
MemMap::MemProtect(s_code_buffer_ptr, RECOMPILER_CODE_CACHE_SIZE, PageProtect::ReadWriteExecute);
MemMap::MemProtect(s_code_buffer_storage, RECOMPILER_CODE_CACHE_SIZE, PageProtect::ReadWriteExecute);
#else
s_code_buffer_ptr = static_cast<u8*>(MemMap::AllocateJITMemory(RECOMPILER_CODE_CACHE_SIZE));
const bool code_buffer_allocated = (s_code_buffer_ptr != nullptr);
s_locals.code_buffer_ptr = static_cast<u8*>(MemMap::AllocateJITMemory(RECOMPILER_CODE_CACHE_SIZE));
const bool code_buffer_allocated = (s_locals.code_buffer_ptr != nullptr);
#endif
if (!code_buffer_allocated) [[unlikely]]
{
@ -201,7 +208,7 @@ void CPU::CodeCache::ProcessShutdown()
DeallocateLUTs();
#ifndef USE_CODE_BUFFER_SECTION
MemMap::ReleaseJITMemory(s_code_buffer_ptr, RECOMPILER_CODE_CACHE_SIZE);
MemMap::ReleaseJITMemory(s_locals.code_buffer_ptr, RECOMPILER_CODE_CACHE_SIZE);
#endif
}
@ -226,7 +233,7 @@ void CPU::CodeCache::Execute()
{
if (IsUsingRecompiler())
{
g_enter_recompiler();
g_recompiler_functions.enter_recompiler();
UnreachableCode();
}
else
@ -278,13 +285,13 @@ void CPU::CodeCache::AllocateLUTs()
constexpr u32 num_code_slots = GetLUTSlotCount(true);
constexpr u32 num_block_slots = GetLUTSlotCount(false);
Assert(!s_lut_code_pointers && !s_lut_block_pointers);
s_lut_code_pointers = std::make_unique<const void*[]>(num_code_slots);
s_lut_block_pointers = std::make_unique<Block*[]>(num_block_slots);
std::memset(s_lut_block_pointers.get(), 0, sizeof(Block*) * num_block_slots);
Assert(!s_locals.lut_code_pointers && !s_locals.lut_block_pointers);
s_locals.lut_code_pointers = std::make_unique<const void*[]>(num_code_slots);
s_locals.lut_block_pointers = std::make_unique<Block*[]>(num_block_slots);
std::memset(s_locals.lut_block_pointers.get(), 0, sizeof(Block*) * num_block_slots);
CodeLUT code_table_ptr = s_lut_code_pointers.get();
Block** block_table_ptr = s_lut_block_pointers.get();
CodeLUT code_table_ptr = s_locals.lut_code_pointers.get();
Block** block_table_ptr = s_locals.lut_block_pointers.get();
CodeLUT const code_table_ptr_end = code_table_ptr + num_code_slots;
Block** const block_table_ptr_end = block_table_ptr + num_block_slots;
@ -324,23 +331,23 @@ void CPU::CodeCache::AllocateLUTs()
void CPU::CodeCache::DeallocateLUTs()
{
s_lut_block_pointers.reset();
s_lut_code_pointers.reset();
s_locals.lut_block_pointers.reset();
s_locals.lut_code_pointers.reset();
}
void CPU::CodeCache::ResetCodeLUT()
{
// Make the unreachable table jump to the invalid code callback.
MemsetPtrs(s_lut_code_pointers.get(), g_interpret_block, LUT_TABLE_COUNT);
MemsetPtrs(s_locals.lut_code_pointers.get(), g_recompiler_functions.interpret_block, LUT_TABLE_COUNT);
for (u32 i = 0; i < LUT_TABLE_COUNT; i++)
{
// Don't overwrite anything bound to unreachable.
CodeLUT ptr = g_code_lut[i];
if (ptr == s_lut_code_pointers.get())
if (ptr == s_locals.lut_code_pointers.get())
continue;
MemsetPtrs(ptr, g_compile_or_revalidate_block, LUT_TABLE_SIZE);
MemsetPtrs(ptr, g_recompiler_functions.compile_or_revalidate_block, LUT_TABLE_SIZE);
}
}
@ -348,7 +355,7 @@ void CPU::CodeCache::SetCodeLUT(u32 pc, const void* function)
{
const u32 table = pc >> LUT_TABLE_SHIFT;
const u32 idx = (pc & 0xFFFF) >> 2;
DebugAssert(g_code_lut[table] != s_lut_code_pointers.get());
DebugAssert(g_code_lut[table] != s_locals.lut_code_pointers.get());
g_code_lut[table][idx] = function;
}
@ -396,9 +403,9 @@ CPU::CodeCache::Block* CPU::CodeCache::CreateBlock(u32 pc, const BlockInstructio
{
// this sucks.. hopefully won't happen very often
// TODO: allocate max size, allow shrink but not grow
auto it = std::find(s_blocks.begin(), s_blocks.end(), block);
Assert(it != s_blocks.end());
s_blocks.erase(it);
auto it = std::find(s_locals.blocks.begin(), s_locals.blocks.end(), block);
Assert(it != s_locals.blocks.end());
s_locals.blocks.erase(it);
block->~Block();
Common::AlignedFree(block);
@ -412,7 +419,7 @@ CPU::CodeCache::Block* CPU::CodeCache::CreateBlock(u32 pc, const BlockInstructio
sizeof(Block) + (sizeof(Instruction) * size) + (sizeof(InstructionInfo) * size), alignof(Block)));
Assert(block);
new (block) Block();
s_blocks.push_back(block);
s_locals.blocks.push_back(block);
}
block->pc = pc;
@ -629,8 +636,8 @@ void CPU::CodeCache::InvalidateBlock(Block* block, BlockState new_state)
{
if (block->state == BlockState::Valid || block->state == BlockState::FallbackToInterpreter)
{
SetCodeLUT(block->pc, g_compile_or_revalidate_block);
BacklinkBlocks(block->pc, g_compile_or_revalidate_block);
SetCodeLUT(block->pc, g_recompiler_functions.compile_or_revalidate_block);
BacklinkBlocks(block->pc, g_recompiler_functions.compile_or_revalidate_block);
}
block->state = new_state;
@ -641,7 +648,7 @@ void CPU::CodeCache::InvalidateAllRAMBlocks()
// TODO: maybe combine the backlink into one big instruction flush cache?
MemMap::BeginCodeWrite();
for (Block* block : s_blocks)
for (Block* block : s_locals.blocks)
{
if (AddressInRAM(block->pc))
{
@ -671,18 +678,18 @@ void CPU::CodeCache::ClearBlocks()
ppi = {};
}
s_fastmem_backpatch_info.clear();
s_fastmem_faulting_pcs.clear();
s_block_links.clear();
s_locals.fastmem_backpatch_info.clear();
s_locals.fastmem_faulting_pcs.clear();
s_locals.block_links.clear();
for (Block* block : s_blocks)
for (Block* block : s_locals.blocks)
{
block->~Block();
Common::AlignedFree(block);
}
s_blocks.clear();
s_locals.blocks.clear();
std::memset(s_lut_block_pointers.get(), 0, sizeof(Block*) * GetLUTSlotCount(false));
std::memset(s_locals.lut_block_pointers.get(), 0, sizeof(Block*) * GetLUTSlotCount(false));
}
PageFaultHandler::HandlerResult PageFaultHandler::HandlePageFault(void* exception_pc, void* fault_address,
@ -710,8 +717,8 @@ PageFaultHandler::HandlerResult PageFaultHandler::HandlePageFault(void* exceptio
CPU::CodeCache::Block* CPU::CodeCache::CreateCachedInterpreterBlock(u32 pc)
{
BlockMetadata metadata = {};
ReadBlockInstructions(pc, &s_block_instructions, &metadata);
return CreateBlock(pc, s_block_instructions, metadata);
ReadBlockInstructions(pc, &s_locals.block_instructions, &metadata);
return CreateBlock(pc, s_locals.block_instructions, metadata);
}
template<PGXPMode pgxp_mode>
@ -1307,18 +1314,18 @@ void CPU::CodeCache::CompileOrRevalidateBlock(u32 start_pc)
}
BlockMetadata metadata = {};
if (!ReadBlockInstructions(start_pc, &s_block_instructions, &metadata))
if (!ReadBlockInstructions(start_pc, &s_locals.block_instructions, &metadata))
{
ERROR_LOG("Failed to read block at 0x{:08X}, falling back to uncached interpreter", start_pc);
SetCodeLUT(start_pc, g_interpret_block);
BacklinkBlocks(start_pc, g_interpret_block);
SetCodeLUT(start_pc, g_recompiler_functions.interpret_block);
BacklinkBlocks(start_pc, g_recompiler_functions.interpret_block);
MemMap::EndCodeWrite();
return;
}
// Ensure we're not going to run out of space while compiling this block.
// We could definitely do better here...
const u32 block_size = static_cast<u32>(s_block_instructions.size());
const u32 block_size = static_cast<u32>(s_locals.block_instructions.size());
const u32 free_code_space = GetFreeCodeSpace();
const u32 free_far_code_space = GetFreeFarCodeSpace();
if (free_code_space < (block_size * Recompiler::MAX_NEAR_HOST_BYTES_PER_INSTRUCTION) ||
@ -1329,12 +1336,12 @@ void CPU::CodeCache::CompileOrRevalidateBlock(u32 start_pc)
CodeCache::Reset();
}
if ((block = CreateBlock(start_pc, s_block_instructions, metadata)) == nullptr || block->size == 0 ||
if ((block = CreateBlock(start_pc, s_locals.block_instructions, metadata)) == nullptr || block->size == 0 ||
!CompileBlock(block))
{
ERROR_LOG("Failed to compile block at 0x{:08X}, falling back to uncached interpreter", start_pc);
SetCodeLUT(start_pc, g_interpret_block);
BacklinkBlocks(start_pc, g_interpret_block);
SetCodeLUT(start_pc, g_recompiler_functions.interpret_block);
BacklinkBlocks(start_pc, g_recompiler_functions.interpret_block);
MemMap::EndCodeWrite();
return;
}
@ -1358,41 +1365,42 @@ const void* CPU::CodeCache::CreateBlockLink(Block* block, void* code, u32 newpc)
// self-linking should be handled by the caller
DebugAssert(newpc != block->pc);
const void* dst = g_dispatcher;
const void* dst = g_recompiler_functions.dispatcher;
if (g_settings.cpu_recompiler_block_linking)
{
const Block* next_block = LookupBlock(newpc);
if (next_block)
{
dst = (next_block->state == BlockState::Valid) ?
next_block->host_code :
((next_block->state == BlockState::FallbackToInterpreter) ? g_interpret_block :
g_compile_or_revalidate_block);
dst = (next_block->state == BlockState::Valid) ? next_block->host_code :
((next_block->state == BlockState::FallbackToInterpreter) ?
g_recompiler_functions.interpret_block :
g_recompiler_functions.compile_or_revalidate_block);
DebugAssert(dst);
}
else
{
dst = HasBlockLUT(newpc) ? g_compile_or_revalidate_block : g_interpret_block;
dst = HasBlockLUT(newpc) ? g_recompiler_functions.compile_or_revalidate_block :
g_recompiler_functions.interpret_block;
}
BlockLinkMap::iterator iter = s_block_links.emplace(newpc, code);
BlockLinkMap::iterator iter = s_locals.block_links.emplace(newpc, code);
DebugAssert(block->num_exit_links < MAX_BLOCK_EXIT_LINKS);
block->exit_links[block->num_exit_links++] = iter;
}
DEBUG_LOG("Linking {} with dst pc {:08X} to {}{}", code, newpc, dst,
(dst == g_compile_or_revalidate_block) ? "[compiler]" : "");
(dst == g_recompiler_functions.compile_or_revalidate_block) ? "[compiler]" : "");
return dst;
}
const void* CPU::CodeCache::CreateSelfBlockLink(Block* block, void* code, const void* block_start)
{
const void* dst = g_dispatcher;
const void* dst = g_recompiler_functions.dispatcher;
if (g_settings.cpu_recompiler_block_linking)
{
dst = block_start;
BlockLinkMap::iterator iter = s_block_links.emplace(block->pc, code);
BlockLinkMap::iterator iter = s_locals.block_links.emplace(block->pc, code);
DebugAssert(block->num_exit_links < MAX_BLOCK_EXIT_LINKS);
block->exit_links[block->num_exit_links++] = iter;
}
@ -1406,11 +1414,11 @@ void CPU::CodeCache::BacklinkBlocks(u32 pc, const void* dst)
if (!g_settings.cpu_recompiler_block_linking)
return;
const auto link_range = s_block_links.equal_range(pc);
const auto link_range = s_locals.block_links.equal_range(pc);
for (auto it = link_range.first; it != link_range.second; ++it)
{
DEBUG_LOG("Backlinking {} with dst pc {:08X} to {}{}", it->second, pc, dst,
(dst == g_compile_or_revalidate_block) ? "[compiler]" : "");
(dst == g_recompiler_functions.compile_or_revalidate_block) ? "[compiler]" : "");
EmitJump(it->second, dst, true);
}
}
@ -1419,53 +1427,57 @@ void CPU::CodeCache::UnlinkBlockExits(Block* block)
{
const u32 num_exit_links = block->num_exit_links;
for (u32 i = 0; i < num_exit_links; i++)
s_block_links.erase(block->exit_links[i]);
s_locals.block_links.erase(block->exit_links[i]);
block->num_exit_links = 0;
}
void CPU::CodeCache::ResetCodeBuffer()
{
if (s_code_used > 0 || s_far_code_used > 0)
if (s_locals.code_used > 0 || s_locals.far_code_used > 0)
{
MemMap::BeginCodeWrite();
if (s_code_used > 0)
if (s_locals.code_used > 0)
{
std::memset(s_code_ptr, 0, s_code_used);
MemMap::FlushInstructionCache(s_code_ptr, s_code_used);
std::memset(s_locals.code_ptr, 0, s_locals.code_used);
MemMap::FlushInstructionCache(s_locals.code_ptr, s_locals.code_used);
}
if (s_far_code_used > 0)
if (s_locals.far_code_used > 0)
{
std::memset(s_far_code_ptr, 0, s_far_code_used);
MemMap::FlushInstructionCache(s_far_code_ptr, s_far_code_used);
std::memset(s_locals.far_code_ptr, 0, s_locals.far_code_used);
MemMap::FlushInstructionCache(s_locals.far_code_ptr, s_locals.far_code_used);
}
MemMap::EndCodeWrite();
}
s_code_ptr = static_cast<u8*>(s_code_buffer_ptr);
s_free_code_ptr = s_code_ptr;
s_code_size = RECOMPILER_CODE_CACHE_SIZE - RECOMPILER_FAR_CODE_CACHE_SIZE;
s_code_used = 0;
#ifdef USE_CODE_BUFFER_SECTION
s_locals.code_ptr = s_code_buffer_storage;
#else
s_locals.code_ptr = s_locals.code_buffer_ptr;
#endif
s_locals.free_code_ptr = s_locals.code_ptr;
s_locals.code_size = RECOMPILER_CODE_CACHE_SIZE - RECOMPILER_FAR_CODE_CACHE_SIZE;
s_locals.code_used = 0;
// Use half the far code size when memory exceptions aren't enabled. It's only used for backpatching.
const u32 far_code_size = (!g_settings.cpu_recompiler_memory_exceptions) ? (RECOMPILER_FAR_CODE_CACHE_SIZE / 2) :
RECOMPILER_FAR_CODE_CACHE_SIZE;
s_far_code_size = far_code_size;
s_far_code_ptr = (far_code_size > 0) ? (static_cast<u8*>(s_code_ptr) + s_code_size) : nullptr;
s_free_far_code_ptr = s_far_code_ptr;
s_far_code_used = 0;
s_locals.far_code_size = far_code_size;
s_locals.far_code_ptr = (far_code_size > 0) ? (static_cast<u8*>(s_locals.code_ptr) + s_locals.code_size) : nullptr;
s_locals.free_far_code_ptr = s_locals.far_code_ptr;
s_locals.far_code_used = 0;
}
u8* CPU::CodeCache::GetFreeCodePointer()
{
return s_free_code_ptr;
return s_locals.free_code_ptr;
}
u32 CPU::CodeCache::GetFreeCodeSpace()
{
return s_code_size - s_code_used;
return s_locals.code_size - s_locals.code_used;
}
void CPU::CodeCache::CommitCode(u32 length)
@ -1473,21 +1485,21 @@ void CPU::CodeCache::CommitCode(u32 length)
if (length == 0) [[unlikely]]
return;
MemMap::FlushInstructionCache(s_free_code_ptr, length);
MemMap::FlushInstructionCache(s_locals.free_code_ptr, length);
Assert(length <= (s_code_size - s_code_used));
s_free_code_ptr += length;
s_code_used += length;
Assert(length <= (s_locals.code_size - s_locals.code_used));
s_locals.free_code_ptr += length;
s_locals.code_used += length;
}
u8* CPU::CodeCache::GetFreeFarCodePointer()
{
return s_free_far_code_ptr;
return s_locals.free_far_code_ptr;
}
u32 CPU::CodeCache::GetFreeFarCodeSpace()
{
return s_far_code_size - s_far_code_used;
return s_locals.far_code_size - s_locals.far_code_used;
}
void CPU::CodeCache::CommitFarCode(u32 length)
@ -1495,26 +1507,26 @@ void CPU::CodeCache::CommitFarCode(u32 length)
if (length == 0) [[unlikely]]
return;
MemMap::FlushInstructionCache(s_free_far_code_ptr, length);
MemMap::FlushInstructionCache(s_locals.free_far_code_ptr, length);
Assert(length <= (s_far_code_size - s_far_code_used));
s_free_far_code_ptr += length;
s_far_code_used += length;
Assert(length <= (s_locals.far_code_size - s_locals.far_code_used));
s_locals.free_far_code_ptr += length;
s_locals.far_code_used += length;
}
void CPU::CodeCache::AlignCode(u32 alignment)
{
DebugAssert(Common::IsPow2(alignment));
const u32 num_padding_bytes =
std::min(static_cast<u32>(Common::AlignUpPow2(reinterpret_cast<uintptr_t>(s_free_code_ptr), alignment) -
reinterpret_cast<uintptr_t>(s_free_code_ptr)),
std::min(static_cast<u32>(Common::AlignUpPow2(reinterpret_cast<uintptr_t>(s_locals.free_code_ptr), alignment) -
reinterpret_cast<uintptr_t>(s_locals.free_code_ptr)),
GetFreeCodeSpace());
if (num_padding_bytes > 0)
EmitAlignmentPadding(s_free_code_ptr, num_padding_bytes);
EmitAlignmentPadding(s_locals.free_code_ptr, num_padding_bytes);
s_free_code_ptr += num_padding_bytes;
s_code_used += num_padding_bytes;
s_locals.free_code_ptr += num_padding_bytes;
s_locals.code_used += num_padding_bytes;
}
const void* CPU::CodeCache::GetInterpretUncachedBlockFunction()
@ -1537,9 +1549,9 @@ void CPU::CodeCache::CompileASMFunctions()
MemMap::BeginCodeWrite();
#ifdef DUMP_CODE_SIZE_STATS
s_total_instructions_compiled = 0;
s_total_host_instructions_emitted = 0;
s_total_host_code_used_by_instructions = 0;
s_locals.total_instructions_compiled = 0;
s_locals.total_host_instructions_emitted = 0;
s_locals.total_host_code_used_by_instructions = 0;
#endif
const u32 asm_size = EmitASMFunctions(GetFreeCodePointer(), GetFreeCodeSpace());
@ -1575,20 +1587,22 @@ bool CPU::CodeCache::CompileBlock(Block* block)
#ifdef DUMP_CODE_SIZE_STATS
const u32 host_instructions = GetHostInstructionCount(host_code, host_code_size);
s_total_instructions_compiled += block->size;
s_total_host_instructions_emitted += host_instructions;
s_total_host_code_used_by_instructions += host_code_size;
s_locals.total_instructions_compiled += block->size;
s_locals.total_host_instructions_emitted += host_instructions;
s_locals.total_host_code_used_by_instructions += host_code_size;
DEV_LOG(
"0x{:08X}: {}/{}b for {}b ({}i), blowup: {:.2f}x, cache: {:.2f}%/{:.2f}%, ipi: {:.2f}/{:.2f}, bpi: {:.2f}/{:.2f}",
block->pc, host_code_size, host_far_code_size, block->size * 4, block->size,
static_cast<float>(host_code_size) / static_cast<float>(block->size * 4),
(static_cast<float>(s_code_used) / static_cast<float>(s_code_size)) * 100.0f,
(static_cast<float>(s_far_code_used) / static_cast<float>(s_far_code_size)) * 100.0f,
(static_cast<float>(s_locals.code_used) / static_cast<float>(s_locals.code_size)) * 100.0f,
(static_cast<float>(s_locals.far_code_used) / static_cast<float>(s_locals.far_code_size)) * 100.0f,
static_cast<float>(host_instructions) / static_cast<float>(block->size),
static_cast<float>(s_total_host_instructions_emitted) / static_cast<float>(s_total_instructions_compiled),
static_cast<float>(s_locals.total_host_instructions_emitted) /
static_cast<float>(s_locals.total_instructions_compiled),
static_cast<float>(block->host_code_size) / static_cast<float>(block->size),
static_cast<float>(s_total_host_code_used_by_instructions) / static_cast<float>(s_total_instructions_compiled));
static_cast<float>(s_locals.total_host_code_used_by_instructions) /
static_cast<float>(s_locals.total_instructions_compiled));
#endif
#if 0
@ -1607,16 +1621,16 @@ void CPU::CodeCache::AddLoadStoreInfo(void* code_address, u32 code_size, u32 gue
{
DebugAssert(code_size < std::numeric_limits<u8>::max());
auto iter = s_fastmem_backpatch_info.find(code_address);
if (iter != s_fastmem_backpatch_info.end())
s_fastmem_backpatch_info.erase(iter);
auto iter = s_locals.fastmem_backpatch_info.find(code_address);
if (iter != s_locals.fastmem_backpatch_info.end())
s_locals.fastmem_backpatch_info.erase(iter);
LoadstoreBackpatchInfo info;
info.thunk_address = thunk_address;
info.guest_pc = guest_pc;
info.guest_block = 0;
info.code_size = static_cast<u8>(code_size);
s_fastmem_backpatch_info.emplace(code_address, info);
s_locals.fastmem_backpatch_info.emplace(code_address, info);
}
void CPU::CodeCache::AddLoadStoreInfo(void* code_address, u32 code_size, u32 guest_pc, u32 guest_block,
@ -1626,9 +1640,9 @@ void CPU::CodeCache::AddLoadStoreInfo(void* code_address, u32 code_size, u32 gue
DebugAssert(code_size < std::numeric_limits<u8>::max());
DebugAssert(cycles >= 0 && cycles < std::numeric_limits<u16>::max());
auto iter = s_fastmem_backpatch_info.find(code_address);
if (iter != s_fastmem_backpatch_info.end())
s_fastmem_backpatch_info.erase(iter);
auto iter = s_locals.fastmem_backpatch_info.find(code_address);
if (iter != s_locals.fastmem_backpatch_info.end())
s_locals.fastmem_backpatch_info.erase(iter);
LoadstoreBackpatchInfo info;
info.thunk_address = nullptr;
@ -1642,7 +1656,7 @@ void CPU::CodeCache::AddLoadStoreInfo(void* code_address, u32 code_size, u32 gue
info.is_signed = is_signed;
info.is_load = is_load;
info.code_size = static_cast<u8>(code_size);
s_fastmem_backpatch_info.emplace(code_address, info);
s_locals.fastmem_backpatch_info.emplace(code_address, info);
}
PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(void* exception_pc, void* fault_address,
@ -1681,8 +1695,8 @@ PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(void* exc
guest_address = std::numeric_limits<PhysicalMemoryAddress>::max();
}
auto iter = s_fastmem_backpatch_info.find(exception_pc);
if (iter == s_fastmem_backpatch_info.end())
auto iter = s_locals.fastmem_backpatch_info.find(exception_pc);
if (iter == s_locals.fastmem_backpatch_info.end())
return PageFaultHandler::HandlerResult::ExecuteNextHandler;
DEV_LOG("Page fault handler invoked at PC={} Address={} {}, fastmem offset {:08X}", exception_pc, fault_address,
@ -1715,14 +1729,14 @@ PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(void* exc
MemMap::EndCodeWrite();
// and store the pc in the faulting list, so that we don't emit another fastmem loadstore
s_fastmem_faulting_pcs.insert(info.guest_pc);
s_fastmem_backpatch_info.erase(iter);
s_locals.fastmem_faulting_pcs.insert(info.guest_pc);
s_locals.fastmem_backpatch_info.erase(iter);
return PageFaultHandler::HandlerResult::ContinueExecution;
}
bool CPU::CodeCache::HasPreviouslyFaultedOnPC(u32 guest_pc)
{
return (s_fastmem_faulting_pcs.find(guest_pc) != s_fastmem_faulting_pcs.end());
return (s_locals.fastmem_faulting_pcs.find(guest_pc) != s_locals.fastmem_faulting_pcs.end());
}
void CPU::CodeCache::BackpatchLoadStore(void* host_pc, const LoadstoreBackpatchInfo& info)
@ -1738,8 +1752,8 @@ void CPU::CodeCache::RemoveBackpatchInfoForRange(const void* host_code, u32 size
const u8* start = static_cast<const u8*>(host_code);
const u8* end = start + size;
auto start_iter = s_fastmem_backpatch_info.lower_bound(start);
if (start_iter == s_fastmem_backpatch_info.end())
auto start_iter = s_locals.fastmem_backpatch_info.lower_bound(start);
if (start_iter == s_locals.fastmem_backpatch_info.end())
return;
// this might point to another block, so bail out in that case
@ -1751,8 +1765,8 @@ void CPU::CodeCache::RemoveBackpatchInfoForRange(const void* host_code, u32 size
do
{
++end_iter;
} while (end_iter != s_fastmem_backpatch_info.end() && end_iter->first < end);
} while (end_iter != s_locals.fastmem_backpatch_info.end() && end_iter->first < end);
// erase the whole range at once
s_fastmem_backpatch_info.erase(start_iter, end_iter);
s_locals.fastmem_backpatch_info.erase(start_iter, end_iter);
}

@ -254,13 +254,18 @@ u32 GetHostInstructionCount(const void* start, u32 size);
extern CodeLUTArray g_code_lut;
extern NORETURN_FUNCTION_POINTER void (*g_enter_recompiler)();
extern const void* g_compile_or_revalidate_block;
extern const void* g_run_events_and_dispatch;
extern const void* g_dispatcher;
extern const void* g_block_dispatcher;
extern const void* g_interpret_block;
extern const void* g_discard_and_recompile_block;
struct RecompilerFunctions
{
NORETURN_FUNCTION_POINTER void (*enter_recompiler)();
const void* compile_or_revalidate_block;
const void* run_events_and_dispatch;
const void* dispatcher;
const void* block_dispatcher;
const void* interpret_block;
const void* discard_and_recompile_block;
};
extern RecompilerFunctions g_recompiler_functions;
#ifdef ENABLE_RECOMPILER_PROFILING

@ -261,7 +261,8 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
Label dispatch;
Label run_events_and_dispatch;
g_enter_recompiler = armAsm->GetCursorAddress<decltype(g_enter_recompiler)>();
g_recompiler_functions.enter_recompiler =
armAsm->GetCursorAddress<decltype(g_recompiler_functions.enter_recompiler)>();
{
// Need the CPU state for basically everything :-)
armMoveAddressToReg(armAsm, RSTATE, &g_state);
@ -275,7 +276,7 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
armAsm->cmp(RARG1, RARG2);
armAsm->b(lt, &skip_event_check);
g_run_events_and_dispatch = armAsm->GetCursorAddress<const void*>();
g_recompiler_functions.run_events_and_dispatch = armAsm->GetCursorAddress<const void*>();
armAsm->bind(&run_events_and_dispatch);
armEmitCall(armAsm, reinterpret_cast<const void*>(&TimingEvents::RunEvents), true);
@ -283,7 +284,7 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
}
// TODO: align?
g_dispatcher = armAsm->GetCursorAddress<const void*>();
g_recompiler_functions.dispatcher = armAsm->GetCursorAddress<const void*>();
{
armAsm->bind(&dispatch);
@ -299,21 +300,21 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
armAsm->bx(RARG1);
}
g_compile_or_revalidate_block = armAsm->GetCursorAddress<const void*>();
g_recompiler_functions.compile_or_revalidate_block = armAsm->GetCursorAddress<const void*>();
{
armAsm->ldr(RARG1, PTR(&g_state.pc));
armEmitCall(armAsm, reinterpret_cast<const void*>(&CompileOrRevalidateBlock), true);
armAsm->b(&dispatch);
}
g_discard_and_recompile_block = armAsm->GetCursorAddress<const void*>();
g_recompiler_functions.discard_and_recompile_block = armAsm->GetCursorAddress<const void*>();
{
armAsm->ldr(RARG1, PTR(&g_state.pc));
armEmitCall(armAsm, reinterpret_cast<const void*>(&DiscardAndRecompileBlock), true);
armAsm->b(&dispatch);
}
g_interpret_block = armAsm->GetCursorAddress<const void*>();
g_recompiler_functions.interpret_block = armAsm->GetCursorAddress<const void*>();
{
armEmitCall(armAsm, reinterpret_cast<const void*>(GetInterpretUncachedBlockFunction()), true);
armAsm->ldr(RARG1, PTR(&g_state.pending_ticks));
@ -593,7 +594,7 @@ bool foo(const void* a, const void* b)
Label block_unchanged;
armAsm->b(&block_unchanged);
armAsm->bind(&block_changed);
armEmitJmp(armAsm, CodeCache::g_discard_and_recompile_block, false);
armEmitJmp(armAsm, CodeCache::g_recompiler_functions.discard_and_recompile_block, false);
armAsm->bind(&block_unchanged);
}
@ -749,16 +750,16 @@ void CPU::ARM32Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool
if (cycles > 0)
armAsm->str(RARG1, PTR(&g_state.pending_ticks));
if (do_event_test)
armEmitCondBranch(armAsm, ge, CodeCache::g_run_events_and_dispatch);
armEmitCondBranch(armAsm, ge, CodeCache::g_recompiler_functions.run_events_and_dispatch);
// jump to dispatcher or next block
if (force_run_events)
{
armEmitJmp(armAsm, CodeCache::g_run_events_and_dispatch, false);
armEmitJmp(armAsm, CodeCache::g_recompiler_functions.run_events_and_dispatch, false);
}
else if (!newpc.has_value())
{
armEmitJmp(armAsm, CodeCache::g_dispatcher, false);
armEmitJmp(armAsm, CodeCache::g_recompiler_functions.dispatcher, false);
}
else
{

@ -455,7 +455,8 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
Label dispatch;
Label run_events_and_dispatch;
g_enter_recompiler = armAsm->GetCursorAddress<decltype(g_enter_recompiler)>();
g_recompiler_functions.enter_recompiler =
armAsm->GetCursorAddress<decltype(g_recompiler_functions.enter_recompiler)>();
{
#ifdef _WIN32
// Frame pointer setup is needed on Windows
@ -481,13 +482,13 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
armAsm->cmp(RWARG1, RWARG2);
armAsm->b(&dispatch, lt);
g_run_events_and_dispatch = armAsm->GetCursorAddress<const void*>();
g_recompiler_functions.run_events_and_dispatch = armAsm->GetCursorAddress<const void*>();
armAsm->bind(&run_events_and_dispatch);
armEmitCall(armAsm, reinterpret_cast<const void*>(&TimingEvents::RunEvents), true);
}
armAlignCode(armAsm, Recompiler::FUNCTION_ALIGNMENT);
g_dispatcher = armAsm->GetCursorAddress<const void*>();
g_recompiler_functions.dispatcher = armAsm->GetCursorAddress<const void*>();
{
armAsm->bind(&dispatch);
@ -504,7 +505,7 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
}
armAlignCode(armAsm, Recompiler::FUNCTION_ALIGNMENT);
g_compile_or_revalidate_block = armAsm->GetCursorAddress<const void*>();
g_recompiler_functions.compile_or_revalidate_block = armAsm->GetCursorAddress<const void*>();
{
armAsm->ldr(RWARG1, PTR(&g_state.pc));
armEmitCall(armAsm, reinterpret_cast<const void*>(&CompileOrRevalidateBlock), true);
@ -512,7 +513,7 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
}
armAlignCode(armAsm, Recompiler::FUNCTION_ALIGNMENT);
g_discard_and_recompile_block = armAsm->GetCursorAddress<const void*>();
g_recompiler_functions.discard_and_recompile_block = armAsm->GetCursorAddress<const void*>();
{
armAsm->ldr(RWARG1, PTR(&g_state.pc));
armEmitCall(armAsm, reinterpret_cast<const void*>(&DiscardAndRecompileBlock), true);
@ -520,7 +521,7 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
}
armAlignCode(armAsm, Recompiler::FUNCTION_ALIGNMENT);
g_interpret_block = armAsm->GetCursorAddress<const void*>();
g_recompiler_functions.interpret_block = armAsm->GetCursorAddress<const void*>();
{
armEmitCall(armAsm, reinterpret_cast<const void*>(GetInterpretUncachedBlockFunction()), true);
armAsm->ldr(RWARG1, PTR(&g_state.pending_ticks));
@ -780,7 +781,7 @@ void CPU::ARM64Recompiler::GenerateBlockProtectCheck(const u8* ram_ptr, const u8
Label block_unchanged;
armAsm->b(&block_unchanged);
armAsm->bind(&block_changed);
armEmitJmp(armAsm, CodeCache::g_discard_and_recompile_block, false);
armEmitJmp(armAsm, CodeCache::g_recompiler_functions.discard_and_recompile_block, false);
armAsm->bind(&block_unchanged);
}
@ -925,16 +926,16 @@ void CPU::ARM64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool
if (cycles > 0)
armAsm->str(RWARG1, PTR(&g_state.pending_ticks));
if (do_event_test)
armEmitCondBranch(armAsm, ge, CodeCache::g_run_events_and_dispatch);
armEmitCondBranch(armAsm, ge, CodeCache::g_recompiler_functions.run_events_and_dispatch);
// jump to dispatcher or next block
if (force_run_events)
{
armEmitJmp(armAsm, CodeCache::g_run_events_and_dispatch, false);
armEmitJmp(armAsm, CodeCache::g_recompiler_functions.run_events_and_dispatch, false);
}
else if (!newpc.has_value())
{
armEmitJmp(armAsm, CodeCache::g_dispatcher, false);
armEmitJmp(armAsm, CodeCache::g_recompiler_functions.dispatcher, false);
}
else
{

@ -212,7 +212,8 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
lagoon_label_t dispatch = {};
lagoon_label_t run_events_and_dispatch = {};
g_enter_recompiler = reinterpret_cast<decltype(g_enter_recompiler)>(laAsm->cursor);
g_recompiler_functions.enter_recompiler =
reinterpret_cast<decltype(g_recompiler_functions.enter_recompiler)>(laAsm->cursor);
{
// TODO: reserve some space for saving caller-saved registers
@ -233,7 +234,7 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
la_bltu(laAsm, RARG1, RARG2, la_label(laAsm, &skip_event_check));
la_bind(laAsm, &run_events_and_dispatch);
g_run_events_and_dispatch = laAsm->cursor;
g_recompiler_functions.run_events_and_dispatch = laAsm->cursor;
laEmitCall(laAsm, reinterpret_cast<const void*>(&TimingEvents::RunEvents));
la_bind(laAsm, &skip_event_check);
@ -241,7 +242,7 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
}
// TODO: align?
g_dispatcher = laAsm->cursor;
g_recompiler_functions.dispatcher = laAsm->cursor;
{
la_bind(laAsm, &dispatch);
@ -262,21 +263,21 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
la_jirl(laAsm, LA_ZERO, RARG1, 0);
}
g_compile_or_revalidate_block = laAsm->cursor;
g_recompiler_functions.compile_or_revalidate_block = laAsm->cursor;
{
la_ld_w(laAsm, RARG1, RSTATE, OFFS(&g_state.pc));
laEmitCall(laAsm, reinterpret_cast<const void*>(&CompileOrRevalidateBlock));
la_b(laAsm, la_label(laAsm, &dispatch));
}
g_discard_and_recompile_block = laAsm->cursor;
g_recompiler_functions.discard_and_recompile_block = laAsm->cursor;
{
la_ld_w(laAsm, RARG1, RSTATE, OFFS(&g_state.pc));
laEmitCall(laAsm, reinterpret_cast<const void*>(&DiscardAndRecompileBlock));
la_b(laAsm, la_label(laAsm, &dispatch));
}
g_interpret_block = laAsm->cursor;
g_recompiler_functions.interpret_block = laAsm->cursor;
{
laEmitCall(laAsm, CodeCache::GetInterpretUncachedBlockFunction());
la_ld_w(laAsm, RARG1, RSTATE, OFFS(&g_state.pending_ticks));
@ -547,7 +548,7 @@ void CPU::LoongArch64Recompiler::GenerateBlockProtectCheck(const u8* ram_ptr, co
lagoon_label_t block_unchanged = {};
la_b(laAsm, la_label(laAsm, &block_unchanged));
la_bind(laAsm, &block_changed);
laEmitJmp(laAsm, CodeCache::g_discard_and_recompile_block);
laEmitJmp(laAsm, CodeCache::g_recompiler_functions.discard_and_recompile_block);
la_bind(laAsm, &block_unchanged);
la_label_free(laAsm, &block_changed);
la_label_free(laAsm, &block_unchanged);
@ -714,7 +715,7 @@ void CPU::LoongArch64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc
// TODO: see if we can do a far jump somehow with this..
lagoon_label_t cont = {};
la_blt(laAsm, RARG1, RARG2, la_label(laAsm, &cont));
laEmitJmp(laAsm, CodeCache::g_run_events_and_dispatch);
laEmitJmp(laAsm, CodeCache::g_recompiler_functions.run_events_and_dispatch);
la_bind(laAsm, &cont);
la_label_free(laAsm, &cont);
}
@ -722,11 +723,11 @@ void CPU::LoongArch64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc
// jump to dispatcher or next block
if (force_run_events)
{
laEmitJmp(laAsm, CodeCache::g_run_events_and_dispatch);
laEmitJmp(laAsm, CodeCache::g_recompiler_functions.run_events_and_dispatch);
}
else if (!newpc.has_value())
{
laEmitJmp(laAsm, CodeCache::g_dispatcher);
laEmitJmp(laAsm, CodeCache::g_recompiler_functions.dispatcher);
}
else
{

@ -243,7 +243,8 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
Label dispatch;
Label run_events_and_dispatch;
g_enter_recompiler = reinterpret_cast<decltype(g_enter_recompiler)>(rvAsm->GetCursorPointer());
g_recompiler_functions.enter_recompiler =
reinterpret_cast<decltype(g_recompiler_functions.enter_recompiler)>(rvAsm->GetCursorPointer());
{
// TODO: reserve some space for saving caller-saved registers
@ -265,14 +266,14 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
rvAsm->BLTU(RARG1, RARG2, &skip_event_check);
rvAsm->Bind(&run_events_and_dispatch);
g_run_events_and_dispatch = rvAsm->GetCursorPointer();
g_recompiler_functions.run_events_and_dispatch = rvAsm->GetCursorPointer();
rvEmitCall(rvAsm, reinterpret_cast<const void*>(&TimingEvents::RunEvents));
rvAsm->Bind(&skip_event_check);
}
// TODO: align?
g_dispatcher = rvAsm->GetCursorPointer();
g_recompiler_functions.dispatcher = rvAsm->GetCursorPointer();
{
rvAsm->Bind(&dispatch);
@ -293,21 +294,21 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
rvAsm->JR(RARG1);
}
g_compile_or_revalidate_block = rvAsm->GetCursorPointer();
g_recompiler_functions.compile_or_revalidate_block = rvAsm->GetCursorPointer();
{
rvAsm->LW(RARG1, PTR(&g_state.pc));
rvEmitCall(rvAsm, reinterpret_cast<const void*>(&CompileOrRevalidateBlock));
rvAsm->J(&dispatch);
}
g_discard_and_recompile_block = rvAsm->GetCursorPointer();
g_recompiler_functions.discard_and_recompile_block = rvAsm->GetCursorPointer();
{
rvAsm->LW(RARG1, PTR(&g_state.pc));
rvEmitCall(rvAsm, reinterpret_cast<const void*>(&DiscardAndRecompileBlock));
rvAsm->J(&dispatch);
}
g_interpret_block = rvAsm->GetCursorPointer();
g_recompiler_functions.interpret_block = rvAsm->GetCursorPointer();
{
rvEmitCall(rvAsm, CodeCache::GetInterpretUncachedBlockFunction());
rvAsm->LW(RARG1, PTR(&g_state.pending_ticks));
@ -553,7 +554,7 @@ void CPU::RISCV64Recompiler::GenerateBlockProtectCheck(const u8* ram_ptr, const
Label block_unchanged;
rvAsm->J(&block_unchanged);
rvAsm->Bind(&block_changed);
rvEmitJmp(rvAsm, CodeCache::g_discard_and_recompile_block);
rvEmitJmp(rvAsm, CodeCache::g_recompiler_functions.discard_and_recompile_block);
rvAsm->Bind(&block_unchanged);
}
@ -716,18 +717,18 @@ void CPU::RISCV64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bo
// TODO: see if we can do a far jump somehow with this..
Label cont;
rvAsm->BLT(RARG1, RARG2, &cont);
rvEmitJmp(rvAsm, CodeCache::g_run_events_and_dispatch);
rvEmitJmp(rvAsm, CodeCache::g_recompiler_functions.run_events_and_dispatch);
rvAsm->Bind(&cont);
}
// jump to dispatcher or next block
if (force_run_events)
{
rvEmitJmp(rvAsm, CodeCache::g_run_events_and_dispatch);
rvEmitJmp(rvAsm, CodeCache::g_recompiler_functions.run_events_and_dispatch);
}
else if (!newpc.has_value())
{
rvEmitJmp(rvAsm, CodeCache::g_dispatcher);
rvEmitJmp(rvAsm, CodeCache::g_recompiler_functions.dispatcher);
}
else
{

@ -120,7 +120,8 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
Label exit_recompiler;
Label run_events_and_dispatch;
g_enter_recompiler = reinterpret_cast<decltype(g_enter_recompiler)>(const_cast<u8*>(cg->getCurr()));
g_recompiler_functions.enter_recompiler =
reinterpret_cast<decltype(g_recompiler_functions.enter_recompiler)>(const_cast<u8*>(cg->getCurr()));
{
// Don't need to save registers, because we fastjmp out when execution is interrupted.
cg->sub(cg->rsp, stack_size);
@ -142,13 +143,13 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
cg->cmp(RWARG1, cg->dword[PTR(&g_state.downcount)]);
cg->jl(dispatch);
g_run_events_and_dispatch = cg->getCurr();
g_recompiler_functions.run_events_and_dispatch = cg->getCurr();
cg->L(run_events_and_dispatch);
cg->call(reinterpret_cast<const void*>(&TimingEvents::RunEvents));
}
cg->align(FUNCTION_ALIGNMENT);
g_dispatcher = cg->getCurr();
g_recompiler_functions.dispatcher = cg->getCurr();
{
cg->L(dispatch);
@ -165,7 +166,7 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
}
cg->align(FUNCTION_ALIGNMENT);
g_compile_or_revalidate_block = cg->getCurr();
g_recompiler_functions.compile_or_revalidate_block = cg->getCurr();
{
cg->mov(RWARG1, cg->dword[PTR(&g_state.pc)]);
cg->call(&CompileOrRevalidateBlock);
@ -173,7 +174,7 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
}
cg->align(FUNCTION_ALIGNMENT);
g_discard_and_recompile_block = cg->getCurr();
g_recompiler_functions.discard_and_recompile_block = cg->getCurr();
{
cg->mov(RWARG1, cg->dword[PTR(&g_state.pc)]);
cg->call(&DiscardAndRecompileBlock);
@ -181,7 +182,7 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
}
cg->align(FUNCTION_ALIGNMENT);
g_interpret_block = cg->getCurr();
g_recompiler_functions.interpret_block = cg->getCurr();
{
cg->call(CodeCache::GetInterpretUncachedBlockFunction());
cg->mov(RWARG1, cg->dword[PTR(&g_state.pending_ticks)]);
@ -499,14 +500,14 @@ void CPU::X64Recompiler::GenerateBlockProtectCheck(const u8* ram_ptr, const u8*
{
cg->movmskps(cg->eax, cg->xmm0);
cg->cmp(cg->eax, 0xf);
cg->jne(CodeCache::g_discard_and_recompile_block);
cg->jne(CodeCache::g_recompiler_functions.discard_and_recompile_block);
}
while (size >= 8)
{
cg->mov(RXARG3, cg->qword[RXARG1 + offset]);
cg->cmp(RXARG3, cg->qword[RXARG2 + offset]);
cg->jne(CodeCache::g_discard_and_recompile_block);
cg->jne(CodeCache::g_recompiler_functions.discard_and_recompile_block);
offset += 8;
size -= 8;
}
@ -515,7 +516,7 @@ void CPU::X64Recompiler::GenerateBlockProtectCheck(const u8* ram_ptr, const u8*
{
cg->mov(RWARG3, cg->dword[RXARG1 + offset]);
cg->cmp(RWARG3, cg->dword[RXARG2 + offset]);
cg->jne(CodeCache::g_discard_and_recompile_block);
cg->jne(CodeCache::g_recompiler_functions.discard_and_recompile_block);
offset += 4;
size -= 4;
}
@ -644,7 +645,7 @@ void CPU::X64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool d
if (force_run_events)
{
cg->jmp(CodeCache::g_run_events_and_dispatch);
cg->jmp(CodeCache::g_recompiler_functions.run_events_and_dispatch);
return;
}
}
@ -667,13 +668,13 @@ void CPU::X64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool d
if (cycles > 0)
cg->mov(cg->dword[PTR(&g_state.pending_ticks)], RWARG1);
if (do_event_test)
cg->jge(CodeCache::g_run_events_and_dispatch);
cg->jge(CodeCache::g_recompiler_functions.run_events_and_dispatch);
}
// jump to dispatcher or next block
if (!newpc.has_value())
{
cg->jmp(CodeCache::g_dispatcher);
cg->jmp(CodeCache::g_recompiler_functions.dispatcher);
}
else
{

Loading…
Cancel
Save