diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2018-02-18 10:07:35 +0900 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-02-18 10:07:35 +0900 | 
| commit | 39b5ec91ae361355f53b0906976081405c09910c (patch) | |
| tree | ca8933e34bafd16fa6ef8e53e5ee51e1156193b8 /library/stdarch/crates/assert-instr-macro | |
| parent | d097221fafc3100d4d139a6180ebdc6840258bb0 (diff) | |
| download | rust-39b5ec91ae361355f53b0906976081405c09910c.tar.gz rust-39b5ec91ae361355f53b0906976081405c09910c.zip | |
Reorganize and refactor source tree (#324)
With RFC 2325 looking close to being accepted, I took a crack at reorganizing this repository to being more amenable for inclusion in libstd/libcore. My current plan is to add stdsimd as a submodule in rust-lang/rust and then use `#[path]` to include the modules directly into libstd/libcore. Before this commit, however, the source code of coresimd/stdsimd themselves were not quite ready for this. Imports wouldn't compile for one reason or another, and the organization was also different than the RFC itself! In addition to moving a lot of files around, this commit has the following major changes: * The `cfg_feature_enabled!` macro is now renamed to `is_target_feature_detected!` * The `vendor` module is now called `arch`. * Under the `arch` module is a suite of modules like `x86`, `x86_64`, etc. One per `cfg!(target_arch)`. * The `is_target_feature_detected!` macro was removed from coresimd. Unfortunately libcore has no ability to export unstable macros, so for now all feature detection is canonicalized in stdsimd. The `coresimd` and `stdsimd` crates have been updated to the planned organization in RFC 2325 as well. The runtime bits saw the largest amount of refactoring, seeing a good deal of simplification without the core/std split.
Diffstat (limited to 'library/stdarch/crates/assert-instr-macro')
| -rw-r--r-- | library/stdarch/crates/assert-instr-macro/Cargo.toml | 13 | ||||
| -rw-r--r-- | library/stdarch/crates/assert-instr-macro/build.rs | 13 | ||||
| -rw-r--r-- | library/stdarch/crates/assert-instr-macro/src/lib.rs | 155 | 
3 files changed, 181 insertions, 0 deletions
| diff --git a/library/stdarch/crates/assert-instr-macro/Cargo.toml b/library/stdarch/crates/assert-instr-macro/Cargo.toml new file mode 100644 index 00000000000..7fd1d2b80cc --- /dev/null +++ b/library/stdarch/crates/assert-instr-macro/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "assert-instr-macro" +version = "0.1.0" +authors = ["Alex Crichton <alex@alexcrichton.com>"] + +[lib] +proc-macro = true +test = false + +[dependencies] +proc-macro2 = { version = "0.2", features = ["nightly"] } +quote = "0.4" +syn = { version = "0.12", features = ["full"] } diff --git a/library/stdarch/crates/assert-instr-macro/build.rs b/library/stdarch/crates/assert-instr-macro/build.rs new file mode 100644 index 00000000000..45a868441c4 --- /dev/null +++ b/library/stdarch/crates/assert-instr-macro/build.rs @@ -0,0 +1,13 @@ +use std::env; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + let opt_level = env::var("OPT_LEVEL") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(0); + let profile = env::var("PROFILE").unwrap_or(String::new()); + if profile == "release" || opt_level >= 2 { + println!("cargo:rustc-cfg=optimized"); + } +} diff --git a/library/stdarch/crates/assert-instr-macro/src/lib.rs b/library/stdarch/crates/assert-instr-macro/src/lib.rs new file mode 100644 index 00000000000..24b0e888cf6 --- /dev/null +++ b/library/stdarch/crates/assert-instr-macro/src/lib.rs @@ -0,0 +1,155 @@ +//! 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. + +#![feature(proc_macro)] + +extern crate proc_macro; +extern crate proc_macro2; +#[macro_use] +extern crate quote; +#[macro_use] +extern crate syn; + +use proc_macro2::TokenStream; + +#[proc_macro_attribute] +pub fn assert_instr( + attr: proc_macro::TokenStream, item: proc_macro::TokenStream +) -> proc_macro::TokenStream { + let invoc = syn::parse::<Invoc>(attr) + .expect("expected #[assert_instr(instr, a = b, ...)]"); + let item = + syn::parse::<syn::Item>(item).expect("must be attached to an item"); + let func = match item { + syn::Item::Fn(ref f) => f, + _ => panic!("must be attached to a function"), + }; + + let instr = &invoc.instr; + let maybe_ignore = if cfg!(optimized) { + TokenStream::empty() + } else { + (quote! { #[ignore] }).into() + }; + let name = &func.ident; + let assert_name = syn::Ident::from( + &format!("assert_{}_{}", name.as_ref(), instr.as_ref())[..], + ); + let shim_name = syn::Ident::from(format!("{}_shim", name.as_ref())); + 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"), + }; + match invoc.args.iter().find(|a| a.0 == ident.as_ref()) { + Some(&(_, ref tts)) => { + input_vals.push(quote! { #tts }); + } + None => { + inputs.push(capture); + input_vals.push(quote! { #ident }); + } + }; + } + + let attrs = func.attrs + .iter() + .filter(|attr| { + attr.path + .segments + .first() + .unwrap() + .value() + .ident + .as_ref() + .starts_with("target") + }) + .collect::<Vec<_>>(); + let attrs = Append(&attrs); + + // 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()) + }; + let to_test = quote! { + #attrs + unsafe extern #abi fn #shim_name(#(#inputs),*) #ret { + #name(#(#input_vals),*) + } + }; + + let tts: TokenStream = quote_spanned! { + proc_macro2::Span::call_site() => + #[test] + #[allow(non_snake_case)] + #maybe_ignore + fn #assert_name() { + #to_test + + ::stdsimd_test::assert(#shim_name as usize, + stringify!(#shim_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() +} + +struct Invoc { + instr: syn::Ident, + args: Vec<(syn::Ident, syn::Expr)>, +} + +impl syn::synom::Synom for Invoc { + named!(parse -> Self, map!(parens!(do_parse!( + instr: syn!(syn::Ident) >> + args: many0!(do_parse!( + syn!(syn::token::Comma) >> + name: syn!(syn::Ident) >> + syn!(syn::token::Eq) >> + expr: syn!(syn::Expr) >> + (name, expr) + )) >> + (Invoc { + instr, + args, + }) + )), |p| p.1)); +} + +struct Append<T>(T); + +impl<T> quote::ToTokens for Append<T> +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); + } + } +} | 
