[A64] Skip software denormal flushing when FPCR.FZ handles inputs

Depends on hardware support, detected and runtime and controllable
via a64_extension_mask bit 1.
This commit is contained in:
Herman S.
2026-03-29 19:14:09 +09:00
parent 02ea71ed51
commit 84b05bd20a
5 changed files with 81 additions and 14 deletions

View File

@@ -7,6 +7,10 @@
******************************************************************************
*/
#include <cfenv>
#include <cmath>
#include <cstring>
#include "xenia/base/cvar.h"
#include "xenia/base/platform.h"
#define XBYAK_NO_OP_NAMES
@@ -17,6 +21,7 @@ DEFINE_int64(a64_extension_mask, -1LL,
"features.\n"
" 0 = armv8.0\n"
" 1 = Large System Extensions(LSE) atomic operations\n"
" 2 = FPCR.FZ flushes denormal inputs (skip software flush)\n"
" -1 = Detect and utilize all possible processor features\n",
"a64");
namespace xe {
@@ -43,6 +48,43 @@ void InitFeatureFlags() {
Xbyak_aarch64::util::XBYAK_AARCH64_HWCAP_ATOMIC);
#undef TEST_EMIT_FEATURE
}
// Detect whether FPCR.FZ flushes denormal float32 inputs to zero.
// The ARM spec says input flushing is implementation-defined.
// Modern cores (Cortex-A76+, Apple M1+) flush inputs; older ones may not.
if ((cvars::a64_extension_mask & kA64FZFlushesInputs) ==
kA64FZFlushesInputs) {
// Build a denormal float32: smallest positive denormal = 0x00000001.
uint32_t denorm_bits = 1;
float denorm;
std::memcpy(&denorm, &denorm_bits, 4);
// Save FPCR, enable FZ, add two denormals, check result.
uint64_t saved_fpcr;
#if XE_COMPILER_MSVC
saved_fpcr = _ReadStatusReg(ARM64_FPCR);
_WriteStatusReg(ARM64_FPCR, saved_fpcr | (1ULL << 24));
#else
asm volatile("mrs %0, fpcr" : "=r"(saved_fpcr));
uint64_t fz_fpcr = saved_fpcr | (1ULL << 24);
asm volatile("msr fpcr, %0" ::"r"(fz_fpcr));
#endif
volatile float a = denorm;
volatile float b = denorm;
volatile float result = a + b;
#if XE_COMPILER_MSVC
_WriteStatusReg(ARM64_FPCR, saved_fpcr);
#else
asm volatile("msr fpcr, %0" ::"r"(saved_fpcr));
#endif
if (result == 0.0f) {
feature_flags_ |= kA64FZFlushesInputs;
}
}
g_feature_flags = feature_flags_;
g_did_initialize_feature_flags = true;
}

View File

@@ -15,7 +15,7 @@ namespace xe {
namespace arm64 {
enum A64FeatureFlags : uint64_t {
kA64EmitLSE = 1 << 0,
kA64FZFlushesInputs = 1 << 1,
};
XE_NOALIAS