From ccce1149498bb0dbf55a4a144d84708a3f5da18d Mon Sep 17 00:00:00 2001 From: Jordan Peck Date: Sat, 10 Oct 2026 23:00:02 +0100 Subject: [PATCH 1/9] Fix MSVC x86 miscompile of register binary operators The binary operators took lhs by value and modified it in place. MSVC x86 /O2 doesn't copy over-aligned by value arguments, so the result was written into the caller's register, e.g. Select( m, a * b, a ) read back a * b. Take lhs by const& and copy into a local instead. --- include/FastSIMD/ToolSet/Generic/Register.h | 68 ++++++++++++++------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/include/FastSIMD/ToolSet/Generic/Register.h b/include/FastSIMD/ToolSet/Generic/Register.h index c676fa6..6e6bb77 100644 --- a/include/FastSIMD/ToolSet/Generic/Register.h +++ b/include/FastSIMD/ToolSet/Generic/Register.h @@ -175,70 +175,94 @@ namespace FS DoubledType v0, v1; }; + // Binary operators copy lhs into a local rather than taking it by value + // MSVC x86 doesn't copy over-aligned by value arguments, so modifying a by value lhs wrote into the caller's register template - FS_FORCEINLINE static Register operator +( Register lhs, const Register& rhs ) + FS_FORCEINLINE static Register operator +( const Register& lhs, const Register& rhs ) { - return lhs += rhs; + Register result = lhs; + result += rhs; + return result; } template - FS_FORCEINLINE static Register operator -( Register lhs, const Register& rhs ) + FS_FORCEINLINE static Register operator -( const Register& lhs, const Register& rhs ) { - return lhs -= rhs; + Register result = lhs; + result -= rhs; + return result; } template - FS_FORCEINLINE static Register operator *( Register lhs, const Register& rhs ) + FS_FORCEINLINE static Register operator *( const Register& lhs, const Register& rhs ) { - return lhs *= rhs; + Register result = lhs; + result *= rhs; + return result; } template - FS_FORCEINLINE static Register operator /( Register lhs, const Register& rhs ) + FS_FORCEINLINE static Register operator /( const Register& lhs, const Register& rhs ) { - return lhs /= rhs; + Register result = lhs; + result /= rhs; + return result; } template - FS_FORCEINLINE static Register operator &( Register lhs, const Register& rhs ) + FS_FORCEINLINE static Register operator &( const Register& lhs, const Register& rhs ) { - return lhs &= rhs; + Register result = lhs; + result &= rhs; + return result; } template - FS_FORCEINLINE static Register operator |( Register lhs, const Register& rhs ) + FS_FORCEINLINE static Register operator |( const Register& lhs, const Register& rhs ) { - return lhs |= rhs; + Register result = lhs; + result |= rhs; + return result; } template - FS_FORCEINLINE static Register operator ^( Register lhs, const Register& rhs ) + FS_FORCEINLINE static Register operator ^( const Register& lhs, const Register& rhs ) { - return lhs ^= rhs; + Register result = lhs; + result ^= rhs; + return result; } template - FS_FORCEINLINE static Register operator <<( Register lhs, int rhs ) + FS_FORCEINLINE static Register operator <<( const Register& lhs, int rhs ) { - return lhs <<= rhs; + Register result = lhs; + result <<= rhs; + return result; } template - FS_FORCEINLINE static Register operator >>( Register lhs, int rhs ) + FS_FORCEINLINE static Register operator >>( const Register& lhs, int rhs ) { - return lhs >>= rhs; + Register result = lhs; + result >>= rhs; + return result; } template - FS_FORCEINLINE static Register operator<<( Register lhs, Register rhs ) + FS_FORCEINLINE static Register operator<<( const Register& lhs, const Register& rhs ) { - return lhs <<= rhs; + Register result = lhs; + result <<= rhs; + return result; } template - FS_FORCEINLINE static Register operator>>( Register lhs, Register rhs ) + FS_FORCEINLINE static Register operator>>( const Register& lhs, const Register& rhs ) { - return lhs >>= rhs; + Register result = lhs; + result >>= rhs; + return result; } template From 9c15d95ef33bda7101fe1d5f86525341f1931067 Mon Sep 17 00:00:00 2001 From: Jordan Peck Date: Sat, 10 Oct 2026 23:00:04 +0100 Subject: [PATCH 2/9] Fix Log using *= instead of += for its e term The e * -2.12194440e-4f correction multiplied the polynomial instead of being added to it, making FS::Log (and FS::Pow, which uses it) up to ~1.6% wrong, e.g. Log( 1.5 ) gave 0.41211 instead of 0.40547. --- include/FastSIMD/ToolSet/Generic/Functions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/FastSIMD/ToolSet/Generic/Functions.h b/include/FastSIMD/ToolSet/Generic/Functions.h index 217aeb8..b8c46d1 100644 --- a/include/FastSIMD/ToolSet/Generic/Functions.h +++ b/include/FastSIMD/ToolSet/Generic/Functions.h @@ -634,7 +634,7 @@ namespace FS RegisterF xx = value * value; y *= xx; - y *= e * RegisterF( -2.12194440e-4f ); + y += e * RegisterF( -2.12194440e-4f ); y -= xx * RegisterF( 0.5f ); value += y; From 2dd369b8e7b12fc82a12da949a8a721dfb306ebd Mon Sep 17 00:00:00 2001 From: Jordan Peck Date: Sat, 10 Oct 2026 23:00:17 +0100 Subject: [PATCH 3/9] Fix AVX/AVX512 float != for NaN _CMP_NEQ_OQ is false when either input is NaN. Use the unordered _CMP_NEQ_UQ, matching SSE cmpneqps, the other platforms and C++ !=. --- include/FastSIMD/ToolSet/x86/256/f32x8.h | 2 +- include/FastSIMD/ToolSet/x86/512/f32x16.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/FastSIMD/ToolSet/x86/256/f32x8.h b/include/FastSIMD/ToolSet/x86/256/f32x8.h index 1c6513d..b3b6e90 100644 --- a/include/FastSIMD/ToolSet/x86/256/f32x8.h +++ b/include/FastSIMD/ToolSet/x86/256/f32x8.h @@ -86,7 +86,7 @@ namespace FS FS_FORCEINLINE MaskType operator !=( const Register& rhs ) const { - return _mm256_cmp_ps( native, rhs.native, _CMP_NEQ_OQ ); + return _mm256_cmp_ps( native, rhs.native, _CMP_NEQ_UQ ); } FS_FORCEINLINE MaskType operator >=( const Register& rhs ) const diff --git a/include/FastSIMD/ToolSet/x86/512/f32x16.h b/include/FastSIMD/ToolSet/x86/512/f32x16.h index 9711645..9c54d83 100644 --- a/include/FastSIMD/ToolSet/x86/512/f32x16.h +++ b/include/FastSIMD/ToolSet/x86/512/f32x16.h @@ -86,7 +86,7 @@ namespace FS FS_FORCEINLINE MaskType operator !=( const Register& rhs ) const { - return _mm512_cmp_ps_mask( native, rhs.native, _CMP_NEQ_OQ ); + return _mm512_cmp_ps_mask( native, rhs.native, _CMP_NEQ_UQ ); } FS_FORCEINLINE MaskType operator >=( const Register& rhs ) const From 9befcc633a0ae01ffbeb1fa8734594e2178b3781 Mon Sep 17 00:00:00 2001 From: Jordan Peck Date: Sat, 10 Oct 2026 23:00:25 +0100 Subject: [PATCH 4/9] Match std::min/std::max in Min/Max for +-0 and NaN x86 minps/maxps return their 2nd operand for equal (+-0) or NaN inputs, and WASM min/max and ARM vminq/vmaxq propagate NaN, so feature sets disagreed with each other and with the scalar std::min/std::max. Non relaxed Min/Max now all give b < a ? b : a (and a < b ? b : a): x86 swaps the operands, WASM uses pmin/pmax and ARM selects with vbslq. Relaxed keeps the native ops. --- include/FastSIMD/ToolSet/ARM/128/f32x4.h | 20 ++++++++++++++++++-- include/FastSIMD/ToolSet/WASM/128/f32x4.h | 6 ++++-- include/FastSIMD/ToolSet/x86/128/f32x4.h | 6 ++++-- include/FastSIMD/ToolSet/x86/256/f32x8.h | 6 ++++-- include/FastSIMD/ToolSet/x86/512/f32x16.h | 6 ++++-- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/include/FastSIMD/ToolSet/ARM/128/f32x4.h b/include/FastSIMD/ToolSet/ARM/128/f32x4.h index f64ba49..0d5e1b0 100644 --- a/include/FastSIMD/ToolSet/ARM/128/f32x4.h +++ b/include/FastSIMD/ToolSet/ARM/128/f32x4.h @@ -186,13 +186,29 @@ namespace FS template>> FS_FORCEINLINE f32<4, SIMD> Min( const f32<4, SIMD>& a, const f32<4, SIMD>& b ) { - return vminq_f32( a.native, b.native ); + if constexpr( FastSIMD::IsRelaxed() ) + { + return vminq_f32( a.native, b.native ); + } + else + { + // b < a ? b : a, matching std::min for +-0 and NaN, which vminq does not + return vbslq_f32( vcltq_f32( b.native, a.native ), b.native, a.native ); + } } template>> FS_FORCEINLINE f32<4, SIMD> Max( const f32<4, SIMD>& a, const f32<4, SIMD>& b ) { - return vmaxq_f32( a.native, b.native ); + if constexpr( FastSIMD::IsRelaxed() ) + { + return vmaxq_f32( a.native, b.native ); + } + else + { + // a < b ? b : a, matching std::max for +-0 and NaN, which vmaxq does not + return vbslq_f32( vcltq_f32( a.native, b.native ), b.native, a.native ); + } } template>> diff --git a/include/FastSIMD/ToolSet/WASM/128/f32x4.h b/include/FastSIMD/ToolSet/WASM/128/f32x4.h index 1dbd036..1b3fde1 100644 --- a/include/FastSIMD/ToolSet/WASM/128/f32x4.h +++ b/include/FastSIMD/ToolSet/WASM/128/f32x4.h @@ -168,7 +168,8 @@ namespace FS } else { - return wasm_f32x4_min( a.native, b.native ); + // pmin is b < a ? b : a, matching std::min for +-0 and NaN, which min does not + return wasm_f32x4_pmin( a.native, b.native ); } } @@ -181,7 +182,8 @@ namespace FS } else { - return wasm_f32x4_max( a.native, b.native ); + // pmax is a < b ? b : a, matching std::max for +-0 and NaN, which max does not + return wasm_f32x4_pmax( a.native, b.native ); } } diff --git a/include/FastSIMD/ToolSet/x86/128/f32x4.h b/include/FastSIMD/ToolSet/x86/128/f32x4.h index 9c51613..badd308 100644 --- a/include/FastSIMD/ToolSet/x86/128/f32x4.h +++ b/include/FastSIMD/ToolSet/x86/128/f32x4.h @@ -204,13 +204,15 @@ namespace FS template>> FS_FORCEINLINE f32<4, SIMD> Min( const f32<4, SIMD>& a, const f32<4, SIMD>& b ) { - return _mm_min_ps( a.native, b.native ); + // minps returns its 2nd operand for equal (+-0) or NaN inputs, swapped to match std::min which returns a + return _mm_min_ps( b.native, a.native ); } template>> FS_FORCEINLINE f32<4, SIMD> Max( const f32<4, SIMD>& a, const f32<4, SIMD>& b ) { - return _mm_max_ps( a.native, b.native ); + // maxps returns its 2nd operand for equal (+-0) or NaN inputs, swapped to match std::max which returns a + return _mm_max_ps( b.native, a.native ); } template>> diff --git a/include/FastSIMD/ToolSet/x86/256/f32x8.h b/include/FastSIMD/ToolSet/x86/256/f32x8.h index b3b6e90..c1e7008 100644 --- a/include/FastSIMD/ToolSet/x86/256/f32x8.h +++ b/include/FastSIMD/ToolSet/x86/256/f32x8.h @@ -165,13 +165,15 @@ namespace FS template>> FS_FORCEINLINE f32<8, SIMD> Min( const f32<8, SIMD>& a, const f32<8, SIMD>& b ) { - return _mm256_min_ps( a.native, b.native ); + // minps returns its 2nd operand for equal (+-0) or NaN inputs, swapped to match std::min which returns a + return _mm256_min_ps( b.native, a.native ); } template>> FS_FORCEINLINE f32<8, SIMD> Max( const f32<8, SIMD>& a, const f32<8, SIMD>& b ) { - return _mm256_max_ps( a.native, b.native ); + // maxps returns its 2nd operand for equal (+-0) or NaN inputs, swapped to match std::max which returns a + return _mm256_max_ps( b.native, a.native ); } template>> diff --git a/include/FastSIMD/ToolSet/x86/512/f32x16.h b/include/FastSIMD/ToolSet/x86/512/f32x16.h index 9c54d83..09bc12a 100644 --- a/include/FastSIMD/ToolSet/x86/512/f32x16.h +++ b/include/FastSIMD/ToolSet/x86/512/f32x16.h @@ -164,13 +164,15 @@ namespace FS template>> FS_FORCEINLINE f32<16, SIMD> Min( const f32<16, SIMD>& a, const f32<16, SIMD>& b ) { - return _mm512_min_ps( a.native, b.native ); + // minps returns its 2nd operand for equal (+-0) or NaN inputs, swapped to match std::min which returns a + return _mm512_min_ps( b.native, a.native ); } template>> FS_FORCEINLINE f32<16, SIMD> Max( const f32<16, SIMD>& a, const f32<16, SIMD>& b ) { - return _mm512_max_ps( a.native, b.native ); + // maxps returns its 2nd operand for equal (+-0) or NaN inputs, swapped to match std::max which returns a + return _mm512_max_ps( b.native, a.native ); } template>> From 1c9ccf92d8b1def9ec206673dfd7d1535cc15b75 Mon Sep 17 00:00:00 2001 From: Jordan Peck Date: Sat, 10 Oct 2026 23:00:27 +0100 Subject: [PATCH 5/9] Fix NEON Cast of mask registers on GCC GCC has no implicit conversion between uint32x4_t and float32x4_t or int32x4_t, so casting a mask register didn't compile. Reinterpret every native type through uint32x4_t, picked by element type since with the MSVC ARM64 ABI every NEON vector type is the same __n128. --- include/FastSIMD/ToolSet/ARM/NEON.h | 54 +++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/include/FastSIMD/ToolSet/ARM/NEON.h b/include/FastSIMD/ToolSet/ARM/NEON.h index 00f39dc..3d48a06 100644 --- a/include/FastSIMD/ToolSet/ARM/NEON.h +++ b/include/FastSIMD/ToolSet/ARM/NEON.h @@ -19,24 +19,50 @@ namespace FS return vcvtq_f32_s32( a.native ); } - template>> - FS_FORCEINLINE Register Cast( const Register& a, TypeDummy ) + namespace impl { - if constexpr( - std::is_same_v::NativeType, float32x4_t> && - std::is_same_v::NativeType, int32x4_t> ) + // Native casts go through uint32x4_t and pick the reinterpret by element type, not native type + // With the MSVC ARM64 ABI every NEON vector type is the same __n128, so native types can't be told apart + template + FS_FORCEINLINE uint32x4_t NeonCastToU32( V a ) { - return vreinterpretq_s32_f32( a.GetNative() ); + if constexpr( std::is_same_v ) + { + return vreinterpretq_u32_f32( a ); + } + else if constexpr( std::is_same_v ) + { + return vreinterpretq_u32_s32( a ); + } + else + { + static_assert( std::is_same_v> || std::is_same_v>, "FastSIMD: FS::Cast not supported with provided types" ); + return a; + } } - else if constexpr( - std::is_same_v::NativeType, int32x4_t> && - std::is_same_v::NativeType, float32x4_t> ) - { - return vreinterpretq_f32_s32( a.GetNative() ); - } - else + + template + FS_FORCEINLINE auto NeonCastFromU32( uint32x4_t a ) { - return a.GetNative(); + if constexpr( std::is_same_v ) + { + return vreinterpretq_f32_u32( a ); + } + else if constexpr( std::is_same_v ) + { + return vreinterpretq_s32_u32( a ); + } + else + { + static_assert( std::is_same_v> || std::is_same_v>, "FastSIMD: FS::Cast not supported with provided types" ); + return a; + } } } + + template>> + FS_FORCEINLINE Register Cast( const Register& a, TypeDummy ) + { + return impl::NeonCastFromU32( impl::NeonCastToU32( a.GetNative() ) ); + } } From de19a1ce281bf2b59d270699fb6750df4b4079e0 Mon Sep 17 00:00:00 2001 From: Jordan Peck Date: Sat, 10 Oct 2026 23:00:29 +0100 Subject: [PATCH 6/9] Disable FMA contraction in non relaxed dispatch libraries GCC and Clang fused mul + add into FMA, and -mno-fma isn't enough because AVX512F has its own FMA instructions, so non relaxed results differed between feature sets. clang-cl needs the /clang: form. --- dispatch/cmake/ClassSIMD.cmake | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dispatch/cmake/ClassSIMD.cmake b/dispatch/cmake/ClassSIMD.cmake index 7646021..cba0e8d 100644 --- a/dispatch/cmake/ClassSIMD.cmake +++ b/dispatch/cmake/ClassSIMD.cmake @@ -111,6 +111,17 @@ function(fastsimd_create_dispatch_library simd_library_name) target_compile_options(${simd_library_name} PRIVATE -Wa,-muse-unaligned-vector-move) endif() + # Non relaxed results must match across feature sets, stop the compiler fusing mul + add into FMA + # -mno-fma isn't enough, AVX512F has its own FMA instructions + if(NOT fastsimd_create_dispatch_library_RELAXED) + # MSVC is also set for clang-cl, which ignores a bare -ffp-contract=off + if(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(${simd_library_name} PRIVATE /clang:-ffp-contract=off) + elseif(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + target_compile_options(${simd_library_name} PRIVATE -ffp-contract=off) + endif() + endif() + if(fastsimd_create_dispatch_library_RELAXED) target_compile_definitions(${simd_library_name} PUBLIC FASTSIMD_IS_RELAXED=1) set(relaxed_log_msg " (RELAXED)") From 8d460cbbdde58d61eab2131ecd389c8da21ee1a9 Mon Sep 17 00:00:00 2001 From: Jordan Peck Date: Tue, 13 Oct 2026 23:25:45 +0100 Subject: [PATCH 7/9] Simplify register binary operators to copy lhs into a temporary --- include/FastSIMD/ToolSet/Generic/Register.h | 46 ++++++--------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/include/FastSIMD/ToolSet/Generic/Register.h b/include/FastSIMD/ToolSet/Generic/Register.h index 6e6bb77..6b58d51 100644 --- a/include/FastSIMD/ToolSet/Generic/Register.h +++ b/include/FastSIMD/ToolSet/Generic/Register.h @@ -175,94 +175,72 @@ namespace FS DoubledType v0, v1; }; - // Binary operators copy lhs into a local rather than taking it by value + // Binary operators copy lhs into a temporary rather than taking it by value // MSVC x86 doesn't copy over-aligned by value arguments, so modifying a by value lhs wrote into the caller's register template FS_FORCEINLINE static Register operator +( const Register& lhs, const Register& rhs ) { - Register result = lhs; - result += rhs; - return result; + return Register( lhs ) += rhs; } template FS_FORCEINLINE static Register operator -( const Register& lhs, const Register& rhs ) { - Register result = lhs; - result -= rhs; - return result; + return Register( lhs ) -= rhs; } template FS_FORCEINLINE static Register operator *( const Register& lhs, const Register& rhs ) { - Register result = lhs; - result *= rhs; - return result; + return Register( lhs ) *= rhs; } template FS_FORCEINLINE static Register operator /( const Register& lhs, const Register& rhs ) { - Register result = lhs; - result /= rhs; - return result; + return Register( lhs ) /= rhs; } template FS_FORCEINLINE static Register operator &( const Register& lhs, const Register& rhs ) { - Register result = lhs; - result &= rhs; - return result; + return Register( lhs ) &= rhs; } template FS_FORCEINLINE static Register operator |( const Register& lhs, const Register& rhs ) { - Register result = lhs; - result |= rhs; - return result; + return Register( lhs ) |= rhs; } template FS_FORCEINLINE static Register operator ^( const Register& lhs, const Register& rhs ) { - Register result = lhs; - result ^= rhs; - return result; + return Register( lhs ) ^= rhs; } template FS_FORCEINLINE static Register operator <<( const Register& lhs, int rhs ) { - Register result = lhs; - result <<= rhs; - return result; + return Register( lhs ) <<= rhs; } template FS_FORCEINLINE static Register operator >>( const Register& lhs, int rhs ) { - Register result = lhs; - result >>= rhs; - return result; + return Register( lhs ) >>= rhs; } template FS_FORCEINLINE static Register operator<<( const Register& lhs, const Register& rhs ) { - Register result = lhs; - result <<= rhs; - return result; + return Register( lhs ) <<= rhs; } template FS_FORCEINLINE static Register operator>>( const Register& lhs, const Register& rhs ) { - Register result = lhs; - result >>= rhs; - return result; + return Register( lhs ) >>= rhs; } template From d661f46115568eef9065fdd96b6f4ad438ace79e Mon Sep 17 00:00:00 2001 From: Jordan Peck Date: Tue, 13 Oct 2026 23:25:45 +0100 Subject: [PATCH 8/9] Use Select for non relaxed NEON Min/Max --- include/FastSIMD/ToolSet/ARM/128/f32x4.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/FastSIMD/ToolSet/ARM/128/f32x4.h b/include/FastSIMD/ToolSet/ARM/128/f32x4.h index 0d5e1b0..60f91be 100644 --- a/include/FastSIMD/ToolSet/ARM/128/f32x4.h +++ b/include/FastSIMD/ToolSet/ARM/128/f32x4.h @@ -192,8 +192,8 @@ namespace FS } else { - // b < a ? b : a, matching std::min for +-0 and NaN, which vminq does not - return vbslq_f32( vcltq_f32( b.native, a.native ), b.native, a.native ); + // Same as std::min for +-0 and NaN, which vminq is not + return Select( b < a, b, a ); } } @@ -206,8 +206,8 @@ namespace FS } else { - // a < b ? b : a, matching std::max for +-0 and NaN, which vmaxq does not - return vbslq_f32( vcltq_f32( a.native, b.native ), b.native, a.native ); + // Same as std::max for +-0 and NaN, which vmaxq is not + return Select( a < b, b, a ); } } From e614b657c28cf7422e95c97d1e1590e92ab7be5a Mon Sep 17 00:00:00 2001 From: Jordan Peck Date: Tue, 13 Oct 2026 23:25:45 +0100 Subject: [PATCH 9/9] Merge relaxed and non relaxed dispatch library options into one if/else --- dispatch/cmake/ClassSIMD.cmake | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/dispatch/cmake/ClassSIMD.cmake b/dispatch/cmake/ClassSIMD.cmake index cba0e8d..be3f614 100644 --- a/dispatch/cmake/ClassSIMD.cmake +++ b/dispatch/cmake/ClassSIMD.cmake @@ -111,22 +111,20 @@ function(fastsimd_create_dispatch_library simd_library_name) target_compile_options(${simd_library_name} PRIVATE -Wa,-muse-unaligned-vector-move) endif() - # Non relaxed results must match across feature sets, stop the compiler fusing mul + add into FMA - # -mno-fma isn't enough, AVX512F has its own FMA instructions - if(NOT fastsimd_create_dispatch_library_RELAXED) + if(fastsimd_create_dispatch_library_RELAXED) + target_compile_definitions(${simd_library_name} PUBLIC FASTSIMD_IS_RELAXED=1) + set(relaxed_log_msg " (RELAXED)") + else() + # Non relaxed results must match across feature sets, stop the compiler fusing mul + add into FMA + # -mno-fma isn't enough, AVX512F has its own FMA instructions # MSVC is also set for clang-cl, which ignores a bare -ffp-contract=off if(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") target_compile_options(${simd_library_name} PRIVATE /clang:-ffp-contract=off) - elseif(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + elseif(NOT MSVC) target_compile_options(${simd_library_name} PRIVATE -ffp-contract=off) endif() endif() - if(fastsimd_create_dispatch_library_RELAXED) - target_compile_definitions(${simd_library_name} PUBLIC FASTSIMD_IS_RELAXED=1) - set(relaxed_log_msg " (RELAXED)") - endif() - set(feature_set_list "") set(feature_set_list_debug "")