diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /library/stdarch/crates/stdarch-verify/tests | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/stdarch/crates/stdarch-verify/tests')
-rw-r--r-- | library/stdarch/crates/stdarch-verify/tests/arm.rs | 988 | ||||
-rw-r--r-- | library/stdarch/crates/stdarch-verify/tests/mips.rs | 366 | ||||
-rw-r--r-- | library/stdarch/crates/stdarch-verify/tests/x86-intel.rs | 841 |
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(()) +} |