diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /library/stdarch/crates/stdarch-verify/src/lib.rs | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | library/stdarch/crates/stdarch-verify/src/lib.rs | 525 |
1 files changed, 525 insertions, 0 deletions
diff --git a/library/stdarch/crates/stdarch-verify/src/lib.rs b/library/stdarch/crates/stdarch-verify/src/lib.rs new file mode 100644 index 000000000..22108d26a --- /dev/null +++ b/library/stdarch/crates/stdarch-verify/src/lib.rs @@ -0,0 +1,525 @@ +#![deny(rust_2018_idioms)] +#[macro_use] +extern crate quote; +#[macro_use] +extern crate syn; + +use proc_macro::TokenStream; +use std::{fs::File, io::Read, path::Path}; +use syn::ext::IdentExt; + +#[proc_macro] +pub fn x86_functions(input: TokenStream) -> TokenStream { + functions(input, &["core_arch/src/x86", "core_arch/src/x86_64"]) +} + +#[proc_macro] +pub fn arm_functions(input: TokenStream) -> TokenStream { + functions( + input, + &[ + "core_arch/src/arm", + "core_arch/src/aarch64", + "core_arch/src/arm_shared/neon", + ], + ) +} + +#[proc_macro] +pub fn mips_functions(input: TokenStream) -> TokenStream { + functions(input, &["core_arch/src/mips"]) +} + +fn functions(input: TokenStream, dirs: &[&str]) -> TokenStream { + let dir = Path::new(env!("CARGO_MANIFEST_DIR")); + let root = dir.parent().expect("root-dir not found"); + + let mut files = Vec::new(); + for dir in dirs { + walk(&root.join(dir), &mut files); + } + assert!(!files.is_empty()); + + let mut functions = Vec::new(); + for &mut (ref mut file, ref path) in &mut files { + for mut item in file.items.drain(..) { + match item { + syn::Item::Fn(f) => functions.push((f, path)), + syn::Item::Mod(ref mut m) => { + if let Some(ref mut m) = m.content { + for i in m.1.drain(..) { + if let syn::Item::Fn(f) = i { + functions.push((f, path)) + } + } + } + } + _ => (), + } + } + } + assert!(!functions.is_empty()); + + let mut tests = std::collections::HashSet::<String>::new(); + for f in &functions { + let id = format!("{}", f.0.sig.ident); + if id.starts_with("test_") { + tests.insert(id); + } + } + assert!(!tests.is_empty()); + + functions.retain(|&(ref f, _)| { + if let syn::Visibility::Public(_) = f.vis { + if f.sig.unsafety.is_some() { + return true; + } + } + false + }); + assert!(!functions.is_empty()); + + let input = proc_macro2::TokenStream::from(input); + + let functions = functions + .iter() + .map(|&(ref f, path)| { + let name = &f.sig.ident; + // println!("{}", name); + let mut arguments = Vec::new(); + let mut const_arguments = Vec::new(); + for input in f.sig.inputs.iter() { + let ty = match *input { + syn::FnArg::Typed(ref c) => &c.ty, + _ => panic!("invalid argument on {}", name), + }; + arguments.push(to_type(ty)); + } + for generic in f.sig.generics.params.iter() { + let ty = match *generic { + syn::GenericParam::Const(ref c) => &c.ty, + _ => panic!("invalid generic argument on {}", name), + }; + const_arguments.push(to_type(ty)); + } + let ret = match f.sig.output { + syn::ReturnType::Default => quote! { None }, + syn::ReturnType::Type(_, ref t) => { + let ty = to_type(t); + quote! { Some(#ty) } + } + }; + let instrs = find_instrs(&f.attrs); + let target_feature = if let Some(i) = find_target_feature(&f.attrs) { + quote! { Some(#i) } + } else { + quote! { None } + }; + + let required_const = find_required_const("rustc_args_required_const", &f.attrs); + let mut legacy_const_generics = + find_required_const("rustc_legacy_const_generics", &f.attrs); + if !required_const.is_empty() && !legacy_const_generics.is_empty() { + panic!( + "Can't have both #[rustc_args_required_const] and \ + #[rustc_legacy_const_generics]" + ); + } + + // The list of required consts, used to verify the arguments, comes from either the + // `rustc_args_required_const` or the `rustc_legacy_const_generics` attribute. + let required_const = if required_const.is_empty() { + legacy_const_generics.clone() + } else { + required_const + }; + + legacy_const_generics.sort(); + for (idx, ty) in legacy_const_generics + .into_iter() + .zip(const_arguments.into_iter()) + { + arguments.insert(idx, ty); + } + + // strip leading underscore from fn name when building a test + // _mm_foo -> mm_foo such that the test name is test_mm_foo. + let test_name_string = format!("{}", name); + let mut test_name_id = test_name_string.as_str(); + while test_name_id.starts_with('_') { + test_name_id = &test_name_id[1..]; + } + let has_test = tests.contains(&format!("test_{}", test_name_id)); + + quote! { + Function { + name: stringify!(#name), + arguments: &[#(#arguments),*], + ret: #ret, + target_feature: #target_feature, + instrs: &[#(#instrs),*], + file: stringify!(#path), + required_const: &[#(#required_const),*], + has_test: #has_test, + } + } + }) + .collect::<Vec<_>>(); + + let ret = quote! { #input: &[Function] = &[#(#functions),*]; }; + // println!("{}", ret); + ret.into() +} + +fn to_type(t: &syn::Type) -> proc_macro2::TokenStream { + match *t { + syn::Type::Path(ref p) => match extract_path_ident(&p.path).to_string().as_ref() { + // x86 ... + "__m128" => quote! { &M128 }, + "__m128bh" => quote! { &M128BH }, + "__m128d" => quote! { &M128D }, + "__m128i" => quote! { &M128I }, + "__m256" => quote! { &M256 }, + "__m256bh" => quote! { &M256BH }, + "__m256d" => quote! { &M256D }, + "__m256i" => quote! { &M256I }, + "__m512" => quote! { &M512 }, + "__m512bh" => quote! { &M512BH }, + "__m512d" => quote! { &M512D }, + "__m512i" => quote! { &M512I }, + "__mmask8" => quote! { &MMASK8 }, + "__mmask16" => quote! { &MMASK16 }, + "__mmask32" => quote! { &MMASK32 }, + "__mmask64" => quote! { &MMASK64 }, + "_MM_CMPINT_ENUM" => quote! { &MM_CMPINT_ENUM }, + "_MM_MANTISSA_NORM_ENUM" => quote! { &MM_MANTISSA_NORM_ENUM }, + "_MM_MANTISSA_SIGN_ENUM" => quote! { &MM_MANTISSA_SIGN_ENUM }, + "_MM_PERM_ENUM" => quote! { &MM_PERM_ENUM }, + "__m64" => quote! { &M64 }, + "bool" => quote! { &BOOL }, + "f32" => quote! { &F32 }, + "f64" => quote! { &F64 }, + "i16" => quote! { &I16 }, + "i32" => quote! { &I32 }, + "i64" => quote! { &I64 }, + "i8" => quote! { &I8 }, + "u16" => quote! { &U16 }, + "u32" => quote! { &U32 }, + "u64" => quote! { &U64 }, + "u128" => quote! { &U128 }, + "u8" => quote! { &U8 }, + "p8" => quote! { &P8 }, + "p16" => quote! { &P16 }, + "Ordering" => quote! { &ORDERING }, + "CpuidResult" => quote! { &CPUID }, + + // arm ... + "int8x4_t" => quote! { &I8X4 }, + "int8x8_t" => quote! { &I8X8 }, + "int8x8x2_t" => quote! { &I8X8X2 }, + "int8x8x3_t" => quote! { &I8X8X3 }, + "int8x8x4_t" => quote! { &I8X8X4 }, + "int8x16x2_t" => quote! { &I8X16X2 }, + "int8x16x3_t" => quote! { &I8X16X3 }, + "int8x16x4_t" => quote! { &I8X16X4 }, + "int8x16_t" => quote! { &I8X16 }, + "int16x2_t" => quote! { &I16X2 }, + "int16x4_t" => quote! { &I16X4 }, + "int16x4x2_t" => quote! { &I16X4X2 }, + "int16x4x3_t" => quote! { &I16X4X3 }, + "int16x4x4_t" => quote! { &I16X4X4 }, + "int16x8_t" => quote! { &I16X8 }, + "int16x8x2_t" => quote! { &I16X8X2 }, + "int16x8x3_t" => quote! { &I16X8X3 }, + "int16x8x4_t" => quote! { &I16X8X4 }, + "int32x2_t" => quote! { &I32X2 }, + "int32x2x2_t" => quote! { &I32X2X2 }, + "int32x2x3_t" => quote! { &I32X2X3 }, + "int32x2x4_t" => quote! { &I32X2X4 }, + "int32x4_t" => quote! { &I32X4 }, + "int32x4x2_t" => quote! { &I32X4X2 }, + "int32x4x3_t" => quote! { &I32X4X3 }, + "int32x4x4_t" => quote! { &I32X4X4 }, + "int64x1_t" => quote! { &I64X1 }, + "int64x1x2_t" => quote! { &I64X1X2 }, + "int64x1x3_t" => quote! { &I64X1X3 }, + "int64x1x4_t" => quote! { &I64X1X4 }, + "int64x2_t" => quote! { &I64X2 }, + "int64x2x2_t" => quote! { &I64X2X2 }, + "int64x2x3_t" => quote! { &I64X2X3 }, + "int64x2x4_t" => quote! { &I64X2X4 }, + "uint8x8_t" => quote! { &U8X8 }, + "uint8x4_t" => quote! { &U8X4 }, + "uint8x8x2_t" => quote! { &U8X8X2 }, + "uint8x16x2_t" => quote! { &U8X16X2 }, + "uint8x16x3_t" => quote! { &U8X16X3 }, + "uint8x16x4_t" => quote! { &U8X16X4 }, + "uint8x8x3_t" => quote! { &U8X8X3 }, + "uint8x8x4_t" => quote! { &U8X8X4 }, + "uint8x16_t" => quote! { &U8X16 }, + "uint16x4_t" => quote! { &U16X4 }, + "uint16x4x2_t" => quote! { &U16X4X2 }, + "uint16x4x3_t" => quote! { &U16X4X3 }, + "uint16x4x4_t" => quote! { &U16X4X4 }, + "uint16x8_t" => quote! { &U16X8 }, + "uint16x8x2_t" => quote! { &U16X8X2 }, + "uint16x8x3_t" => quote! { &U16X8X3 }, + "uint16x8x4_t" => quote! { &U16X8X4 }, + "uint32x2_t" => quote! { &U32X2 }, + "uint32x2x2_t" => quote! { &U32X2X2 }, + "uint32x2x3_t" => quote! { &U32X2X3 }, + "uint32x2x4_t" => quote! { &U32X2X4 }, + "uint32x4_t" => quote! { &U32X4 }, + "uint32x4x2_t" => quote! { &U32X4X2 }, + "uint32x4x3_t" => quote! { &U32X4X3 }, + "uint32x4x4_t" => quote! { &U32X4X4 }, + "uint64x1_t" => quote! { &U64X1 }, + "uint64x1x2_t" => quote! { &U64X1X2 }, + "uint64x1x3_t" => quote! { &U64X1X3 }, + "uint64x1x4_t" => quote! { &U64X1X4 }, + "uint64x2_t" => quote! { &U64X2 }, + "uint64x2x2_t" => quote! { &U64X2X2 }, + "uint64x2x3_t" => quote! { &U64X2X3 }, + "uint64x2x4_t" => quote! { &U64X2X4 }, + "float32x2_t" => quote! { &F32X2 }, + "float32x2x2_t" => quote! { &F32X2X2 }, + "float32x2x3_t" => quote! { &F32X2X3 }, + "float32x2x4_t" => quote! { &F32X2X4 }, + "float32x4_t" => quote! { &F32X4 }, + "float32x4x2_t" => quote! { &F32X4X2 }, + "float32x4x3_t" => quote! { &F32X4X3 }, + "float32x4x4_t" => quote! { &F32X4X4 }, + "float64x1_t" => quote! { &F64X1 }, + "float64x1x2_t" => quote! { &F64X1X2 }, + "float64x1x3_t" => quote! { &F64X1X3 }, + "float64x1x4_t" => quote! { &F64X1X4 }, + "float64x2_t" => quote! { &F64X2 }, + "float64x2x2_t" => quote! { &F64X2X2 }, + "float64x2x3_t" => quote! { &F64X2X3 }, + "float64x2x4_t" => quote! { &F64X2X4 }, + "poly8x8_t" => quote! { &POLY8X8 }, + "poly8x8x2_t" => quote! { &POLY8X8X2 }, + "poly8x8x3_t" => quote! { &POLY8X8X3 }, + "poly8x8x4_t" => quote! { &POLY8X8X4 }, + "poly8x16x2_t" => quote! { &POLY8X16X2 }, + "poly8x16x3_t" => quote! { &POLY8X16X3 }, + "poly8x16x4_t" => quote! { &POLY8X16X4 }, + "p64" => quote! { &P64 }, + "poly64x1_t" => quote! { &POLY64X1 }, + "poly64x2_t" => quote! { &POLY64X2 }, + "poly8x16_t" => quote! { &POLY8X16 }, + "poly16x4_t" => quote! { &POLY16X4 }, + "poly16x4x2_t" => quote! { &P16X4X2 }, + "poly16x4x3_t" => quote! { &P16X4X3 }, + "poly16x4x4_t" => quote! { &P16X4X4 }, + "poly16x8_t" => quote! { &POLY16X8 }, + "poly16x8x2_t" => quote! { &P16X8X2 }, + "poly16x8x3_t" => quote! { &P16X8X3 }, + "poly16x8x4_t" => quote! { &P16X8X4 }, + "poly64x1x2_t" => quote! { &P64X1X2 }, + "poly64x1x3_t" => quote! { &P64X1X3 }, + "poly64x1x4_t" => quote! { &P64X1X4 }, + "poly64x2x2_t" => quote! { &P64X2X2 }, + "poly64x2x3_t" => quote! { &P64X2X3 }, + "poly64x2x4_t" => quote! { &P64X2X4 }, + "p128" => quote! { &P128 }, + + "v16i8" => quote! { &v16i8 }, + "v8i16" => quote! { &v8i16 }, + "v4i32" => quote! { &v4i32 }, + "v2i64" => quote! { &v2i64 }, + "v16u8" => quote! { &v16u8 }, + "v8u16" => quote! { &v8u16 }, + "v4u32" => quote! { &v4u32 }, + "v2u64" => quote! { &v2u64 }, + "v8f16" => quote! { &v8f16 }, + "v4f32" => quote! { &v4f32 }, + "v2f64" => quote! { &v2f64 }, + + s => panic!("unsupported type: \"{}\"", s), + }, + syn::Type::Ptr(syn::TypePtr { + ref elem, + ref mutability, + .. + }) + | syn::Type::Reference(syn::TypeReference { + ref elem, + ref mutability, + .. + }) => { + // Both pointers and references can have a mut token (*mut and &mut) + if mutability.is_some() { + let tokens = to_type(&elem); + quote! { &Type::MutPtr(#tokens) } + } else { + // If they don't (*const or &) then they are "const" + let tokens = to_type(&elem); + quote! { &Type::ConstPtr(#tokens) } + } + } + + syn::Type::Slice(_) => panic!("unsupported slice"), + syn::Type::Array(_) => panic!("unsupported array"), + syn::Type::Tuple(_) => quote! { &TUPLE }, + syn::Type::Never(_) => quote! { &NEVER }, + _ => panic!("unsupported type"), + } +} + +fn extract_path_ident(path: &syn::Path) -> syn::Ident { + if path.leading_colon.is_some() { + panic!("unsupported leading colon in path") + } + if path.segments.len() != 1 { + panic!("unsupported path that needs name resolution") + } + match path.segments.first().expect("segment not found").arguments { + syn::PathArguments::None => {} + _ => panic!("unsupported path that has path arguments"), + } + path.segments + .first() + .expect("segment not found") + .ident + .clone() +} + +fn walk(root: &Path, files: &mut Vec<(syn::File, String)>) { + for file in root.read_dir().unwrap() { + let file = file.unwrap(); + if file.file_type().unwrap().is_dir() { + walk(&file.path(), files); + continue; + } + let path = file.path(); + if path.extension().and_then(std::ffi::OsStr::to_str) != Some("rs") { + continue; + } + + if path.file_name().and_then(std::ffi::OsStr::to_str) == Some("test.rs") { + continue; + } + + let mut contents = String::new(); + File::open(&path) + .unwrap_or_else(|_| panic!("can't open file at path: {}", path.display())) + .read_to_string(&mut contents) + .expect("failed to read file to string"); + + files.push(( + syn::parse_str::<syn::File>(&contents).expect("failed to parse"), + path.display().to_string(), + )); + } +} + +fn find_instrs(attrs: &[syn::Attribute]) -> Vec<String> { + struct AssertInstr { + instr: String, + } + + // A small custom parser to parse out the instruction in `assert_instr`. + // + // TODO: should probably just reuse `Invoc` from the `assert-instr-macro` + // crate. + impl syn::parse::Parse for AssertInstr { + fn parse(content: syn::parse::ParseStream<'_>) -> syn::Result<Self> { + let input; + parenthesized!(input in content); + let _ = input.parse::<syn::Meta>()?; + let _ = input.parse::<Token![,]>()?; + let ident = input.parse::<syn::Ident>()?; + if ident != "assert_instr" { + return Err(input.error("expected `assert_instr`")); + } + let instrs; + parenthesized!(instrs in input); + + let mut instr = String::new(); + while !instrs.is_empty() { + if let Ok(lit) = instrs.parse::<syn::LitStr>() { + instr.push_str(&lit.value()); + } else if let Ok(ident) = instrs.call(syn::Ident::parse_any) { + instr.push_str(&ident.to_string()); + } else if instrs.parse::<Token![.]>().is_ok() { + instr.push('.'); + } else if instrs.parse::<Token![,]>().is_ok() { + // consume everything remaining + drop(instrs.parse::<proc_macro2::TokenStream>()); + break; + } else { + return Err(input.error("failed to parse instruction")); + } + } + Ok(Self { instr }) + } + } + + attrs + .iter() + .filter(|a| a.path.is_ident("cfg_attr")) + .filter_map(|a| { + syn::parse2::<AssertInstr>(a.tokens.clone()) + .ok() + .map(|a| a.instr) + }) + .collect() +} + +fn find_target_feature(attrs: &[syn::Attribute]) -> Option<syn::Lit> { + attrs + .iter() + .flat_map(|a| { + if let Ok(a) = a.parse_meta() { + if let syn::Meta::List(i) = a { + if i.path.is_ident("target_feature") { + return i.nested; + } + } + } + syn::punctuated::Punctuated::new() + }) + .filter_map(|nested| match nested { + syn::NestedMeta::Meta(m) => Some(m), + syn::NestedMeta::Lit(_) => None, + }) + .find_map(|m| match m { + syn::Meta::NameValue(ref i) if i.path.is_ident("enable") => Some(i.clone().lit), + _ => None, + }) +} + +fn find_required_const(name: &str, attrs: &[syn::Attribute]) -> Vec<usize> { + attrs + .iter() + .flat_map(|a| { + if a.path.segments[0].ident == name { + syn::parse::<RustcArgsRequiredConst>(a.tokens.clone().into()) + .unwrap() + .args + } else { + Vec::new() + } + }) + .collect() +} + +struct RustcArgsRequiredConst { + args: Vec<usize>, +} + +impl syn::parse::Parse for RustcArgsRequiredConst { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { + let content; + parenthesized!(content in input); + let list = + syn::punctuated::Punctuated::<syn::LitInt, Token![,]>::parse_terminated(&content)?; + Ok(Self { + args: list + .into_iter() + .map(|a| a.base10_parse::<usize>()) + .collect::<syn::Result<_>>()?, + }) + } +} |