Jit: Raise program exception on floating point exceptions

This is done entirely through interpreter fallbacks. It would
probably be possible to implement this using host exception
handlers instead, but I think it would be a lot of complexity
for a rarely used feature, so let's not do it for now.

For performance reasons, there are two settings for this feature:
One setting which does enables just what True Crime: New York City
needs and one setting which enables it all. The latter makes
almost all float instructions fall back to the interpreter.
This commit is contained in:
JosJuice 2021-08-18 12:39:02 +02:00
commit 9f525d69c8
24 changed files with 216 additions and 88 deletions

View file

@ -75,6 +75,8 @@ private:
bool bCPUThread; bool bCPUThread;
bool bJITFollowBranch; bool bJITFollowBranch;
bool bSyncGPUOnSkipIdleHack; bool bSyncGPUOnSkipIdleHack;
bool bFloatExceptions;
bool bDivideByZeroExceptions;
bool bFPRF; bool bFPRF;
bool bAccurateNaNs; bool bAccurateNaNs;
bool bMMU; bool bMMU;
@ -109,6 +111,8 @@ void ConfigCache::SaveConfig(const SConfig& config)
bCPUThread = config.bCPUThread; bCPUThread = config.bCPUThread;
bJITFollowBranch = config.bJITFollowBranch; bJITFollowBranch = config.bJITFollowBranch;
bSyncGPUOnSkipIdleHack = config.bSyncGPUOnSkipIdleHack; bSyncGPUOnSkipIdleHack = config.bSyncGPUOnSkipIdleHack;
bFloatExceptions = config.bFloatExceptions;
bDivideByZeroExceptions = config.bDivideByZeroExceptions;
bFPRF = config.bFPRF; bFPRF = config.bFPRF;
bAccurateNaNs = config.bAccurateNaNs; bAccurateNaNs = config.bAccurateNaNs;
bDisableICache = config.bDisableICache; bDisableICache = config.bDisableICache;
@ -154,6 +158,8 @@ void ConfigCache::RestoreConfig(SConfig* config)
config->bCPUThread = bCPUThread; config->bCPUThread = bCPUThread;
config->bJITFollowBranch = bJITFollowBranch; config->bJITFollowBranch = bJITFollowBranch;
config->bSyncGPUOnSkipIdleHack = bSyncGPUOnSkipIdleHack; config->bSyncGPUOnSkipIdleHack = bSyncGPUOnSkipIdleHack;
config->bFloatExceptions = bFloatExceptions;
config->bDivideByZeroExceptions = bDivideByZeroExceptions;
config->bFPRF = bFPRF; config->bFPRF = bFPRF;
config->bAccurateNaNs = bAccurateNaNs; config->bAccurateNaNs = bAccurateNaNs;
config->bDisableICache = bDisableICache; config->bDisableICache = bDisableICache;
@ -256,6 +262,9 @@ bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
core_section->Get("JITFollowBranch", &StartUp.bJITFollowBranch, StartUp.bJITFollowBranch); core_section->Get("JITFollowBranch", &StartUp.bJITFollowBranch, StartUp.bJITFollowBranch);
core_section->Get("SyncOnSkipIdle", &StartUp.bSyncGPUOnSkipIdleHack, core_section->Get("SyncOnSkipIdle", &StartUp.bSyncGPUOnSkipIdleHack,
StartUp.bSyncGPUOnSkipIdleHack); StartUp.bSyncGPUOnSkipIdleHack);
core_section->Get("FloatExceptions", &StartUp.bFloatExceptions, StartUp.bFloatExceptions);
core_section->Get("DivByZeroExceptions", &StartUp.bDivideByZeroExceptions,
StartUp.bDivideByZeroExceptions);
core_section->Get("FPRF", &StartUp.bFPRF, StartUp.bFPRF); core_section->Get("FPRF", &StartUp.bFPRF, StartUp.bFPRF);
core_section->Get("AccurateNaNs", &StartUp.bAccurateNaNs, StartUp.bAccurateNaNs); core_section->Get("AccurateNaNs", &StartUp.bAccurateNaNs, StartUp.bAccurateNaNs);
core_section->Get("DisableICache", &StartUp.bDisableICache, StartUp.bDisableICache); core_section->Get("DisableICache", &StartUp.bDisableICache, StartUp.bDisableICache);
@ -370,6 +379,8 @@ bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
StartUp.bAccurateNaNs = netplay_settings.m_AccurateNaNs; StartUp.bAccurateNaNs = netplay_settings.m_AccurateNaNs;
StartUp.bDisableICache = netplay_settings.m_DisableICache; StartUp.bDisableICache = netplay_settings.m_DisableICache;
StartUp.bSyncGPUOnSkipIdleHack = netplay_settings.m_SyncOnSkipIdle; StartUp.bSyncGPUOnSkipIdleHack = netplay_settings.m_SyncOnSkipIdle;
StartUp.bFloatExceptions = netplay_settings.m_FloatExceptions;
StartUp.bDivideByZeroExceptions = netplay_settings.m_DivideByZeroExceptions;
StartUp.bSyncGPU = netplay_settings.m_SyncGPU; StartUp.bSyncGPU = netplay_settings.m_SyncGPU;
StartUp.iSyncGpuMaxDistance = netplay_settings.m_SyncGpuMaxDistance; StartUp.iSyncGpuMaxDistance = netplay_settings.m_SyncGpuMaxDistance;
StartUp.iSyncGpuMinDistance = netplay_settings.m_SyncGpuMinDistance; StartUp.iSyncGpuMinDistance = netplay_settings.m_SyncGpuMinDistance;

View file

@ -86,6 +86,9 @@ const Info<int> MAIN_SYNC_GPU_MIN_DISTANCE{{System::Main, "Core", "SyncGpuMinDis
const Info<float> MAIN_SYNC_GPU_OVERCLOCK{{System::Main, "Core", "SyncGpuOverclock"}, 1.0f}; const Info<float> MAIN_SYNC_GPU_OVERCLOCK{{System::Main, "Core", "SyncGpuOverclock"}, 1.0f};
const Info<bool> MAIN_FAST_DISC_SPEED{{System::Main, "Core", "FastDiscSpeed"}, false}; const Info<bool> MAIN_FAST_DISC_SPEED{{System::Main, "Core", "FastDiscSpeed"}, false};
const Info<bool> MAIN_LOW_DCBZ_HACK{{System::Main, "Core", "LowDCBZHack"}, false}; const Info<bool> MAIN_LOW_DCBZ_HACK{{System::Main, "Core", "LowDCBZHack"}, false};
const Info<bool> MAIN_FLOAT_EXCEPTIONS{{System::Main, "Core", "FloatExceptions"}, false};
const Info<bool> MAIN_DIVIDE_BY_ZERO_EXCEPTIONS{{System::Main, "Core", "DivByZeroExceptions"},
false};
const Info<bool> MAIN_FPRF{{System::Main, "Core", "FPRF"}, false}; const Info<bool> MAIN_FPRF{{System::Main, "Core", "FPRF"}, false};
const Info<bool> MAIN_ACCURATE_NANS{{System::Main, "Core", "AccurateNaNs"}, false}; const Info<bool> MAIN_ACCURATE_NANS{{System::Main, "Core", "AccurateNaNs"}, false};
const Info<bool> MAIN_DISABLE_ICACHE{{System::Main, "Core", "DisableICache"}, false}; const Info<bool> MAIN_DISABLE_ICACHE{{System::Main, "Core", "DisableICache"}, false};

View file

@ -68,6 +68,8 @@ extern const Info<int> MAIN_SYNC_GPU_MIN_DISTANCE;
extern const Info<float> MAIN_SYNC_GPU_OVERCLOCK; extern const Info<float> MAIN_SYNC_GPU_OVERCLOCK;
extern const Info<bool> MAIN_FAST_DISC_SPEED; extern const Info<bool> MAIN_FAST_DISC_SPEED;
extern const Info<bool> MAIN_LOW_DCBZ_HACK; extern const Info<bool> MAIN_LOW_DCBZ_HACK;
extern const Info<bool> MAIN_FLOAT_EXCEPTIONS;
extern const Info<bool> MAIN_DIVIDE_BY_ZERO_EXCEPTIONS;
extern const Info<bool> MAIN_FPRF; extern const Info<bool> MAIN_FPRF;
extern const Info<bool> MAIN_ACCURATE_NANS; extern const Info<bool> MAIN_ACCURATE_NANS;
extern const Info<bool> MAIN_DISABLE_ICACHE; extern const Info<bool> MAIN_DISABLE_ICACHE;

View file

@ -69,6 +69,8 @@ public:
layer->Set(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES, layer->Set(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES,
m_settings.m_SafeTextureCacheColorSamples); m_settings.m_SafeTextureCacheColorSamples);
layer->Set(Config::GFX_PERF_QUERIES_ENABLE, m_settings.m_PerfQueriesEnable); layer->Set(Config::GFX_PERF_QUERIES_ENABLE, m_settings.m_PerfQueriesEnable);
layer->Set(Config::MAIN_FLOAT_EXCEPTIONS, m_settings.m_FloatExceptions);
layer->Set(Config::MAIN_DIVIDE_BY_ZERO_EXCEPTIONS, m_settings.m_DivideByZeroExceptions);
layer->Set(Config::MAIN_FPRF, m_settings.m_FPRF); layer->Set(Config::MAIN_FPRF, m_settings.m_FPRF);
layer->Set(Config::MAIN_ACCURATE_NANS, m_settings.m_AccurateNaNs); layer->Set(Config::MAIN_ACCURATE_NANS, m_settings.m_AccurateNaNs);
layer->Set(Config::MAIN_DISABLE_ICACHE, m_settings.m_DisableICache); layer->Set(Config::MAIN_DISABLE_ICACHE, m_settings.m_DisableICache);

View file

@ -213,6 +213,8 @@ void SConfig::SaveCoreSettings(IniFile& ini)
core->Set("SyncGpuMaxDistance", iSyncGpuMaxDistance); core->Set("SyncGpuMaxDistance", iSyncGpuMaxDistance);
core->Set("SyncGpuMinDistance", iSyncGpuMinDistance); core->Set("SyncGpuMinDistance", iSyncGpuMinDistance);
core->Set("SyncGpuOverclock", fSyncGpuOverclock); core->Set("SyncGpuOverclock", fSyncGpuOverclock);
core->Set("FloatExceptions", bFloatExceptions);
core->Set("DivByZeroExceptions", bDivideByZeroExceptions);
core->Set("FPRF", bFPRF); core->Set("FPRF", bFPRF);
core->Set("AccurateNaNs", bAccurateNaNs); core->Set("AccurateNaNs", bAccurateNaNs);
core->Set("SelectedLanguage", SelectedLanguage); core->Set("SelectedLanguage", SelectedLanguage);
@ -509,6 +511,8 @@ void SConfig::LoadCoreSettings(IniFile& ini)
core->Get("SyncGpuOverclock", &fSyncGpuOverclock, 1.0f); core->Get("SyncGpuOverclock", &fSyncGpuOverclock, 1.0f);
core->Get("FastDiscSpeed", &bFastDiscSpeed, false); core->Get("FastDiscSpeed", &bFastDiscSpeed, false);
core->Get("LowDCBZHack", &bLowDCBZHack, false); core->Get("LowDCBZHack", &bLowDCBZHack, false);
core->Get("FloatExceptions", &bFloatExceptions, false);
core->Get("DivByZeroExceptions", &bDivideByZeroExceptions, false);
core->Get("FPRF", &bFPRF, false); core->Get("FPRF", &bFPRF, false);
core->Get("AccurateNaNs", &bAccurateNaNs, false); core->Get("AccurateNaNs", &bAccurateNaNs, false);
core->Get("DisableICache", &bDisableICache, false); core->Get("DisableICache", &bDisableICache, false);
@ -747,6 +751,8 @@ void SConfig::LoadDefaults()
bRunCompareServer = false; bRunCompareServer = false;
bDSPHLE = true; bDSPHLE = true;
bFastmem = true; bFastmem = true;
bFloatExceptions = false;
bDivideByZeroExceptions = false;
bFPRF = false; bFPRF = false;
bAccurateNaNs = false; bAccurateNaNs = false;
bDisableICache = false; bDisableICache = false;

View file

@ -108,6 +108,8 @@ struct SConfig
bool bJITRegisterCacheOff = false; bool bJITRegisterCacheOff = false;
bool bFastmem; bool bFastmem;
bool bFloatExceptions = false;
bool bDivideByZeroExceptions = false;
bool bFPRF = false; bool bFPRF = false;
bool bAccurateNaNs = false; bool bAccurateNaNs = false;
bool bDisableICache = false; bool bDisableICache = false;

View file

@ -831,6 +831,8 @@ void NetPlayClient::OnStartGame(sf::Packet& packet)
packet >> m_net_settings.m_EFBEmulateFormatChanges; packet >> m_net_settings.m_EFBEmulateFormatChanges;
packet >> m_net_settings.m_SafeTextureCacheColorSamples; packet >> m_net_settings.m_SafeTextureCacheColorSamples;
packet >> m_net_settings.m_PerfQueriesEnable; packet >> m_net_settings.m_PerfQueriesEnable;
packet >> m_net_settings.m_FloatExceptions;
packet >> m_net_settings.m_DivideByZeroExceptions;
packet >> m_net_settings.m_FPRF; packet >> m_net_settings.m_FPRF;
packet >> m_net_settings.m_AccurateNaNs; packet >> m_net_settings.m_AccurateNaNs;
packet >> m_net_settings.m_DisableICache; packet >> m_net_settings.m_DisableICache;

View file

@ -58,6 +58,8 @@ struct NetSettings
bool m_EFBEmulateFormatChanges; bool m_EFBEmulateFormatChanges;
int m_SafeTextureCacheColorSamples; int m_SafeTextureCacheColorSamples;
bool m_PerfQueriesEnable; bool m_PerfQueriesEnable;
bool m_FloatExceptions;
bool m_DivideByZeroExceptions;
bool m_FPRF; bool m_FPRF;
bool m_AccurateNaNs; bool m_AccurateNaNs;
bool m_DisableICache; bool m_DisableICache;

View file

@ -1329,6 +1329,8 @@ bool NetPlayServer::SetupNetSettings()
settings.m_SafeTextureCacheColorSamples = settings.m_SafeTextureCacheColorSamples =
Config::Get(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES); Config::Get(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES);
settings.m_PerfQueriesEnable = Config::Get(Config::GFX_PERF_QUERIES_ENABLE); settings.m_PerfQueriesEnable = Config::Get(Config::GFX_PERF_QUERIES_ENABLE);
settings.m_FloatExceptions = Config::Get(Config::MAIN_FLOAT_EXCEPTIONS);
settings.m_DivideByZeroExceptions = Config::Get(Config::MAIN_DIVIDE_BY_ZERO_EXCEPTIONS);
settings.m_FPRF = Config::Get(Config::MAIN_FPRF); settings.m_FPRF = Config::Get(Config::MAIN_FPRF);
settings.m_AccurateNaNs = Config::Get(Config::MAIN_ACCURATE_NANS); settings.m_AccurateNaNs = Config::Get(Config::MAIN_ACCURATE_NANS);
settings.m_DisableICache = Config::Get(Config::MAIN_DISABLE_ICACHE); settings.m_DisableICache = Config::Get(Config::MAIN_DISABLE_ICACHE);
@ -1505,6 +1507,8 @@ bool NetPlayServer::StartGame()
spac << m_settings.m_EFBEmulateFormatChanges; spac << m_settings.m_EFBEmulateFormatChanges;
spac << m_settings.m_SafeTextureCacheColorSamples; spac << m_settings.m_SafeTextureCacheColorSamples;
spac << m_settings.m_PerfQueriesEnable; spac << m_settings.m_PerfQueriesEnable;
spac << m_settings.m_FloatExceptions;
spac << m_settings.m_DivideByZeroExceptions;
spac << m_settings.m_FPRF; spac << m_settings.m_FPRF;
spac << m_settings.m_AccurateNaNs; spac << m_settings.m_AccurateNaNs;
spac << m_settings.m_DisableICache; spac << m_settings.m_DisableICache;

View file

@ -58,7 +58,7 @@ void CachedInterpreter::Init()
jo.enableBlocklink = false; jo.enableBlocklink = false;
m_block_cache.Init(); m_block_cache.Init();
UpdateMemoryOptions(); UpdateMemoryAndExceptionOptions();
code_block.m_stats = &js.st; code_block.m_stats = &js.st;
code_block.m_gpa = &js.gpa; code_block.m_gpa = &js.gpa;
@ -180,6 +180,17 @@ static bool CheckDSI(u32 data)
return false; return false;
} }
static bool CheckProgramException(u32 data)
{
if (PowerPC::ppcState.Exceptions & EXCEPTION_PROGRAM)
{
PowerPC::CheckExceptions();
PowerPC::ppcState.downcount -= data;
return true;
}
return false;
}
static bool CheckBreakpoint(u32 data) static bool CheckBreakpoint(u32 data)
{ {
PowerPC::CheckBreakPoints(); PowerPC::CheckBreakPoints();
@ -267,26 +278,26 @@ void CachedInterpreter::Jit(u32 address)
const bool check_fpu = (op.opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound; const bool check_fpu = (op.opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound;
const bool endblock = (op.opinfo->flags & FL_ENDBLOCK) != 0; const bool endblock = (op.opinfo->flags & FL_ENDBLOCK) != 0;
const bool memcheck = (op.opinfo->flags & FL_LOADSTORE) && jo.memcheck; const bool memcheck = (op.opinfo->flags & FL_LOADSTORE) && jo.memcheck;
const bool check_program_exception = !endblock && ShouldHandleFPExceptionForInstruction(&op);
const bool idle_loop = op.branchIsIdleLoop; const bool idle_loop = op.branchIsIdleLoop;
if (breakpoint) if (breakpoint || check_fpu || endblock || memcheck || check_program_exception)
{
m_code.emplace_back(WritePC, op.address); m_code.emplace_back(WritePC, op.address);
if (breakpoint)
m_code.emplace_back(CheckBreakpoint, js.downcountAmount); m_code.emplace_back(CheckBreakpoint, js.downcountAmount);
}
if (check_fpu) if (check_fpu)
{ {
m_code.emplace_back(WritePC, op.address);
m_code.emplace_back(CheckFPU, js.downcountAmount); m_code.emplace_back(CheckFPU, js.downcountAmount);
js.firstFPInstructionFound = true; js.firstFPInstructionFound = true;
} }
if (endblock || memcheck)
m_code.emplace_back(WritePC, op.address);
m_code.emplace_back(PPCTables::GetInterpreterOp(op.inst), op.inst); m_code.emplace_back(PPCTables::GetInterpreterOp(op.inst), op.inst);
if (memcheck) if (memcheck)
m_code.emplace_back(CheckDSI, js.downcountAmount); m_code.emplace_back(CheckDSI, js.downcountAmount);
if (check_program_exception)
m_code.emplace_back(CheckProgramException, js.downcountAmount);
if (idle_loop) if (idle_loop)
m_code.emplace_back(CheckIdle, js.blockStart); m_code.emplace_back(CheckIdle, js.blockStart);
if (endblock) if (endblock)
@ -316,5 +327,5 @@ void CachedInterpreter::ClearCache()
{ {
m_code.clear(); m_code.clear();
m_block_cache.Clear(); m_block_cache.Clear();
UpdateMemoryOptions(); UpdateMemoryAndExceptionOptions();
} }

View file

@ -94,14 +94,14 @@ static std::array<GekkoOPTemplate, 54> primarytable =
static std::array<GekkoOPTemplate, 13> table4 = static std::array<GekkoOPTemplate, 13> table4 =
{{ //SUBOP10 {{ //SUBOP10
{0, Interpreter::ps_cmpu0, {"ps_cmpu0", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {0, Interpreter::ps_cmpu0, {"ps_cmpu0", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{32, Interpreter::ps_cmpo0, {"ps_cmpo0", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {32, Interpreter::ps_cmpo0, {"ps_cmpo0", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{40, Interpreter::ps_neg, {"ps_neg", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {40, Interpreter::ps_neg, {"ps_neg", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
{136, Interpreter::ps_nabs, {"ps_nabs", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {136, Interpreter::ps_nabs, {"ps_nabs", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
{264, Interpreter::ps_abs, {"ps_abs", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {264, Interpreter::ps_abs, {"ps_abs", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
{64, Interpreter::ps_cmpu1, {"ps_cmpu1", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {64, Interpreter::ps_cmpu1, {"ps_cmpu1", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{72, Interpreter::ps_mr, {"ps_mr", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {72, Interpreter::ps_mr, {"ps_mr", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
{96, Interpreter::ps_cmpo1, {"ps_cmpo1", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {96, Interpreter::ps_cmpo1, {"ps_cmpo1", OpType::PS, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{528, Interpreter::ps_merge00, {"ps_merge00", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {528, Interpreter::ps_merge00, {"ps_merge00", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
{560, Interpreter::ps_merge01, {"ps_merge01", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {560, Interpreter::ps_merge01, {"ps_merge01", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
{592, Interpreter::ps_merge10, {"ps_merge10", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {592, Interpreter::ps_merge10, {"ps_merge10", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_IN_FLOAT_AB_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
@ -112,23 +112,23 @@ static std::array<GekkoOPTemplate, 13> table4 =
static std::array<GekkoOPTemplate, 17> table4_2 = static std::array<GekkoOPTemplate, 17> table4_2 =
{{ {{
{10, Interpreter::ps_sum0, {"ps_sum0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {10, Interpreter::ps_sum0, {"ps_sum0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{11, Interpreter::ps_sum1, {"ps_sum1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {11, Interpreter::ps_sum1, {"ps_sum1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{12, Interpreter::ps_muls0, {"ps_muls0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {12, Interpreter::ps_muls0, {"ps_muls0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{13, Interpreter::ps_muls1, {"ps_muls1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {13, Interpreter::ps_muls1, {"ps_muls1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{14, Interpreter::ps_madds0, {"ps_madds0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {14, Interpreter::ps_madds0, {"ps_madds0", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{15, Interpreter::ps_madds1, {"ps_madds1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {15, Interpreter::ps_madds1, {"ps_madds1", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{18, Interpreter::ps_div, {"ps_div", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 17, 0, 0, 0}}, {18, Interpreter::ps_div, {"ps_div", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV, 17, 0, 0, 0}},
{20, Interpreter::ps_sub, {"ps_sub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {20, Interpreter::ps_sub, {"ps_sub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{21, Interpreter::ps_add, {"ps_add", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {21, Interpreter::ps_add, {"ps_add", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{23, Interpreter::ps_sel, {"ps_sel", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_IN_FLOAT_BC_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {23, Interpreter::ps_sel, {"ps_sel", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_IN_FLOAT_BC_BITEXACT | FL_RC_BIT_F | FL_USE_FPU | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
{24, Interpreter::ps_res, {"ps_res", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {24, Interpreter::ps_res, {"ps_res", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV, 1, 0, 0, 0}},
{25, Interpreter::ps_mul, {"ps_mul", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {25, Interpreter::ps_mul, {"ps_mul", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{26, Interpreter::ps_rsqrte, {"ps_rsqrte", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 2, 0, 0, 0}}, {26, Interpreter::ps_rsqrte, {"ps_rsqrte", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV, 2, 0, 0, 0}},
{28, Interpreter::ps_msub, {"ps_msub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {28, Interpreter::ps_msub, {"ps_msub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{29, Interpreter::ps_madd, {"ps_madd", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {29, Interpreter::ps_madd, {"ps_madd", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{30, Interpreter::ps_nmsub, {"ps_nmsub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {30, Interpreter::ps_nmsub, {"ps_nmsub", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{31, Interpreter::ps_nmadd, {"ps_nmadd", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {31, Interpreter::ps_nmadd, {"ps_nmadd", OpType::PS, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
}}; }};
@ -280,7 +280,7 @@ static std::array<GekkoOPTemplate, 107> table31 =
{19, Interpreter::mfcr, {"mfcr", OpType::System, FL_OUT_D, 1, 0, 0, 0}}, {19, Interpreter::mfcr, {"mfcr", OpType::System, FL_OUT_D, 1, 0, 0, 0}},
{83, Interpreter::mfmsr, {"mfmsr", OpType::System, FL_OUT_D | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {83, Interpreter::mfmsr, {"mfmsr", OpType::System, FL_OUT_D | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
{144, Interpreter::mtcrf, {"mtcrf", OpType::System, FL_IN_S | FL_SET_CRn, 1, 0, 0, 0}}, {144, Interpreter::mtcrf, {"mtcrf", OpType::System, FL_IN_S | FL_SET_CRn, 1, 0, 0, 0}},
{146, Interpreter::mtmsr, {"mtmsr", OpType::System, FL_IN_S | FL_ENDBLOCK | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {146, Interpreter::mtmsr, {"mtmsr", OpType::System, FL_IN_S | FL_ENDBLOCK | FL_PROGRAMEXCEPTION | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{210, Interpreter::mtsr, {"mtsr", OpType::System, FL_IN_S | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {210, Interpreter::mtsr, {"mtsr", OpType::System, FL_IN_S | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
{242, Interpreter::mtsrin, {"mtsrin", OpType::System, FL_IN_SB | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {242, Interpreter::mtsrin, {"mtsrin", OpType::System, FL_IN_SB | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
{339, Interpreter::mfspr, {"mfspr", OpType::SPR, FL_OUT_D | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}}, {339, Interpreter::mfspr, {"mfspr", OpType::SPR, FL_OUT_D | FL_PROGRAMEXCEPTION, 1, 0, 0, 0}},
@ -304,15 +304,15 @@ static std::array<GekkoOPTemplate, 107> table31 =
static std::array<GekkoOPTemplate, 9> table59 = static std::array<GekkoOPTemplate, 9> table59 =
{{ {{
{18, Interpreter::fdivsx, {"fdivsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 17, 0, 0, 0}}, // TODO {18, Interpreter::fdivsx, {"fdivsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV, 17, 0, 0, 0}}, // TODO
{20, Interpreter::fsubsx, {"fsubsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {20, Interpreter::fsubsx, {"fsubsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{21, Interpreter::faddsx, {"faddsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {21, Interpreter::faddsx, {"faddsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{24, Interpreter::fresx, {"fresx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {24, Interpreter::fresx, {"fresx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV, 1, 0, 0, 0}},
{25, Interpreter::fmulsx, {"fmulsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {25, Interpreter::fmulsx, {"fmulsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{28, Interpreter::fmsubsx, {"fmsubsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {28, Interpreter::fmsubsx, {"fmsubsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{29, Interpreter::fmaddsx, {"fmaddsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {29, Interpreter::fmaddsx, {"fmaddsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{30, Interpreter::fnmsubsx, {"fnmsubsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {30, Interpreter::fnmsubsx, {"fnmsubsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{31, Interpreter::fnmaddsx, {"fnmaddsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {31, Interpreter::fnmaddsx, {"fnmaddsx", OpType::SingleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
}}; }};
static std::array<GekkoOPTemplate, 15> table63 = static std::array<GekkoOPTemplate, 15> table63 =
@ -323,36 +323,36 @@ static std::array<GekkoOPTemplate, 15> table63 =
// we don't actually need to calculate or store them here. So FL_READ_FPRF and FL_SET_FPRF is not // we don't actually need to calculate or store them here. So FL_READ_FPRF and FL_SET_FPRF is not
// an ideal representation of fcmp's effect on FPRF flags and might result in // an ideal representation of fcmp's effect on FPRF flags and might result in
// slightly sub-optimal code. // slightly sub-optimal code.
{32, Interpreter::fcmpo, {"fcmpo", OpType::DoubleFP, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 1, 0, 0, 0}}, {32, Interpreter::fcmpo, {"fcmpo", OpType::DoubleFP, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{0, Interpreter::fcmpu, {"fcmpu", OpType::DoubleFP, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 1, 0, 0, 0}}, {0, Interpreter::fcmpu, {"fcmpu", OpType::DoubleFP, FL_IN_FLOAT_AB | FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{14, Interpreter::fctiwx, {"fctiwx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}}, {14, Interpreter::fctiwx, {"fctiwx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{15, Interpreter::fctiwzx, {"fctiwzx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}}, {15, Interpreter::fctiwzx, {"fctiwzx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{72, Interpreter::fmrx, {"fmrx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}}, {72, Interpreter::fmrx, {"fmrx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_IN_FLOAT_B_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
{136, Interpreter::fnabsx, {"fnabsx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_IN_FLOAT_B_BITEXACT | FL_USE_FPU, 1, 0, 0, 0}}, {136, Interpreter::fnabsx, {"fnabsx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_IN_FLOAT_B_BITEXACT | FL_USE_FPU, 1, 0, 0, 0}},
{40, Interpreter::fnegx, {"fnegx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_IN_FLOAT_B_BITEXACT | FL_USE_FPU, 1, 0, 0, 0}}, {40, Interpreter::fnegx, {"fnegx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_IN_FLOAT_B_BITEXACT | FL_USE_FPU, 1, 0, 0, 0}},
{12, Interpreter::frspx, {"frspx", OpType::DoubleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {12, Interpreter::frspx, {"frspx", OpType::DoubleFP, FL_OUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{64, Interpreter::mcrfs, {"mcrfs", OpType::SystemFP, FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF, 1, 0, 0, 0}}, {64, Interpreter::mcrfs, {"mcrfs", OpType::SystemFP, FL_SET_CRn | FL_USE_FPU | FL_READ_FPRF, 1, 0, 0, 0}},
{583, Interpreter::mffsx, {"mffsx", OpType::SystemFP, FL_RC_BIT_F | FL_INOUT_FLOAT_D | FL_USE_FPU | FL_READ_FPRF, 1, 0, 0, 0}}, {583, Interpreter::mffsx, {"mffsx", OpType::SystemFP, FL_RC_BIT_F | FL_INOUT_FLOAT_D | FL_USE_FPU | FL_READ_FPRF, 1, 0, 0, 0}},
{70, Interpreter::mtfsb0x, {"mtfsb0x", OpType::SystemFP, FL_RC_BIT_F | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 3, 0, 0, 0}}, {70, Interpreter::mtfsb0x, {"mtfsb0x", OpType::SystemFP, FL_RC_BIT_F | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 3, 0, 0, 0}},
{38, Interpreter::mtfsb1x, {"mtfsb1x", OpType::SystemFP, FL_RC_BIT_F | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 3, 0, 0, 0}}, {38, Interpreter::mtfsb1x, {"mtfsb1x", OpType::SystemFP, FL_RC_BIT_F | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 3, 0, 0, 0}},
{134, Interpreter::mtfsfix, {"mtfsfix", OpType::SystemFP, FL_RC_BIT_F | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 3, 0, 0, 0}}, {134, Interpreter::mtfsfix, {"mtfsfix", OpType::SystemFP, FL_RC_BIT_F | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 3, 0, 0, 0}},
{711, Interpreter::mtfsfx, {"mtfsfx", OpType::SystemFP, FL_RC_BIT_F | FL_IN_FLOAT_B | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF, 3, 0, 0, 0}}, {711, Interpreter::mtfsfx, {"mtfsfx", OpType::SystemFP, FL_RC_BIT_F | FL_IN_FLOAT_B | FL_USE_FPU | FL_READ_FPRF | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 3, 0, 0, 0}},
}}; }};
static std::array<GekkoOPTemplate, 10> table63_2 = static std::array<GekkoOPTemplate, 10> table63_2 =
{{ {{
{18, Interpreter::fdivx, {"fdivx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 31, 0, 0, 0}}, {18, Interpreter::fdivx, {"fdivx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV, 31, 0, 0, 0}},
{20, Interpreter::fsubx, {"fsubx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {20, Interpreter::fsubx, {"fsubx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{21, Interpreter::faddx, {"faddx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {21, Interpreter::faddx, {"faddx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AB | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{23, Interpreter::fselx, {"fselx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_IN_FLOAT_BC_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}}, {23, Interpreter::fselx, {"fselx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_IN_FLOAT_BC_BITEXACT | FL_RC_BIT_F | FL_USE_FPU, 1, 0, 0, 0}},
{25, Interpreter::fmulx, {"fmulx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {25, Interpreter::fmulx, {"fmulx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_AC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{26, Interpreter::frsqrtex, {"frsqrtex", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {26, Interpreter::frsqrtex, {"frsqrtex", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_B | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION | FL_FLOAT_DIV, 1, 0, 0, 0}},
{28, Interpreter::fmsubx, {"fmsubx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {28, Interpreter::fmsubx, {"fmsubx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{29, Interpreter::fmaddx, {"fmaddx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {29, Interpreter::fmaddx, {"fmaddx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{30, Interpreter::fnmsubx, {"fnmsubx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {30, Interpreter::fnmsubx, {"fnmsubx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
{31, Interpreter::fnmaddx, {"fnmaddx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF, 1, 0, 0, 0}}, {31, Interpreter::fnmaddx, {"fnmaddx", OpType::DoubleFP, FL_INOUT_FLOAT_D | FL_IN_FLOAT_ABC | FL_RC_BIT_F | FL_USE_FPU | FL_SET_FPRF | FL_FLOAT_EXCEPTION, 1, 0, 0, 0}},
}}; }};
// clang-format on // clang-format on

View file

@ -335,7 +335,7 @@ void Jit64::Init()
jo.fastmem_arena = SConfig::GetInstance().bFastmem && Memory::InitFastmemArena(); jo.fastmem_arena = SConfig::GetInstance().bFastmem && Memory::InitFastmemArena();
jo.optimizeGatherPipe = true; jo.optimizeGatherPipe = true;
jo.accurateSinglePrecision = true; jo.accurateSinglePrecision = true;
UpdateMemoryOptions(); UpdateMemoryAndExceptionOptions();
js.fastmemLoadStore = nullptr; js.fastmemLoadStore = nullptr;
js.compilerPC = 0; js.compilerPC = 0;
@ -389,7 +389,7 @@ void Jit64::ClearCache()
m_const_pool.Clear(); m_const_pool.Clear();
ClearCodeSpace(); ClearCodeSpace();
Clear(); Clear();
UpdateMemoryOptions(); UpdateMemoryAndExceptionOptions();
ResetFreeMemoryRanges(); ResetFreeMemoryRanges();
} }
@ -453,6 +453,24 @@ void Jit64::FallBackToInterpreter(UGeckoInstruction inst)
SetJumpTarget(c); SetJumpTarget(c);
} }
} }
else if (ShouldHandleFPExceptionForInstruction(js.op))
{
TEST(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_PROGRAM));
FixupBranch exception = J_CC(CC_NZ, true);
SwitchToFarCode();
SetJumpTarget(exception);
RCForkGuard gpr_guard = gpr.Fork();
RCForkGuard fpr_guard = fpr.Fork();
gpr.Flush();
fpr.Flush();
MOV(32, PPCSTATE(pc), Imm32(js.op->address));
WriteExceptionExit();
SwitchToNearCode();
}
} }
void Jit64::HLEFunction(u32 hook_index) void Jit64::HLEFunction(u32 hook_index)

View file

@ -208,6 +208,7 @@ void Jit64::fp_arith(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || (jo.div_by_zero_exceptions && inst.SUBOP5 == 18));
int a = inst.FA; int a = inst.FA;
int b = inst.FB; int b = inst.FB;
@ -292,6 +293,7 @@ void Jit64::fmaddXX(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
// We would like to emulate FMA instructions accurately without rounding error if possible, but // We would like to emulate FMA instructions accurately without rounding error if possible, but
// unfortunately emulating FMA in software is just too slow on CPUs that are too old to have FMA // unfortunately emulating FMA in software is just too slow on CPUs that are too old to have FMA
@ -733,6 +735,7 @@ void Jit64::fcmpX(UGeckoInstruction inst)
{ {
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(jo.fp_exceptions);
FloatCompare(inst); FloatCompare(inst);
} }
@ -742,6 +745,7 @@ void Jit64::fctiwx(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
int d = inst.RD; int d = inst.RD;
int b = inst.RB; int b = inst.RB;
@ -784,6 +788,7 @@ void Jit64::frspx(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
int b = inst.FB; int b = inst.FB;
int d = inst.FD; int d = inst.FD;
bool packed = js.op->fprIsDuplicated[b] && !cpu_info.bAtom; bool packed = js.op->fprIsDuplicated[b] && !cpu_info.bAtom;
@ -800,6 +805,7 @@ void Jit64::frsqrtex(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
int b = inst.FB; int b = inst.FB;
int d = inst.FD; int d = inst.FD;
@ -818,6 +824,7 @@ void Jit64::fresx(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
int b = inst.FB; int b = inst.FB;
int d = inst.FD; int d = inst.FD;

View file

@ -33,6 +33,7 @@ void Jit64::ps_sum(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITPairedOff); JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
int d = inst.FD; int d = inst.FD;
int a = inst.FA; int a = inst.FA;
@ -84,6 +85,7 @@ void Jit64::ps_muls(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITPairedOff); JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
int d = inst.FD; int d = inst.FD;
int a = inst.FA; int a = inst.FA;
@ -152,6 +154,7 @@ void Jit64::ps_rsqrte(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
int b = inst.FB; int b = inst.FB;
int d = inst.FD; int d = inst.FD;
@ -176,6 +179,7 @@ void Jit64::ps_res(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
int b = inst.FB; int b = inst.FB;
int d = inst.FD; int d = inst.FD;
@ -199,6 +203,7 @@ void Jit64::ps_cmpXX(UGeckoInstruction inst)
{ {
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(jo.fp_exceptions);
FloatCompare(inst, !!(inst.SUBOP10 & 64)); FloatCompare(inst, !!(inst.SUBOP10 & 64));
} }

View file

@ -424,6 +424,7 @@ void Jit64::mtmsr(UGeckoInstruction inst)
{ {
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(jo.fp_exceptions);
{ {
RCOpArg Rs = gpr.BindOrImm(inst.RS, RCMode::Read); RCOpArg Rs = gpr.BindOrImm(inst.RS, RCMode::Read);
@ -777,6 +778,7 @@ void Jit64::mtfsb1x(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
const u32 mask = 0x80000000 >> inst.CRBD; const u32 mask = 0x80000000 >> inst.CRBD;
@ -814,6 +816,7 @@ void Jit64::mtfsfix(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
u8 imm = (inst.hex >> (31 - 19)) & 0xF; u8 imm = (inst.hex >> (31 - 19)) & 0xF;
u32 mask = 0xF0000000 >> (4 * inst.CRFD); u32 mask = 0xF0000000 >> (4 * inst.CRFD);
@ -844,6 +847,7 @@ void Jit64::mtfsfx(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
u32 mask = 0; u32 mask = 0;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)

View file

@ -50,7 +50,7 @@ void JitArm64::Init()
jo.fastmem_arena = SConfig::GetInstance().bFastmem && Memory::InitFastmemArena(); jo.fastmem_arena = SConfig::GetInstance().bFastmem && Memory::InitFastmemArena();
jo.enableBlocklink = true; jo.enableBlocklink = true;
jo.optimizeGatherPipe = true; jo.optimizeGatherPipe = true;
UpdateMemoryOptions(); UpdateMemoryAndExceptionOptions();
gpr.Init(this); gpr.Init(this);
fpr.Init(this); fpr.Init(this);
blocks.Init(); blocks.Init();
@ -129,7 +129,7 @@ void JitArm64::ClearCache()
const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes; const Common::ScopedJITPageWriteAndNoExecute enable_jit_page_writes;
ClearCodeSpace(); ClearCodeSpace();
farcode.ClearCodeSpace(); farcode.ClearCodeSpace();
UpdateMemoryOptions(); UpdateMemoryAndExceptionOptions();
GenerateAsm(); GenerateAsm();
} }
@ -193,25 +193,14 @@ void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
gpr.Unlock(WA); gpr.Unlock(WA);
} }
} }
else if (ShouldHandleFPExceptionForInstruction(js.op))
{
WriteConditionalExceptionExit(EXCEPTION_PROGRAM);
}
if (jo.memcheck && (js.op->opinfo->flags & FL_LOADSTORE)) if (jo.memcheck && (js.op->opinfo->flags & FL_LOADSTORE))
{ {
ARM64Reg WA = gpr.GetReg(); WriteConditionalExceptionExit(EXCEPTION_DSI);
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
FixupBranch noException = TBZ(WA, IntLog2(EXCEPTION_DSI));
FixupBranch handleException = B();
SwitchToFarCode();
SetJumpTarget(handleException);
gpr.Flush(FlushMode::MaintainState, WA);
fpr.Flush(FlushMode::MaintainState, ARM64Reg::INVALID_REG);
WriteExceptionExit(js.compilerPC, false, true);
SwitchToNearCode();
SetJumpTarget(noException);
gpr.Unlock(WA);
} }
} }
@ -495,6 +484,26 @@ void JitArm64::WriteExceptionExit(ARM64Reg dest, bool only_external, bool always
B(dispatcher); B(dispatcher);
} }
void JitArm64::WriteConditionalExceptionExit(int exception)
{
ARM64Reg WA = gpr.GetReg();
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(Exceptions));
FixupBranch noException = TBZ(WA, IntLog2(exception));
FixupBranch handleException = B();
SwitchToFarCode();
SetJumpTarget(handleException);
gpr.Flush(FlushMode::MaintainState, WA);
fpr.Flush(FlushMode::MaintainState, ARM64Reg::INVALID_REG);
WriteExceptionExit(js.compilerPC, false, true);
SwitchToNearCode();
SetJumpTarget(noException);
gpr.Unlock(WA);
}
bool JitArm64::HandleFunctionHooking(u32 address) bool JitArm64::HandleFunctionHooking(u32 address)
{ {
return HLE::ReplaceFunctionIfPossible(address, [&](u32 hook_index, HLE::HookType type) { return HLE::ReplaceFunctionIfPossible(address, [&](u32 hook_index, HLE::HookType type) {

View file

@ -268,6 +268,7 @@ protected:
bool always_exception = false); bool always_exception = false);
void WriteExceptionExit(Arm64Gen::ARM64Reg dest, bool only_external = false, void WriteExceptionExit(Arm64Gen::ARM64Reg dest, bool only_external = false,
bool always_exception = false); bool always_exception = false);
void WriteConditionalExceptionExit(int exception);
void FakeLKExit(u32 exit_address_after_return); void FakeLKExit(u32 exit_address_after_return);
void WriteBLRExit(Arm64Gen::ARM64Reg dest); void WriteBLRExit(Arm64Gen::ARM64Reg dest);

View file

@ -67,6 +67,7 @@ void JitArm64::fp_arith(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || (jo.div_by_zero_exceptions && inst.SUBOP5 == 18));
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD; u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
u32 op5 = inst.SUBOP5; u32 op5 = inst.SUBOP5;
@ -339,6 +340,7 @@ void JitArm64::frspx(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
const u32 b = inst.FB; const u32 b = inst.FB;
const u32 d = inst.FD; const u32 d = inst.FD;
@ -500,6 +502,7 @@ void JitArm64::fcmpX(UGeckoInstruction inst)
{ {
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(jo.fp_exceptions);
FloatCompare(inst); FloatCompare(inst);
} }
@ -509,6 +512,7 @@ void JitArm64::fctiwzx(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
const u32 b = inst.FB; const u32 b = inst.FB;
const u32 d = inst.FD; const u32 d = inst.FD;
@ -551,6 +555,7 @@ void JitArm64::fresx(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
const u32 b = inst.FB; const u32 b = inst.FB;
const u32 d = inst.FD; const u32 d = inst.FD;
@ -579,6 +584,7 @@ void JitArm64::frsqrtex(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITFloatingPointOff); JITDISABLE(bJITFloatingPointOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
const u32 b = inst.FB; const u32 b = inst.FB;
const u32 d = inst.FD; const u32 d = inst.FD;

View file

@ -75,6 +75,7 @@ void JitArm64::ps_mulsX(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITPairedOff); JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
const u32 a = inst.FA; const u32 a = inst.FA;
const u32 c = inst.FC; const u32 c = inst.FC;
@ -125,6 +126,7 @@ void JitArm64::ps_maddXX(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITPairedOff); JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
const u32 a = inst.FA; const u32 a = inst.FA;
const u32 b = inst.FB; const u32 b = inst.FB;
@ -316,6 +318,7 @@ void JitArm64::ps_sumX(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITPairedOff); JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
const u32 a = inst.FA; const u32 a = inst.FA;
const u32 b = inst.FB; const u32 b = inst.FB;
@ -362,6 +365,7 @@ void JitArm64::ps_res(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITPairedOff); JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
const u32 b = inst.FB; const u32 b = inst.FB;
const u32 d = inst.FD; const u32 d = inst.FD;
@ -394,6 +398,7 @@ void JitArm64::ps_rsqrte(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITPairedOff); JITDISABLE(bJITPairedOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions || jo.div_by_zero_exceptions);
const u32 b = inst.FB; const u32 b = inst.FB;
const u32 d = inst.FD; const u32 d = inst.FD;
@ -425,6 +430,7 @@ void JitArm64::ps_cmpXX(UGeckoInstruction inst)
{ {
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITPairedOff); JITDISABLE(bJITPairedOff);
FALLBACK_IF(jo.fp_exceptions);
const bool upper = inst.SUBOP10 & 64; const bool upper = inst.SUBOP10 & 64;
FloatCompare(inst, upper); FloatCompare(inst, upper);

View file

@ -86,6 +86,7 @@ void JitArm64::mtmsr(UGeckoInstruction inst)
{ {
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(jo.fp_exceptions);
gpr.BindToRegister(inst.RS, true); gpr.BindToRegister(inst.RS, true);
STR(IndexType::Unsigned, gpr.R(inst.RS), PPC_REG, PPCSTATE_OFF(msr)); STR(IndexType::Unsigned, gpr.R(inst.RS), PPC_REG, PPCSTATE_OFF(msr));
@ -815,6 +816,7 @@ void JitArm64::mtfsb1x(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
const u32 mask = 0x80000000 >> inst.CRBD; const u32 mask = 0x80000000 >> inst.CRBD;
@ -850,6 +852,7 @@ void JitArm64::mtfsfix(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
u8 imm = (inst.hex >> (31 - 19)) & 0xF; u8 imm = (inst.hex >> (31 - 19)) & 0xF;
u8 shift = 28 - 4 * inst.CRFD; u8 shift = 28 - 4 * inst.CRFD;
@ -891,6 +894,7 @@ void JitArm64::mtfsfx(UGeckoInstruction inst)
INSTRUCTION_START INSTRUCTION_START
JITDISABLE(bJITSystemRegistersOff); JITDISABLE(bJITSystemRegistersOff);
FALLBACK_IF(inst.Rc); FALLBACK_IF(inst.Rc);
FALLBACK_IF(jo.fp_exceptions);
u32 mask = 0; u32 mask = 0;
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)

View file

@ -41,9 +41,21 @@ bool JitBase::CanMergeNextInstructions(int count) const
return true; return true;
} }
void JitBase::UpdateMemoryOptions() void JitBase::UpdateMemoryAndExceptionOptions()
{ {
bool any_watchpoints = PowerPC::memchecks.HasAny(); bool any_watchpoints = PowerPC::memchecks.HasAny();
jo.fastmem = SConfig::GetInstance().bFastmem && jo.fastmem_arena && (MSR.DR || !any_watchpoints); jo.fastmem = SConfig::GetInstance().bFastmem && jo.fastmem_arena && (MSR.DR || !any_watchpoints);
jo.memcheck = SConfig::GetInstance().bMMU || any_watchpoints; jo.memcheck = SConfig::GetInstance().bMMU || any_watchpoints;
jo.fp_exceptions = SConfig::GetInstance().bFloatExceptions;
jo.div_by_zero_exceptions = SConfig::GetInstance().bDivideByZeroExceptions;
}
bool JitBase::ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op)
{
if (jo.fp_exceptions)
return (op->opinfo->flags & FL_FLOAT_EXCEPTION) != 0;
else if (jo.div_by_zero_exceptions)
return (op->opinfo->flags & FL_FLOAT_DIV) != 0;
else
return false;
} }

View file

@ -63,6 +63,8 @@ protected:
bool fastmem; bool fastmem;
bool fastmem_arena; bool fastmem_arena;
bool memcheck; bool memcheck;
bool fp_exceptions;
bool div_by_zero_exceptions;
bool profile_blocks; bool profile_blocks;
}; };
struct JitState struct JitState
@ -113,7 +115,9 @@ protected:
bool CanMergeNextInstructions(int count) const; bool CanMergeNextInstructions(int count) const;
void UpdateMemoryOptions(); void UpdateMemoryAndExceptionOptions();
bool ShouldHandleFPExceptionForInstruction(const PPCAnalyst::CodeOp* op);
public: public:
JitBase(); JitBase();

View file

@ -524,8 +524,12 @@ void PPCAnalyzer::SetInstructionStats(CodeBlock* block, CodeOp* code, const Gekk
code->wantsCR0 = false; code->wantsCR0 = false;
code->wantsCR1 = false; code->wantsCR1 = false;
bool first_fpu_instruction = false;
if (opinfo->flags & FL_USE_FPU) if (opinfo->flags & FL_USE_FPU)
{
first_fpu_instruction = !block->m_fpa->any;
block->m_fpa->any = true; block->m_fpa->any = true;
}
if (opinfo->flags & FL_TIMER) if (opinfo->flags & FL_TIMER)
block->m_gpa->anyTimer = true; block->m_gpa->anyTimer = true;
@ -550,9 +554,10 @@ void PPCAnalyzer::SetInstructionStats(CodeBlock* block, CodeOp* code, const Gekk
code->outputFPRF = (opinfo->flags & FL_SET_FPRF) != 0; code->outputFPRF = (opinfo->flags & FL_SET_FPRF) != 0;
code->canEndBlock = (opinfo->flags & FL_ENDBLOCK) != 0; code->canEndBlock = (opinfo->flags & FL_ENDBLOCK) != 0;
// TODO: Is it possible to determine that some FPU instructions never cause exceptions?
code->canCauseException = code->canCauseException =
(opinfo->flags & (FL_LOADSTORE | FL_USE_FPU | FL_PROGRAMEXCEPTION)) != 0; first_fpu_instruction || (opinfo->flags & (FL_LOADSTORE | FL_PROGRAMEXCEPTION)) != 0 ||
(SConfig::GetInstance().bFloatExceptions && (opinfo->flags & FL_FLOAT_EXCEPTION)) ||
(SConfig::GetInstance().bDivideByZeroExceptions && (opinfo->flags & FL_FLOAT_DIV));
code->wantsCA = (opinfo->flags & FL_READ_CA) != 0; code->wantsCA = (opinfo->flags & FL_READ_CA) != 0;
code->outputCA = (opinfo->flags & FL_SET_CA) != 0; code->outputCA = (opinfo->flags & FL_SET_CA) != 0;
@ -928,14 +933,14 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer, std:
const bool opWantsCR1 = op.wantsCR1; const bool opWantsCR1 = op.wantsCR1;
const bool opWantsFPRF = op.wantsFPRF; const bool opWantsFPRF = op.wantsFPRF;
const bool opWantsCA = op.wantsCA; const bool opWantsCA = op.wantsCA;
op.wantsCR0 = wantsCR0 || op.canEndBlock; op.wantsCR0 = wantsCR0 || op.canEndBlock || op.canCauseException;
op.wantsCR1 = wantsCR1 || op.canEndBlock; op.wantsCR1 = wantsCR1 || op.canEndBlock || op.canCauseException;
op.wantsFPRF = wantsFPRF || op.canEndBlock; op.wantsFPRF = wantsFPRF || op.canEndBlock || op.canCauseException;
op.wantsCA = wantsCA || op.canEndBlock; op.wantsCA = wantsCA || op.canEndBlock || op.canCauseException;
wantsCR0 |= opWantsCR0 || op.canEndBlock; wantsCR0 |= opWantsCR0 || op.canEndBlock || op.canCauseException;
wantsCR1 |= opWantsCR1 || op.canEndBlock; wantsCR1 |= opWantsCR1 || op.canEndBlock || op.canCauseException;
wantsFPRF |= opWantsFPRF || op.canEndBlock; wantsFPRF |= opWantsFPRF || op.canEndBlock || op.canCauseException;
wantsCA |= opWantsCA || op.canEndBlock; wantsCA |= opWantsCA || op.canEndBlock || op.canCauseException;
wantsCR0 &= !op.outputCR0 || opWantsCR0; wantsCR0 &= !op.outputCR0 || opWantsCR0;
wantsCR1 &= !op.outputCR1 || opWantsCR1; wantsCR1 &= !op.outputCR1 || opWantsCR1;
wantsFPRF &= !op.outputFPRF || opWantsFPRF; wantsFPRF &= !op.outputFPRF || opWantsFPRF;

View file

@ -64,7 +64,9 @@ enum InstructionFlags : u64
FL_IN_FLOAT_C_BITEXACT = (1ull << 31), // The output is based on the exact bits in frC. FL_IN_FLOAT_C_BITEXACT = (1ull << 31), // The output is based on the exact bits in frC.
FL_IN_FLOAT_AB_BITEXACT = FL_IN_FLOAT_A_BITEXACT | FL_IN_FLOAT_B_BITEXACT, FL_IN_FLOAT_AB_BITEXACT = FL_IN_FLOAT_A_BITEXACT | FL_IN_FLOAT_B_BITEXACT,
FL_IN_FLOAT_BC_BITEXACT = FL_IN_FLOAT_B_BITEXACT | FL_IN_FLOAT_C_BITEXACT, FL_IN_FLOAT_BC_BITEXACT = FL_IN_FLOAT_B_BITEXACT | FL_IN_FLOAT_C_BITEXACT,
FL_PROGRAMEXCEPTION = (1ull << 32), // May generate a system exception. FL_PROGRAMEXCEPTION = (1ull << 32), // May generate a program exception (not floating point).
FL_FLOAT_EXCEPTION = (1ull << 33), // May generate a program exception (floating point).
FL_FLOAT_DIV = (1ull << 34), // May generate a program exception (FP) due to division by 0.
}; };
enum class OpType enum class OpType