From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- .../stdarch/crates/stdarch-verify/tests/mips.rs | 366 +++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 library/stdarch/crates/stdarch-verify/tests/mips.rs (limited to 'library/stdarch/crates/stdarch-verify/tests/mips.rs') 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, + 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 { + return inner(line).ok_or(NoneError); + + fn inner(line: &'static str) -> Option { + 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::::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(()) +} -- cgit v1.2.3