diff --git a/library/stdarch/src/x86/avx.rs b/library/stdarch/src/x86/avx.rs index 828aeefe6e80..77a8738e19b9 100644 --- a/library/stdarch/src/x86/avx.rs +++ b/library/stdarch/src/x86/avx.rs @@ -84,6 +84,7 @@ pub unsafe fn _mm256_sub_ps(a: f32x8, b: f32x8) -> f32x8 { /// ``` #[inline(always)] #[target_feature = "+avx"] +#[cfg_attr(test, assert_instr(vroundpd, b = 0x3))] pub unsafe fn _mm256_round_pd(a: f64x4, b: i32) -> f64x4 { macro_rules! call { ($imm8:expr) => { roundpd256(a, $imm8) } @@ -91,13 +92,6 @@ pub unsafe fn _mm256_round_pd(a: f64x4, b: i32) -> f64x4 { constify_imm8!(b, call) } -#[cfg(test)] -#[cfg_attr(test, assert_instr(vroundpd))] -#[target_feature = "+avx"] -fn test_mm256_round_pd(a: f64x4) -> f64x4 { - unsafe { _mm256_round_pd(a, 0x3) } -} - /// Round packed double-precision (64-bit) floating point elements in `a` toward /// positive infinity. #[inline(always)] diff --git a/library/stdarch/src/x86/sse.rs b/library/stdarch/src/x86/sse.rs index 217bbd8b4637..4dc38f724538 100644 --- a/library/stdarch/src/x86/sse.rs +++ b/library/stdarch/src/x86/sse.rs @@ -171,6 +171,7 @@ pub unsafe fn _mm_max_ps(a: f32x4, b: f32x4) -> f32x4 { /// `b`. Mask is split to 2 control bits each to index the element from inputs. #[inline(always)] #[target_feature = "+sse"] +#[cfg_attr(test, assert_instr(shufps, mask = 3))] pub unsafe fn _mm_shuffle_ps(a: f32x4, b: f32x4, mask: i32) -> f32x4 { let mask = (mask & 0xFF) as u8; @@ -217,13 +218,6 @@ pub unsafe fn _mm_shuffle_ps(a: f32x4, b: f32x4, mask: i32) -> f32x4 { } } -#[cfg(test)] -#[target_feature = "+sse"] -#[cfg_attr(test, assert_instr(shufps))] -fn _test_mm_shuffle_ps(a: f32x4, b: f32x4) -> f32x4 { - unsafe { _mm_shuffle_ps(a, b, 3) } -} - /// Unpack and interleave single-precision (32-bit) floating-point elements /// from the higher half of `a` and `b`. #[inline(always)] diff --git a/library/stdarch/stdsimd-test/assert-instr-macro/Cargo.toml b/library/stdarch/stdsimd-test/assert-instr-macro/Cargo.toml index 367f4b5e94ba..ba66015381d7 100644 --- a/library/stdarch/stdsimd-test/assert-instr-macro/Cargo.toml +++ b/library/stdarch/stdsimd-test/assert-instr-macro/Cargo.toml @@ -5,3 +5,9 @@ authors = ["Alex Crichton "] [lib] proc-macro = true + +[dependencies] +proc-macro2 = { version = "0.1", features = ["unstable"] } +quote = { git = 'https://github.com/dtolnay/quote' } +syn = { git = 'https://github.com/dtolnay/syn', features = ["full"] } +synom = { git = 'https://github.com/dtolnay/syn' } diff --git a/library/stdarch/stdsimd-test/assert-instr-macro/src/lib.rs b/library/stdarch/stdsimd-test/assert-instr-macro/src/lib.rs index 358119306962..72b8f32368c9 100644 --- a/library/stdarch/stdsimd-test/assert-instr-macro/src/lib.rs +++ b/library/stdarch/stdsimd-test/assert-instr-macro/src/lib.rs @@ -10,62 +10,125 @@ #![feature(proc_macro)] +extern crate proc_macro2; extern crate proc_macro; +#[macro_use] +extern crate quote; +extern crate syn; +#[macro_use] +extern crate synom; -use proc_macro::{TokenStream, Term, TokenNode, Delimiter}; +use proc_macro2::TokenStream; #[proc_macro_attribute] -pub fn assert_instr(attr: TokenStream, item: TokenStream) -> TokenStream { - let name = find_name(item.clone()); - let tokens = attr.into_iter().collect::>(); - if tokens.len() != 1 { - panic!("expected #[assert_instr(foo)]"); - } - let tokens = match tokens[0].kind { - TokenNode::Group(Delimiter::Parenthesis, ref rest) => rest.clone(), - _ => panic!("expected #[assert_instr(foo)]"), - }; - let tokens = tokens.into_iter().collect::>(); - if tokens.len() != 1 { - panic!("expected #[assert_instr(foo)]"); - } - let instr = match tokens[0].kind { - TokenNode::Term(term) => term, - _ => panic!("expected #[assert_instr(foo)]"), +pub fn assert_instr(attr: proc_macro::TokenStream, + item: proc_macro::TokenStream) + -> proc_macro::TokenStream +{ + let invoc = syn::parse::(attr) + .expect("expected #[assert_instr(instr, a = b, ...)]"); + let item = syn::parse::(item).expect("must be attached to an item"); + let func = match item.node { + syn::ItemKind::Fn(ref f) => f, + _ => panic!("must be attached to a function"), }; - let ignore = if cfg!(optimized) { - "" + let instr = &invoc.instr; + let maybe_ignore = if cfg!(optimized) { + TokenStream::empty() } else { - "#[ignore]" + (quote! { #[ignore] }).into() }; - let test = format!(" + let name = &func.ident; + let assert_name = syn::Ident::from(&format!("assert_{}", name.sym.as_str())[..]); + let shim_name = syn::Ident::from(&format!("{}_shim", name.sym.as_str())[..]); + let (to_test, test_name) = if invoc.args.len() == 0 { + (TokenStream::empty(), &func.ident) + } else { + let mut inputs = Vec::new(); + let mut input_vals = Vec::new(); + let ret = &func.decl.output; + for arg in func.decl.inputs.iter() { + let capture = match **arg.item() { + syn::FnArg::Captured(ref c) => c, + _ => panic!("arguments must not have patterns"), + }; + let ident = match capture.pat { + syn::Pat::Ident(ref i) => &i.ident, + _ => panic!("must have bare arguments"), + }; + match invoc.args.iter().find(|a| a.0 == ident.sym.as_str()) { + Some(&(_, ref tts)) => { + input_vals.push(quote! { #tts }); + } + None => { + inputs.push(capture); + input_vals.push(quote! { #ident }); + } + }; + } + let attrs = Append(&item.attrs); + (quote! { + #attrs + unsafe fn #shim_name(#(#inputs),*) #ret { + #name(#(#input_vals),*) + } + }.into(), &shim_name) + }; + + let tts: TokenStream = quote! { #[test] #[allow(non_snake_case)] - {ignore} - fn assert_instr_{name}() {{ - ::stdsimd_test::assert({name} as usize, - \"{name}\", - \"{instr}\"); - }} - ", name = name.as_str(), instr = instr.as_str(), ignore = ignore); - let test: TokenStream = test.parse().unwrap(); + #maybe_ignore + fn #assert_name() { + #to_test - item.into_iter().chain(test.into_iter()).collect() + ::stdsimd_test::assert(#test_name as usize, + stringify!(#test_name), + stringify!(#instr)); + } + }.into(); + // why? necessary now to get tests to work? + let tts: TokenStream = tts.to_string().parse().unwrap(); + + let tts: TokenStream = quote! { + #item + #tts + }.into(); + tts.into() } -fn find_name(item: TokenStream) -> Term { - let mut tokens = item.into_iter(); - while let Some(tok) = tokens.next() { - if let TokenNode::Term(word) = tok.kind { - if word.as_str() == "fn" { - break - } +struct Invoc { + instr: syn::Ident, + args: Vec<(syn::Ident, syn::Expr)>, +} + +impl synom::Synom for Invoc { + named!(parse -> Self, map!(parens!(do_parse!( + instr: syn!(syn::Ident) >> + args: many0!(do_parse!( + syn!(syn::tokens::Comma) >> + name: syn!(syn::Ident) >> + syn!(syn::tokens::Eq) >> + expr: syn!(syn::Expr) >> + (name, expr) + )) >> + (Invoc { + instr, + args, + }) + )), |p| p.0)); +} + +struct Append(T); + +impl quote::ToTokens for Append + where T: Clone + IntoIterator, + T::Item: quote::ToTokens +{ + fn to_tokens(&self, tokens: &mut quote::Tokens) { + for item in self.0.clone() { + item.to_tokens(tokens); } } - - match tokens.next().map(|t| t.kind) { - Some(TokenNode::Term(word)) => word, - _ => panic!("failed to find function name"), - } }