diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /library/stdarch/crates/simd-test-macro/src | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/stdarch/crates/simd-test-macro/src')
-rw-r--r-- | library/stdarch/crates/simd-test-macro/src/lib.rs | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/library/stdarch/crates/simd-test-macro/src/lib.rs b/library/stdarch/crates/simd-test-macro/src/lib.rs new file mode 100644 index 000000000..9d81a4c5e --- /dev/null +++ b/library/stdarch/crates/simd-test-macro/src/lib.rs @@ -0,0 +1,151 @@ +//! Implementation of the `#[simd_test]` macro +//! +//! This macro expands to a `#[test]` function which tests the local machine +//! for the appropriate cfg before calling the inner test function. +#![deny(rust_2018_idioms)] + +#[macro_use] +extern crate quote; + +use proc_macro2::{Delimiter, Ident, Literal, Span, TokenStream, TokenTree}; +use quote::ToTokens; +use std::env; + +fn string(s: &str) -> TokenTree { + Literal::string(s).into() +} + +#[proc_macro_attribute] +pub fn simd_test( + attr: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let tokens = TokenStream::from(attr).into_iter().collect::<Vec<_>>(); + if tokens.len() != 3 { + panic!("expected #[simd_test(enable = \"feature\")]"); + } + match &tokens[0] { + TokenTree::Ident(tt) if *tt == "enable" => {} + _ => panic!("expected #[simd_test(enable = \"feature\")]"), + } + match &tokens[1] { + TokenTree::Punct(tt) if tt.as_char() == '=' => {} + _ => panic!("expected #[simd_test(enable = \"feature\")]"), + } + let enable_feature = match &tokens[2] { + TokenTree::Literal(tt) => tt.to_string(), + _ => panic!("expected #[simd_test(enable = \"feature\")]"), + }; + let enable_feature = enable_feature.trim_start_matches('"').trim_end_matches('"'); + let target_features: Vec<String> = enable_feature + .replace('+', "") + .split(',') + .map(String::from) + .collect(); + + let enable_feature = string(enable_feature); + let item = TokenStream::from(item); + let name = find_name(item.clone()); + + let name: TokenStream = name + .to_string() + .parse() + .unwrap_or_else(|_| panic!("failed to parse name: {}", name.to_string())); + + let target = env::var("TARGET").expect( + "TARGET environment variable should be set for rustc (e.g. TARGET=x86_64-apple-darwin cargo test)" + ); + let mut force_test = false; + let macro_test = match target + .split('-') + .next() + .unwrap_or_else(|| panic!("target triple contained no \"-\": {}", target)) + { + "i686" | "x86_64" | "i586" => "is_x86_feature_detected", + "arm" | "armv7" => "is_arm_feature_detected", + "aarch64" => "is_aarch64_feature_detected", + maybe_riscv if maybe_riscv.starts_with("riscv") => "is_riscv_feature_detected", + "powerpc" | "powerpcle" => "is_powerpc_feature_detected", + "powerpc64" | "powerpc64le" => "is_powerpc64_feature_detected", + "mips" | "mipsel" | "mipsisa32r6" | "mipsisa32r6el" => { + // FIXME: + // On MIPS CI run-time feature detection always returns false due + // to this qemu bug: https://bugs.launchpad.net/qemu/+bug/1754372 + // + // This is a workaround to force the MIPS tests to always run on + // CI. + force_test = true; + "is_mips_feature_detected" + } + "mips64" | "mips64el" | "mipsisa64r6" | "mipsisa64r6el" => { + // FIXME: see above + force_test = true; + "is_mips64_feature_detected" + } + t => panic!("unknown target: {}", t), + }; + let macro_test = Ident::new(macro_test, Span::call_site()); + + let mut cfg_target_features = TokenStream::new(); + for feature in target_features { + let q = quote_spanned! { + proc_macro2::Span::call_site() => + #macro_test!(#feature) && + }; + q.to_tokens(&mut cfg_target_features); + } + let q = quote! { true }; + q.to_tokens(&mut cfg_target_features); + + let test_norun = std::env::var("STDSIMD_TEST_NORUN").is_ok(); + let maybe_ignore = if test_norun { + quote! { #[ignore] } + } else { + TokenStream::new() + }; + + let ret: TokenStream = quote_spanned! { + proc_macro2::Span::call_site() => + #[allow(non_snake_case)] + #[test] + #maybe_ignore + fn #name() { + if #force_test | (#cfg_target_features) { + let v = unsafe { #name() }; + return v; + } else { + ::stdarch_test::assert_skip_test_ok(stringify!(#name)); + } + + #[target_feature(enable = #enable_feature)] + #item + } + }; + ret.into() +} + +fn find_name(item: TokenStream) -> Ident { + let mut tokens = item.into_iter(); + while let Some(tok) = tokens.next() { + if let TokenTree::Ident(word) = tok { + if word == "fn" { + break; + } + } + } + + fn get_ident(tt: TokenTree) -> Option<Ident> { + match tt { + TokenTree::Ident(i) => Some(i), + TokenTree::Group(g) if g.delimiter() == Delimiter::None => { + get_ident(g.stream().into_iter().next()?) + } + _ => None, + } + } + + tokens + .next() + .and_then(get_ident) + .expect("failed to find function name") +} |