about summary refs log tree commit diff
path: root/library/stdarch/crates/assert-instr-macro
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2018-02-18 10:07:35 +0900
committerGitHub <noreply@github.com>2018-02-18 10:07:35 +0900
commit39b5ec91ae361355f53b0906976081405c09910c (patch)
treeca8933e34bafd16fa6ef8e53e5ee51e1156193b8 /library/stdarch/crates/assert-instr-macro
parentd097221fafc3100d4d139a6180ebdc6840258bb0 (diff)
downloadrust-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.toml13
-rw-r--r--library/stdarch/crates/assert-instr-macro/build.rs13
-rw-r--r--library/stdarch/crates/assert-instr-macro/src/lib.rs155
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);
+        }
+    }
+}