Use arm_intrinsics.json in stdarch-verify

This commit is contained in:
Adam Gemmell 2023-04-20 17:59:16 +01:00 committed by Amanieu d'Antras
parent 0125fa17c8
commit ba671a06c1
5 changed files with 18867 additions and 93704 deletions

View file

@ -16,4 +16,4 @@ test = false
[dev-dependencies]
serde = { version = "1.0", features = ['derive'] }
serde-xml-rs = "0.3"
html5ever = "0.23.0"
serde_json = "1.0.96"

File diff suppressed because one or more lines are too long

View file

@ -69,7 +69,7 @@ fn functions(input: TokenStream, dirs: &[&str]) -> TokenStream {
}
assert!(!tests.is_empty());
functions.retain(|&(ref f, _)| {
functions.retain(|(f, _)| {
if let syn::Visibility::Public(_) = f.vis {
if f.sig.unsafety.is_some() {
return true;
@ -350,11 +350,11 @@ fn to_type(t: &syn::Type) -> proc_macro2::TokenStream {
}) => {
// Both pointers and references can have a mut token (*mut and &mut)
if mutability.is_some() {
let tokens = to_type(&elem);
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);
let tokens = to_type(elem);
quote! { &Type::ConstPtr(#tokens) }
}
}
@ -471,11 +471,9 @@ 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;
}
if let Ok(syn::Meta::List(i)) = a.parse_meta() {
if i.path.is_ident("target_feature") {
return i.nested;
}
}
syn::punctuated::Punctuated::new()

View file

@ -1,15 +1,8 @@
#![allow(bad_style)]
#![allow(unused)]
use std::{collections::HashMap, rc::Rc};
use std::collections::HashMap;
use html5ever::{
driver::ParseOpts,
parse_document,
rcdom::{Node, NodeData, RcDom},
tendril::TendrilSink,
tree_builder::TreeBuilderOpts,
};
use serde::Deserialize;
#[allow(unused)]
struct Function {
name: &'static str,
arguments: &'static [&'static Type],
@ -177,28 +170,13 @@ macro_rules! bail {
#[test]
fn verify_all_signatures() {
// This is a giant HTML blob downloaded from
// https://developer.arm.com/technologies/neon/intrinsics which contains all
// NEON intrinsics at least. We do manual HTML parsing below.
let html = include_bytes!("../arm-intrinsics.html");
let mut html = &html[..];
let opts = ParseOpts {
tree_builder: TreeBuilderOpts {
drop_doctype: true,
..Default::default()
},
..Default::default()
};
let dom = parse_document(RcDom::default(), opts)
.from_utf8()
.read_from(&mut html)
.unwrap();
let accordion = find_accordion(&dom.document).unwrap();
let map = parse_intrinsics(&accordion);
// Reference: https://developer.arm.com/architectures/instruction-sets/intrinsics
let json = include_bytes!("../../../intrinsics_data/arm_intrinsics.json");
let intrinsics: Vec<JsonIntrinsic> = serde_json::from_slice(json).unwrap();
let map = parse_intrinsics(intrinsics);
let mut all_valid = true;
'outer: for rust in FUNCTIONS {
for rust in FUNCTIONS {
if !rust.has_test {
let skip = [
"vaddq_s64",
@ -457,143 +435,6 @@ fn verify_all_signatures() {
"vreinterpretq_p64_p128",
"vreinterpretq_p128_p64",
"vreinterpretq_f32_p128",
"vqrdmlahh_s16",
"vqrdmlahs_s32",
"vqrdmlahh_lane_s16",
"vqrdmlahh_laneq_s16",
"vqrdmlahs_lane_s32",
"vqrdmlahs_laneq_s32",
"vqrdmlah_s16",
"vqrdmlah_s32",
"vqrdmlahq_s16",
"vqrdmlahq_s32",
"vqrdmlah_lane_s16",
"vqrdmlah_laneq_s16",
"vqrdmlahq_lane_s16",
"vqrdmlahq_laneq_s16",
"vqrdmlah_lane_s32",
"vqrdmlah_laneq_s32",
"vqrdmlahq_lane_s32",
"vqrdmlahq_laneq_s32",
"vqrdmlshh_s16",
"vqrdmlshs_s32",
"vqrdmlshh_lane_s16",
"vqrdmlshh_laneq_s16",
"vqrdmlshs_lane_s32",
"vqrdmlshs_laneq_s32",
"vqrdmlsh_s16",
"vqrdmlshq_s16",
"vqrdmlsh_s32",
"vqrdmlshq_s32",
"vqrdmlsh_lane_s16",
"vqrdmlsh_laneq_s16",
"vqrdmlshq_lane_s16",
"vqrdmlshq_laneq_s16",
"vqrdmlsh_lane_s32",
"vqrdmlsh_laneq_s32",
"vqrdmlshq_lane_s32",
"vqrdmlshq_laneq_s32",
"vcadd_rot270_f32",
"vcadd_rot90_f32",
"vcaddq_rot270_f32",
"vcaddq_rot270_f64",
"vcaddq_rot90_f32",
"vcaddq_rot90_f64",
"vcmla_f32",
"vcmlaq_f32",
"vcmlaq_f64",
"vcmla_rot90_f32",
"vcmlaq_rot90_f32",
"vcmlaq_rot90_f64",
"vcmla_rot180_f32",
"vcmlaq_rot180_f32",
"vcmlaq_rot180_f64",
"vcmla_rot270_f32",
"vcmlaq_rot270_f32",
"vcmlaq_rot270_f64",
"vcmla_lane_f32",
"vcmla_laneq_f32",
"vcmlaq_lane_f32",
"vcmlaq_laneq_f32",
"vcmla_rot90_lane_f32",
"vcmla_rot90_laneq_f32",
"vcmlaq_rot90_lane_f32",
"vcmlaq_rot90_laneq_f32",
"vcmla_rot180_lane_f32",
"vcmla_rot180_laneq_f32",
"vcmlaq_rot180_lane_f32",
"vcmlaq_rot180_laneq_f32",
"vcmla_rot270_lane_f32",
"vcmla_rot270_laneq_f32",
"vcmlaq_rot270_lane_f32",
"vcmlaq_rot270_laneq_f32",
"vdot_s32",
"vdot_u32",
"vdotq_s32",
"vdotq_u32",
"vdot_lane_s32",
"vdot_laneq_s32",
"vdotq_lane_s32",
"vdotq_laneq_s32",
"vdot_lane_u32",
"vdot_laneq_u32",
"vdotq_lane_u32",
"vdotq_laneq_u32",
"vbcaxq_s8",
"vbcaxq_s16",
"vbcaxq_s32",
"vbcaxq_s64",
"vbcaxq_u8",
"vbcaxq_u16",
"vbcaxq_u32",
"vbcaxq_u64",
"veor3q_s8",
"veor3q_s16",
"veor3q_s32",
"veor3q_s64",
"veor3q_u8",
"veor3q_u16",
"veor3q_u32",
"veor3q_u64",
"vadd_p8",
"vadd_p16",
"vadd_p64",
"vaddq_p8",
"vaddq_p16",
"vaddq_p64",
"vaddq_p128",
"vsm4ekeyq_u32",
"vsm4eq_u32",
"vmmlaq_s32",
"vmmlaq_u32",
"vusmmlaq_s32",
"vsm3partw1q_u32",
"vsm3partw2q_u32",
"vsm3ss1q_u32",
"vsm3tt1aq_u32",
"vsm3tt1bq_u32",
"vsm3tt2aq_u32",
"vsm3tt2bq_u32",
"vrax1q_u64",
"vxarq_u64",
"vsha512hq_u64",
"vsha512h2q_u64",
"vsha512su0q_u64",
"vsha512su1q_u64",
"vrnd32x_f32",
"vrnd32xq_f32",
"vrnd32z_f32",
"vrnd32zq_f32",
"vrnd64x_f32",
"vrnd64xq_f32",
"vrnd64z_f32",
"vrnd64zq_f32",
"vcls_u8",
"vcls_u16",
"vcls_u32",
"vclsq_u8",
"vclsq_u16",
"vclsq_u32",
"vtst_p16",
"vtstq_p16",
"__dbg",
@ -608,7 +449,6 @@ fn verify_all_signatures() {
// reference for them, need to figure out where though!
if !rust.file.ends_with("dsp.rs\"")
&& !rust.file.ends_with("simd32.rs\"")
&& !rust.file.ends_with("cmsis.rs\"")
&& !rust.file.ends_with("v6.rs\"")
&& !rust.file.ends_with("v7.rs\"")
&& !rust.file.ends_with("v8.rs\"")
@ -647,7 +487,7 @@ fn matches(rust: &Function, arm: &Intrinsic) -> Result<(), String> {
let iter = rust.arguments.iter().zip(&arm.arguments).enumerate();
for (i, (rust_ty, (arm, arm_const))) in iter {
if *rust_ty != arm {
bail!("mismatched arguments")
bail!("mismatched arguments: {rust_ty:?} != {arm:?}")
}
if *arm_const {
nconst += 1;
@ -667,7 +507,11 @@ fn matches(rust: &Function, arm: &Intrinsic) -> Result<(), String> {
arm.instruction
);
} else if false
/* not super reliable, but can be used to manually check */
// TODO: This instruction checking logic needs work to handle multiple instructions and to only
// look at aarch64 insructions.
// The ACLE's listed instructions are a guideline only and compilers have the freedom to use
// different instructions in dfferent cases which makes this an unreliable testing method. It
// is of questionable value given the intrinsic test tool.
{
for instr in rust.instrs {
if arm.instruction.starts_with(instr) {
@ -695,24 +539,6 @@ fn matches(rust: &Function, arm: &Intrinsic) -> Result<(), String> {
Ok(())
}
fn find_accordion(node: &Rc<Node>) -> Option<Rc<Node>> {
if let NodeData::Element { attrs, .. } = &node.data {
for attr in attrs.borrow().iter() {
if attr.name.local.eq_str_ignore_ascii_case("class")
&& attr.value.to_string() == "intrinsic-accordion"
{
return Some(node.clone());
}
}
}
node.children
.borrow()
.iter()
.filter_map(|node| find_accordion(node))
.next()
}
#[derive(PartialEq)]
struct Intrinsic {
name: String,
@ -721,121 +547,69 @@ struct Intrinsic {
instruction: String,
}
fn parse_intrinsics(node: &Rc<Node>) -> HashMap<String, Intrinsic> {
// These structures are similar to those in json_parser.rs in intrinsics-test
#[derive(Deserialize, Debug)]
struct JsonIntrinsic {
name: String,
arguments: Vec<String>,
return_type: ReturnType,
#[serde(default)]
instructions: Vec<Vec<String>>,
}
#[derive(Deserialize, Debug)]
struct ReturnType {
value: String,
}
fn parse_intrinsics(intrinsics: Vec<JsonIntrinsic>) -> HashMap<String, Intrinsic> {
let mut ret = HashMap::new();
for child in node.children.borrow().iter() {
if let NodeData::Element { .. } = child.data {
let f = parse_intrinsic(child);
ret.insert(f.name.clone(), f);
}
for intr in intrinsics.into_iter() {
let f = parse_intrinsic(intr);
ret.insert(f.name.clone(), f);
}
ret
}
fn parse_intrinsic(node: &Rc<Node>) -> Intrinsic {
// <div class='intrinsic'>
// <input>...</input>
// <label for=$name>
// <div>
// $signature...
// <article>
// ...
let children = node.children.borrow();
let mut children = children
.iter()
.filter(|node| matches!(node.data, NodeData::Element { .. }));
let _input = children.next().expect("no <input>");
let label = children.next().expect("no <label>");
let article = children.next().expect("no <article>");
assert!(children.next().is_none());
// Find `for="..."` in `<label>`
let name = match &label.data {
NodeData::Element { attrs, .. } => attrs
.borrow()
.iter()
.filter(|attr| attr.name.local.eq_str_ignore_ascii_case("for"))
.map(|attr| attr.value.to_string())
.next()
.expect("no `for` attribute"),
_ => panic!(),
fn parse_intrinsic(mut intr: JsonIntrinsic) -> Intrinsic {
let name = intr.name;
let ret = if intr.return_type.value == "void" {
None
} else {
Some(parse_ty(&intr.return_type.value))
};
// Find contents of inner `<div>` in `<label>`
let label_children = label.children.borrow();
let mut label_children = label_children
.iter()
.filter(|node| matches!(node.data, NodeData::Element { .. }));
let label_div = label_children.next().expect("no <div> in <label>");
assert!(label_children.next().is_none());
let text = label_div.children.borrow();
let mut text = text.iter().filter_map(|node| match &node.data {
NodeData::Text { contents } => Some(contents.borrow().to_string()),
_ => None,
});
let ret = text.next().unwrap();
let ret = ret.trim();
let args = text.next().unwrap();
let args = args.trim();
assert!(text.next().is_none());
// This ignores multiple instructions and different optional sequences for now to mimic
// the old HTML scraping behaviour
let instruction = intr.instructions.swap_remove(0).swap_remove(0);
// Find the instruction within the article
let article_children = article.children.borrow();
let mut article_children = article_children
let arguments = intr
.arguments
.iter()
.filter(|node| matches!(node.data, NodeData::Element { .. }));
let mut instruction = None;
while let Some(child) = article_children.next() {
let mut header = String::new();
collect_text(&mut header, child);
if !header.ends_with(" Instruction") {
continue;
}
let next = article_children.next().expect("no next child");
assert!(instruction.is_none());
let mut instr = String::new();
collect_text(&mut instr, &next);
instruction = Some(instr);
}
let instruction = match instruction {
Some(s) => s.trim().to_lowercase(),
None => panic!("can't find instruction for `{name}`"),
};
.map(|s| {
let (ty, konst) = match s.strip_prefix("const") {
Some(stripped) => (stripped.trim_start(), true),
None => (s.as_str(), false),
};
let ty = ty.rsplit_once(' ').unwrap().0;
(parse_ty(ty), konst)
})
.collect::<Vec<_>>();
Intrinsic {
name,
ret: if ret == "void" {
None
} else {
Some(parse_ty(ret))
},
ret,
instruction,
arguments: args // "(...)"
.trim_start_matches('(') // "...)"
.trim_end_matches(')') // "..."
.split(',') // " Type name ", ".."
.map(|s| s.trim()) // "Type name"
.map(|s| s.rsplitn(2, ' ').nth(1).unwrap()) // "Type"
.map(|s| {
let const_ = "const ";
if s.starts_with(const_) {
(parse_ty(&s[const_.len()..]), true)
} else {
(parse_ty(s), false)
}
})
.collect(),
arguments,
}
}
fn parse_ty(s: &str) -> Type {
let suffix = " const *";
if s.ends_with(suffix) {
Type::ConstPtr(parse_ty_base(&s[..s.len() - suffix.len()]))
} else if s.ends_with(" *") {
Type::MutPtr(parse_ty_base(&s[..s.len() - 2]))
if let Some(base) = s.strip_suffix(suffix) {
Type::ConstPtr(parse_ty_base(base))
} else if let Some(base) = s.strip_suffix(" *") {
Type::MutPtr(parse_ty_base(base))
} else {
*parse_ty_base(s)
}
@ -973,16 +747,6 @@ fn parse_ty_base(s: &str) -> &'static Type {
"uint8x8x3_t" => &U8X8X3,
"uint8x8x4_t" => &U8X8X4,
_ => panic!("failed to parse html type {s:?}"),
}
}
fn collect_text(s: &mut String, node: &Node) {
if let NodeData::Text { contents } = &node.data {
s.push(' ');
s.push_str(&contents.borrow().to_string());
}
for child in node.children.borrow().iter() {
collect_text(s, child);
_ => panic!("failed to parse json type {s:?}"),
}
}

File diff suppressed because it is too large Load diff