From f3414889afeee7fed1c20a9fc20d04c6abc2d2d9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 11 Apr 2019 12:35:22 -0700 Subject: [PATCH] Fix verifying instructions for MIPS Looks like MIPS is the first architecture to have verification which exercises the more flavorful forms of `assert_instr`, so the parsing code for `assert_instr` needed an update. Closes #713 --- .../stdarch/crates/stdsimd-verify/src/lib.rs | 76 ++++++++++++------- .../crates/stdsimd-verify/tests/mips.rs | 8 +- 2 files changed, 53 insertions(+), 31 deletions(-) diff --git a/library/stdarch/crates/stdsimd-verify/src/lib.rs b/library/stdarch/crates/stdsimd-verify/src/lib.rs index 125d8ced4d60..284b35dc13b3 100644 --- a/library/stdarch/crates/stdsimd-verify/src/lib.rs +++ b/library/stdarch/crates/stdsimd-verify/src/lib.rs @@ -5,11 +5,12 @@ extern crate quote; #[macro_use] extern crate syn; +use proc_macro::TokenStream; +use proc_macro2::Span; use std::fs::File; use std::io::Read; use std::path::Path; - -use proc_macro::TokenStream; +use syn::ext::IdentExt; #[proc_macro] pub fn x86_functions(input: TokenStream) -> TokenStream { @@ -91,7 +92,7 @@ fn functions(input: TokenStream, dirs: &[&str]) -> TokenStream { arguments: &[#(#arguments),*], ret: #ret, target_feature: #target_feature, - instrs: &[#(stringify!(#instrs)),*], + instrs: &[#(#instrs),*], file: stringify!(#path), required_const: &[#(#required_const),*], } @@ -263,35 +264,54 @@ fn walk(root: &Path, files: &mut Vec<(syn::File, String)>) { } } -fn find_instrs(attrs: &[syn::Attribute]) -> Vec { - attrs +fn find_instrs(attrs: &[syn::Attribute]) -> Vec { + return attrs .iter() - .filter_map(|a| a.interpret_meta()) - .filter_map(|a| match a { - syn::Meta::List(i) => { - if i.ident == "cfg_attr" { - i.nested.into_iter().nth(1) + .filter(|a| a.path == syn::Ident::new("cfg_attr", Span::call_site()).into()) + .filter_map(|a| syn::parse2::(a.tts.clone()).ok()) + .map(|a| a.instr) + .collect(); + + 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::parse::Result { + let input; + parenthesized!(input in content); + drop(input.parse::()?); + drop(input.parse::()?); + let ident = input.parse::()?; + 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::() { + 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::().is_ok() { + instr.push_str("."); + } else if instrs.parse::().is_ok() { + // consume everything remaining + drop(instrs.parse::()); + break; } else { - None + return Err(input.error("failed to parse instruction")); } } - _ => None, - }) - .filter_map(|nested| match nested { - syn::NestedMeta::Meta(syn::Meta::List(i)) => { - if i.ident == "assert_instr" { - i.nested.into_iter().next() - } else { - None - } - } - _ => None, - }) - .filter_map(|nested| match nested { - syn::NestedMeta::Meta(syn::Meta::Word(i)) => Some(i), - _ => None, - }) - .collect() + Ok(AssertInstr { instr }) + } + } } fn find_target_feature(attrs: &[syn::Attribute]) -> Option { diff --git a/library/stdarch/crates/stdsimd-verify/tests/mips.rs b/library/stdarch/crates/stdsimd-verify/tests/mips.rs index 382669b4af5d..17ab8a0720d3 100644 --- a/library/stdarch/crates/stdsimd-verify/tests/mips.rs +++ b/library/stdarch/crates/stdsimd-verify/tests/mips.rs @@ -313,16 +313,18 @@ fn matches(rust: &Function, mips: &MsaIntrinsic) -> Result<(), String> { bail!("wrong target_feature"); } - /* FIXME: if !rust.instrs.is_empty() { - if rust.instrs[0] != mips.instruction { + // Normalize slightly to get rid of assembler differences + let actual = rust.instrs[0].replace(".", "_"); + let expected = mips.instruction.replace(".", "_"); + if actual != expected { bail!("wrong instruction: \"{}\" != \"{}\"", rust.instrs[0], mips.instruction); } } else { bail!( "missing assert_instr for \"{}\" (should be \"{}\")", mips.id, mips.instruction); - }*/ + } Ok(()) }