diff --git a/fearless_simd_dev_macros/src/lib.rs b/fearless_simd_dev_macros/src/lib.rs index b38f1f70..dd6bd321 100644 --- a/fearless_simd_dev_macros/src/lib.rs +++ b/fearless_simd_dev_macros/src/lib.rs @@ -23,91 +23,86 @@ 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 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); + } + }; + + // 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'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() { + if std::arch::is_aarch64_feature_detected!("neon") { let neon = unsafe { fearless_simd::aarch64::Neon::new_unchecked() }; #input_fn_name(neon); } } - } else { - quote! {} }; - let sse4_snippet = if include_sse4 { - quote! { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn #sse4_name() { + let sse4_snippet = quote! { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + #ignore_sse4 + fn #sse4_name() { + if 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_snippet = quote! { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + #[test] + #ignore_avx2 + fn #avx2_name() { + 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); } } - } 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 +120,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 +128,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 +136,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 +144,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, diff --git a/fearless_simd_tests/tests/mod.rs b/fearless_simd_tests/tests/mod.rs index 8f5285a6..e2831f75 100644 --- a/fearless_simd_tests/tests/mod.rs +++ b/fearless_simd_tests/tests/mod.rs @@ -37,3 +37,47 @@ fn saturate_float_to_int(simd: S) { 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." +)] +#[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() { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + assert!( + level.as_avx2().is_some(), + "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 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." + ); +}