diff --git a/android/app/src/main/res/xml/advanced_preferences.xml b/android/app/src/main/res/xml/advanced_preferences.xml
index 75e718d14..c1ce2a1ac 100644
--- a/android/app/src/main/res/xml/advanced_preferences.xml
+++ b/android/app/src/main/res/xml/advanced_preferences.xml
@@ -40,6 +40,12 @@
app:defaultValue="false"
app:summary="Determines whether the CPU's instruction cache is simulated in the recompiler. Improves accuracy at a small cost to performance. If games are running too fast, try enabling this option."
app:iconSpaceReserved="false" />
+
#include
#include
+#include
Log_SetChannel(Common::PageFaultHandler);
#if defined(WIN32)
diff --git a/src/common/page_fault_handler.h b/src/common/page_fault_handler.h
index b2c4f9040..67ef38cbd 100644
--- a/src/common/page_fault_handler.h
+++ b/src/common/page_fault_handler.h
@@ -1,6 +1,5 @@
#pragma once
#include "types.h"
-#include
namespace Common::PageFaultHandler {
enum class HandlerResult
@@ -9,7 +8,7 @@ enum class HandlerResult
ExecuteNextHandler,
};
-using Callback = std::function;
+using Callback = HandlerResult(*)(void* exception_pc, void* fault_address, bool is_write);
using Handle = void*;
bool InstallHandler(void* owner, Callback callback);
diff --git a/src/core/bus.cpp b/src/core/bus.cpp
index f238e6b17..52fde0a77 100644
--- a/src/core/bus.cpp
+++ b/src/core/bus.cpp
@@ -10,6 +10,7 @@
#include "cpu_disasm.h"
#include "dma.h"
#include "gpu.h"
+#include "host_interface.h"
#include "interrupt_controller.h"
#include "mdec.h"
#include "pad.h"
@@ -22,11 +23,6 @@ Log_SetChannel(Bus);
namespace Bus {
-enum : TickCount
-{
- RAM_READ_TICKS = 4
-};
-
union MEMDELAY
{
u32 bits;
@@ -74,7 +70,7 @@ union MEMCTRL
};
std::bitset m_ram_code_bits{};
-u8 g_ram[RAM_SIZE]{}; // 2MB RAM
+u8* g_ram = nullptr; // 2MB RAM
u8 g_bios[BIOS_SIZE]{}; // 512K BIOS ROM
static std::array m_exp1_access_time = {};
@@ -90,9 +86,17 @@ static u32 m_ram_size_reg = 0;
static std::string m_tty_line_buffer;
+static Common::MemoryArena m_memory_arena;
+static u8* m_fastmem_base = nullptr;
+static std::vector m_fastmem_ram_views;
+
static std::tuple CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay);
static void RecalculateMemoryTimings();
+static void SetCodePageFastmemProtection(u32 page_index, bool writable);
+static bool AllocateMemory();
+static void UnmapFastmemViews();
+
#define FIXUP_WORD_READ_OFFSET(offset) ((offset) & ~u32(3))
#define FIXUP_WORD_READ_VALUE(offset, value) ((value) >> (((offset)&u32(3)) * 8u))
#define FIXUP_HALFWORD_READ_OFFSET(offset) ((offset) & ~u32(1))
@@ -108,19 +112,30 @@ ALWAYS_INLINE static void FixupUnalignedWordAccessW32(u32& offset, u32& value)
value <<= byte_offset * 8;
}
-void Initialize()
+bool Initialize()
{
+ if (!AllocateMemory())
+ {
+ g_host_interface->ReportError("Failed to allocate memory");
+ return false;
+ }
+
Reset();
+ return true;
}
void Shutdown()
{
- //
+ UnmapFastmemViews();
+ if (g_ram)
+ m_memory_arena.ReleaseViewPtr(g_ram, RAM_SIZE);
+
+ CPU::g_state.fastmem_base = nullptr;
}
void Reset()
{
- std::memset(g_ram, 0, sizeof(g_ram));
+ std::memset(g_ram, 0, RAM_SIZE);
m_MEMCTRL.exp1_base = 0x1F000000;
m_MEMCTRL.exp2_base = 0x1F802000;
m_MEMCTRL.exp1_delay_size.bits = 0x0013243F;
@@ -142,8 +157,8 @@ bool DoState(StateWrapper& sw)
sw.Do(&m_bios_access_time);
sw.Do(&m_cdrom_access_time);
sw.Do(&m_spu_access_time);
- sw.DoBytes(g_ram, sizeof(g_ram));
- sw.DoBytes(g_bios, sizeof(g_bios));
+ sw.DoBytes(g_ram, RAM_SIZE);
+ sw.DoBytes(g_bios, BIOS_SIZE);
sw.DoArray(m_MEMCTRL.regs, countof(m_MEMCTRL.regs));
sw.Do(&m_ram_size_reg);
sw.Do(&m_tty_line_buffer);
@@ -222,6 +237,179 @@ void RecalculateMemoryTimings()
m_spu_access_time[2] + 1);
}
+bool AllocateMemory()
+{
+ if (!m_memory_arena.Create(MEMORY_ARENA_SIZE, true, false))
+ {
+ Log_ErrorPrint("Failed to create memory arena");
+ return false;
+ }
+
+ // Create the base views.
+ g_ram = static_cast(m_memory_arena.CreateViewPtr(MEMORY_ARENA_RAM_OFFSET, RAM_SIZE, true, false));
+ if (!g_ram)
+ {
+ Log_ErrorPrint("Failed to create base views of memory");
+ return false;
+ }
+
+ return true;
+}
+
+void UnmapFastmemViews()
+{
+ m_fastmem_ram_views.clear();
+}
+
+void UpdateFastmemViews(bool enabled, bool isolate_cache)
+{
+ UnmapFastmemViews();
+ if (!enabled)
+ {
+ m_fastmem_base = nullptr;
+ return;
+ }
+
+ Log_DevPrintf("Remapping fastmem area, isolate cache = %s", isolate_cache ? "true " : "false");
+ if (!m_fastmem_base)
+ {
+ m_fastmem_base = static_cast(m_memory_arena.FindBaseAddressForMapping(FASTMEM_REGION_SIZE));
+ if (!m_fastmem_base)
+ {
+ Log_ErrorPrint("Failed to find base address for fastmem");
+ return;
+ }
+
+ Log_InfoPrintf("Fastmem base: %p", m_fastmem_base);
+ CPU::g_state.fastmem_base = m_fastmem_base;
+ }
+
+ auto MapRAM = [](u32 base_address) {
+ u8* map_address = m_fastmem_base + base_address;
+ auto view = m_memory_arena.CreateView(MEMORY_ARENA_RAM_OFFSET, RAM_SIZE, true, false, map_address);
+ if (!view)
+ {
+ Log_ErrorPrintf("Failed to map RAM at fastmem area %p (offset 0x%08X)", map_address, RAM_SIZE);
+ return;
+ }
+
+ // mark all pages with code as non-writable
+ for (u32 i = 0; i < CPU_CODE_CACHE_PAGE_COUNT; i++)
+ {
+ if (m_ram_code_bits[i])
+ {
+ u8* page_address = map_address + (i * CPU_CODE_CACHE_PAGE_SIZE);
+ if (!m_memory_arena.SetPageProtection(page_address, CPU_CODE_CACHE_PAGE_SIZE, true, false, false))
+ {
+ Log_ErrorPrintf("Failed to write-protect code page at %p");
+ return;
+ }
+ }
+ }
+
+ m_fastmem_ram_views.push_back(std::move(view.value()));
+ };
+
+ if (!isolate_cache)
+ {
+ // KUSEG - cached
+ MapRAM(0x00000000);
+ //MapRAM(0x00200000);
+ //MapRAM(0x00400000);
+ //MapRAM(0x00600000);
+
+ // KSEG0 - cached
+ MapRAM(0x80000000);
+ //MapRAM(0x80200000);
+ //MapRAM(0x80400000);
+ //MapRAM(0x80600000);
+ }
+
+ // KSEG1 - uncached
+ MapRAM(0xA0000000);
+ //MapRAM(0xA0200000);
+ //MapRAM(0xA0400000);
+ //MapRAM(0xA0600000);
+}
+
+bool IsRAMCodePage(u32 index)
+{
+ return m_ram_code_bits[index];
+}
+
+void SetRAMCodePage(u32 index)
+{
+ if (m_ram_code_bits[index])
+ return;
+
+ // protect fastmem pages
+ m_ram_code_bits[index] = true;
+ SetCodePageFastmemProtection(index, false);
+}
+
+void ClearRAMCodePage(u32 index)
+{
+ if (!m_ram_code_bits[index])
+ return;
+
+ // unprotect fastmem pages
+ m_ram_code_bits[index] = false;
+ SetCodePageFastmemProtection(index, true);
+}
+
+void SetCodePageFastmemProtection(u32 page_index, bool writable)
+{
+ // unprotect fastmem pages
+ for (const auto& view : m_fastmem_ram_views)
+ {
+ u8* page_address = static_cast(view.GetBasePointer()) + (page_index * CPU_CODE_CACHE_PAGE_SIZE);
+ if (!m_memory_arena.SetPageProtection(page_address, CPU_CODE_CACHE_PAGE_SIZE, true, writable, false))
+ {
+ Log_ErrorPrintf("Failed to %s code page %u (0x%08X) @ %p", writable ? "unprotect" : "protect", page_index,
+ page_index * CPU_CODE_CACHE_PAGE_SIZE, page_address);
+ }
+ }
+}
+
+void ClearRAMCodePageFlags()
+{
+ m_ram_code_bits.reset();
+
+ // unprotect fastmem pages
+ for (const auto& view : m_fastmem_ram_views)
+ {
+ if (!m_memory_arena.SetPageProtection(view.GetBasePointer(), view.GetMappingSize(), true, true, false))
+ {
+ Log_ErrorPrintf("Failed to unprotect code pages for fastmem view @ %p", view.GetBasePointer());
+ }
+ }
+}
+
+bool IsCodePageAddress(PhysicalMemoryAddress address)
+{
+ return IsRAMAddress(address) ? m_ram_code_bits[(address & RAM_MASK) / CPU_CODE_CACHE_PAGE_SIZE] : false;
+}
+
+bool HasCodePagesInRange(PhysicalMemoryAddress start_address, u32 size)
+{
+ if (!IsRAMAddress(start_address))
+ return false;
+
+ start_address = (start_address & RAM_MASK);
+
+ const u32 end_address = start_address + size;
+ while (start_address < end_address)
+ {
+ const u32 code_page_index = start_address / CPU_CODE_CACHE_PAGE_SIZE;
+ if (m_ram_code_bits[code_page_index])
+ return true;
+
+ start_address += CPU_CODE_CACHE_PAGE_SIZE;
+ }
+
+ return false;
+}
+
static TickCount DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, PhysicalMemoryAddress address,
u32& value)
{
diff --git a/src/core/bus.h b/src/core/bus.h
index d2f187ba6..9b8056f9a 100644
--- a/src/core/bus.h
+++ b/src/core/bus.h
@@ -1,5 +1,6 @@
#pragma once
#include "common/bitfield.h"
+#include "common/memory_arena.h"
#include "types.h"
#include
#include
@@ -65,26 +66,67 @@ enum : u32
MEMCTRL_REG_COUNT = 9
};
-void Initialize();
+enum : TickCount
+{
+ RAM_READ_TICKS = 4
+};
+
+enum : size_t
+{
+ // Our memory arena contains storage for RAM.
+ MEMORY_ARENA_SIZE = RAM_SIZE,
+
+ // Offsets within the memory arena.
+ MEMORY_ARENA_RAM_OFFSET = 0,
+
+ // Fastmem region size is 4GB to cover the entire 32-bit address space.
+ FASTMEM_REGION_SIZE = UINT64_C(0x100000000)
+};
+
+bool Initialize();
void Shutdown();
void Reset();
bool DoState(StateWrapper& sw);
+u8* GetFastmemBase();
+void UpdateFastmemViews(bool enabled, bool isolate_cache);
+
void SetExpansionROM(std::vector data);
void SetBIOS(const std::vector& image);
extern std::bitset m_ram_code_bits;
-extern u8 g_ram[RAM_SIZE]; // 2MB RAM
+extern u8* g_ram; // 2MB RAM
extern u8 g_bios[BIOS_SIZE]; // 512K BIOS ROM
+/// Returns true if the address specified is writable (RAM).
+ALWAYS_INLINE static bool IsRAMAddress(PhysicalMemoryAddress address)
+{
+ return address < RAM_MIRROR_END;
+}
+
+/// Returns the code page index for a RAM address.
+ALWAYS_INLINE static u32 GetRAMCodePageIndex(PhysicalMemoryAddress address)
+{
+ return (address & RAM_MASK) / CPU_CODE_CACHE_PAGE_SIZE;
+}
+
+/// Returns true if the specified page contains code.
+bool IsRAMCodePage(u32 index);
+
/// Flags a RAM region as code, so we know when to invalidate blocks.
-ALWAYS_INLINE void SetRAMCodePage(u32 index) { m_ram_code_bits[index] = true; }
+void SetRAMCodePage(u32 index);
/// Unflags a RAM region as code, the code cache will no longer be notified when writes occur.
-ALWAYS_INLINE void ClearRAMCodePage(u32 index) { m_ram_code_bits[index] = false; }
+void ClearRAMCodePage(u32 index);
/// Clears all code bits for RAM regions.
-ALWAYS_INLINE void ClearRAMCodePageFlags() { m_ram_code_bits.reset(); }
+void ClearRAMCodePageFlags();
+
+/// Returns true if the specified address is in a code page.
+bool IsCodePageAddress(PhysicalMemoryAddress address);
+
+/// Returns true if the range specified overlaps with a code page.
+bool HasCodePagesInRange(PhysicalMemoryAddress start_address, u32 size);
/// Returns the number of cycles stolen by DMA RAM access.
ALWAYS_INLINE TickCount GetDMARAMTickCount(u32 word_count)
diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp
index 7e8db76f5..d8242d8e2 100644
--- a/src/core/cpu_code_cache.cpp
+++ b/src/core/cpu_code_cache.cpp
@@ -5,6 +5,7 @@
#include "cpu_core.h"
#include "cpu_core_private.h"
#include "cpu_disasm.h"
+#include "settings.h"
#include "system.h"
#include "timing_event.h"
Log_SetChannel(CPU::CodeCache);
@@ -62,6 +63,7 @@ static void SetFastMap(u32 pc, CodeBlock::HostCodePointer function)
#endif
using BlockMap = std::unordered_map;
+using HostCodeMap = std::map;
void LogCurrentState();
@@ -86,36 +88,68 @@ static void LinkBlock(CodeBlock* from, CodeBlock* to);
/// Unlink all blocks which point to this block, and any that this block links to.
static void UnlinkBlock(CodeBlock* block);
-static bool s_use_recompiler = false;
+static void ClearState();
+
static BlockMap s_blocks;
static std::array, CPU_CODE_CACHE_PAGE_COUNT> m_ram_block_map;
-void Initialize(bool use_recompiler)
+#ifdef WITH_RECOMPILER
+static HostCodeMap s_host_code_map;
+
+static void AddBlockToHostCodeMap(CodeBlock* block);
+static void RemoveBlockFromHostCodeMap(CodeBlock* block);
+static bool InitializeFastmem();
+static void ShutdownFastmem();
+static Common::PageFaultHandler::HandlerResult PageFaultHandler(void* exception_pc, void* fault_address, bool is_write);
+#endif
+
+void Initialize()
{
Assert(s_blocks.empty());
#ifdef WITH_RECOMPILER
- s_use_recompiler = use_recompiler;
-#ifdef USE_STATIC_CODE_BUFFER
- if (!s_code_buffer.Initialize(s_code_storage, sizeof(s_code_storage), RECOMPILER_FAR_CODE_CACHE_SIZE,
- RECOMPILER_GUARD_SIZE))
-#else
- if (!s_code_buffer.Allocate(RECOMPILER_CODE_CACHE_SIZE, RECOMPILER_FAR_CODE_CACHE_SIZE))
-#endif
+ if (g_settings.IsUsingRecompiler())
{
- Panic("Failed to initialize code space");
- }
-
- ResetFastMap();
- CompileDispatcher();
+#ifdef USE_STATIC_CODE_BUFFER
+ if (!s_code_buffer.Initialize(s_code_storage, sizeof(s_code_storage), RECOMPILER_FAR_CODE_CACHE_SIZE,
+ RECOMPILER_GUARD_SIZE))
#else
- s_use_recompiler = false;
+ if (!s_code_buffer.Allocate(RECOMPILER_CODE_CACHE_SIZE, RECOMPILER_FAR_CODE_CACHE_SIZE))
+#endif
+ {
+ Panic("Failed to initialize code space");
+ }
+
+ if (g_settings.IsUsingFastmem() && !InitializeFastmem())
+ Panic("Failed to initialize fastmem");
+
+ ResetFastMap();
+ CompileDispatcher();
+ }
+#endif
+}
+
+void ClearState()
+{
+ Bus::ClearRAMCodePageFlags();
+ for (auto& it : m_ram_block_map)
+ it.clear();
+
+ for (const auto& it : s_blocks)
+ delete it.second;
+
+ s_blocks.clear();
+#ifdef WITH_RECOMPILER
+ s_host_code_map.clear();
+ s_code_buffer.Reset();
+ ResetFastMap();
#endif
}
void Shutdown()
{
- Flush();
+ ClearState();
+ ShutdownFastmem();
#ifdef WITH_RECOMPILER
s_code_buffer.Destroy();
#endif
@@ -286,31 +320,42 @@ void ExecuteRecompiler()
#endif
-void SetUseRecompiler(bool enable)
+void Reinitialize()
{
-#ifdef WITH_RECOMPILER
- if (s_use_recompiler == enable)
- return;
+ ClearState();
- s_use_recompiler = enable;
- Flush();
+#ifdef WITH_RECOMPILER
+
+ ShutdownFastmem();
+ s_code_buffer.Destroy();
+
+ if (g_settings.IsUsingRecompiler())
+ {
+
+#ifdef USE_STATIC_CODE_BUFFER
+ if (!s_code_buffer.Initialize(s_code_storage, sizeof(s_code_storage), RECOMPILER_FAR_CODE_CACHE_SIZE,
+ RECOMPILER_GUARD_SIZE))
+#else
+ if (!s_code_buffer.Allocate(RECOMPILER_CODE_CACHE_SIZE, RECOMPILER_FAR_CODE_CACHE_SIZE))
+#endif
+ {
+ Panic("Failed to initialize code space");
+ }
+
+ if (g_settings.IsUsingFastmem() && !InitializeFastmem())
+ Panic("Failed to initialize fastmem");
+
+ ResetFastMap();
+ CompileDispatcher();
+ }
#endif
}
void Flush()
{
- Bus::ClearRAMCodePageFlags();
- for (auto& it : m_ram_block_map)
- it.clear();
-
- for (const auto& it : s_blocks)
- delete it.second;
- s_blocks.clear();
-#ifdef WITH_RECOMPILER
- s_code_buffer.Reset();
- ResetFastMap();
- CompileDispatcher();
-#endif
+ ClearState();
+ if (g_settings.IsUsingRecompiler())
+ CompileDispatcher();
}
void LogCurrentState()
@@ -365,6 +410,8 @@ CodeBlock* LookupBlock(CodeBlockKey key)
}
iter = s_blocks.emplace(key.bits, block).first;
+ AddBlockToHostCodeMap(block);
+
return block;
}
@@ -391,6 +438,8 @@ bool RevalidateBlock(CodeBlock* block)
return true;
recompile:
+ RemoveBlockFromHostCodeMap(block);
+
block->instructions.clear();
if (!CompileBlock(block))
{
@@ -400,6 +449,7 @@ recompile:
}
// re-add to page map again
+ AddBlockToHostCodeMap(block);
if (block->IsInRAM())
AddBlockToPageMap(block);
@@ -446,6 +496,9 @@ bool CompileBlock(CodeBlock* block)
block->uncached_fetch_ticks += GetInstructionReadTicks(pc);
}
+ block->contains_loadstore_instructions |= cbi.is_load_instruction;
+ block->contains_loadstore_instructions |= cbi.is_store_instruction;
+
// instruction is decoded now
block->instructions.push_back(cbi);
pc += sizeof(cbi.instruction.bits);
@@ -488,7 +541,7 @@ bool CompileBlock(CodeBlock* block)
}
#ifdef WITH_RECOMPILER
- if (s_use_recompiler)
+ if (g_settings.IsUsingRecompiler())
{
// Ensure we're not going to run out of space while compiling this block.
if (s_code_buffer.GetFreeCodeSpace() <
@@ -559,6 +612,9 @@ void FlushBlock(CodeBlock* block)
RemoveBlockFromPageMap(block);
UnlinkBlock(block);
+#ifdef WITH_RECOMPILER
+ RemoveBlockFromHostCodeMap(block);
+#endif
s_blocks.erase(iter);
delete block;
@@ -620,4 +676,107 @@ void UnlinkBlock(CodeBlock* block)
block->link_successors.clear();
}
+#ifdef WITH_RECOMPILER
+
+void AddBlockToHostCodeMap(CodeBlock* block)
+{
+ if (!g_settings.IsUsingRecompiler())
+ return;
+
+ auto ir = s_host_code_map.emplace(block->host_code, block);
+ Assert(ir.second);
+}
+
+void RemoveBlockFromHostCodeMap(CodeBlock* block)
+{
+ if (!g_settings.IsUsingRecompiler())
+ return;
+
+ HostCodeMap::iterator hc_iter = s_host_code_map.find(block->host_code);
+ Assert(hc_iter != s_host_code_map.end());
+ s_host_code_map.erase(hc_iter);
+}
+
+bool InitializeFastmem()
+{
+ if (!Common::PageFaultHandler::InstallHandler(&s_host_code_map, PageFaultHandler))
+ {
+ Log_ErrorPrintf("Failed to install page fault handler");
+ return false;
+ }
+
+ Bus::UpdateFastmemViews(true, g_state.cop0_regs.sr.Isc);
+ return true;
+}
+
+void ShutdownFastmem()
+{
+ Common::PageFaultHandler::RemoveHandler(&s_host_code_map);
+ Bus::UpdateFastmemViews(false, false);
+}
+
+Common::PageFaultHandler::HandlerResult PageFaultHandler(void* exception_pc, void* fault_address, bool is_write)
+{
+ if (static_cast(fault_address) < g_state.fastmem_base ||
+ (static_cast(fault_address) - g_state.fastmem_base) >= Bus::FASTMEM_REGION_SIZE)
+ {
+ return Common::PageFaultHandler::HandlerResult::ExecuteNextHandler;
+ }
+
+ const PhysicalMemoryAddress fastmem_address =
+ static_cast(static_cast(static_cast(fault_address) - g_state.fastmem_base));
+
+ Log_DevPrintf("Page fault handler invoked at PC=%p Address=%p %s, fastmem offset 0x%08X", exception_pc, fault_address,
+ is_write ? "(write)" : "(read)", fastmem_address);
+
+ if (is_write && !g_state.cop0_regs.sr.Isc && Bus::IsRAMAddress(fastmem_address))
+ {
+ // this is probably a code page, since we aren't going to fault due to requiring fastmem on RAM.
+ const u32 code_page_index = Bus::GetRAMCodePageIndex(fastmem_address);
+ if (Bus::IsRAMCodePage(code_page_index))
+ {
+ InvalidateBlocksWithPageIndex(code_page_index);
+ return Common::PageFaultHandler::HandlerResult::ContinueExecution;
+ }
+ }
+
+ // use upper_bound to find the next block after the pc
+ HostCodeMap::iterator upper_iter =
+ s_host_code_map.upper_bound(reinterpret_cast(exception_pc));
+ if (upper_iter == s_host_code_map.begin())
+ return Common::PageFaultHandler::HandlerResult::ExecuteNextHandler;
+
+ // then decrement it by one to (hopefully) get the block we want
+ upper_iter--;
+
+ // find the loadstore info in the code block
+ CodeBlock* block = upper_iter->second;
+ for (auto bpi_iter = block->loadstore_backpatch_info.begin(); bpi_iter != block->loadstore_backpatch_info.end();
+ ++bpi_iter)
+ {
+ const Recompiler::LoadStoreBackpatchInfo& lbi = *bpi_iter;
+ if (lbi.host_pc == exception_pc)
+ {
+ // found it, do fixup
+ if (Recompiler::CodeGenerator::BackpatchLoadStore(lbi))
+ {
+ // remove the backpatch entry since we won't be coming back to this one
+ block->loadstore_backpatch_info.erase(bpi_iter);
+ return Common::PageFaultHandler::HandlerResult::ContinueExecution;
+ }
+ else
+ {
+ Log_ErrorPrintf("Failed to backpatch %p in block 0x%08X", exception_pc, block->GetPC());
+ return Common::PageFaultHandler::HandlerResult::ExecuteNextHandler;
+ }
+ }
+ }
+
+ // we didn't find the pc in our list..
+ Log_ErrorPrintf("Loadstore PC not found for %p in block 0x%08X", exception_pc, block->GetPC());
+ return Common::PageFaultHandler::HandlerResult::ExecuteNextHandler;
+}
+
+#endif
+
} // namespace CPU::CodeCache
diff --git a/src/core/cpu_code_cache.h b/src/core/cpu_code_cache.h
index 6d09a8c0d..ee03aea6b 100644
--- a/src/core/cpu_code_cache.h
+++ b/src/core/cpu_code_cache.h
@@ -2,12 +2,18 @@
#include "bus.h"
#include "common/bitfield.h"
#include "common/jit_code_buffer.h"
+#include "common/page_fault_handler.h"
#include "cpu_types.h"
#include
+#include