Merge pull request #12079 from JosJuice/blr-no-fastmem

Jit: Allow BLR optimization without fastmem
This commit is contained in:
JMC47
2023-09-02 12:45:39 -04:00
committed by GitHub
4 changed files with 56 additions and 16 deletions

View File

@ -160,18 +160,25 @@ void* AllocateAlignedMemory(size_t size, size_t alignment)
return ptr; return ptr;
} }
void FreeMemoryPages(void* ptr, size_t size) bool FreeMemoryPages(void* ptr, size_t size)
{ {
if (ptr) if (ptr)
{ {
#ifdef _WIN32 #ifdef _WIN32
if (!VirtualFree(ptr, 0, MEM_RELEASE)) if (!VirtualFree(ptr, 0, MEM_RELEASE))
{
PanicAlertFmt("FreeMemoryPages failed!\nVirtualFree: {}", GetLastErrorString()); PanicAlertFmt("FreeMemoryPages failed!\nVirtualFree: {}", GetLastErrorString());
return false;
}
#else #else
if (munmap(ptr, size) != 0) if (munmap(ptr, size) != 0)
{
PanicAlertFmt("FreeMemoryPages failed!\nmunmap: {}", LastStrerrorString()); PanicAlertFmt("FreeMemoryPages failed!\nmunmap: {}", LastStrerrorString());
return false;
}
#endif #endif
} }
return true;
} }
void FreeAlignedMemory(void* ptr) void FreeAlignedMemory(void* ptr)
@ -186,39 +193,56 @@ void FreeAlignedMemory(void* ptr)
} }
} }
void ReadProtectMemory(void* ptr, size_t size) bool ReadProtectMemory(void* ptr, size_t size)
{ {
#ifdef _WIN32 #ifdef _WIN32
DWORD oldValue; DWORD oldValue;
if (!VirtualProtect(ptr, size, PAGE_NOACCESS, &oldValue)) if (!VirtualProtect(ptr, size, PAGE_NOACCESS, &oldValue))
{
PanicAlertFmt("ReadProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString()); PanicAlertFmt("ReadProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString());
return false;
}
#else #else
if (mprotect(ptr, size, PROT_NONE) != 0) if (mprotect(ptr, size, PROT_NONE) != 0)
{
PanicAlertFmt("ReadProtectMemory failed!\nmprotect: {}", LastStrerrorString()); PanicAlertFmt("ReadProtectMemory failed!\nmprotect: {}", LastStrerrorString());
return false;
}
#endif #endif
return true;
} }
void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) bool WriteProtectMemory(void* ptr, size_t size, bool allowExecute)
{ {
#ifdef _WIN32 #ifdef _WIN32
DWORD oldValue; DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
{
PanicAlertFmt("WriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString()); PanicAlertFmt("WriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString());
return false;
}
#elif !(defined(_M_ARM_64) && defined(__APPLE__)) #elif !(defined(_M_ARM_64) && defined(__APPLE__))
// MacOS 11.2 on ARM does not allow for changing the access permissions of pages // MacOS 11.2 on ARM does not allow for changing the access permissions of pages
// that were marked executable, instead it uses the protections offered by MAP_JIT // that were marked executable, instead it uses the protections offered by MAP_JIT
// for write protection. // for write protection.
if (mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ) != 0) if (mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ) != 0)
{
PanicAlertFmt("WriteProtectMemory failed!\nmprotect: {}", LastStrerrorString()); PanicAlertFmt("WriteProtectMemory failed!\nmprotect: {}", LastStrerrorString());
return false;
}
#endif #endif
return true;
} }
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) bool UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute)
{ {
#ifdef _WIN32 #ifdef _WIN32
DWORD oldValue; DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue)) if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue))
{
PanicAlertFmt("UnWriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString()); PanicAlertFmt("UnWriteProtectMemory failed!\nVirtualProtect: {}", GetLastErrorString());
return false;
}
#elif !(defined(_M_ARM_64) && defined(__APPLE__)) #elif !(defined(_M_ARM_64) && defined(__APPLE__))
// MacOS 11.2 on ARM does not allow for changing the access permissions of pages // MacOS 11.2 on ARM does not allow for changing the access permissions of pages
// that were marked executable, instead it uses the protections offered by MAP_JIT // that were marked executable, instead it uses the protections offered by MAP_JIT
@ -227,8 +251,10 @@ void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute)
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ) != 0) allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ) != 0)
{ {
PanicAlertFmt("UnWriteProtectMemory failed!\nmprotect: {}", LastStrerrorString()); PanicAlertFmt("UnWriteProtectMemory failed!\nmprotect: {}", LastStrerrorString());
return false;
} }
#endif #endif
return true;
} }
size_t MemPhysical() size_t MemPhysical()

View File

@ -27,12 +27,12 @@ struct ScopedJITPageWriteAndNoExecute
~ScopedJITPageWriteAndNoExecute() { JITPageWriteDisableExecuteEnable(); } ~ScopedJITPageWriteAndNoExecute() { JITPageWriteDisableExecuteEnable(); }
}; };
void* AllocateMemoryPages(size_t size); void* AllocateMemoryPages(size_t size);
void FreeMemoryPages(void* ptr, size_t size); bool FreeMemoryPages(void* ptr, size_t size);
void* AllocateAlignedMemory(size_t size, size_t alignment); void* AllocateAlignedMemory(size_t size, size_t alignment);
void FreeAlignedMemory(void* ptr); void FreeAlignedMemory(void* ptr);
void ReadProtectMemory(void* ptr, size_t size); bool ReadProtectMemory(void* ptr, size_t size);
void WriteProtectMemory(void* ptr, size_t size, bool executable = false); bool WriteProtectMemory(void* ptr, size_t size, bool executable = false);
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); bool UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
size_t MemPhysical(); size_t MemPhysical();
} // namespace Common } // namespace Common

View File

@ -391,9 +391,10 @@ static void CpuThread(const std::optional<std::string>& savestate_path, bool del
static_cast<void>(IDCache::GetEnvForThread()); static_cast<void>(IDCache::GetEnvForThread());
#endif #endif
const bool fastmem_enabled = Config::Get(Config::MAIN_FASTMEM); // The JIT need to be able to intercept faults, both for fastmem and for the BLR optimization.
if (fastmem_enabled) const bool exception_handler = EMM::IsExceptionHandlerSupported();
EMM::InstallExceptionHandler(); // Let's run under memory watch if (exception_handler)
EMM::InstallExceptionHandler();
#ifdef USE_MEMORYWATCHER #ifdef USE_MEMORYWATCHER
s_memory_watcher = std::make_unique<MemoryWatcher>(); s_memory_watcher = std::make_unique<MemoryWatcher>();
@ -441,7 +442,7 @@ static void CpuThread(const std::optional<std::string>& savestate_path, bool del
s_is_started = false; s_is_started = false;
if (fastmem_enabled) if (exception_handler)
EMM::UninstallExceptionHandler(); EMM::UninstallExceptionHandler();
if (GDBStub::IsActive()) if (GDBStub::IsActive())

View File

@ -18,6 +18,7 @@
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/CoreTiming.h" #include "Core/CoreTiming.h"
#include "Core/HW/CPU.h" #include "Core/HW/CPU.h"
#include "Core/MemTools.h"
#include "Core/PowerPC/PPCAnalyst.h" #include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PowerPC.h"
#include "Core/System.h" #include "Core/System.h"
@ -132,7 +133,8 @@ void JitBase::RefreshConfig()
analyzer.SetDivByZeroExceptionsEnabled(m_enable_div_by_zero_exceptions); analyzer.SetDivByZeroExceptionsEnabled(m_enable_div_by_zero_exceptions);
bool any_watchpoints = m_system.GetPowerPC().GetMemChecks().HasAny(); bool any_watchpoints = m_system.GetPowerPC().GetMemChecks().HasAny();
jo.fastmem = m_fastmem_enabled && jo.fastmem_arena && (m_ppc_state.msr.DR || !any_watchpoints); jo.fastmem = m_fastmem_enabled && jo.fastmem_arena && (m_ppc_state.msr.DR || !any_watchpoints) &&
EMM::IsExceptionHandlerSupported();
jo.memcheck = m_system.IsMMUMode() || m_system.IsPauseOnPanicMode() || any_watchpoints; jo.memcheck = m_system.IsMMUMode() || m_system.IsPauseOnPanicMode() || any_watchpoints;
jo.fp_exceptions = m_enable_float_exceptions; jo.fp_exceptions = m_enable_float_exceptions;
jo.div_by_zero_exceptions = m_enable_div_by_zero_exceptions; jo.div_by_zero_exceptions = m_enable_div_by_zero_exceptions;
@ -140,7 +142,8 @@ void JitBase::RefreshConfig()
void JitBase::InitBLROptimization() void JitBase::InitBLROptimization()
{ {
m_enable_blr_optimization = jo.enableBlocklink && m_fastmem_enabled && !m_enable_debugging; m_enable_blr_optimization =
jo.enableBlocklink && !m_enable_debugging && EMM::IsExceptionHandlerSupported();
m_cleanup_after_stackfault = false; m_cleanup_after_stackfault = false;
} }
@ -151,7 +154,12 @@ void JitBase::ProtectStack()
#ifdef _WIN32 #ifdef _WIN32
ULONG reserveSize = SAFE_STACK_SIZE; ULONG reserveSize = SAFE_STACK_SIZE;
SetThreadStackGuarantee(&reserveSize); if (!SetThreadStackGuarantee(&reserveSize))
{
PanicAlertFmt("Failed to set thread stack guarantee");
m_enable_blr_optimization = false;
return;
}
#else #else
auto [stack_addr, stack_size] = Common::GetCurrentThreadStack(); auto [stack_addr, stack_size] = Common::GetCurrentThreadStack();
@ -184,7 +192,12 @@ void JitBase::ProtectStack()
} }
m_stack_guard = reinterpret_cast<u8*>(stack_guard_addr); m_stack_guard = reinterpret_cast<u8*>(stack_guard_addr);
Common::ReadProtectMemory(m_stack_guard, GUARD_SIZE); if (!Common::ReadProtectMemory(m_stack_guard, GUARD_SIZE))
{
m_stack_guard = nullptr;
m_enable_blr_optimization = false;
return;
}
#endif #endif
} }