diff --git a/CMakeLists.txt b/CMakeLists.txt index 804998c..f9db75a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,19 +9,22 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) option(STREFLOP_AUTO "Autodetect the most appropriate Streflop implementation" ON) option(STREFLOP_SSE "Use SSE instead of x87 extended precision" OFF) option(STREFLOP_NEON "Use ARM NEON (auto-enabled on ARM64 when SSE/SOFT are off)" OFF) +option(STREFLOP_WASM "Use WebAssembly fixed round-to-nearest arithmetic" OFF) option(STREFLOP_SOFT "Use soft float" OFF) option(STREFLOP_NO_DENORMALS "Squash denormals to zero" OFF) option(BUILD_TESTS "Build test executables" OFF) # Autodetect STREFLOP mode when STREFLOP_AUTO is ON and no explicit mode is selected -if(STREFLOP_AUTO AND NOT STREFLOP_SSE AND NOT STREFLOP_NEON AND NOT STREFLOP_SOFT AND NOT STREFLOP_NO_DENORMALS AND NOT BUILD_TESTS) +if(STREFLOP_AUTO AND NOT STREFLOP_SSE AND NOT STREFLOP_NEON AND NOT STREFLOP_SOFT AND NOT STREFLOP_WASM AND NOT STREFLOP_NO_DENORMALS AND NOT BUILD_TESTS) # Use HOST processor as fallback when cross-compiling (CMAKE_SYSTEM_PROCESSOR may be empty) if(NOT CMAKE_SYSTEM_PROCESSOR AND CMAKE_HOST_SYSTEM_PROCESSOR) set(_streflop_processor "${CMAKE_HOST_SYSTEM_PROCESSOR}") else() set(_streflop_processor "${CMAKE_SYSTEM_PROCESSOR}") endif() - if(_streflop_processor MATCHES "arm64|aarch64|armv8|ARM64|AARCH64") + if(EMSCRIPTEN) + set(STREFLOP_WASM ON CACHE BOOL "Use WebAssembly arithmetic" FORCE) + elseif(_streflop_processor MATCHES "arm64|aarch64|armv8|ARM64|AARCH64") set(STREFLOP_NEON ON CACHE BOOL "Use ARM NEON (autodetected)" FORCE) message(STATUS "STREFLOP: Autodetected ARM64 platform - enabling NEON mode") elseif(_streflop_processor MATCHES "x86|i686|amd64|AMD64|x86_64") @@ -264,7 +267,7 @@ SET_SOURCE_FILES_PROPERTIES(${libm_flt32_source} PROPERTIES COMPILE_FLAGS "-DLIB SET_SOURCE_FILES_PROPERTIES(${libm_dbl64_source} PROPERTIES COMPILE_FLAGS "-DLIBM_COMPILING_DBL64 ${streflop_cxx_flags} ${libm_extra_flags}") # Conditionally add long double sources (not for SSE or NEON mode — no x87 extended precision) -if(NOT STREFLOP_SSE AND NOT STREFLOP_NEON) +if(NOT STREFLOP_SSE AND NOT STREFLOP_NEON AND NOT STREFLOP_WASM) SET_SOURCE_FILES_PROPERTIES(${libm_ldbl96_source} PROPERTIES COMPILE_FLAGS "-DLIBM_COMPILING_LDBL96 ${streflop_cxx_flags} ${libm_extra_flags}") SET(full_libm_source ${libm_flt32_source} ${libm_dbl64_source} ${libm_ldbl96_source}) else() @@ -322,6 +325,9 @@ target_include_directories(streflop PUBLIC # Set FPU type compile definitions and flags if(STREFLOP_SOFT) target_compile_definitions(streflop PUBLIC STREFLOP_SOFT) +elseif(STREFLOP_WASM) + target_compile_definitions(streflop PUBLIC STREFLOP_WASM) + target_compile_options(streflop PRIVATE -ffp-contract=off -fno-fast-math) elseif(STREFLOP_SSE) target_compile_definitions(streflop PUBLIC STREFLOP_SSE) if(NOT MSVC) diff --git a/FPUSettings.h b/FPUSettings.h index 5883b36..b4d09bf 100644 --- a/FPUSettings.h +++ b/FPUSettings.h @@ -625,6 +625,22 @@ template<> inline void streflop_init() { #error "Extended precision not supported on ARM NEON" #endif +#elif defined(STREFLOP_WASM) + +// WebAssembly has fixed round-to-nearest/ties-to-even, gradual underflow, +// and no observable FP exception flags or configurable FP traps. Unsupported +// environment requests fail instead of pretending to change the hardware. +struct fpenv_t { int rounding; }; +extern fpenv_t FE_DFL_ENV; +inline int fegetround() { return FE_TONEAREST; } +inline int fesetround(FPU_RoundMode mode) { return mode == FE_TONEAREST ? 0 : -1; } +inline int feraiseexcept(FPU_Exceptions excepts) { return excepts == 0 ? 0 : -1; } +inline int feclearexcept(int) { return 0; } +inline int fegetenv(fpenv_t* env) { env->rounding = FE_TONEAREST; return 0; } +inline int fesetenv(const fpenv_t* env) { return env->rounding == FE_TONEAREST ? 0 : -1; } +inline int feholdexcept(fpenv_t* env) { return fegetenv(env); } +template inline void streflop_init() {} + #elif defined(STREFLOP_SOFT) /// Raise exception for these flags inline int feraiseexcept(FPU_Exceptions excepts) { diff --git a/SMath.cpp b/SMath.cpp index 5d317ed..81b5d65 100644 --- a/SMath.cpp +++ b/SMath.cpp @@ -35,7 +35,7 @@ namespace streflop { fpenv_t FE_DFL_ENV = 0; #elif defined(STREFLOP_SSE) fpenv_t FE_DFL_ENV = {0,0}; -#elif defined(STREFLOP_NEON) +#elif defined(STREFLOP_NEON) || defined(STREFLOP_WASM) fpenv_t FE_DFL_ENV = {0}; #elif defined(STREFLOP_SOFT) fpenv_t FE_DFL_ENV = {42,0,0}; diff --git a/SMath.h b/SMath.h index 36187b8..620876b 100644 --- a/SMath.h +++ b/SMath.h @@ -19,6 +19,15 @@ // just in case, should already be included #include "streflop.h" +// Emscripten's compatibility math header defines these as macros. +// They must not rename the namespace-scoped float overloads below. +#ifdef isnanf +#undef isnanf +#endif +#ifdef isinff +#undef isinff +#endif + // Names from the libm conversion namespace streflop_libm { using streflop::Simple; diff --git a/streflop.h b/streflop.h index a39c1f1..3336ccf 100644 --- a/streflop.h +++ b/streflop.h @@ -12,21 +12,13 @@ #ifndef STREFLOP_H #define STREFLOP_H -// protect against bad defines -#if defined(STREFLOP_SSE) && defined(STREFLOP_NEON) -#error You have to define exactly one of STREFLOP_SSE STREFLOP_NEON STREFLOP_X87 STREFLOP_SOFT, but you defined both STREFLOP_SSE and STREFLOP_NEON -#elif defined(STREFLOP_SSE) && defined(STREFLOP_X87) -#error You have to define exactly one of STREFLOP_SSE STREFLOP_NEON STREFLOP_X87 STREFLOP_SOFT, but you defined both STREFLOP_SSE and STREFLOP_X87 -#elif defined(STREFLOP_SSE) && defined(STREFLOP_SOFT) -#error You have to define exactly one of STREFLOP_SSE STREFLOP_NEON STREFLOP_X87 STREFLOP_SOFT, but you defined both STREFLOP_SSE and STREFLOP_SOFT -#elif defined(STREFLOP_NEON) && defined(STREFLOP_X87) -#error You have to define exactly one of STREFLOP_SSE STREFLOP_NEON STREFLOP_X87 STREFLOP_SOFT, but you defined both STREFLOP_NEON and STREFLOP_X87 -#elif defined(STREFLOP_NEON) && defined(STREFLOP_SOFT) -#error You have to define exactly one of STREFLOP_SSE STREFLOP_NEON STREFLOP_X87 STREFLOP_SOFT, but you defined both STREFLOP_NEON and STREFLOP_SOFT -#elif defined(STREFLOP_X87) && defined(STREFLOP_SOFT) -#error You have to define exactly one of STREFLOP_SSE STREFLOP_NEON STREFLOP_X87 STREFLOP_SOFT, but you defined both STREFLOP_X87 and STREFLOP_SOFT -#elif !defined(STREFLOP_SSE) && !defined(STREFLOP_NEON) && !defined(STREFLOP_X87) && !defined(STREFLOP_SOFT) -#error You have to define exactly one of STREFLOP_SSE STREFLOP_NEON STREFLOP_X87 STREFLOP_SOFT, but you defined none +// Exactly one backend is required. +#if (defined(STREFLOP_SSE) + defined(STREFLOP_NEON) + defined(STREFLOP_X87) + defined(STREFLOP_SOFT) + defined(STREFLOP_WASM)) != 1 +#error STREFLOP: Select exactly one floating-point backend. +#endif + +#if defined(STREFLOP_WASM) && (!defined(__wasm__) || defined(STREFLOP_NO_DENORMALS)) +#error STREFLOP_WASM requires WebAssembly with gradual underflow. #endif // First, define the numerical types @@ -40,9 +32,9 @@ namespace streflop { typedef double Double; #undef Extended -#elif defined(STREFLOP_NEON) +#elif defined(STREFLOP_NEON) || defined(STREFLOP_WASM) - // NEON always uses native types, denormals are handled by FPU flags + // NEON and WASM use native IEEE binary32/binary64 types. typedef float Simple; typedef double Double; #undef Extended diff --git a/streflop_cond.h b/streflop_cond.h index f199a2e..48589ef 100644 --- a/streflop_cond.h +++ b/streflop_cond.h @@ -9,7 +9,7 @@ #ifndef STREFLOP_COND_H #define STREFLOP_COND_H -#if (!defined(NOT_USING_STREFLOP) && (defined(STREFLOP_SSE) || defined(STREFLOP_NEON) || defined(STREFLOP_X87) || defined(STREFLOP_SOFT))) +#if (!defined(NOT_USING_STREFLOP) && (defined(STREFLOP_SSE) || defined(STREFLOP_NEON) || defined(STREFLOP_X87) || defined(STREFLOP_SOFT) || defined(STREFLOP_WASM))) #define STREFLOP_ENABLED 1 #endif