2017-09-20 10:28:00 -07:00
|
|
|
//! Implementation of the `#[assert_instr]` macro
|
|
|
|
|
//!
|
|
|
|
|
//! This macro is used when testing the `stdsimd` crate and is used to generate
|
|
|
|
|
//! test cases to assert that functions do indeed contain the instructions that
|
|
|
|
|
//! we're expecting them to contain.
|
|
|
|
|
//!
|
|
|
|
|
//! The procedural macro here is relatively simple, it simply appends a
|
|
|
|
|
//! `#[test]` function to the original token stream which asserts that the
|
|
|
|
|
//! function itself contains the relevant instruction.
|
|
|
|
|
|
2017-09-19 14:46:00 -07:00
|
|
|
#![feature(proc_macro)]
|
|
|
|
|
|
|
|
|
|
extern crate proc_macro;
|
2018-02-02 16:08:27 +01:00
|
|
|
extern crate proc_macro2;
|
2017-09-26 19:03:38 -07:00
|
|
|
#[macro_use]
|
|
|
|
|
extern crate quote;
|
|
|
|
|
#[macro_use]
|
2017-12-27 07:56:38 -08:00
|
|
|
extern crate syn;
|
2017-09-19 14:46:00 -07:00
|
|
|
|
2017-09-26 19:03:38 -07:00
|
|
|
use proc_macro2::TokenStream;
|
2017-09-19 14:46:00 -07:00
|
|
|
|
|
|
|
|
#[proc_macro_attribute]
|
2017-10-27 17:55:29 +02:00
|
|
|
pub fn assert_instr(
|
|
|
|
|
attr: proc_macro::TokenStream, item: proc_macro::TokenStream
|
|
|
|
|
) -> proc_macro::TokenStream {
|
2017-09-26 19:03:38 -07:00
|
|
|
let invoc = syn::parse::<Invoc>(attr)
|
|
|
|
|
.expect("expected #[assert_instr(instr, a = b, ...)]");
|
2017-10-27 17:55:29 +02:00
|
|
|
let item =
|
|
|
|
|
syn::parse::<syn::Item>(item).expect("must be attached to an item");
|
2017-11-11 23:35:00 +01:00
|
|
|
let func = match item {
|
|
|
|
|
syn::Item::Fn(ref f) => f,
|
2017-09-26 19:03:38 -07:00
|
|
|
_ => panic!("must be attached to a function"),
|
2017-09-19 14:46:00 -07:00
|
|
|
};
|
|
|
|
|
|
2017-09-26 19:03:38 -07:00
|
|
|
let instr = &invoc.instr;
|
|
|
|
|
let maybe_ignore = if cfg!(optimized) {
|
|
|
|
|
TokenStream::empty()
|
2017-09-19 14:46:00 -07:00
|
|
|
} else {
|
2017-09-26 19:03:38 -07:00
|
|
|
(quote! { #[ignore] }).into()
|
2017-09-19 14:46:00 -07:00
|
|
|
};
|
2017-09-26 19:03:38 -07:00
|
|
|
let name = &func.ident;
|
2018-03-10 19:22:54 +01:00
|
|
|
use quote::ToTokens;
|
|
|
|
|
let instr_str = instr
|
|
|
|
|
.clone()
|
|
|
|
|
.into_tokens()
|
|
|
|
|
.to_string()
|
|
|
|
|
.replace('.', "_")
|
|
|
|
|
.replace(|c: char| c.is_whitespace(), "");
|
2017-10-27 17:55:29 +02:00
|
|
|
let assert_name = syn::Ident::from(
|
2018-03-10 19:22:54 +01:00
|
|
|
&format!("assert_{}_{}", name.as_ref(), instr_str)[..],
|
2017-10-27 17:55:29 +02:00
|
|
|
);
|
2018-01-04 17:15:23 +01:00
|
|
|
let shim_name = syn::Ident::from(format!("{}_shim", name.as_ref()));
|
2018-01-25 12:13:48 -06:00
|
|
|
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 {
|
|
|
|
|
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"),
|
|
|
|
|
};
|
2018-03-22 17:09:01 +01:00
|
|
|
match invoc
|
|
|
|
|
.args
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|a| a.0 == ident.as_ref())
|
|
|
|
|
{
|
2018-01-25 12:13:48 -06:00
|
|
|
Some(&(_, ref tts)) => {
|
|
|
|
|
input_vals.push(quote! { #tts });
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
inputs.push(capture);
|
|
|
|
|
input_vals.push(quote! { #ident });
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2017-11-11 23:35:00 +01:00
|
|
|
|
2018-01-25 12:13:48 -06:00
|
|
|
let attrs = func.attrs
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|attr| {
|
|
|
|
|
attr.path
|
|
|
|
|
.segments
|
|
|
|
|
.first()
|
2018-03-10 19:22:54 +01:00
|
|
|
.expect("attr.path.segments.first() failed")
|
2018-01-25 12:13:48 -06:00
|
|
|
.value()
|
|
|
|
|
.ident
|
|
|
|
|
.as_ref()
|
|
|
|
|
.starts_with("target")
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
let attrs = Append(&attrs);
|
2018-02-11 10:04:53 -06:00
|
|
|
|
|
|
|
|
// Use an ABI on Windows that passes SIMD values in registers, like what
|
|
|
|
|
// happens on Unix (I think?) by default.
|
|
|
|
|
let abi = if cfg!(windows) {
|
|
|
|
|
syn::LitStr::new("vectorcall", proc_macro2::Span::call_site())
|
|
|
|
|
} else {
|
|
|
|
|
syn::LitStr::new("C", proc_macro2::Span::call_site())
|
|
|
|
|
};
|
2018-01-25 12:13:48 -06:00
|
|
|
let to_test = quote! {
|
|
|
|
|
#attrs
|
2018-02-11 10:04:53 -06:00
|
|
|
unsafe extern #abi fn #shim_name(#(#inputs),*) #ret {
|
2018-01-25 12:13:48 -06:00
|
|
|
#name(#(#input_vals),*)
|
|
|
|
|
}
|
2017-09-26 19:03:38 -07:00
|
|
|
};
|
|
|
|
|
|
2017-11-21 12:54:06 -06:00
|
|
|
let tts: TokenStream = quote_spanned! {
|
2018-01-08 10:10:52 -08:00
|
|
|
proc_macro2::Span::call_site() =>
|
2017-09-19 14:46:00 -07:00
|
|
|
#[test]
|
|
|
|
|
#[allow(non_snake_case)]
|
2017-09-26 19:03:38 -07:00
|
|
|
#maybe_ignore
|
|
|
|
|
fn #assert_name() {
|
|
|
|
|
#to_test
|
|
|
|
|
|
2018-01-25 12:13:48 -06:00
|
|
|
::stdsimd_test::assert(#shim_name as usize,
|
|
|
|
|
stringify!(#shim_name),
|
2017-09-26 19:03:38 -07:00
|
|
|
stringify!(#instr));
|
|
|
|
|
}
|
|
|
|
|
}.into();
|
|
|
|
|
// why? necessary now to get tests to work?
|
2018-03-22 17:09:01 +01:00
|
|
|
let tts: TokenStream = tts.to_string()
|
|
|
|
|
.parse()
|
|
|
|
|
.expect("cannot parse tokenstream");
|
2017-09-19 14:46:00 -07:00
|
|
|
|
2017-09-26 19:03:38 -07:00
|
|
|
let tts: TokenStream = quote! {
|
|
|
|
|
#item
|
|
|
|
|
#tts
|
|
|
|
|
}.into();
|
|
|
|
|
tts.into()
|
2017-09-19 14:46:00 -07:00
|
|
|
}
|
|
|
|
|
|
2017-09-26 19:03:38 -07:00
|
|
|
struct Invoc {
|
2018-03-10 19:22:54 +01:00
|
|
|
instr: syn::Expr,
|
2017-09-26 19:03:38 -07:00
|
|
|
args: Vec<(syn::Ident, syn::Expr)>,
|
|
|
|
|
}
|
2017-09-19 14:46:00 -07:00
|
|
|
|
2017-12-27 07:56:38 -08:00
|
|
|
impl syn::synom::Synom for Invoc {
|
2018-04-27 04:54:15 +02:00
|
|
|
named!(parse -> Self, do_parse!(
|
2018-03-10 19:22:54 +01:00
|
|
|
instr: syn!(syn::Expr) >>
|
2017-09-26 19:03:38 -07:00
|
|
|
args: many0!(do_parse!(
|
2017-12-27 07:56:38 -08:00
|
|
|
syn!(syn::token::Comma) >>
|
2017-09-26 19:03:38 -07:00
|
|
|
name: syn!(syn::Ident) >>
|
2017-12-27 07:56:38 -08:00
|
|
|
syn!(syn::token::Eq) >>
|
2017-09-26 19:03:38 -07:00
|
|
|
expr: syn!(syn::Expr) >>
|
|
|
|
|
(name, expr)
|
|
|
|
|
)) >>
|
|
|
|
|
(Invoc {
|
|
|
|
|
instr,
|
|
|
|
|
args,
|
|
|
|
|
})
|
2018-04-27 04:54:15 +02:00
|
|
|
));
|
2017-09-26 19:03:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Append<T>(T);
|
|
|
|
|
|
|
|
|
|
impl<T> quote::ToTokens for Append<T>
|
2017-10-27 17:55:29 +02:00
|
|
|
where
|
|
|
|
|
T: Clone + IntoIterator,
|
|
|
|
|
T::Item: quote::ToTokens,
|
2017-09-26 19:03:38 -07:00
|
|
|
{
|
|
|
|
|
fn to_tokens(&self, tokens: &mut quote::Tokens) {
|
|
|
|
|
for item in self.0.clone() {
|
|
|
|
|
item.to_tokens(tokens);
|
|
|
|
|
}
|
2017-09-19 14:46:00 -07:00
|
|
|
}
|
|
|
|
|
}
|