diff options
Diffstat (limited to 'libgrust/rustc-lib/stdarch/crates/simd-test-macro')
| -rw-r--r-- | libgrust/rustc-lib/stdarch/crates/simd-test-macro/Cargo.toml | 12 | ||||
| -rw-r--r-- | libgrust/rustc-lib/stdarch/crates/simd-test-macro/src/lib.rs | 151 | 
2 files changed, 163 insertions, 0 deletions
diff --git a/libgrust/rustc-lib/stdarch/crates/simd-test-macro/Cargo.toml b/libgrust/rustc-lib/stdarch/crates/simd-test-macro/Cargo.toml new file mode 100644 index 0000000..039fe8c --- /dev/null +++ b/libgrust/rustc-lib/stdarch/crates/simd-test-macro/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "simd-test-macro" +version = "0.1.0" +authors = ["Alex Crichton <alex@alexcrichton.com>"] + +[lib] +proc-macro = true +test = false + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" diff --git a/libgrust/rustc-lib/stdarch/crates/simd-test-macro/src/lib.rs b/libgrust/rustc-lib/stdarch/crates/simd-test-macro/src/lib.rs new file mode 100644 index 0000000..8e65c43 --- /dev/null +++ b/libgrust/rustc-lib/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. + +extern crate proc_macro; +extern crate proc_macro2; +#[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", +        "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") +}  | 
