diff --git a/Cargo.lock b/Cargo.lock index 2fa2bfe..3fe6ad7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "eyre" version = "0.6.11" @@ -31,9 +37,21 @@ dependencies = [ "eyre", "heck", "logos", + "rand", "strong-xml", ] +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "heck" version = "0.4.1" @@ -49,6 +67,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "intringen" version = "0.1.0" +dependencies = [ + "rand", +] [[package]] name = "jetscii" @@ -62,6 +83,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + [[package]] name = "logos" version = "0.13.0" @@ -106,6 +133,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.74" @@ -124,6 +157,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -182,6 +245,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "xmlparser" version = "0.13.6" diff --git a/crates/generate/Cargo.toml b/crates/generate/Cargo.toml index 36c704e..1ba9dc0 100644 --- a/crates/generate/Cargo.toml +++ b/crates/generate/Cargo.toml @@ -9,4 +9,5 @@ edition = "2021" eyre = "0.6.11" heck = "0.4.1" logos = "0.13.0" +rand = { version = "0.8.5", features = ["small_rng"] } strong-xml = "0.6.3" diff --git a/crates/generate/src/generate.rs b/crates/generate/src/generate.rs index 29a9be2..8c7e775 100644 --- a/crates/generate/src/generate.rs +++ b/crates/generate/src/generate.rs @@ -3,6 +3,7 @@ use crate::{ Intrinsic, }; use eyre::{bail, Context, OptionExt, Result}; +use rand::{rngs::SmallRng, Rng, SeedableRng}; pub fn generate(intrinsics: &[Intrinsic]) -> Result<()> { println!("impl Intrinsics for C {{}}"); @@ -14,7 +15,9 @@ pub fn generate(intrinsics: &[Intrinsic]) -> Result<()> { println!("}}"); println!(); - generate_soft_arch_module(intrinsics).context("generating soft_arch module")?; + generate_soft_arch_module(intrinsics).wrap_err("generating soft_arch module")?; + + generate_test_module(intrinsics).wrap_err("generating test module")?; Ok(()) } @@ -33,6 +36,21 @@ fn generate_soft_arch_module(intrinsics: &[Intrinsic]) -> Result<()> { Ok(()) } +fn generate_test_module(intrinsics: &[Intrinsic]) -> Result<()> { + println!("#[cfg(all(test, target_arch = \"x86_64\"))]"); + println!("pub mod tests {{"); + println!(" use super::super::compare_test_helper::hard_soft_same_128;"); + let rng = &mut SmallRng::seed_from_u64(44); + + for intr in intrinsics { + generate_intr_test(intr, rng) + .wrap_err_with(|| format!("generating soft_arch `{}`", intr.name))?; + } + + println!("}}"); + Ok(()) +} + fn generate_intr(intr: &Intrinsic) -> Result<(), eyre::Error> { eprintln!("generating {}...", intr.name); let signature = signature(intr)?; @@ -53,6 +71,20 @@ fn generate_intr_soft_arch_wrap(intr: &Intrinsic) -> Result<(), eyre::Error> { Ok(()) } +fn generate_intr_test(intr: &Intrinsic, rng: &mut SmallRng) -> Result<(), eyre::Error> { + eprintln!("generating test {}...", intr.name); + println!(" #[test]"); + let name = &intr.name; + println!(" fn {name}() {{"); + // TODO: non-128 + println!(" hard_soft_same_128! {{"); + let body = generate_body_test(intr, rng).wrap_err("generating body")?; + println!("{}", indent(&body, 12)); + println!(" }}"); + println!(" }}"); + Ok(()) +} + fn generate_body_soft_arch(intr: &Intrinsic) -> Result { let mut rust_stmts = Vec::::new(); @@ -74,6 +106,47 @@ fn generate_body_soft_arch(intr: &Intrinsic) -> Result { Ok(rust_stmts.join("\n")) } +fn generate_body_test(intr: &Intrinsic, rng: &mut SmallRng) -> Result { + let mut rust_stmts = Vec::::new(); + + let args = intr + .parameter + .iter() + .map(|param| -> Result<&str> { + let name = param.varname.as_deref().unwrap(); + let ty = map_type_to_rust(param.r#type.as_deref().unwrap()); + + rust_stmts.push(format!("let {name} = {};", random_value(ty, rng)?)); + Ok(name) + }) + .collect::>>() + .wrap_err("preparing arguments")? + .join(", "); + + let name = &intr.name; + rust_stmts.push(format!("{name}({args})")); + + Ok(rust_stmts.join("\n")) +} + +fn random_value(ty: &str, rng: &mut SmallRng) -> Result { + Ok(match ty { + "i16" => rng.gen::().to_string(), + "__m128i" => format!( + "_mm_setr_epi16({}, {}, {}, {}, {}, {}, {}, {})", + rng.gen::().to_string(), + rng.gen::().to_string(), + rng.gen::().to_string(), + rng.gen::().to_string(), + rng.gen::().to_string(), + rng.gen::().to_string(), + rng.gen::().to_string(), + rng.gen::().to_string(), + ), + _ => bail!("unknown type: {ty}"), + }) +} + fn indent(input: &str, indent: usize) -> String { let replace = format!("\n{}", " ".repeat(indent)); let mut s = " ".repeat(indent); @@ -98,6 +171,7 @@ impl VariableType { _ => bail!("unknown type: {ty}"), }; let (is_signed, elem_width) = match etype { + "SI8" => (true, 8), "SI16" => (true, 16), "UI8" => (false, 8), "UI16" => (false, 16), diff --git a/crates/generate/src/main.rs b/crates/generate/src/main.rs index 5924df2..616f4b7 100644 --- a/crates/generate/src/main.rs +++ b/crates/generate/src/main.rs @@ -86,4 +86,4 @@ fn main() -> Result<()> { Ok(()) } -const INTRINSICS_GENERATE: &[&str] = &["_mm_packus_epi16", "_mm_setr_epi16"]; +const INTRINSICS_GENERATE: &[&str] = &["_mm_packus_epi16", "_mm_packs_epi16", "_mm_setr_epi16"]; diff --git a/crates/generate/src/parse.rs b/crates/generate/src/parse.rs index 2ffeb87..1718246 100644 --- a/crates/generate/src/parse.rs +++ b/crates/generate/src/parse.rs @@ -1,7 +1,8 @@ -use eyre::{bail, ContextCompat, OptionExt, Result}; +use eyre::{bail, OptionExt, Result}; use logos::Logos; #[derive(Debug)] +#[allow(dead_code)] pub(crate) enum Stmt { FnDef, Assign { @@ -16,6 +17,7 @@ pub(crate) enum Stmt { } #[derive(Debug)] +#[allow(dead_code)] pub(crate) enum Expr { Int(u64), Ident(String), @@ -133,7 +135,7 @@ fn parse_expr_call(parser: &mut Parser) -> Result { loop { if parser.eat(Token::ParenOpen) { let arg = parse_expr(parser)?; - parser.expect(Token::ParenClose); + parser.expect(Token::ParenClose)?; let Expr::Ident(ident) = lhs else { panic!("lhs of function must be ident"); }; @@ -145,7 +147,7 @@ fn parse_expr_call(parser: &mut Parser) -> Result { } if parser.eat(Token::BracketOpen) { let arg = parse_expr(parser)?; - parser.expect(Token::BracketClose); + parser.expect(Token::BracketClose)?; lhs = Expr::Index { lhs: Box::new(lhs), idx: Box::new(arg), diff --git a/crates/intringen/Cargo.toml b/crates/intringen/Cargo.toml index 81f7bbd..b1366a3 100644 --- a/crates/intringen/Cargo.toml +++ b/crates/intringen/Cargo.toml @@ -6,3 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + +[dev-dependencies] +rand = { version = "0.8.5", features = ["small_rng"] } diff --git a/crates/intringen/src/x86/generated.rs b/crates/intringen/src/x86/generated.rs index aa058fc..5a118b3 100644 --- a/crates/intringen/src/x86/generated.rs +++ b/crates/intringen/src/x86/generated.rs @@ -18,6 +18,56 @@ pub trait Intrinsics: super::Core { let __tmp = self.cast_sign_i16_u16(e0); self.set_lane___m128i_u16(dst, 7, __tmp); } + fn _mm_packs_epi16(&mut self, dst: &mut Self::__m128i, a: Self::__m128i, b: Self::__m128i) { + let __tmp = self.get_lane___m128i_i16(a, 0); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 0, __tmp); + let __tmp = self.get_lane___m128i_i16(a, 1); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 1, __tmp); + let __tmp = self.get_lane___m128i_i16(a, 2); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 2, __tmp); + let __tmp = self.get_lane___m128i_i16(a, 3); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 3, __tmp); + let __tmp = self.get_lane___m128i_i16(a, 4); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 4, __tmp); + let __tmp = self.get_lane___m128i_i16(a, 5); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 5, __tmp); + let __tmp = self.get_lane___m128i_i16(a, 6); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 6, __tmp); + let __tmp = self.get_lane___m128i_i16(a, 7); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 7, __tmp); + let __tmp = self.get_lane___m128i_i16(b, 0); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 8, __tmp); + let __tmp = self.get_lane___m128i_i16(b, 1); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 9, __tmp); + let __tmp = self.get_lane___m128i_i16(b, 2); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 10, __tmp); + let __tmp = self.get_lane___m128i_i16(b, 3); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 11, __tmp); + let __tmp = self.get_lane___m128i_i16(b, 4); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 12, __tmp); + let __tmp = self.get_lane___m128i_i16(b, 5); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 13, __tmp); + let __tmp = self.get_lane___m128i_i16(b, 6); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 14, __tmp); + let __tmp = self.get_lane___m128i_i16(b, 7); + let __tmp = self.saturate8(__tmp); + self.set_lane___m128i_i8(dst, 15, __tmp); + } fn _mm_packus_epi16(&mut self, dst: &mut Self::__m128i, a: Self::__m128i, b: Self::__m128i) { let __tmp = self.get_lane___m128i_i16(a, 0); let __tmp = self.saturate_u8(__tmp); @@ -78,9 +128,48 @@ pub mod soft_arch { super::super::ValueCore._mm_setr_epi16(&mut output, e7, e6, e5, e4, e3, e2, e1, e0); output } + pub fn _mm_packs_epi16(a: __m128i, b: __m128i) -> __m128i { + let mut output = unsafe { std::mem::zeroed() }; + super::super::ValueCore._mm_packs_epi16(&mut output, a, b); + output + } pub fn _mm_packus_epi16(a: __m128i, b: __m128i) -> __m128i { let mut output = unsafe { std::mem::zeroed() }; super::super::ValueCore._mm_packus_epi16(&mut output, a, b); output } } +#[cfg(all(test, target_arch = "x86_64"))] +pub mod tests { + use super::super::compare_test_helper::hard_soft_same_128; + #[test] + fn _mm_setr_epi16() { + hard_soft_same_128! { + let e7 = -24391; + let e6 = 19541; + let e5 = -16509; + let e4 = 7733; + let e3 = -15140; + let e2 = 30719; + let e1 = 16513; + let e0 = 22878; + _mm_setr_epi16(e7, e6, e5, e4, e3, e2, e1, e0) + } + } + #[test] + fn _mm_packs_epi16() { + hard_soft_same_128! { + let a = _mm_setr_epi16(23986, 27900, -8343, -10648, 4841, 14610, -17251, -3971); + let b = _mm_setr_epi16(22390, -23547, 15401, 15832, -14212, -1286, -18062, 22296); + _mm_packs_epi16(a, b) + } + } + #[test] + fn _mm_packus_epi16() { + hard_soft_same_128! { + let a = _mm_setr_epi16(18077, 23617, -9205, 21233, -4332, -31339, 23623, -22080); + let b = _mm_setr_epi16(-1436, -30227, 8629, 10922, -16731, -1013, -14310, 2892); + _mm_packus_epi16(a, b) + } + } +} diff --git a/crates/intringen/src/x86/mod.rs b/crates/intringen/src/x86/mod.rs index 15d0a0c..16aaf5d 100644 --- a/crates/intringen/src/x86/mod.rs +++ b/crates/intringen/src/x86/mod.rs @@ -26,6 +26,7 @@ pub trait Core { fn set_lane___m128i_i8(&mut self, place: &mut Self::__m128i, idx: u64, value: Self::i8); fn set_lane___m128i_u16(&mut self, place: &mut Self::__m128i, idx: u64, value: Self::u16); + fn saturate8(&mut self, elem: Self::i16) -> Self::i8; fn saturate_u8(&mut self, elem: Self::i16) -> Self::u8; } @@ -77,6 +78,11 @@ impl Core for ValueCore { place[(idx * 2 + 1) as usize] = second; } + fn saturate8(&mut self, elem: Self::i16) -> Self::i8 { + let clamp = elem.clamp(i8::MIN as i16, i8::MAX as i16); + clamp as i8 + } + fn saturate_u8(&mut self, elem: Self::i16) -> Self::u8 { let clamp = elem.clamp(0, u8::MAX as i16); clamp as u8 @@ -87,5 +93,22 @@ mod soft_arch_types { pub type __m128i = [u8; 16]; } -#[cfg(test)] -mod tests; \ No newline at end of file +#[cfg(all(test, target_arch = "x86_64"))] +mod compare_test_helper { + macro_rules! hard_soft_same_128 { + ($($stmt:tt)*) => { + let soft = { + use crate::x86::soft_arch::*; + $($stmt)* + }; + let hard = unsafe { + std::mem::transmute::<_, [u8; 16]>({ + use core::arch::x86_64::*; + $($stmt)* + }) + }; + assert_eq!(soft, hard); + }; + } + pub(super) use hard_soft_same_128; +} diff --git a/crates/intringen/src/x86/tests.rs b/crates/intringen/src/x86/tests.rs deleted file mode 100644 index fc016b3..0000000 --- a/crates/intringen/src/x86/tests.rs +++ /dev/null @@ -1,34 +0,0 @@ -#[cfg(target_arch = "x86_64")] -mod x86_compare { - macro_rules! hard_soft_same_128 { - ($($stmt:tt)*) => { - let soft = { - use crate::x86::soft_arch::*; - $($stmt)* - }; - let hard = unsafe { - std::mem::transmute::<_, [u8; 16]>({ - use core::arch::x86_64::*; - $($stmt)* - }) - }; - assert_eq!(soft, hard); - }; - } - - #[test] - fn _mm_setr_epi16() { - hard_soft_same_128! { - _mm_setr_epi16(0, -1, 100, 3535, 35, 2, i16::MIN, i16::MAX) - }; - } - - #[test] - fn _mm_packus_epi16() { - hard_soft_same_128! { - let a = _mm_setr_epi16(0x100, -1, 0, 0, 0, 0, 0, 0); - let b = _mm_setr_epi16(0, 0, 0, 0, 0, 0, -1, 0x100); - _mm_packus_epi16(a, b) - } - } -}