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
This commit is contained in:
Alex Crichton 2019-04-11 12:35:22 -07:00 committed by gnzlbg
parent 3bfbff7c0c
commit f3414889af
2 changed files with 53 additions and 31 deletions

View file

@ -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<syn::Ident> {
attrs
fn find_instrs(attrs: &[syn::Attribute]) -> Vec<String> {
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::<AssertInstr>(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<Self> {
let input;
parenthesized!(input in content);
drop(input.parse::<syn::Meta>()?);
drop(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_str(".");
} else if instrs.parse::<Token![,]>().is_ok() {
// consume everything remaining
drop(instrs.parse::<proc_macro2::TokenStream>());
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<syn::Lit> {

View file

@ -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(())
}