From 4842b8bd31f961b6c8650106c19623ccb65823ab Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 14 Nov 2025 01:33:09 -0500 Subject: [PATCH 1/4] Build all tests for better cross-compilation --- fearless_simd_dev_macros/src/lib.rs | 146 +++++++++++++--------------- 1 file changed, 66 insertions(+), 80 deletions(-) diff --git a/fearless_simd_dev_macros/src/lib.rs b/fearless_simd_dev_macros/src/lib.rs index b38f1f7..fff4c77 100644 --- a/fearless_simd_dev_macros/src/lib.rs +++ b/fearless_simd_dev_macros/src/lib.rs @@ -23,91 +23,85 @@ pub fn simd_test(_: TokenStream, item: TokenStream) -> TokenStream { let avx2_name = get_ident("avx2"); let wasm_name = get_ident("wasm"); - let include_fallback = !exclude_fallback(&input_fn_name.to_string()); - #[cfg(target_arch = "aarch64")] - let include_neon = std::arch::is_aarch64_feature_detected!("neon") - && !exclude_neon(&input_fn_name.to_string()); - #[cfg(not(target_arch = "aarch64"))] - let include_neon = false; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - let include_sse4 = - std::arch::is_x86_feature_detected!("sse4.2") && !exclude_sse4(&input_fn_name.to_string()); - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - let include_sse4 = false; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - let include_avx2 = std::arch::is_x86_feature_detected!("avx2") - && std::arch::is_x86_feature_detected!("fma") - && !exclude_avx2(&input_fn_name.to_string()); - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - let include_avx2 = false; - // Note that we cannot feature-gate this with `target_arch`. If we run - // `cargo test --target wasm32-wasip1`, then the `target_arch` will still be set to - // the operating system you are running on. Because of this, we instead add the `target_arch` - // feature gate to the actual test. - let include_wasm = !exclude_wasm(&input_fn_name.to_string()); - - let fallback_snippet = if include_fallback { - quote! { - #[test] - fn #fallback_name() { - let fallback = fearless_simd::Fallback::new(); - #input_fn_name(fallback); - } + let ignore_attr = |f: fn(&str) -> bool| { + let should_ignore = f(&input_fn_name.to_string()); + if should_ignore { + quote! { #[ignore] } + } else { + quote! {} } - } else { - quote! {} }; - let neon_snippet = if include_neon { - quote! { - #[cfg(target_arch = "aarch64")] - #[test] - fn #neon_name() { - let neon = unsafe { fearless_simd::aarch64::Neon::new_unchecked() }; - #input_fn_name(neon); - } + let ignore_fallback = ignore_attr(exclude_fallback); + let ignore_neon = ignore_attr(exclude_neon); + let ignore_sse4 = ignore_attr(exclude_sse4); + let ignore_avx2 = ignore_attr(exclude_avx2); + let ignore_wasm = ignore_attr(exclude_wasm); + + let fallback_snippet = quote! { + #[test] + #ignore_fallback + fn #fallback_name() { + let fallback = fearless_simd::Fallback::new(); + #input_fn_name(fallback); } - } else { - quote! {} }; - let sse4_snippet = if include_sse4 { - quote! { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn #sse4_name() { - let sse4 = unsafe { fearless_simd::x86::Sse4_2::new_unchecked() }; - #input_fn_name(sse4); - } + // All of the architecture-specific tests need to be included every time, and #[cfg]'d out depending on the target + // architecture. We can't use `CARGO_CFG_TARGET_ARCH` to conditionally omit them because it's not available when + // proc macros are evaluated. + + // There is currently no way to conditionally ignore a test at runtime (see + // https://internals.rust-lang.org/t/pre-rfc-skippable-tests/14611). Instead, we have to assert that these CPU + // features are always detected and fail the test if they aren't. + + let neon_snippet = quote! { + #[cfg(target_arch = "aarch64")] + #[test] + #ignore_neon + fn #neon_name() { + assert!(std::arch::is_aarch64_feature_detected!("neon")); + + let neon = unsafe { fearless_simd::aarch64::Neon::new_unchecked() }; + #input_fn_name(neon); + } + }; + + let sse4_snippet = quote! { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + #ignore_sse4 + fn #sse4_name() { + assert!(std::arch::is_x86_feature_detected!("sse4.2")); + + let sse4 = unsafe { fearless_simd::x86::Sse4_2::new_unchecked() }; + #input_fn_name(sse4); } - } else { - quote! {} }; - let avx2_snippet = if include_avx2 { - quote! { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn #avx2_name() { - let avx2 = unsafe { fearless_simd::x86::Avx2::new_unchecked() }; - #input_fn_name(avx2); - } + let avx2_snippet = quote! { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + #ignore_avx2 + fn #avx2_name() { + assert!( + std::arch::is_x86_feature_detected!("avx2") + && std::arch::is_x86_feature_detected!("fma") + ); + + let avx2 = unsafe { fearless_simd::x86::Avx2::new_unchecked() }; + #input_fn_name(avx2); } - } else { - quote! {} }; - let wasm_snippet = if include_wasm { - quote! { - #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))] - #[test] - fn #wasm_name() { - let wasm = unsafe { fearless_simd::wasm32::WasmSimd128::new_unchecked() }; - #input_fn_name(wasm); - } + let wasm_snippet = quote! { + #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))] + #[test] + #ignore_wasm + fn #wasm_name() { + let wasm = unsafe { fearless_simd::wasm32::WasmSimd128::new_unchecked() }; + #input_fn_name(wasm); } - } else { - quote! {} }; quote! { @@ -125,8 +119,6 @@ pub fn simd_test(_: TokenStream, item: TokenStream) -> TokenStream { // You can update below functions if you want to exclude certain tests from different architectures // (for example because they haven't been implemented yet). -#[allow(clippy::allow_attributes, reason = "Lints only apply in some cfgs.")] -#[allow(dead_code, reason = "Used only on aarch64, but always type-checked.")] fn exclude_neon(_test_name: &str) -> bool { false } @@ -135,8 +127,6 @@ fn exclude_fallback(_test_name: &str) -> bool { false } -#[allow(clippy::allow_attributes, reason = "Lints only apply in some cfgs.")] -#[allow(dead_code, reason = "Used only on x86(-64), but always type-checked.")] fn exclude_sse4(test_name: &str) -> bool { matches!( test_name, @@ -145,8 +135,6 @@ fn exclude_sse4(test_name: &str) -> bool { ) || test_name.contains("precise") } -#[allow(clippy::allow_attributes, reason = "Lints only apply in some cfgs.")] -#[allow(dead_code, reason = "Used only on x86(-64), but always type-checked.")] fn exclude_avx2(test_name: &str) -> bool { matches!( test_name, @@ -155,8 +143,6 @@ fn exclude_avx2(test_name: &str) -> bool { ) || test_name.contains("precise") } -#[allow(clippy::allow_attributes, reason = "Lints only apply in some cfgs.")] -#[allow(dead_code, reason = "Used only on wasm32, but always type-checked.")] fn exclude_wasm(test_name: &str) -> bool { matches!( test_name, From 4a5c0b665e9b2a7b8af6d205b7541a6593e2520e Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 19 Nov 2025 01:34:05 -0500 Subject: [PATCH 2/4] Rework SIMD level handling in tests --- fearless_simd_dev_macros/src/lib.rs | 33 +++++++++++++++-------------- fearless_simd_tests/tests/mod.rs | 27 +++++++++++++++++++++++ 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/fearless_simd_dev_macros/src/lib.rs b/fearless_simd_dev_macros/src/lib.rs index fff4c77..dd6bd32 100644 --- a/fearless_simd_dev_macros/src/lib.rs +++ b/fearless_simd_dev_macros/src/lib.rs @@ -52,18 +52,20 @@ pub fn simd_test(_: TokenStream, item: TokenStream) -> TokenStream { // proc macros are evaluated. // There is currently no way to conditionally ignore a test at runtime (see - // https://internals.rust-lang.org/t/pre-rfc-skippable-tests/14611). Instead, we have to assert that these CPU - // features are always detected and fail the test if they aren't. + // https://internals.rust-lang.org/t/pre-rfc-skippable-tests/14611). Instead, we'll just pass the tests if the + // target features aren't supported. This is not ideal, since it may mislead you into thinking tests have passed + // when they haven't even been run, but some CI runners don't support all target features and we don't want failures + // as a result of that. let neon_snippet = quote! { #[cfg(target_arch = "aarch64")] #[test] #ignore_neon fn #neon_name() { - assert!(std::arch::is_aarch64_feature_detected!("neon")); - - let neon = unsafe { fearless_simd::aarch64::Neon::new_unchecked() }; - #input_fn_name(neon); + if std::arch::is_aarch64_feature_detected!("neon") { + let neon = unsafe { fearless_simd::aarch64::Neon::new_unchecked() }; + #input_fn_name(neon); + } } }; @@ -72,10 +74,10 @@ pub fn simd_test(_: TokenStream, item: TokenStream) -> TokenStream { #[test] #ignore_sse4 fn #sse4_name() { - assert!(std::arch::is_x86_feature_detected!("sse4.2")); - - let sse4 = unsafe { fearless_simd::x86::Sse4_2::new_unchecked() }; - #input_fn_name(sse4); + if std::arch::is_x86_feature_detected!("sse4.2") { + let sse4 = unsafe { fearless_simd::x86::Sse4_2::new_unchecked() }; + #input_fn_name(sse4); + } } }; @@ -84,13 +86,12 @@ pub fn simd_test(_: TokenStream, item: TokenStream) -> TokenStream { #[test] #ignore_avx2 fn #avx2_name() { - assert!( - std::arch::is_x86_feature_detected!("avx2") + if std::arch::is_x86_feature_detected!("avx2") && std::arch::is_x86_feature_detected!("fma") - ); - - let avx2 = unsafe { fearless_simd::x86::Avx2::new_unchecked() }; - #input_fn_name(avx2); + { + let avx2 = unsafe { fearless_simd::x86::Avx2::new_unchecked() }; + #input_fn_name(avx2); + } } }; diff --git a/fearless_simd_tests/tests/mod.rs b/fearless_simd_tests/tests/mod.rs index 8f5285a..9f9992e 100644 --- a/fearless_simd_tests/tests/mod.rs +++ b/fearless_simd_tests/tests/mod.rs @@ -37,3 +37,30 @@ fn saturate_float_to_int(simd: S) { fn generic_cast(x: S::f32s) -> S::u32s { x.to_int() } + +#[test] +fn supports_highest_level() { + // When running tests locally, ensure that every SIMD level to be tested is actually supported. The tests themselves + // will return early and pass if run with an unsupported SIMD level. + // + // We skip this on CI because some runners may not support all SIMD levels--in particular, the macOS x86_64 runner + // doesn't support AVX2. + if std::env::var_os("CI").is_none() { + let level = Level::new(); + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + assert!( + level.as_avx2().is_some(), + "This machine supports AVX2 and below" + ); + + #[cfg(target_arch = "aarch64")] + assert!(level.as_neon().is_some(), "This machine supports NEON"); + + #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))] + assert!( + level.as_wasm_simd128().is_some(), + "This environment supports WASM SIMD128" + ); + } +} From afe33d184d64c0cf4c087762dead515df66d0ec6 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 19 Nov 2025 01:39:23 -0500 Subject: [PATCH 3/4] Appease the paperclip --- fearless_simd_tests/tests/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fearless_simd_tests/tests/mod.rs b/fearless_simd_tests/tests/mod.rs index 9f9992e..5ef3edb 100644 --- a/fearless_simd_tests/tests/mod.rs +++ b/fearless_simd_tests/tests/mod.rs @@ -38,6 +38,11 @@ fn generic_cast(x: S::f32s) -> S::u32s { x.to_int() } +#[allow(clippy::allow_attributes, reason = "Only needed in some cfgs.")] +#[allow( + unused_variables, + reason = "The constructed `Level` is only used in some cfgs." +)] #[test] fn supports_highest_level() { // When running tests locally, ensure that every SIMD level to be tested is actually supported. The tests themselves From 407420a93df4285f748b29a734ac38424d640440 Mon Sep 17 00:00:00 2001 From: valadaptive Date: Wed, 19 Nov 2025 12:11:01 -0500 Subject: [PATCH 4/4] Update the assertion message --- fearless_simd_tests/tests/mod.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/fearless_simd_tests/tests/mod.rs b/fearless_simd_tests/tests/mod.rs index 5ef3edb..e2831f7 100644 --- a/fearless_simd_tests/tests/mod.rs +++ b/fearless_simd_tests/tests/mod.rs @@ -43,29 +43,41 @@ fn generic_cast(x: S::f32s) -> S::u32s { unused_variables, reason = "The constructed `Level` is only used in some cfgs." )] +#[allow( + dead_code, + reason = "The `UNSUPPORTED_LEVEL_MESSAGE` is only used in some cfgs." +)] #[test] fn supports_highest_level() { + const UNSUPPORTED_LEVEL_MESSAGE: &str = "This means that some of the other tests in this run may be false positives, that is, they have been marked as succeeding even though they would actually fail if they could run.\n\ + When these tests are run on CI, any false positives should be caught.\n\ + However, please open a thread in the #simd channel on the Linebender Zulip if you see this message.\n\ + That would allow us to know whether it's worth us setting up the tests to run on an emulated system (such as using QEMU)."; + + let level = Level::new(); + // When running tests locally, ensure that every SIMD level to be tested is actually supported. The tests themselves // will return early and pass if run with an unsupported SIMD level. // // We skip this on CI because some runners may not support all SIMD levels--in particular, the macOS x86_64 runner // doesn't support AVX2. if std::env::var_os("CI").is_none() { - let level = Level::new(); - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] assert!( level.as_avx2().is_some(), - "This machine supports AVX2 and below" + "This machine does not support every `Level` supported by Fearless SIMD (currently AVX2 and below).\n{UNSUPPORTED_LEVEL_MESSAGE}", ); #[cfg(target_arch = "aarch64")] - assert!(level.as_neon().is_some(), "This machine supports NEON"); - - #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))] assert!( - level.as_wasm_simd128().is_some(), - "This environment supports WASM SIMD128" + level.as_neon().is_some(), + "This machine does not support every `Level` supported by Fearless SIMD (currently NEON and below).\n{UNSUPPORTED_LEVEL_MESSAGE}", ); } + + #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))] + assert!( + level.as_wasm_simd128().is_some(), + "This environment does not support WASM SIMD128. This should never happen, since it should always be supported if the `simd128` feature is enabled." + ); }