summaryrefslogtreecommitdiffstats
path: root/library/stdarch/crates/stdarch-verify/tests
diff options
context:
space:
mode:
Diffstat (limited to 'library/stdarch/crates/stdarch-verify/tests')
-rw-r--r--library/stdarch/crates/stdarch-verify/tests/arm.rs988
-rw-r--r--library/stdarch/crates/stdarch-verify/tests/mips.rs366
-rw-r--r--library/stdarch/crates/stdarch-verify/tests/x86-intel.rs841
3 files changed, 2195 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);
+ }
+}
diff --git a/library/stdarch/crates/stdarch-verify/tests/mips.rs b/library/stdarch/crates/stdarch-verify/tests/mips.rs
new file mode 100644
index 000000000..1eb86dc29
--- /dev/null
+++ b/library/stdarch/crates/stdarch-verify/tests/mips.rs
@@ -0,0 +1,366 @@
+//! Verification of MIPS MSA intrinsics
+#![allow(bad_style, unused)]
+
+// This file is obtained from
+// https://gcc.gnu.org/onlinedocs//gcc/MIPS-SIMD-Architecture-Built-in-Functions.html
+static HEADER: &str = include_str!("../mips-msa.h");
+
+stdarch_verify::mips_functions!(static FUNCTIONS);
+
+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 I8: Type = Type::PrimSigned(8);
+static I16: Type = Type::PrimSigned(16);
+static I32: Type = Type::PrimSigned(32);
+static I64: Type = Type::PrimSigned(64);
+static U8: Type = Type::PrimUnsigned(8);
+static U16: Type = Type::PrimUnsigned(16);
+static U32: Type = Type::PrimUnsigned(32);
+static U64: Type = Type::PrimUnsigned(64);
+static NEVER: Type = Type::Never;
+static TUPLE: Type = Type::Tuple;
+static v16i8: Type = Type::I(8, 16, 1);
+static v8i16: Type = Type::I(16, 8, 1);
+static v4i32: Type = Type::I(32, 4, 1);
+static v2i64: Type = Type::I(64, 2, 1);
+static v16u8: Type = Type::U(8, 16, 1);
+static v8u16: Type = Type::U(16, 8, 1);
+static v4u32: Type = Type::U(32, 4, 1);
+static v2u64: Type = Type::U(64, 2, 1);
+static v8f16: Type = Type::F(16, 8, 1);
+static v4f32: Type = Type::F(32, 4, 1);
+static v2f64: Type = Type::F(64, 2, 1);
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+enum Type {
+ PrimFloat(u8),
+ PrimSigned(u8),
+ PrimUnsigned(u8),
+ PrimPoly(u8),
+ MutPtr(&'static Type),
+ ConstPtr(&'static Type),
+ Tuple,
+ I(u8, u8, u8),
+ U(u8, u8, u8),
+ P(u8, u8, u8),
+ F(u8, u8, u8),
+ Never,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[allow(non_camel_case_types)]
+enum MsaTy {
+ v16i8,
+ v8i16,
+ v4i32,
+ v2i64,
+ v16u8,
+ v8u16,
+ v4u32,
+ v2u64,
+ v8f16,
+ v4f32,
+ v2f64,
+ imm0_1,
+ imm0_3,
+ imm0_7,
+ imm0_15,
+ imm0_31,
+ imm0_63,
+ imm0_255,
+ imm_n16_15,
+ imm_n512_511,
+ imm_n1024_1022,
+ imm_n2048_2044,
+ imm_n4096_4088,
+ i32,
+ u32,
+ i64,
+ u64,
+ Void,
+ MutVoidPtr,
+}
+
+impl<'a> From<&'a str> for MsaTy {
+ fn from(s: &'a str) -> MsaTy {
+ match s {
+ "v16i8" => MsaTy::v16i8,
+ "v8i16" => MsaTy::v8i16,
+ "v4i32" => MsaTy::v4i32,
+ "v2i64" => MsaTy::v2i64,
+ "v16u8" => MsaTy::v16u8,
+ "v8u16" => MsaTy::v8u16,
+ "v4u32" => MsaTy::v4u32,
+ "v2u64" => MsaTy::v2u64,
+ "v8f16" => MsaTy::v8f16,
+ "v4f32" => MsaTy::v4f32,
+ "v2f64" => MsaTy::v2f64,
+ "imm0_1" => MsaTy::imm0_1,
+ "imm0_3" => MsaTy::imm0_3,
+ "imm0_7" => MsaTy::imm0_7,
+ "imm0_15" => MsaTy::imm0_15,
+ "imm0_31" => MsaTy::imm0_31,
+ "imm0_63" => MsaTy::imm0_63,
+ "imm0_255" => MsaTy::imm0_255,
+ "imm_n16_15" => MsaTy::imm_n16_15,
+ "imm_n512_511" => MsaTy::imm_n512_511,
+ "imm_n1024_1022" => MsaTy::imm_n1024_1022,
+ "imm_n2048_2044" => MsaTy::imm_n2048_2044,
+ "imm_n4096_4088" => MsaTy::imm_n4096_4088,
+ "i32" => MsaTy::i32,
+ "u32" => MsaTy::u32,
+ "i64" => MsaTy::i64,
+ "u64" => MsaTy::u64,
+ "void" => MsaTy::Void,
+ "void *" => MsaTy::MutVoidPtr,
+ v => panic!("unknown ty: \"{}\"", v),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+struct MsaIntrinsic {
+ id: String,
+ arg_tys: Vec<MsaTy>,
+ ret_ty: MsaTy,
+ instruction: String,
+}
+
+struct NoneError;
+
+impl std::convert::TryFrom<&'static str> for MsaIntrinsic {
+ // The intrinsics are just C function declarations of the form:
+ // $ret_ty __builtin_${fn_id}($($arg_ty),*);
+ type Error = NoneError;
+ fn try_from(line: &'static str) -> Result<Self, Self::Error> {
+ return inner(line).ok_or(NoneError);
+
+ fn inner(line: &'static str) -> Option<MsaIntrinsic> {
+ let first_whitespace = line.find(char::is_whitespace)?;
+ let ret_ty = &line[0..first_whitespace];
+ let ret_ty = MsaTy::from(ret_ty);
+
+ let first_parentheses = line.find('(')?;
+ assert!(first_parentheses > first_whitespace);
+ let id = &line[first_whitespace + 1..first_parentheses].trim();
+ assert!(id.starts_with("__builtin"));
+ let mut id_str = "_".to_string();
+ id_str += &id[9..];
+ let id = id_str;
+
+ let mut arg_tys = Vec::new();
+
+ let last_parentheses = line.find(')')?;
+ for arg in (&line[first_parentheses + 1..last_parentheses]).split(',') {
+ let arg = arg.trim();
+ arg_tys.push(MsaTy::from(arg));
+ }
+
+ // The instruction is the intrinsic name without the __msa_ prefix.
+ let instruction = &id[6..];
+ let mut instruction = instruction.to_string();
+ // With all underscores but the first one replaced with a `.`
+ if let Some(first_underscore) = instruction.find('_') {
+ let postfix = instruction[first_underscore + 1..].replace('_', ".");
+ instruction = instruction[0..=first_underscore].to_string();
+ instruction += &postfix;
+ }
+
+ Some(MsaIntrinsic {
+ id,
+ ret_ty,
+ arg_tys,
+ instruction,
+ })
+ }
+ }
+}
+
+#[test]
+fn verify_all_signatures() {
+ // Parse the C intrinsic header file:
+ let mut intrinsics = std::collections::HashMap::<String, MsaIntrinsic>::new();
+ for line in HEADER.lines() {
+ if line.is_empty() {
+ continue;
+ }
+
+ use std::convert::TryFrom;
+ let intrinsic: MsaIntrinsic = TryFrom::try_from(line)
+ .unwrap_or_else(|_| panic!("failed to parse line: \"{}\"", line));
+ assert!(!intrinsics.contains_key(&intrinsic.id));
+ intrinsics.insert(intrinsic.id.clone(), intrinsic);
+ }
+
+ let mut all_valid = true;
+ for rust in FUNCTIONS {
+ if !rust.has_test {
+ let skip = [
+ "__msa_ceqi_d",
+ "__msa_cfcmsa",
+ "__msa_clei_s_d",
+ "__msa_clti_s_d",
+ "__msa_ctcmsa",
+ "__msa_ldi_d",
+ "__msa_maxi_s_d",
+ "__msa_mini_s_d",
+ "break_",
+ ];
+ 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 part of MSA
+ match rust.name {
+ "break_" => continue,
+ _ => {}
+ }
+ let mips = match intrinsics.get(rust.name) {
+ Some(i) => i,
+ None => {
+ eprintln!(
+ "missing mips definition for {:?} in {}",
+ rust.name, rust.file
+ );
+ all_valid = false;
+ continue;
+ }
+ };
+
+ if let Err(e) = matches(rust, mips) {
+ println!("failed to verify `{}`", rust.name);
+ println!(" * {}", e);
+ all_valid = false;
+ }
+ }
+ assert!(all_valid);
+}
+
+fn matches(rust: &Function, mips: &MsaIntrinsic) -> Result<(), String> {
+ macro_rules! bail {
+ ($($t:tt)*) => (return Err(format!($($t)*)))
+ }
+
+ if rust.ret.is_none() && mips.ret_ty != MsaTy::Void {
+ bail!("mismatched return value")
+ }
+
+ if rust.arguments.len() != mips.arg_tys.len() {
+ bail!("mismatched argument lengths");
+ }
+
+ let mut nconst = 0;
+ for (i, (rust_arg, mips_arg)) in rust.arguments.iter().zip(mips.arg_tys.iter()).enumerate() {
+ match mips_arg {
+ MsaTy::v16i8 if **rust_arg == v16i8 => (),
+ MsaTy::v8i16 if **rust_arg == v8i16 => (),
+ MsaTy::v4i32 if **rust_arg == v4i32 => (),
+ MsaTy::v2i64 if **rust_arg == v2i64 => (),
+ MsaTy::v16u8 if **rust_arg == v16u8 => (),
+ MsaTy::v8u16 if **rust_arg == v8u16 => (),
+ MsaTy::v4u32 if **rust_arg == v4u32 => (),
+ MsaTy::v2u64 if **rust_arg == v2u64 => (),
+ MsaTy::v4f32 if **rust_arg == v4f32 => (),
+ MsaTy::v2f64 if **rust_arg == v2f64 => (),
+ MsaTy::imm0_1
+ | MsaTy::imm0_3
+ | MsaTy::imm0_7
+ | MsaTy::imm0_15
+ | MsaTy::imm0_31
+ | MsaTy::imm0_63
+ | MsaTy::imm0_255
+ | MsaTy::imm_n16_15
+ | MsaTy::imm_n512_511
+ | MsaTy::imm_n1024_1022
+ | MsaTy::imm_n2048_2044
+ | MsaTy::imm_n4096_4088
+ if **rust_arg == I32 => {}
+ MsaTy::i32 if **rust_arg == I32 => (),
+ MsaTy::i64 if **rust_arg == I64 => (),
+ MsaTy::u32 if **rust_arg == U32 => (),
+ MsaTy::u64 if **rust_arg == U64 => (),
+ MsaTy::MutVoidPtr if **rust_arg == Type::MutPtr(&U8) => (),
+ m => bail!(
+ "mismatched argument \"{}\"= \"{:?}\" != \"{:?}\"",
+ i,
+ m,
+ *rust_arg
+ ),
+ }
+
+ let is_const = matches!(
+ mips_arg,
+ MsaTy::imm0_1
+ | MsaTy::imm0_3
+ | MsaTy::imm0_7
+ | MsaTy::imm0_15
+ | MsaTy::imm0_31
+ | MsaTy::imm0_63
+ | MsaTy::imm0_255
+ | MsaTy::imm_n16_15
+ | MsaTy::imm_n512_511
+ | MsaTy::imm_n1024_1022
+ | MsaTy::imm_n2048_2044
+ | MsaTy::imm_n4096_4088
+ );
+ if is_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.target_feature != Some("msa") {
+ bail!("wrong target_feature");
+ }
+
+ if !rust.instrs.is_empty() {
+ // 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(())
+}
diff --git a/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs b/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs
new file mode 100644
index 000000000..89494bfd2
--- /dev/null
+++ b/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs
@@ -0,0 +1,841 @@
+#![allow(bad_style)]
+#![allow(unused)]
+#![allow(
+ clippy::shadow_reuse,
+ clippy::cast_lossless,
+ clippy::match_same_arms,
+ clippy::nonminimal_bool,
+ clippy::print_stdout,
+ clippy::use_debug,
+ clippy::eq_op,
+ clippy::useless_format
+)]
+
+use std::collections::{BTreeMap, HashMap};
+
+use serde::Deserialize;
+
+const PRINT_INSTRUCTION_VIOLATIONS: bool = false;
+const PRINT_MISSING_LISTS: bool = false;
+const PRINT_MISSING_LISTS_MARKDOWN: bool = false;
+
+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 F32: Type = Type::PrimFloat(32);
+static F64: Type = Type::PrimFloat(64);
+static I8: Type = Type::PrimSigned(8);
+static I16: Type = Type::PrimSigned(16);
+static I32: Type = Type::PrimSigned(32);
+static I64: Type = Type::PrimSigned(64);
+static U8: Type = Type::PrimUnsigned(8);
+static U16: Type = Type::PrimUnsigned(16);
+static U32: Type = Type::PrimUnsigned(32);
+static U64: Type = Type::PrimUnsigned(64);
+static U128: Type = Type::PrimUnsigned(128);
+static ORDERING: Type = Type::Ordering;
+
+static M64: Type = Type::M64;
+static M128: Type = Type::M128;
+static M128BH: Type = Type::M128BH;
+static M128I: Type = Type::M128I;
+static M128D: Type = Type::M128D;
+static M256: Type = Type::M256;
+static M256BH: Type = Type::M256BH;
+static M256I: Type = Type::M256I;
+static M256D: Type = Type::M256D;
+static M512: Type = Type::M512;
+static M512BH: Type = Type::M512BH;
+static M512I: Type = Type::M512I;
+static M512D: Type = Type::M512D;
+static MMASK8: Type = Type::MMASK8;
+static MMASK16: Type = Type::MMASK16;
+static MMASK32: Type = Type::MMASK32;
+static MMASK64: Type = Type::MMASK64;
+static MM_CMPINT_ENUM: Type = Type::MM_CMPINT_ENUM;
+static MM_MANTISSA_NORM_ENUM: Type = Type::MM_MANTISSA_NORM_ENUM;
+static MM_MANTISSA_SIGN_ENUM: Type = Type::MM_MANTISSA_SIGN_ENUM;
+static MM_PERM_ENUM: Type = Type::MM_PERM_ENUM;
+
+static TUPLE: Type = Type::Tuple;
+static CPUID: Type = Type::CpuidResult;
+static NEVER: Type = Type::Never;
+
+#[derive(Debug)]
+enum Type {
+ PrimFloat(u8),
+ PrimSigned(u8),
+ PrimUnsigned(u8),
+ MutPtr(&'static Type),
+ ConstPtr(&'static Type),
+ M64,
+ M128,
+ M128BH,
+ M128D,
+ M128I,
+ M256,
+ M256BH,
+ M256D,
+ M256I,
+ M512,
+ M512BH,
+ M512D,
+ M512I,
+ MMASK8,
+ MMASK16,
+ MMASK32,
+ MMASK64,
+ MM_CMPINT_ENUM,
+ MM_MANTISSA_NORM_ENUM,
+ MM_MANTISSA_SIGN_ENUM,
+ MM_PERM_ENUM,
+ Tuple,
+ CpuidResult,
+ Never,
+ Ordering,
+}
+
+stdarch_verify::x86_functions!(static FUNCTIONS);
+
+#[derive(Deserialize)]
+struct Data {
+ #[serde(rename = "intrinsic", default)]
+ intrinsics: Vec<Intrinsic>,
+}
+
+#[derive(Deserialize)]
+struct Intrinsic {
+ #[serde(rename = "return")]
+ return_: Return,
+ name: String,
+ #[serde(rename = "CPUID", default)]
+ cpuid: Vec<String>,
+ #[serde(rename = "parameter", default)]
+ parameters: Vec<Parameter>,
+ #[serde(default)]
+ instruction: Vec<Instruction>,
+}
+
+#[derive(Deserialize)]
+struct Parameter {
+ #[serde(rename = "type")]
+ type_: String,
+ #[serde(default)]
+ etype: String,
+}
+
+#[derive(Deserialize)]
+struct Return {
+ #[serde(rename = "type")]
+ type_: String,
+}
+
+#[derive(Deserialize, Debug)]
+struct Instruction {
+ name: String,
+}
+
+macro_rules! bail {
+ ($($t:tt)*) => (return Err(format!($($t)*)))
+}
+
+#[test]
+fn verify_all_signatures() {
+ // This XML document was downloaded from Intel's site. To update this you
+ // can visit intel's intrinsics guide online documentation:
+ //
+ // https://software.intel.com/sites/landingpage/IntrinsicsGuide/#
+ //
+ // Open up the network console and you'll see an xml file was downloaded
+ // (currently called data-3.4.xml). That's the file we downloaded
+ // here.
+ let xml = include_bytes!("../x86-intel.xml");
+
+ let xml = &xml[..];
+ let data: Data = serde_xml_rs::from_reader(xml).expect("failed to deserialize xml");
+ let mut map = HashMap::new();
+ for intrinsic in &data.intrinsics {
+ map.entry(&intrinsic.name[..])
+ .or_insert_with(Vec::new)
+ .push(intrinsic);
+ }
+
+ let mut all_valid = true;
+ 'outer: for rust in FUNCTIONS {
+ if !rust.has_test {
+ // FIXME: this list should be almost empty
+ let skip = [
+ "__readeflags",
+ "__readeflags",
+ "__writeeflags",
+ "__writeeflags",
+ "_mm_comige_ss",
+ "_mm_cvt_ss2si",
+ "_mm_cvtt_ss2si",
+ "_mm_cvt_si2ss",
+ "_mm_set_ps1",
+ "_mm_load_ps1",
+ "_mm_store_ps1",
+ "_mm_getcsr",
+ "_mm_setcsr",
+ "_MM_GET_EXCEPTION_MASK",
+ "_MM_GET_EXCEPTION_STATE",
+ "_MM_GET_FLUSH_ZERO_MODE",
+ "_MM_GET_ROUNDING_MODE",
+ "_MM_SET_EXCEPTION_MASK",
+ "_MM_SET_EXCEPTION_STATE",
+ "_MM_SET_FLUSH_ZERO_MODE",
+ "_MM_SET_ROUNDING_MODE",
+ "_mm_prefetch",
+ "_mm_undefined_ps",
+ "_m_pmaxsw",
+ "_m_pmaxub",
+ "_m_pminsw",
+ "_m_pminub",
+ "_m_pavgb",
+ "_m_pavgw",
+ "_m_psadbw",
+ "_mm_cvt_pi2ps",
+ "_m_maskmovq",
+ "_m_pextrw",
+ "_m_pinsrw",
+ "_m_pmovmskb",
+ "_m_pshufw",
+ "_mm_cvtt_ps2pi",
+ "_mm_cvt_ps2pi",
+ "__cpuid_count",
+ "__cpuid",
+ "__get_cpuid_max",
+ "_xsave",
+ "_xrstor",
+ "_xsetbv",
+ "_xgetbv",
+ "_xsaveopt",
+ "_xsavec",
+ "_xsaves",
+ "_xrstors",
+ "_mm_bslli_si128",
+ "_mm_bsrli_si128",
+ "_mm_undefined_pd",
+ "_mm_undefined_si128",
+ "_mm_cvtps_ph",
+ "_mm256_cvtps_ph",
+ "_rdtsc",
+ "__rdtscp",
+ "_mm256_castps128_ps256",
+ "_mm256_castpd128_pd256",
+ "_mm256_castsi128_si256",
+ "_mm256_undefined_ps",
+ "_mm256_undefined_pd",
+ "_mm256_undefined_si256",
+ "_bextr2_u32",
+ "_mm_tzcnt_32",
+ "_m_paddb",
+ "_m_paddw",
+ "_m_paddd",
+ "_m_paddsb",
+ "_m_paddsw",
+ "_m_paddusb",
+ "_m_paddusw",
+ "_m_psubb",
+ "_m_psubw",
+ "_m_psubd",
+ "_m_psubsb",
+ "_m_psubsw",
+ "_m_psubusb",
+ "_m_psubusw",
+ "_mm_set_pi16",
+ "_mm_set_pi32",
+ "_mm_set_pi8",
+ "_mm_set1_pi16",
+ "_mm_set1_pi32",
+ "_mm_set1_pi8",
+ "_mm_setr_pi16",
+ "_mm_setr_pi32",
+ "_mm_setr_pi8",
+ "ud2",
+ "_mm_min_epi8",
+ "_mm_min_epi32",
+ "_xbegin",
+ "_xend",
+ "_rdrand16_step",
+ "_rdrand32_step",
+ "_rdseed16_step",
+ "_rdseed32_step",
+ "_fxsave",
+ "_fxrstor",
+ "_t1mskc_u64",
+ "_mm256_shuffle_epi32",
+ "_mm256_bslli_epi128",
+ "_mm256_bsrli_epi128",
+ "_mm256_unpackhi_epi8",
+ "_mm256_unpacklo_epi8",
+ "_mm256_unpackhi_epi16",
+ "_mm256_unpacklo_epi16",
+ "_mm256_unpackhi_epi32",
+ "_mm256_unpacklo_epi32",
+ "_mm256_unpackhi_epi64",
+ "_mm256_unpacklo_epi64",
+ "_xsave64",
+ "_xrstor64",
+ "_xsaveopt64",
+ "_xsavec64",
+ "_xsaves64",
+ "_xrstors64",
+ "_mm_cvtsi64x_si128",
+ "_mm_cvtsi128_si64x",
+ "_mm_cvtsi64x_sd",
+ "cmpxchg16b",
+ "_rdrand64_step",
+ "_rdseed64_step",
+ "_bextr2_u64",
+ "_mm_tzcnt_64",
+ "_fxsave64",
+ "_fxrstor64",
+ "_mm512_undefined_ps",
+ "_mm512_undefined_pd",
+ "_mm512_undefined_epi32",
+ "_mm512_undefined",
+ ];
+ 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;
+ }
+ }
+
+ match rust.name {
+ // These aren't defined by Intel but they're defined by what appears
+ // to be all other compilers. For more information see
+ // rust-lang/stdarch#307, and otherwise these signatures
+ // have all been manually verified.
+ "__readeflags" |
+ "__writeeflags" |
+ "__cpuid_count" |
+ "__cpuid" |
+ "__get_cpuid_max" |
+ // Not listed with intel, but manually verified
+ "cmpxchg16b" |
+ // The UD2 intrinsic is not defined by Intel, but it was agreed on
+ // in the RFC Issue 2512:
+ // https://github.com/rust-lang/rfcs/issues/2512
+ "ud2"
+ => continue,
+ // Intel requires the mask argument for _mm_shuffle_ps to be an
+ // unsigned integer, but all other _mm_shuffle_.. intrinsics
+ // take a signed-integer. This breaks `_MM_SHUFFLE` for
+ // `_mm_shuffle_ps`:
+ "_mm_shuffle_ps" => continue,
+ _ => {}
+ }
+
+ // these are all AMD-specific intrinsics
+ if let Some(feature) = rust.target_feature {
+ if feature.contains("sse4a") || feature.contains("tbm") {
+ continue;
+ }
+ }
+
+ let intel = match map.remove(rust.name) {
+ Some(i) => i,
+ None => panic!("missing intel definition for {}", rust.name),
+ };
+
+ let mut errors = Vec::new();
+ for intel in intel {
+ match matches(rust, intel) {
+ Ok(()) => continue 'outer,
+ Err(e) => errors.push(e),
+ }
+ }
+ println!("failed to verify `{}`", rust.name);
+ for error in errors {
+ println!(" * {}", error);
+ }
+ all_valid = false;
+ }
+ assert!(all_valid);
+
+ let mut missing = BTreeMap::new();
+ for (name, intel) in &map {
+ // currently focused mainly on missing SIMD intrinsics, but there's
+ // definitely some other assorted ones that we're missing.
+ if !name.starts_with("_mm") {
+ continue;
+ }
+
+ // we'll get to avx-512 later
+ // let avx512 = intel.iter().any(|i| {
+ // i.name.starts_with("_mm512") || i.cpuid.iter().any(|c| {
+ // c.contains("512")
+ // })
+ // });
+ // if avx512 {
+ // continue
+ // }
+
+ for intel in intel {
+ missing
+ .entry(&intel.cpuid)
+ .or_insert_with(Vec::new)
+ .push(intel);
+ }
+ }
+
+ // generate a bulleted list of missing intrinsics
+ if PRINT_MISSING_LISTS || PRINT_MISSING_LISTS_MARKDOWN {
+ for (k, v) in missing {
+ if PRINT_MISSING_LISTS_MARKDOWN {
+ println!("\n<details><summary>{:?}</summary><p>\n", k);
+ for intel in v {
+ let url = format!(
+ "https://software.intel.com/sites/landingpage\
+ /IntrinsicsGuide/#text={}&expand=5236",
+ intel.name
+ );
+ println!(" * [ ] [`{}`]({})", intel.name, url);
+ }
+ println!("</p></details>\n");
+ } else {
+ println!("\n{:?}\n", k);
+ for intel in v {
+ println!("\t{}", intel.name);
+ }
+ }
+ }
+ }
+}
+
+fn matches(rust: &Function, intel: &Intrinsic) -> Result<(), String> {
+ // Verify that all `#[target_feature]` annotations are correct,
+ // ensuring that we've actually enabled the right instruction
+ // set for this intrinsic.
+ match rust.name {
+ "_bswap" | "_bswap64" => {}
+
+ // These don't actually have a target feature unlike their brethren with
+ // the `x` inside the name which requires adx
+ "_addcarry_u32" | "_addcarry_u64" | "_subborrow_u32" | "_subborrow_u64" => {}
+
+ "_bittest"
+ | "_bittestandset"
+ | "_bittestandreset"
+ | "_bittestandcomplement"
+ | "_bittest64"
+ | "_bittestandset64"
+ | "_bittestandreset64"
+ | "_bittestandcomplement64" => {}
+
+ _ => {
+ if intel.cpuid.is_empty() {
+ bail!("missing cpuid for {}", rust.name);
+ }
+ }
+ }
+
+ for cpuid in &intel.cpuid {
+ // The pause intrinsic is in the SSE2 module, but it is backwards
+ // compatible with CPUs without SSE2, and it therefore does not need the
+ // target-feature attribute.
+ if rust.name == "_mm_pause" {
+ continue;
+ }
+ // this is needed by _xsave and probably some related intrinsics,
+ // but let's just skip it for now.
+ if *cpuid == "XSS" {
+ continue;
+ }
+
+ // these flags on the rdtsc/rtdscp intrinsics we don't test for right
+ // now, but we may wish to add these one day!
+ //
+ // For more info see #308
+ if *cpuid == "TSC" || *cpuid == "RDTSCP" {
+ continue;
+ }
+
+ let cpuid = cpuid
+ .chars()
+ .flat_map(|c| c.to_lowercase())
+ .collect::<String>();
+
+ // Fix mismatching feature names:
+ let fixup_cpuid = |cpuid: String| match cpuid.as_ref() {
+ // The XML file names IFMA as "avx512ifma52", while Rust calls
+ // it "avx512ifma".
+ "avx512ifma52" => String::from("avx512ifma"),
+ // The XML file names BITALG as "avx512_bitalg", while Rust calls
+ // it "avx512bitalg".
+ "avx512_bitalg" => String::from("avx512bitalg"),
+ // The XML file names VBMI as "avx512_vbmi", while Rust calls
+ // it "avx512vbmi".
+ "avx512_vbmi" => String::from("avx512vbmi"),
+ // The XML file names VBMI2 as "avx512_vbmi2", while Rust calls
+ // it "avx512vbmi2".
+ "avx512_vbmi2" => String::from("avx512vbmi2"),
+ // The XML file names VNNI as "avx512_vnni", while Rust calls
+ // it "avx512vnni".
+ "avx512_vnni" => String::from("avx512vnni"),
+ // Some AVX512f intrinsics are also supported by Knight's Corner.
+ // The XML lists them as avx512f/kncni, but we are solely gating
+ // them behind avx512f since we don't have a KNC feature yet.
+ "avx512f/kncni" => String::from("avx512f"),
+ // See: https://github.com/rust-lang/stdarch/issues/738
+ // The intrinsics guide calls `f16c` `fp16c` in disagreement with
+ // Intel's architecture manuals.
+ "fp16c" => String::from("f16c"),
+ "avx512_bf16" => String::from("avx512bf16"),
+ // The XML file names VNNI as "avx512_bf16", while Rust calls
+ // it "avx512bf16".
+ _ => cpuid,
+ };
+ let fixed_cpuid = fixup_cpuid(cpuid);
+
+ let rust_feature = rust
+ .target_feature
+ .unwrap_or_else(|| panic!("no target feature listed for {}", rust.name));
+
+ if rust_feature.contains(&fixed_cpuid) {
+ continue;
+ }
+ bail!(
+ "intel cpuid `{}` not in `{}` for {}",
+ fixed_cpuid,
+ rust_feature,
+ rust.name
+ )
+ }
+
+ if PRINT_INSTRUCTION_VIOLATIONS {
+ if rust.instrs.is_empty() {
+ if !intel.instruction.is_empty() {
+ println!(
+ "instruction not listed for `{}`, but intel lists {:?}",
+ rust.name, intel.instruction
+ );
+ }
+
+ // If intel doesn't list any instructions and we do then don't
+ // bother trying to look for instructions in intel, we've just got
+ // some extra assertions on our end.
+ } else if !intel.instruction.is_empty() {
+ for instr in rust.instrs {
+ let asserting = intel.instruction.iter().any(|a| a.name.starts_with(instr));
+ if !asserting {
+ println!(
+ "intel failed to list `{}` as an instruction for `{}`",
+ instr, rust.name
+ );
+ }
+ }
+ }
+ }
+
+ // Make sure we've got the right return type.
+ if let Some(t) = rust.ret {
+ equate(t, &intel.return_.type_, "", rust.name, false)?;
+ } else if intel.return_.type_ != "" && intel.return_.type_ != "void" {
+ bail!(
+ "{} returns `{}` with intel, void in rust",
+ rust.name,
+ intel.return_.type_
+ )
+ }
+
+ // If there's no arguments on Rust's side intel may list one "void"
+ // argument, so handle that here.
+ if rust.arguments.is_empty() && intel.parameters.len() == 1 {
+ if intel.parameters[0].type_ != "void" {
+ bail!("rust has 0 arguments, intel has one for")
+ }
+ } else {
+ // Otherwise we want all parameters to be exactly the same
+ if rust.arguments.len() != intel.parameters.len() {
+ bail!("wrong number of arguments on {}", rust.name)
+ }
+ for (i, (a, b)) in intel.parameters.iter().zip(rust.arguments).enumerate() {
+ let is_const = rust.required_const.contains(&i);
+ equate(b, &a.type_, &a.etype, &intel.name, is_const)?;
+ }
+ }
+
+ let any_i64 = rust
+ .arguments
+ .iter()
+ .cloned()
+ .chain(rust.ret)
+ .any(|arg| matches!(*arg, Type::PrimSigned(64) | Type::PrimUnsigned(64)));
+ let any_i64_exempt = match rust.name {
+ // These intrinsics have all been manually verified against Clang's
+ // headers to be available on x86, and the u64 arguments seem
+ // spurious I guess?
+ "_xsave" | "_xrstor" | "_xsetbv" | "_xgetbv" | "_xsaveopt" | "_xsavec" | "_xsaves"
+ | "_xrstors" => true,
+
+ // Apparently all of clang/msvc/gcc accept these intrinsics on
+ // 32-bit, so let's do the same
+ "_mm_set_epi64x"
+ | "_mm_set1_epi64x"
+ | "_mm256_set_epi64x"
+ | "_mm256_setr_epi64x"
+ | "_mm256_set1_epi64x"
+ | "_mm512_set1_epi64"
+ | "_mm256_mask_set1_epi64"
+ | "_mm256_maskz_set1_epi64"
+ | "_mm_mask_set1_epi64"
+ | "_mm_maskz_set1_epi64"
+ | "_mm512_set4_epi64"
+ | "_mm512_setr4_epi64"
+ | "_mm512_set_epi64"
+ | "_mm512_setr_epi64"
+ | "_mm512_reduce_add_epi64"
+ | "_mm512_mask_reduce_add_epi64"
+ | "_mm512_reduce_mul_epi64"
+ | "_mm512_mask_reduce_mul_epi64"
+ | "_mm512_reduce_max_epi64"
+ | "_mm512_mask_reduce_max_epi64"
+ | "_mm512_reduce_max_epu64"
+ | "_mm512_mask_reduce_max_epu64"
+ | "_mm512_reduce_min_epi64"
+ | "_mm512_mask_reduce_min_epi64"
+ | "_mm512_reduce_min_epu64"
+ | "_mm512_mask_reduce_min_epu64"
+ | "_mm512_reduce_and_epi64"
+ | "_mm512_mask_reduce_and_epi64"
+ | "_mm512_reduce_or_epi64"
+ | "_mm512_mask_reduce_or_epi64"
+ | "_mm512_mask_set1_epi64"
+ | "_mm512_maskz_set1_epi64"
+ | "_mm_cvt_roundss_si64"
+ | "_mm_cvt_roundss_i64"
+ | "_mm_cvt_roundss_u64"
+ | "_mm_cvtss_i64"
+ | "_mm_cvtss_u64"
+ | "_mm_cvt_roundsd_si64"
+ | "_mm_cvt_roundsd_i64"
+ | "_mm_cvt_roundsd_u64"
+ | "_mm_cvtsd_i64"
+ | "_mm_cvtsd_u64"
+ | "_mm_cvt_roundi64_ss"
+ | "_mm_cvt_roundi64_sd"
+ | "_mm_cvt_roundsi64_ss"
+ | "_mm_cvt_roundsi64_sd"
+ | "_mm_cvt_roundu64_ss"
+ | "_mm_cvt_roundu64_sd"
+ | "_mm_cvti64_ss"
+ | "_mm_cvti64_sd"
+ | "_mm_cvtt_roundss_si64"
+ | "_mm_cvtt_roundss_i64"
+ | "_mm_cvtt_roundss_u64"
+ | "_mm_cvttss_i64"
+ | "_mm_cvttss_u64"
+ | "_mm_cvtt_roundsd_si64"
+ | "_mm_cvtt_roundsd_i64"
+ | "_mm_cvtt_roundsd_u64"
+ | "_mm_cvttsd_i64"
+ | "_mm_cvttsd_u64"
+ | "_mm_cvtu64_ss"
+ | "_mm_cvtu64_sd" => true,
+
+ // These return a 64-bit argument but they're assembled from other
+ // 32-bit registers, so these work on 32-bit just fine. See #308 for
+ // more info.
+ "_rdtsc" | "__rdtscp" => true,
+
+ _ => false,
+ };
+ if any_i64 && !any_i64_exempt && !rust.file.contains("x86_64") {
+ bail!(
+ "intrinsic `{}` uses a 64-bit bare type but may be \
+ available on 32-bit platforms",
+ rust.name
+ )
+ }
+ Ok(())
+}
+
+fn equate(
+ t: &Type,
+ intel: &str,
+ etype: &str,
+ intrinsic: &str,
+ is_const: bool,
+) -> Result<(), String> {
+ // Make pointer adjacent to the type: float * foo => float* foo
+ let mut intel = intel.replace(" *", "*");
+ // Make mutability modifier adjacent to the pointer:
+ // float const * foo => float const* foo
+ intel = intel.replace("const *", "const*");
+ // Normalize mutability modifier to after the type:
+ // const float* foo => float const*
+ if intel.starts_with("const") && intel.ends_with('*') {
+ intel = intel.replace("const ", "");
+ intel = intel.replace("*", " const*");
+ }
+ if etype == "IMM" {
+ // The _bittest intrinsics claim to only accept immediates but actually
+ // accept run-time values as well.
+ if !is_const && !intrinsic.starts_with("_bittest") {
+ return bail!("argument required to be const but isn't");
+ }
+ } else {
+ // const int must be an IMM
+ assert_ne!(intel, "const int");
+ if is_const {
+ return bail!("argument is const but shouldn't be");
+ }
+ }
+ match (t, &intel[..]) {
+ (&Type::PrimFloat(32), "float") => {}
+ (&Type::PrimFloat(64), "double") => {}
+ (&Type::PrimSigned(16), "__int16") => {}
+ (&Type::PrimSigned(16), "short") => {}
+ (&Type::PrimSigned(32), "__int32") => {}
+ (&Type::PrimSigned(32), "const int") => {}
+ (&Type::PrimSigned(32), "int") => {}
+ (&Type::PrimSigned(64), "__int64") => {}
+ (&Type::PrimSigned(64), "long long") => {}
+ (&Type::PrimSigned(8), "__int8") => {}
+ (&Type::PrimSigned(8), "char") => {}
+ (&Type::PrimUnsigned(16), "unsigned short") => {}
+ (&Type::PrimUnsigned(32), "unsigned int") => {}
+ (&Type::PrimUnsigned(32), "const unsigned int") => {}
+ (&Type::PrimUnsigned(64), "unsigned __int64") => {}
+ (&Type::PrimUnsigned(8), "unsigned char") => {}
+ (&Type::M64, "__m64") => {}
+ (&Type::M128, "__m128") => {}
+ (&Type::M128BH, "__m128bh") => {}
+ (&Type::M128I, "__m128i") => {}
+ (&Type::M128D, "__m128d") => {}
+ (&Type::M256, "__m256") => {}
+ (&Type::M256BH, "__m256bh") => {}
+ (&Type::M256I, "__m256i") => {}
+ (&Type::M256D, "__m256d") => {}
+ (&Type::M512, "__m512") => {}
+ (&Type::M512BH, "__m512bh") => {}
+ (&Type::M512I, "__m512i") => {}
+ (&Type::M512D, "__m512d") => {}
+ (&Type::MMASK64, "__mmask64") => {}
+ (&Type::MMASK32, "__mmask32") => {}
+ (&Type::MMASK16, "__mmask16") => {}
+ (&Type::MMASK8, "__mmask8") => {}
+
+ (&Type::MutPtr(&Type::PrimFloat(32)), "float*") => {}
+ (&Type::MutPtr(&Type::PrimFloat(64)), "double*") => {}
+ (&Type::MutPtr(&Type::PrimFloat(32)), "void*") => {}
+ (&Type::MutPtr(&Type::PrimFloat(64)), "void*") => {}
+ (&Type::MutPtr(&Type::PrimSigned(32)), "void*") => {}
+ (&Type::MutPtr(&Type::PrimSigned(16)), "void*") => {}
+ (&Type::MutPtr(&Type::PrimSigned(8)), "void*") => {}
+ (&Type::MutPtr(&Type::PrimSigned(32)), "int*") => {}
+ (&Type::MutPtr(&Type::PrimSigned(32)), "__int32*") => {}
+ (&Type::MutPtr(&Type::PrimSigned(64)), "void*") => {}
+ (&Type::MutPtr(&Type::PrimSigned(64)), "__int64*") => {}
+ (&Type::MutPtr(&Type::PrimSigned(8)), "char*") => {}
+ (&Type::MutPtr(&Type::PrimUnsigned(16)), "unsigned short*") => {}
+ (&Type::MutPtr(&Type::PrimUnsigned(32)), "unsigned int*") => {}
+ (&Type::MutPtr(&Type::PrimUnsigned(64)), "unsigned __int64*") => {}
+ (&Type::MutPtr(&Type::PrimUnsigned(8)), "void*") => {}
+ (&Type::MutPtr(&Type::PrimUnsigned(32)), "__mmask32*") => {}
+ (&Type::MutPtr(&Type::PrimUnsigned(64)), "__mmask64*") => {}
+ (&Type::MutPtr(&Type::M64), "__m64*") => {}
+ (&Type::MutPtr(&Type::M128), "__m128*") => {}
+ (&Type::MutPtr(&Type::M128BH), "__m128bh*") => {}
+ (&Type::MutPtr(&Type::M128I), "__m128i*") => {}
+ (&Type::MutPtr(&Type::M128D), "__m128d*") => {}
+ (&Type::MutPtr(&Type::M256), "__m256*") => {}
+ (&Type::MutPtr(&Type::M256BH), "__m256bh*") => {}
+ (&Type::MutPtr(&Type::M256I), "__m256i*") => {}
+ (&Type::MutPtr(&Type::M256D), "__m256d*") => {}
+ (&Type::MutPtr(&Type::M512), "__m512*") => {}
+ (&Type::MutPtr(&Type::M512BH), "__m512bh*") => {}
+ (&Type::MutPtr(&Type::M512I), "__m512i*") => {}
+ (&Type::MutPtr(&Type::M512D), "__m512d*") => {}
+
+ (&Type::ConstPtr(&Type::PrimFloat(32)), "float const*") => {}
+ (&Type::ConstPtr(&Type::PrimFloat(64)), "double const*") => {}
+ (&Type::ConstPtr(&Type::PrimFloat(32)), "void const*") => {}
+ (&Type::ConstPtr(&Type::PrimFloat(64)), "void const*") => {}
+ (&Type::ConstPtr(&Type::PrimSigned(32)), "int const*") => {}
+ (&Type::ConstPtr(&Type::PrimSigned(32)), "__int32 const*") => {}
+ (&Type::ConstPtr(&Type::PrimSigned(8)), "void const*") => {}
+ (&Type::ConstPtr(&Type::PrimSigned(16)), "void const*") => {}
+ (&Type::ConstPtr(&Type::PrimSigned(32)), "void const*") => {}
+ (&Type::ConstPtr(&Type::PrimSigned(64)), "void const*") => {}
+ (&Type::ConstPtr(&Type::PrimSigned(64)), "__int64 const*") => {}
+ (&Type::ConstPtr(&Type::PrimSigned(8)), "char const*") => {}
+ (&Type::ConstPtr(&Type::PrimUnsigned(16)), "unsigned short const*") => {}
+ (&Type::ConstPtr(&Type::PrimUnsigned(32)), "unsigned int const*") => {}
+ (&Type::ConstPtr(&Type::PrimUnsigned(64)), "unsigned __int64 const*") => {}
+ (&Type::ConstPtr(&Type::PrimUnsigned(8)), "void const*") => {}
+ (&Type::ConstPtr(&Type::PrimUnsigned(32)), "void const*") => {}
+ (&Type::ConstPtr(&Type::M64), "__m64 const*") => {}
+ (&Type::ConstPtr(&Type::M128), "__m128 const*") => {}
+ (&Type::ConstPtr(&Type::M128BH), "__m128bh const*") => {}
+ (&Type::ConstPtr(&Type::M128I), "__m128i const*") => {}
+ (&Type::ConstPtr(&Type::M128D), "__m128d const*") => {}
+ (&Type::ConstPtr(&Type::M256), "__m256 const*") => {}
+ (&Type::ConstPtr(&Type::M256BH), "__m256bh const*") => {}
+ (&Type::ConstPtr(&Type::M256I), "__m256i const*") => {}
+ (&Type::ConstPtr(&Type::M256D), "__m256d const*") => {}
+ (&Type::ConstPtr(&Type::M512), "__m512 const*") => {}
+ (&Type::ConstPtr(&Type::M512BH), "__m512bh const*") => {}
+ (&Type::ConstPtr(&Type::M512I), "__m512i const*") => {}
+ (&Type::ConstPtr(&Type::M512D), "__m512d const*") => {}
+ (&Type::ConstPtr(&Type::PrimUnsigned(32)), "__mmask32*") => {}
+ (&Type::ConstPtr(&Type::PrimUnsigned(64)), "__mmask64*") => {}
+
+ (&Type::MM_CMPINT_ENUM, "_MM_CMPINT_ENUM") => {}
+ (&Type::MM_MANTISSA_NORM_ENUM, "_MM_MANTISSA_NORM_ENUM") => {}
+ (&Type::MM_MANTISSA_SIGN_ENUM, "_MM_MANTISSA_SIGN_ENUM") => {}
+ (&Type::MM_PERM_ENUM, "_MM_PERM_ENUM") => {}
+
+ // This is a macro (?) in C which seems to mutate its arguments, but
+ // that means that we're taking pointers to arguments in rust
+ // as we're not exposing it as a macro.
+ (&Type::MutPtr(&Type::M128), "__m128") if intrinsic == "_MM_TRANSPOSE4_PS" => {}
+
+ // The _rdtsc intrinsic uses a __int64 return type, but this is a bug in
+ // the intrinsics guide: https://github.com/rust-lang/stdarch/issues/559
+ // We have manually fixed the bug by changing the return type to `u64`.
+ (&Type::PrimUnsigned(64), "__int64") if intrinsic == "_rdtsc" => {}
+
+ // The _bittest and _bittest64 intrinsics takes a mutable pointer in the
+ // intrinsics guide even though it never writes through the pointer:
+ (&Type::ConstPtr(&Type::PrimSigned(32)), "__int32*") if intrinsic == "_bittest" => {}
+ (&Type::ConstPtr(&Type::PrimSigned(64)), "__int64*") if intrinsic == "_bittest64" => {}
+ // The _xrstor, _fxrstor, _xrstor64, _fxrstor64 intrinsics take a
+ // mutable pointer in the intrinsics guide even though they never write
+ // through the pointer:
+ (&Type::ConstPtr(&Type::PrimUnsigned(8)), "void*")
+ if intrinsic == "_xrstor"
+ || intrinsic == "_xrstor64"
+ || intrinsic == "_fxrstor"
+ || intrinsic == "_fxrstor64" => {}
+
+ _ => bail!(
+ "failed to equate: `{}` and {:?} for {}",
+ intel,
+ t,
+ intrinsic
+ ),
+ }
+ Ok(())
+}