1 //! Implementation of the `#[simd_test]` macro
3 //! This macro expands to a `#[test]` function which tests the local machine
4 //! for the appropriate cfg before calling the inner test function.
6 extern crate proc_macro
;
7 extern crate proc_macro2
;
11 use proc_macro2
::{Ident, Literal, Span, TokenStream, TokenTree}
;
15 fn string(s
: &str) -> TokenTree
{
16 Literal
::string(s
).into()
19 #[proc_macro_attribute]
21 attr
: proc_macro
::TokenStream
,
22 item
: proc_macro
::TokenStream
,
23 ) -> proc_macro
::TokenStream
{
24 let tokens
= TokenStream
::from(attr
).into_iter().collect
::<Vec
<_
>>();
25 if tokens
.len() != 3 {
26 panic
!("expected #[simd_test(enable = \"feature\")]");
29 TokenTree
::Ident(tt
) if *tt
== "enable" => {}
30 _
=> panic
!("expected #[simd_test(enable = \"feature\")]"),
33 TokenTree
::Punct(tt
) if tt
.as_char() == '
='
=> {}
34 _
=> panic
!("expected #[simd_test(enable = \"feature\")]"),
36 let enable_feature
= match &tokens
[2] {
37 TokenTree
::Literal(tt
) => tt
.to_string(),
38 _
=> panic
!("expected #[simd_test(enable = \"feature\")]"),
40 let enable_feature
= enable_feature
.trim_start_matches('
"').trim_end_matches('"'
);
41 let target_features
: Vec
<String
> = enable_feature
47 let mmx
= target_features
.iter().any(|s
| s
.starts_with("mmx"));
49 let enable_feature
= string(enable_feature
);
50 let item
= TokenStream
::from(item
);
51 let name
= find_name(item
.clone());
53 let name
: TokenStream
= name
56 .unwrap_or_else(|_
| panic
!("failed to parse name: {}", name
.to_string()));
58 let target
= env
::var("TARGET").expect(
59 "TARGET environment variable should be set for rustc (e.g. TARGET=x86_64-apple-darwin cargo test)"
61 let mut force_test
= false;
62 let macro_test
= match target
65 .unwrap_or_else(|| panic
!("target triple contained no \"-\": {}", target
))
67 "i686" | "x86_64" | "i586" => "is_x86_feature_detected",
68 "arm" | "armv7" => "is_arm_feature_detected",
69 "aarch64" => "is_aarch64_feature_detected",
70 "powerpc" | "powerpcle" => "is_powerpc_feature_detected",
71 "powerpc64" | "powerpc64le" => "is_powerpc64_feature_detected",
72 "mips" | "mipsel" | "mipsisa32r6" | "mipsisa32r6el" => {
74 // On MIPS CI run-time feature detection always returns false due
75 // to this qemu bug: https://bugs.launchpad.net/qemu/+bug/1754372
77 // This is a workaround to force the MIPS tests to always run on
80 "is_mips_feature_detected"
82 "mips64" | "mips64el" | "mipsisa64r6" | "mipsisa64r6el" => {
85 "is_mips64_feature_detected"
87 t
=> panic
!("unknown target: {}", t
),
89 let macro_test
= Ident
::new(macro_test
, Span
::call_site());
91 let mut cfg_target_features
= TokenStream
::new();
92 for feature
in target_features
{
93 let q
= quote_spanned
! {
94 proc_macro2
::Span
::call_site() =>
95 #macro_test!(#feature) &&
97 q
.to_tokens(&mut cfg_target_features
);
99 let q
= quote
! { true }
;
100 q
.to_tokens(&mut cfg_target_features
);
102 let test_norun
= std
::env
::var("STDSIMD_TEST_NORUN").is_ok();
103 let maybe_ignore
= if test_norun
{
110 // note: if the test requires MMX we need to clear the FPU
111 // registers once the test finishes before interfacing with
113 quote
! { unsafe { super::_mm_empty() }
; }
118 let ret
: TokenStream
= quote_spanned
! {
119 proc_macro2
::Span
::call_site() =>
120 #[allow(non_snake_case)]
124 if #force_test | (#cfg_target_features) {
125 let v
= unsafe { #name() }
;
129 ::stdarch_test
::assert_skip_test_ok(stringify
!(#name));
132 #[target_feature(enable = #enable_feature)]
139 fn find_name(item
: TokenStream
) -> Ident
{
140 let mut tokens
= item
.into_iter();
141 while let Some(tok
) = tokens
.next() {
142 if let TokenTree
::Ident(word
) = tok
{
149 match tokens
.next() {
150 Some(TokenTree
::Ident(word
)) => word
,
151 _
=> panic
!("failed to find function name"),