1 extern crate proc_macro
;
2 extern crate proc_macro2
;
8 use proc_macro
::TokenStream
;
9 use std
::{fs::File, io::Read, path::Path}
;
10 use syn
::ext
::IdentExt
;
13 pub fn x86_functions(input
: TokenStream
) -> TokenStream
{
14 functions(input
, &["core_arch/src/x86", "core_arch/src/x86_64"])
18 pub fn arm_functions(input
: TokenStream
) -> TokenStream
{
19 functions(input
, &["core_arch/src/arm", "core_arch/src/aarch64"])
23 pub fn mips_functions(input
: TokenStream
) -> TokenStream
{
24 functions(input
, &["core_arch/src/mips"])
27 fn functions(input
: TokenStream
, dirs
: &[&str]) -> TokenStream
{
28 let dir
= Path
::new(env
!("CARGO_MANIFEST_DIR"));
29 let root
= dir
.parent().expect("root-dir not found");
31 let mut files
= Vec
::new();
33 walk(&root
.join(dir
), &mut files
);
35 assert
!(!files
.is_empty());
37 let mut functions
= Vec
::new();
38 for &mut (ref mut file
, ref path
) in &mut files
{
39 for mut item
in file
.items
.drain(..) {
41 syn
::Item
::Fn(f
) => functions
.push((f
, path
)),
42 syn
::Item
::Mod(ref mut m
) => {
43 if let Some(ref mut m
) = m
.content
{
44 for i
in m
.1.drain(..) {
45 if let syn
::Item
::Fn(f
) = i
{
46 functions
.push((f
, path
))
55 assert
!(!functions
.is_empty());
57 let mut tests
= std
::collections
::HashSet
::<String
>::new();
59 let id
= format
!("{}", f
.0.sig
.ident
);
60 if id
.starts_with("test_") {
64 assert
!(!tests
.is_empty());
66 functions
.retain(|&(ref f
, _
)| {
67 if let syn
::Visibility
::Public(_
) = f
.vis
{
68 if f
.sig
.unsafety
.is_some() {
74 assert
!(!functions
.is_empty());
76 let input
= proc_macro2
::TokenStream
::from(input
);
78 let functions
= functions
80 .map(|&(ref f
, path
)| {
81 let name
= &f
.sig
.ident
;
82 // println!("{}", name);
83 let mut arguments
= Vec
::new();
84 for input
in f
.sig
.inputs
.iter() {
85 let ty
= match *input
{
86 syn
::FnArg
::Typed(ref c
) => &c
.ty
,
87 _
=> panic
!("invalid argument on {}", name
),
89 arguments
.push(to_type(ty
));
91 let ret
= match f
.sig
.output
{
92 syn
::ReturnType
::Default
=> quote
! { None }
,
93 syn
::ReturnType
::Type(_
, ref t
) => {
98 let instrs
= find_instrs(&f
.attrs
);
99 let target_feature
= if let Some(i
) = find_target_feature(&f
.attrs
) {
104 let required_const
= find_required_const(&f
.attrs
);
106 // strip leading underscore from fn name when building a test
107 // _mm_foo -> mm_foo such that the test name is test_mm_foo.
108 let test_name_string
= format
!("{}", name
);
109 let mut test_name_id
= test_name_string
.as_str();
110 while test_name_id
.starts_with('_'
) {
111 test_name_id
= &test_name_id
[1..];
113 let has_test
= tests
.contains(&format
!("test_{}", test_name_id
));
117 name
: stringify
!(#name),
118 arguments
: &[#(#arguments),*],
120 target_feature
: #target_feature,
121 instrs
: &[#(#instrs),*],
122 file
: stringify
!(#path),
123 required_const
: &[#(#required_const),*],
128 .collect
::<Vec
<_
>>();
130 let ret
= quote
! { #input: &[Function] = &[#(#functions),*]; }
;
131 // println!("{}", ret);
135 fn to_type(t
: &syn
::Type
) -> proc_macro2
::TokenStream
{
137 syn
::Type
::Path(ref p
) => match extract_path_ident(&p
.path
).to_string().as_ref() {
139 "__m128" => quote
! { &M128 }
,
140 "__m128d" => quote
! { &M128D }
,
141 "__m128i" => quote
! { &M128I }
,
142 "__m256" => quote
! { &M256 }
,
143 "__m256d" => quote
! { &M256D }
,
144 "__m256i" => quote
! { &M256I }
,
145 "__m512" => quote
! { &M512 }
,
146 "__m512d" => quote
! { &M512D }
,
147 "__m512i" => quote
! { &M512I }
,
148 "__mmask8" => quote
! { &MMASK8 }
,
149 "__mmask16" => quote
! { &MMASK16 }
,
150 "__m64" => quote
! { &M64 }
,
151 "bool" => quote
! { &BOOL }
,
152 "f32" => quote
! { &F32 }
,
153 "f64" => quote
! { &F64 }
,
154 "i16" => quote
! { &I16 }
,
155 "i32" => quote
! { &I32 }
,
156 "i64" => quote
! { &I64 }
,
157 "i8" => quote
! { &I8 }
,
158 "u16" => quote
! { &U16 }
,
159 "u32" => quote
! { &U32 }
,
160 "u64" => quote
! { &U64 }
,
161 "u128" => quote
! { &U128 }
,
162 "u8" => quote
! { &U8 }
,
163 "Ordering" => quote
! { &ORDERING }
,
164 "CpuidResult" => quote
! { &CPUID }
,
167 "int8x4_t" => quote
! { &I8X4 }
,
168 "int8x8_t" => quote
! { &I8X8 }
,
169 "int8x8x2_t" => quote
! { &I8X8X2 }
,
170 "int8x8x3_t" => quote
! { &I8X8X3 }
,
171 "int8x8x4_t" => quote
! { &I8X8X4 }
,
172 "int8x16x2_t" => quote
! { &I8X16X2 }
,
173 "int8x16x3_t" => quote
! { &I8X16X3 }
,
174 "int8x16x4_t" => quote
! { &I8X16X4 }
,
175 "int8x16_t" => quote
! { &I8X16 }
,
176 "int16x2_t" => quote
! { &I16X2 }
,
177 "int16x4_t" => quote
! { &I16X4 }
,
178 "int16x8_t" => quote
! { &I16X8 }
,
179 "int32x2_t" => quote
! { &I32X2 }
,
180 "int32x4_t" => quote
! { &I32X4 }
,
181 "int64x1_t" => quote
! { &I64X1 }
,
182 "int64x2_t" => quote
! { &I64X2 }
,
183 "uint8x8_t" => quote
! { &U8X8 }
,
184 "uint8x8x2_t" => quote
! { &U8X8X2 }
,
185 "uint8x16x2_t" => quote
! { &U8X16X2 }
,
186 "uint8x16x3_t" => quote
! { &U8X16X3 }
,
187 "uint8x16x4_t" => quote
! { &U8X16X4 }
,
188 "uint8x8x3_t" => quote
! { &U8X8X3 }
,
189 "uint8x8x4_t" => quote
! { &U8X8X4 }
,
190 "uint8x16_t" => quote
! { &U8X16 }
,
191 "uint16x4_t" => quote
! { &U16X4 }
,
192 "uint16x8_t" => quote
! { &U16X8 }
,
193 "uint32x2_t" => quote
! { &U32X2 }
,
194 "uint32x4_t" => quote
! { &U32X4 }
,
195 "uint64x1_t" => quote
! { &U64X1 }
,
196 "uint64x2_t" => quote
! { &U64X2 }
,
197 "float32x2_t" => quote
! { &F32X2 }
,
198 "float32x4_t" => quote
! { &F32X4 }
,
199 "float64x1_t" => quote
! { &F64X1 }
,
200 "float64x2_t" => quote
! { &F64X2 }
,
201 "poly8x8_t" => quote
! { &POLY8X8 }
,
202 "poly8x8x2_t" => quote
! { &POLY8X8X2 }
,
203 "poly8x8x3_t" => quote
! { &POLY8X8X3 }
,
204 "poly8x8x4_t" => quote
! { &POLY8X8X4 }
,
205 "poly8x16x2_t" => quote
! { &POLY8X16X2 }
,
206 "poly8x16x3_t" => quote
! { &POLY8X16X3 }
,
207 "poly8x16x4_t" => quote
! { &POLY8X16X4 }
,
208 "poly64_t" => quote
! { &P64 }
,
209 "poly64x1_t" => quote
! { &POLY64X1 }
,
210 "poly64x2_t" => quote
! { &POLY64X2 }
,
211 "poly8x16_t" => quote
! { &POLY8X16 }
,
212 "poly16x4_t" => quote
! { &POLY16X4 }
,
213 "poly16x8_t" => quote
! { &POLY16X8 }
,
214 "poly128_t" => quote
! { &P128 }
,
216 "v16i8" => quote
! { &v16i8 }
,
217 "v8i16" => quote
! { &v8i16 }
,
218 "v4i32" => quote
! { &v4i32 }
,
219 "v2i64" => quote
! { &v2i64 }
,
220 "v16u8" => quote
! { &v16u8 }
,
221 "v8u16" => quote
! { &v8u16 }
,
222 "v4u32" => quote
! { &v4u32 }
,
223 "v2u64" => quote
! { &v2u64 }
,
224 "v8f16" => quote
! { &v8f16 }
,
225 "v4f32" => quote
! { &v4f32 }
,
226 "v2f64" => quote
! { &v2f64 }
,
228 s
=> panic
!("unsupported type: \"{}\"", s
),
230 syn
::Type
::Ptr(syn
::TypePtr
{
235 | syn
::Type
::Reference(syn
::TypeReference
{
240 // Both pointers and references can have a mut token (*mut and &mut)
241 if mutability
.is_some() {
242 let tokens
= to_type(&elem
);
243 quote
! { &Type::MutPtr(#tokens) }
245 // If they don't (*const or &) then they are "const"
246 let tokens
= to_type(&elem
);
247 quote
! { &Type::ConstPtr(#tokens) }
251 syn
::Type
::Slice(_
) => panic
!("unsupported slice"),
252 syn
::Type
::Array(_
) => panic
!("unsupported array"),
253 syn
::Type
::Tuple(_
) => quote
! { &TUPLE }
,
254 syn
::Type
::Never(_
) => quote
! { &NEVER }
,
255 _
=> panic
!("unsupported type"),
259 fn extract_path_ident(path
: &syn
::Path
) -> syn
::Ident
{
260 if path
.leading_colon
.is_some() {
261 panic
!("unsupported leading colon in path")
263 if path
.segments
.len() != 1 {
264 panic
!("unsupported path that needs name resolution")
266 match path
.segments
.first().expect("segment not found").arguments
{
267 syn
::PathArguments
::None
=> {}
268 _
=> panic
!("unsupported path that has path arguments"),
272 .expect("segment not found")
277 fn walk(root
: &Path
, files
: &mut Vec
<(syn
::File
, String
)>) {
278 for file
in root
.read_dir().unwrap() {
279 let file
= file
.unwrap();
280 if file
.file_type().unwrap().is_dir() {
281 walk(&file
.path(), files
);
284 let path
= file
.path();
285 if path
.extension().and_then(std
::ffi
::OsStr
::to_str
) != Some("rs") {
289 if path
.file_name().and_then(std
::ffi
::OsStr
::to_str
) == Some("test.rs") {
293 let mut contents
= String
::new();
295 .unwrap_or_else(|_
| panic
!("can't open file at path: {}", path
.display()))
296 .read_to_string(&mut contents
)
297 .expect("failed to read file to string");
300 syn
::parse_str
::<syn
::File
>(&contents
).expect("failed to parse"),
301 path
.display().to_string(),
306 fn find_instrs(attrs
: &[syn
::Attribute
]) -> Vec
<String
> {
311 // A small custom parser to parse out the instruction in `assert_instr`.
313 // TODO: should probably just reuse `Invoc` from the `assert-instr-macro`
315 impl syn
::parse
::Parse
for AssertInstr
{
316 fn parse(content
: syn
::parse
::ParseStream
) -> syn
::Result
<Self> {
318 parenthesized
!(input
in content
);
319 let _
= input
.parse
::<syn
::Meta
>()?
;
320 let _
= input
.parse
::<Token
![,]>()?
;
321 let ident
= input
.parse
::<syn
::Ident
>()?
;
322 if ident
!= "assert_instr" {
323 return Err(input
.error("expected `assert_instr`"));
326 parenthesized
!(instrs
in input
);
328 let mut instr
= String
::new();
329 while !instrs
.is_empty() {
330 if let Ok(lit
) = instrs
.parse
::<syn
::LitStr
>() {
331 instr
.push_str(&lit
.value());
332 } else if let Ok(ident
) = instrs
.call(syn
::Ident
::parse_any
) {
333 instr
.push_str(&ident
.to_string());
334 } else if instrs
.parse
::<Token
![.]>().is_ok() {
336 } else if instrs
.parse
::<Token
![,]>().is_ok() {
337 // consume everything remaining
338 drop(instrs
.parse
::<proc_macro2
::TokenStream
>());
341 return Err(input
.error("failed to parse instruction"));
350 .filter(|a
| a
.path
.is_ident("cfg_attr"))
352 syn
::parse2
::<AssertInstr
>(a
.tokens
.clone())
359 fn find_target_feature(attrs
: &[syn
::Attribute
]) -> Option
<syn
::Lit
> {
363 if let Ok(a
) = a
.parse_meta() {
364 if let syn
::Meta
::List(i
) = a
{
365 if i
.path
.is_ident("target_feature") {
370 syn
::punctuated
::Punctuated
::new()
372 .filter_map(|nested
| match nested
{
373 syn
::NestedMeta
::Meta(m
) => Some(m
),
374 syn
::NestedMeta
::Lit(_
) => None
,
376 .find_map(|m
| match m
{
377 syn
::Meta
::NameValue(ref i
) if i
.path
.is_ident("enable") => Some(i
.clone().lit
),
382 fn find_required_const(attrs
: &[syn
::Attribute
]) -> Vec
<usize> {
386 if a
.path
.segments
[0].ident
== "rustc_args_required_const" {
387 syn
::parse
::<RustcArgsRequiredConst
>(a
.tokens
.clone().into())
397 struct RustcArgsRequiredConst
{
401 impl syn
::parse
::Parse
for RustcArgsRequiredConst
{
402 fn parse(input
: syn
::parse
::ParseStream
) -> syn
::Result
<Self> {
404 parenthesized
!(content
in input
);
406 syn
::punctuated
::Punctuated
::<syn
::LitInt
, Token
![,]>::parse_terminated(&content
)?
;
410 .map(|a
| a
.base10_parse
::<usize>())
411 .collect
::<syn
::Result
<_
>>()?
,