generate tests

This commit is contained in:
nora 2024-01-05 22:54:08 +01:00
parent 8f49c199cc
commit 1dcfb83114
9 changed files with 268 additions and 41 deletions

69
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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<C: super::Core> 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<String> {
let mut rust_stmts = Vec::<String>::new();
@ -74,6 +106,47 @@ fn generate_body_soft_arch(intr: &Intrinsic) -> Result<String> {
Ok(rust_stmts.join("\n"))
}
fn generate_body_test(intr: &Intrinsic, rng: &mut SmallRng) -> Result<String> {
let mut rust_stmts = Vec::<String>::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::<Result<Vec<_>>>()
.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<String> {
Ok(match ty {
"i16" => rng.gen::<i16>().to_string(),
"__m128i" => format!(
"_mm_setr_epi16({}, {}, {}, {}, {}, {}, {}, {})",
rng.gen::<i16>().to_string(),
rng.gen::<i16>().to_string(),
rng.gen::<i16>().to_string(),
rng.gen::<i16>().to_string(),
rng.gen::<i16>().to_string(),
rng.gen::<i16>().to_string(),
rng.gen::<i16>().to_string(),
rng.gen::<i16>().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),

View file

@ -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"];

View file

@ -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<Expr> {
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<Expr> {
}
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),

View file

@ -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"] }

View file

@ -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)
}
}
}

View file

@ -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;
#[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;
}

View file

@ -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)
}
}
}