diff options
Diffstat (limited to '')
-rw-r--r-- | library/stdarch/crates/stdarch-verify/tests/arm.rs | 988 |
1 files changed, 988 insertions, 0 deletions
diff --git a/library/stdarch/crates/stdarch-verify/tests/arm.rs b/library/stdarch/crates/stdarch-verify/tests/arm.rs new file mode 100644 index 000000000..6ce5ce05f --- /dev/null +++ b/library/stdarch/crates/stdarch-verify/tests/arm.rs @@ -0,0 +1,988 @@ +#![allow(bad_style)] +#![allow(unused)] +use std::{collections::HashMap, rc::Rc}; + +use html5ever::{ + driver::ParseOpts, + parse_document, + rcdom::{Node, NodeData, RcDom}, + tendril::TendrilSink, + tree_builder::TreeBuilderOpts, +}; + +struct Function { + name: &'static str, + arguments: &'static [&'static Type], + ret: Option<&'static Type>, + target_feature: Option<&'static str>, + instrs: &'static [&'static str], + file: &'static str, + required_const: &'static [usize], + has_test: bool, +} + +static F16: Type = Type::PrimFloat(16); +static F32: Type = Type::PrimFloat(32); +static F64: Type = Type::PrimFloat(64); +static I16: Type = Type::PrimSigned(16); +static I32: Type = Type::PrimSigned(32); +static I64: Type = Type::PrimSigned(64); +static I8: Type = Type::PrimSigned(8); +static U16: Type = Type::PrimUnsigned(16); +static U32: Type = Type::PrimUnsigned(32); +static U64: Type = Type::PrimUnsigned(64); +static U8: Type = Type::PrimUnsigned(8); +static NEVER: Type = Type::Never; + +static F16X4: Type = Type::F(16, 4, 1); +static F16X4X2: Type = Type::F(16, 4, 2); +static F16X4X3: Type = Type::F(16, 4, 3); +static F16X4X4: Type = Type::F(16, 4, 4); +static F16X8: Type = Type::F(16, 8, 1); +static F16X8X2: Type = Type::F(16, 8, 2); +static F16X8X3: Type = Type::F(16, 8, 3); +static F16X8X4: Type = Type::F(16, 8, 4); +static F32X2: Type = Type::F(32, 2, 1); +static F32X2X2: Type = Type::F(32, 2, 2); +static F32X2X3: Type = Type::F(32, 2, 3); +static F32X2X4: Type = Type::F(32, 2, 4); +static F32X4: Type = Type::F(32, 4, 1); +static F32X4X2: Type = Type::F(32, 4, 2); +static F32X4X3: Type = Type::F(32, 4, 3); +static F32X4X4: Type = Type::F(32, 4, 4); +static F64X1: Type = Type::F(64, 1, 1); +static F64X1X2: Type = Type::F(64, 1, 2); +static F64X1X3: Type = Type::F(64, 1, 3); +static F64X1X4: Type = Type::F(64, 1, 4); +static F64X2: Type = Type::F(64, 2, 1); +static F64X2X2: Type = Type::F(64, 2, 2); +static F64X2X3: Type = Type::F(64, 2, 3); +static F64X2X4: Type = Type::F(64, 2, 4); +static I16X2: Type = Type::I(16, 2, 1); +static I16X4: Type = Type::I(16, 4, 1); +static I16X4X2: Type = Type::I(16, 4, 2); +static I16X4X3: Type = Type::I(16, 4, 3); +static I16X4X4: Type = Type::I(16, 4, 4); +static I16X8: Type = Type::I(16, 8, 1); +static I16X8X2: Type = Type::I(16, 8, 2); +static I16X8X3: Type = Type::I(16, 8, 3); +static I16X8X4: Type = Type::I(16, 8, 4); +static I32X2: Type = Type::I(32, 2, 1); +static I32X2X2: Type = Type::I(32, 2, 2); +static I32X2X3: Type = Type::I(32, 2, 3); +static I32X2X4: Type = Type::I(32, 2, 4); +static I32X4: Type = Type::I(32, 4, 1); +static I32X4X2: Type = Type::I(32, 4, 2); +static I32X4X3: Type = Type::I(32, 4, 3); +static I32X4X4: Type = Type::I(32, 4, 4); +static I64X1: Type = Type::I(64, 1, 1); +static I64X1X2: Type = Type::I(64, 1, 2); +static I64X1X3: Type = Type::I(64, 1, 3); +static I64X1X4: Type = Type::I(64, 1, 4); +static I64X2: Type = Type::I(64, 2, 1); +static I64X2X2: Type = Type::I(64, 2, 2); +static I64X2X3: Type = Type::I(64, 2, 3); +static I64X2X4: Type = Type::I(64, 2, 4); +static I8X16: Type = Type::I(8, 16, 1); +static I8X16X2: Type = Type::I(8, 16, 2); +static I8X16X3: Type = Type::I(8, 16, 3); +static I8X16X4: Type = Type::I(8, 16, 4); +static I8X4: Type = Type::I(8, 4, 1); +static I8X8: Type = Type::I(8, 8, 1); +static I8X8X2: Type = Type::I(8, 8, 2); +static I8X8X3: Type = Type::I(8, 8, 3); +static I8X8X4: Type = Type::I(8, 8, 4); +static P128: Type = Type::PrimPoly(128); +static P16: Type = Type::PrimPoly(16); +static P16X4X2: Type = Type::P(16, 4, 2); +static P16X4X3: Type = Type::P(16, 4, 3); +static P16X4X4: Type = Type::P(16, 4, 4); +static P16X8X2: Type = Type::P(16, 8, 2); +static P16X8X3: Type = Type::P(16, 8, 3); +static P16X8X4: Type = Type::P(16, 8, 4); +static P64: Type = Type::PrimPoly(64); +static P64X1X2: Type = Type::P(64, 1, 2); +static P64X1X3: Type = Type::P(64, 1, 3); +static P64X1X4: Type = Type::P(64, 1, 4); +static P64X2X2: Type = Type::P(64, 2, 2); +static P64X2X3: Type = Type::P(64, 2, 3); +static P64X2X4: Type = Type::P(64, 2, 4); +static P8: Type = Type::PrimPoly(8); +static POLY16X4: Type = Type::P(16, 4, 1); +static POLY16X8: Type = Type::P(16, 8, 1); +static POLY64X1: Type = Type::P(64, 1, 1); +static POLY64X2: Type = Type::P(64, 2, 1); +static POLY8X16: Type = Type::P(8, 16, 1); +static POLY8X16X2: Type = Type::P(8, 16, 2); +static POLY8X16X3: Type = Type::P(8, 16, 3); +static POLY8X16X4: Type = Type::P(8, 16, 4); +static POLY8X8: Type = Type::P(8, 8, 1); +static POLY8X8X2: Type = Type::P(8, 8, 2); +static POLY8X8X3: Type = Type::P(8, 8, 3); +static POLY8X8X4: Type = Type::P(8, 8, 4); +static U16X4: Type = Type::U(16, 4, 1); +static U16X4X2: Type = Type::U(16, 4, 2); +static U16X4X3: Type = Type::U(16, 4, 3); +static U16X4X4: Type = Type::U(16, 4, 4); +static U16X8: Type = Type::U(16, 8, 1); +static U16X8X2: Type = Type::U(16, 8, 2); +static U16X8X3: Type = Type::U(16, 8, 3); +static U16X8X4: Type = Type::U(16, 8, 4); +static U32X2: Type = Type::U(32, 2, 1); +static U32X2X2: Type = Type::U(32, 2, 2); +static U32X2X3: Type = Type::U(32, 2, 3); +static U32X2X4: Type = Type::U(32, 2, 4); +static U32X4: Type = Type::U(32, 4, 1); +static U32X4X2: Type = Type::U(32, 4, 2); +static U32X4X3: Type = Type::U(32, 4, 3); +static U32X4X4: Type = Type::U(32, 4, 4); +static U64X1: Type = Type::U(64, 1, 1); +static U64X1X2: Type = Type::U(64, 1, 2); +static U64X1X3: Type = Type::U(64, 1, 3); +static U64X1X4: Type = Type::U(64, 1, 4); +static U64X2: Type = Type::U(64, 2, 1); +static U64X2X2: Type = Type::U(64, 2, 2); +static U64X2X3: Type = Type::U(64, 2, 3); +static U64X2X4: Type = Type::U(64, 2, 4); +static U8X16: Type = Type::U(8, 16, 1); +static U8X16X2: Type = Type::U(8, 16, 2); +static U8X16X3: Type = Type::U(8, 16, 3); +static U8X16X4: Type = Type::U(8, 16, 4); +static U8X8: Type = Type::U(8, 8, 1); +static U8X4: Type = Type::U(8, 4, 1); +static U8X8X2: Type = Type::U(8, 8, 2); +static U8X8X3: Type = Type::U(8, 8, 3); +static U8X8X4: Type = Type::U(8, 8, 4); + +#[derive(Debug, Copy, Clone, PartialEq)] +enum Type { + PrimFloat(u8), + PrimSigned(u8), + PrimUnsigned(u8), + PrimPoly(u8), + MutPtr(&'static Type), + ConstPtr(&'static Type), + I(u8, u8, u8), + U(u8, u8, u8), + P(u8, u8, u8), + F(u8, u8, u8), + Never, +} + +stdarch_verify::arm_functions!(static FUNCTIONS); + +macro_rules! bail { + ($($t:tt)*) => (return Err(format!($($t)*))) +} + +#[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); + + let mut all_valid = true; + 'outer: for rust in FUNCTIONS { + if !rust.has_test { + let skip = [ + "vaddq_s64", + "vaddq_u64", + "vrsqrte_f32", + "vtbl1_s8", + "vtbl1_u8", + "vtbl1_p8", + "vtbl2_s8", + "vtbl2_u8", + "vtbl2_p8", + "vtbl3_s8", + "vtbl3_u8", + "vtbl3_p8", + "vtbl4_s8", + "vtbl4_u8", + "vtbl4_p8", + "vtbx1_s8", + "vtbx1_u8", + "vtbx1_p8", + "vtbx2_s8", + "vtbx2_u8", + "vtbx2_p8", + "vtbx3_s8", + "vtbx3_u8", + "vtbx3_p8", + "vtbx4_s8", + "vtbx4_u8", + "vtbx4_p8", + "udf", + "_clz_u8", + "_clz_u16", + "_clz_u32", + "_rbit_u32", + "_rev_u16", + "_rev_u32", + "__breakpoint", + "vpminq_f32", + "vpminq_f64", + "vpmaxq_f32", + "vpmaxq_f64", + "vcombine_s8", + "vcombine_s16", + "vcombine_s32", + "vcombine_s64", + "vcombine_u8", + "vcombine_u16", + "vcombine_u32", + "vcombine_u64", + "vcombine_p64", + "vcombine_f32", + "vcombine_p8", + "vcombine_p16", + "vcombine_f64", + "vtbl1_s8", + "vtbl1_u8", + "vtbl1_p8", + "vtbl2_s8", + "vtbl2_u8", + "vtbl2_p8", + "vtbl3_s8", + "vtbl3_u8", + "vtbl3_p8", + "vtbl4_s8", + "vtbl4_u8", + "vtbl4_p8", + "vtbx1_s8", + "vtbx1_u8", + "vtbx1_p8", + "vtbx2_s8", + "vtbx2_u8", + "vtbx2_p8", + "vtbx3_s8", + "vtbx3_u8", + "vtbx3_p8", + "vtbx4_s8", + "vtbx4_u8", + "vtbx4_p8", + "vqtbl1_s8", + "vqtbl1q_s8", + "vqtbl1_u8", + "vqtbl1q_u8", + "vqtbl1_p8", + "vqtbl1q_p8", + "vqtbx1_s8", + "vqtbx1q_s8", + "vqtbx1_u8", + "vqtbx1q_u8", + "vqtbx1_p8", + "vqtbx1q_p8", + "vqtbl2_s8", + "vqtbl2q_s8", + "vqtbl2_u8", + "vqtbl2q_u8", + "vqtbl2_p8", + "vqtbl2q_p8", + "vqtbx2_s8", + "vqtbx2q_s8", + "vqtbx2_u8", + "vqtbx2q_u8", + "vqtbx2_p8", + "vqtbx2q_p8", + "vqtbl3_s8", + "vqtbl3q_s8", + "vqtbl3_u8", + "vqtbl3q_u8", + "vqtbl3_p8", + "vqtbl3q_p8", + "vqtbx3_s8", + "vqtbx3q_s8", + "vqtbx3_u8", + "vqtbx3q_u8", + "vqtbx3_p8", + "vqtbx3q_p8", + "vqtbl4_s8", + "vqtbl4q_s8", + "vqtbl4_u8", + "vqtbl4q_u8", + "vqtbl4_p8", + "vqtbl4q_p8", + "vqtbx4_s8", + "vqtbx4q_s8", + "vqtbx4_u8", + "vqtbx4q_u8", + "vqtbx4_p8", + "vqtbx4q_p8", + "brk", + "_rev_u64", + "_clz_u64", + "_rbit_u64", + "_cls_u32", + "_cls_u64", + "_prefetch", + "vsli_n_s8", + "vsliq_n_s8", + "vsli_n_s16", + "vsliq_n_s16", + "vsli_n_s32", + "vsliq_n_s32", + "vsli_n_s64", + "vsliq_n_s64", + "vsli_n_u8", + "vsliq_n_u8", + "vsli_n_u16", + "vsliq_n_u16", + "vsli_n_u32", + "vsliq_n_u32", + "vsli_n_u64", + "vsliq_n_u64", + "vsli_n_p8", + "vsliq_n_p8", + "vsli_n_p16", + "vsliq_n_p16", + "vsli_n_p64", + "vsliq_n_p64", + "vsri_n_s8", + "vsriq_n_s8", + "vsri_n_s16", + "vsriq_n_s16", + "vsri_n_s32", + "vsriq_n_s32", + "vsri_n_s64", + "vsriq_n_s64", + "vsri_n_u8", + "vsriq_n_u8", + "vsri_n_u16", + "vsriq_n_u16", + "vsri_n_u32", + "vsriq_n_u32", + "vsri_n_u64", + "vsriq_n_u64", + "vsri_n_p8", + "vsriq_n_p8", + "vsri_n_p16", + "vsriq_n_p16", + "vsri_n_p64", + "vsriq_n_p64", + "__smulbb", + "__smultb", + "__smulbt", + "__smultt", + "__smulwb", + "__smulwt", + "__qadd", + "__qsub", + "__qdbl", + "__smlabb", + "__smlabt", + "__smlatb", + "__smlatt", + "__smlawb", + "__smlawt", + "__qadd8", + "__qsub8", + "__qsub16", + "__qadd16", + "__qasx", + "__qsax", + "__sadd16", + "__sadd8", + "__smlad", + "__smlsd", + "__sasx", + "__sel", + "__shadd8", + "__shadd16", + "__shsub8", + "__usub8", + "__ssub8", + "__shsub16", + "__smuad", + "__smuadx", + "__smusd", + "__smusdx", + "__usad8", + "__usada8", + "__ldrex", + "__strex", + "__ldrexb", + "__strexb", + "__ldrexh", + "__strexh", + "__clrex", + "__dbg", + ]; + if !skip.contains(&rust.name) { + println!( + "missing run-time test named `test_{}` for `{}`", + { + let mut id = rust.name; + while id.starts_with('_') { + id = &id[1..]; + } + id + }, + rust.name + ); + all_valid = false; + } + } + + // Skip some intrinsics that aren't NEON and are located in different + // places than the whitelists below. + match rust.name { + "brk" | "__breakpoint" | "udf" | "_prefetch" => continue, + _ => {} + } + // Skip some intrinsics that are present in GCC and Clang but + // are missing from the official documentation. + let skip_intrinsic_verify = [ + "vmov_n_p64", + "vmovq_n_p64", + "vreinterpret_p64_s64", + "vreinterpret_f32_p64", + "vreinterpretq_f32_p64", + "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", + ]; + let arm = match map.get(rust.name) { + Some(i) => i, + None => { + // Skip all these intrinsics as they're not listed in NEON + // descriptions online. + // + // TODO: we still need to verify these intrinsics or find a + // 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\"") + && !rust.file.ends_with("tme.rs\"") + && !rust.file.ends_with("ex.rs\"") + && !skip_intrinsic_verify.contains(&rust.name) + { + println!( + "missing arm definition for {:?} in {}", + rust.name, rust.file + ); + all_valid = false; + } + continue; + } + }; + + if let Err(e) = matches(rust, arm) { + println!("failed to verify `{}`", rust.name); + println!(" * {}", e); + all_valid = false; + } + } + assert!(all_valid); +} + +fn matches(rust: &Function, arm: &Intrinsic) -> Result<(), String> { + if rust.ret != arm.ret.as_ref() { + bail!("mismatched return value") + } + if rust.arguments.len() != arm.arguments.len() { + bail!("mismatched argument lengths"); + } + + let mut nconst = 0; + 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") + } + if *arm_const { + nconst += 1; + if !rust.required_const.contains(&i) { + bail!("argument const mismatch"); + } + } + } + if nconst != rust.required_const.len() { + bail!("wrong number of const arguments"); + } + + if rust.instrs.is_empty() { + bail!( + "instruction not listed for `{}`, but arm lists {:?}", + rust.name, + arm.instruction + ); + } else if false + /* not super reliable, but can be used to manually check */ + { + for instr in rust.instrs { + if arm.instruction.starts_with(instr) { + continue; + } + // sometimes arm says `foo` and disassemblers say `vfoo`, or + // sometimes disassemblers say `vfoo` and arm says `sfoo` or `ffoo` + if instr.starts_with('v') + && (arm.instruction.starts_with(&instr[1..]) + || arm.instruction[1..].starts_with(&instr[1..])) + { + continue; + } + bail!( + "arm failed to list `{}` as an instruction for `{}` in {:?}", + instr, + rust.name, + arm.instruction, + ); + } + } + + // TODO: verify `target_feature`. + + 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, + ret: Option<Type>, + arguments: Vec<(Type, bool)>, + instruction: String, +} + +fn parse_intrinsics(node: &Rc<Node>) -> 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); + } + } + 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!(), + }; + + // 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()); + + // Find the instruction within the article + let article_children = article.children.borrow(); + let mut article_children = article_children + .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), + }; + + Intrinsic { + name, + ret: if ret == "void" { + None + } else { + Some(parse_ty(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(), + } +} + +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])) + } else { + *parse_ty_base(s) + } +} + +fn parse_ty_base(s: &str) -> &'static Type { + match s { + "float16_t" => &F16, + "float16x4_t" => &F16X4, + "float16x4x2_t" => &F16X4X2, + "float16x4x3_t" => &F16X4X3, + "float16x4x4_t" => &F16X4X4, + "float16x8_t" => &F16X8, + "float16x8x2_t" => &F16X8X2, + "float16x8x3_t" => &F16X8X3, + "float16x8x4_t" => &F16X8X4, + "float32_t" => &F32, + "float32x2_t" => &F32X2, + "float32x2x2_t" => &F32X2X2, + "float32x2x3_t" => &F32X2X3, + "float32x2x4_t" => &F32X2X4, + "float32x4_t" => &F32X4, + "float32x4x2_t" => &F32X4X2, + "float32x4x3_t" => &F32X4X3, + "float32x4x4_t" => &F32X4X4, + "float64_t" => &F64, + "float64x1_t" => &F64X1, + "float64x1x2_t" => &F64X1X2, + "float64x1x3_t" => &F64X1X3, + "float64x1x4_t" => &F64X1X4, + "float64x2_t" => &F64X2, + "float64x2x2_t" => &F64X2X2, + "float64x2x3_t" => &F64X2X3, + "float64x2x4_t" => &F64X2X4, + "int16_t" => &I16, + "int16x2_t" => &I16X2, + "int16x4_t" => &I16X4, + "int16x4x2_t" => &I16X4X2, + "int16x4x3_t" => &I16X4X3, + "int16x4x4_t" => &I16X4X4, + "int16x8_t" => &I16X8, + "int16x8x2_t" => &I16X8X2, + "int16x8x3_t" => &I16X8X3, + "int16x8x4_t" => &I16X8X4, + "int32_t" | "int" => &I32, + "int32x2_t" => &I32X2, + "int32x2x2_t" => &I32X2X2, + "int32x2x3_t" => &I32X2X3, + "int32x2x4_t" => &I32X2X4, + "int32x4_t" => &I32X4, + "int32x4x2_t" => &I32X4X2, + "int32x4x3_t" => &I32X4X3, + "int32x4x4_t" => &I32X4X4, + "int64_t" => &I64, + "int64x1_t" => &I64X1, + "int64x1x2_t" => &I64X1X2, + "int64x1x3_t" => &I64X1X3, + "int64x1x4_t" => &I64X1X4, + "int64x2_t" => &I64X2, + "int64x2x2_t" => &I64X2X2, + "int64x2x3_t" => &I64X2X3, + "int64x2x4_t" => &I64X2X4, + "int8_t" => &I8, + "int8x16_t" => &I8X16, + "int8x16x2_t" => &I8X16X2, + "int8x16x3_t" => &I8X16X3, + "int8x16x4_t" => &I8X16X4, + "int8x4_t" => &I8X4, + "int8x8_t" => &I8X8, + "int8x8x2_t" => &I8X8X2, + "int8x8x3_t" => &I8X8X3, + "int8x8x4_t" => &I8X8X4, + "poly128_t" => &P128, + "poly16_t" => &P16, + "poly16x4_t" => &POLY16X4, + "poly16x4x2_t" => &P16X4X2, + "poly16x4x3_t" => &P16X4X3, + "poly16x4x4_t" => &P16X4X4, + "poly16x8_t" => &POLY16X8, + "poly16x8x2_t" => &P16X8X2, + "poly16x8x3_t" => &P16X8X3, + "poly16x8x4_t" => &P16X8X4, + "poly64_t" => &P64, + "poly64x1_t" => &POLY64X1, + "poly64x1x2_t" => &P64X1X2, + "poly64x1x3_t" => &P64X1X3, + "poly64x1x4_t" => &P64X1X4, + "poly64x2_t" => &POLY64X2, + "poly64x2x2_t" => &P64X2X2, + "poly64x2x3_t" => &P64X2X3, + "poly64x2x4_t" => &P64X2X4, + "poly8_t" => &P8, + "poly8x16_t" => &POLY8X16, + "poly8x16x2_t" => &POLY8X16X2, + "poly8x16x3_t" => &POLY8X16X3, + "poly8x16x4_t" => &POLY8X16X4, + "poly8x8_t" => &POLY8X8, + "poly8x8x2_t" => &POLY8X8X2, + "poly8x8x3_t" => &POLY8X8X3, + "poly8x8x4_t" => &POLY8X8X4, + "uint16_t" => &U16, + "uint16x4_t" => &U16X4, + "uint16x4x2_t" => &U16X4X2, + "uint16x4x3_t" => &U16X4X3, + "uint16x4x4_t" => &U16X4X4, + "uint16x8_t" => &U16X8, + "uint16x8x2_t" => &U16X8X2, + "uint16x8x3_t" => &U16X8X3, + "uint16x8x4_t" => &U16X8X4, + "uint32_t" => &U32, + "uint32x2_t" => &U32X2, + "uint32x2x2_t" => &U32X2X2, + "uint32x2x3_t" => &U32X2X3, + "uint32x2x4_t" => &U32X2X4, + "uint32x4_t" => &U32X4, + "uint32x4x2_t" => &U32X4X2, + "uint32x4x3_t" => &U32X4X3, + "uint32x4x4_t" => &U32X4X4, + "uint64_t" => &U64, + "uint64x1_t" => &U64X1, + "uint64x1x2_t" => &U64X1X2, + "uint64x1x3_t" => &U64X1X3, + "uint64x1x4_t" => &U64X1X4, + "uint64x2_t" => &U64X2, + "uint64x2x2_t" => &U64X2X2, + "uint64x2x3_t" => &U64X2X3, + "uint64x2x4_t" => &U64X2X4, + "uint8_t" => &U8, + "uint8x16_t" => &U8X16, + "uint8x16x2_t" => &U8X16X2, + "uint8x16x3_t" => &U8X16X3, + "uint8x16x4_t" => &U8X16X4, + "uint8x8_t" => &U8X8, + "uint8x8x2_t" => &U8X8X2, + "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); + } +} |