From 10d2daf54484fcfff8f8749a5792acd7f16f96f7 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Wed, 22 Mar 2023 07:03:57 +0100 Subject: [PATCH] cmake: add USE_FLOAT_EXCEPTIONS to enable floating point exceptions --- cmake/DaemonFlags.cmake | 20 +++++++- src/engine/framework/System.cpp | 87 +++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/cmake/DaemonFlags.cmake b/cmake/DaemonFlags.cmake index 5b069e1e76..94f1f32ebf 100644 --- a/cmake/DaemonFlags.cmake +++ b/cmake/DaemonFlags.cmake @@ -148,13 +148,20 @@ else() set(WARNMODE "no-") endif() +# Compiler options +option(USE_FLOAT_EXCEPTIONS "Use floating point exceptions" OFF) option(USE_FAST_MATH "Use fast math" ON) -# Compiler options +if (USE_FLOAT_EXCEPTIONS) + add_definitions(-DDAEMON_USE_FLOAT_EXCEPTIONS) +endif() + if (MSVC) set_c_cxx_flag("/MP") - if (USE_FAST_MATH) + if (USE_FLOAT_EXCEPTIONS) + set_c_cxx_flag("/fp:strict") + elseif (USE_FAST_MATH) set_c_cxx_flag("/fp:fast") endif() @@ -248,6 +255,15 @@ else() set_c_cxx_flag("-ffast-math") endif() + if (USE_FLOAT_EXCEPTIONS) + # Floating point exceptions requires trapping math + # to avoid false positives on architectures with SSE. + set_c_cxx_flag("-ftrapping-math") + # GCC prints noisy warnings saying -ftrapping-math implies this. + set_c_cxx_flag("-fno-associative-math") + # Other optimizations from -ffast-math can be kept. + endif() + # Use hidden symbol visibility if possible. try_c_cxx_flag(FVISIBILITY_HIDDEN "-fvisibility=hidden") diff --git a/src/engine/framework/System.cpp b/src/engine/framework/System.cpp index 593b94e207..b3b6e47230 100644 --- a/src/engine/framework/System.cpp +++ b/src/engine/framework/System.cpp @@ -51,6 +51,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #endif +#if defined(DAEMON_USE_FLOAT_EXCEPTIONS) + #if defined(__USE_GNU) || defined (__APPLE__) + #include + #define DAEMON_USE_FLOAT_EXCEPTIONS_AVAILABLE + #elif defined(_MSC_VER) + #include + #define DAEMON_USE_FLOAT_EXCEPTIONS_AVAILABLE + #endif +#endif + +#if defined(DAEMON_USE_FLOAT_EXCEPTIONS_AVAILABLE) + static Cvar::Cvar common_floatExceptions_invalid("common.floatExceptions.invalid", + "enable floating point exception for operation with NaN", + Cvar::INIT, false); + static Cvar::Cvar common_floatExceptions_divByZero("common.floatExceptions.divByZero", + "enable floating point exception for division-by-zero operation", + Cvar::INIT, false); + static Cvar::Cvar common_floatExceptions_overflow("common.floatExceptions.overflow", + "enable floating point exception for operation producing an overflow", + Cvar::INIT, false); +#endif + namespace Sys { static Cvar::Cvar cvar_common_shutdownOnDrop("common.shutdownOnDrop", "shut down engine on game drop", Cvar::TEMPORARY, false); @@ -276,6 +298,69 @@ static void CloseSingletonSocket() #endif } +static void SetFloatingPointExceptions() +{ + // Must be done after Sys::Init() to read cvars from command line. + #if defined(DAEMON_USE_FLOAT_EXCEPTIONS_AVAILABLE) + #if defined(__USE_GNU) || defined(__APPLE__) + int exceptions = 0; + #elif defined(_MSC_VER) + unsigned int exceptions = 0; + #endif + + // Operations with NaN. + if (common_floatExceptions_invalid.Get()) + { + #if defined(__USE_GNU) + exceptions |= FE_INVALID; + #elif defined(__APPLE__) + exceptions |= __fpcr_trap_invalid; + #elif defined(_MSC_VER) + exceptions |= _EM_INVALID + #endif + } + + // Division by zero. + if (common_floatExceptions_divByZero.Get()) + { + #if defined(__USE_GNU) + exceptions |= FE_DIVBYZERO; + #elif defined(__APPLE__) + exceptions |= __fpcr_trap_divbyzero; + #elif defined(_MSC_VER) + exceptions |= _EM_ZERODIVIDE; + #endif + } + + // Operations producing an overflow. + if (common_floatExceptions_overflow.Get()) + { + #if defined(__USE_GNU) + exceptions |= FE_OVERFLOW; + #elif defined(__APPLE__) + exceptions |= __fpcr_trap_overflow; + #elif defined(_MSC_VER) + exceptions |= _EM_OVERFLOW; + #endif + } + + #if defined(__USE_GNU) + // https://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Control-Functions.html + feenableexcept(exceptions); + #elif defined(__APPLE__) + // https://stackoverflow.com/a/71792418 + fenv_t env; + fegetenv(&env); + env.__fpcr = env.__fpcr | exceptions; + fesetenv(&env); + #elif defined(_MSC_VER) + // https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2012/c9676k6h(v=vs.110) + unsigned int current; + _controlfp_s(¤t, exceptions, _MCW_EM); + #endif + #endif +} + // Common code for fatal and non-fatal exit // TODO: Handle shutdown requests coming from multiple threads (could happen from the *nix signal thread) static void Shutdown(bool error, Str::StringRef message) @@ -656,6 +741,8 @@ static void Init(int argc, char** argv) // Set cvars set from the command line having the Cvar::INIT flag SetCvarsWithInitFlag(cmdlineArgs); + SetFloatingPointExceptions(); + // Initialize the filesystem. For pakpaths, the libpath is added first and has the // lowest priority, while the homepath is added last and has the highest. cmdlineArgs.pakPaths.insert(cmdlineArgs.pakPaths.begin(), FS::Path::Build(cmdlineArgs.libPath, "pkg"));