summaryrefslogtreecommitdiffstats
path: root/library/stdarch/crates/stdarch-verify/tests/arm.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/stdarch/crates/stdarch-verify/tests/arm.rs')
-rw-r--r--library/stdarch/crates/stdarch-verify/tests/arm.rs988
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);
+ }
+}