diff options
| author | Henry <mail@henrygressmann.de> | 2026-04-03 23:40:04 +0200 |
|---|---|---|
| committer | Henry <mail@henrygressmann.de> | 2026-04-03 23:40:04 +0200 |
| commit | 1f741ba37d8217de333488d0a66fa6cc7f8537a7 (patch) | |
| tree | 0c37368d8160668b3a79bee4bc2b068be41e6cea /crates | |
| parent | a15acabfc1e23282d8fba9ec660f593b96709b2a (diff) | |
feat: add relaxed simd
Signed-off-by: Henry <mail@henrygressmann.de>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/parser/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/parser/src/visit.rs | 15 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/executor.rs | 28 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/value128.rs | 128 | ||||
| -rw-r--r-- | crates/tinywasm/tests/generated/wasm-relaxed-simd.csv | 2 | ||||
| -rw-r--r-- | crates/tinywasm/tests/test-wast.rs | 3 | ||||
| -rw-r--r-- | crates/tinywasm/tests/testsuite/run.rs | 32 | ||||
| -rw-r--r-- | crates/tinywasm/tests/testsuite/util.rs | 34 | ||||
| -rw-r--r-- | crates/types/src/instructions.rs | 28 |
9 files changed, 233 insertions, 39 deletions
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index ff2cacf..a799f69 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -79,7 +79,7 @@ impl Parser { exceptions: false, gc: false, memory_control: false, - relaxed_simd: false, + relaxed_simd: true, threads: false, shared_everything_threads: false, legacy_exceptions: false, diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 7eefa1c..e3de009 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -363,6 +363,7 @@ macro_rules! impl_visit_operator { (@@saturating_float_to_int $($rest:tt)* ) => {}; (@@bulk_memory $($rest:tt)* ) => {}; (@@simd $($rest:tt)* ) => {}; + (@@relaxed_simd $($rest:tt)* ) => {}; (@@tail_call $($rest:tt)* ) => {}; (@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*)) => { @@ -830,6 +831,7 @@ macro_rules! impl_visit_simd_operator { }; (@@simd $($rest:tt)* ) => {}; + (@@relaxed_simd $($rest:tt)* ) => {}; (@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*)) => { fn $visit(&mut self $($(,$arg: $argty)*)?) { self.unsupported(stringify!($visit)) @@ -879,6 +881,19 @@ impl<R: WasmModuleResources> wasmparser::VisitSimdOperator<'_> for FunctionBuild visit_f64x2_convert_low_i32x4_s(F64x2ConvertLowI32x4S), visit_f64x2_convert_low_i32x4_u(F64x2ConvertLowI32x4U), visit_f32x4_demote_f64x2_zero(F32x4DemoteF64x2Zero), visit_f64x2_promote_low_f32x4(F64x2PromoteLowF32x4), + visit_i8x16_relaxed_swizzle(I8x16RelaxedSwizzle), + visit_i32x4_relaxed_trunc_f32x4_s(I32x4RelaxedTruncF32x4S), visit_i32x4_relaxed_trunc_f32x4_u(I32x4RelaxedTruncF32x4U), + visit_i32x4_relaxed_trunc_f64x2_s_zero(I32x4RelaxedTruncF64x2SZero), visit_i32x4_relaxed_trunc_f64x2_u_zero(I32x4RelaxedTruncF64x2UZero), + visit_f32x4_relaxed_madd(F32x4RelaxedMadd), visit_f32x4_relaxed_nmadd(F32x4RelaxedNmadd), + visit_f64x2_relaxed_madd(F64x2RelaxedMadd), visit_f64x2_relaxed_nmadd(F64x2RelaxedNmadd), + visit_i8x16_relaxed_laneselect(I8x16RelaxedLaneselect), visit_i16x8_relaxed_laneselect(I16x8RelaxedLaneselect), + visit_i32x4_relaxed_laneselect(I32x4RelaxedLaneselect), visit_i64x2_relaxed_laneselect(I64x2RelaxedLaneselect), + visit_f32x4_relaxed_min(F32x4RelaxedMin), visit_f32x4_relaxed_max(F32x4RelaxedMax), + visit_f64x2_relaxed_min(F64x2RelaxedMin), visit_f64x2_relaxed_max(F64x2RelaxedMax), + visit_i16x8_relaxed_q15mulr_s(I16x8RelaxedQ15mulrS), + visit_i16x8_relaxed_dot_i8x16_i7x16_s(I16x8RelaxedDotI8x16I7x16S), + visit_i32x4_relaxed_dot_i8x16_i7x16_add_s(I32x4RelaxedDotI8x16I7x16AddS), + visit_i8x16_extract_lane_s(I8x16ExtractLaneS, u8), visit_i8x16_extract_lane_u(I8x16ExtractLaneU, u8), visit_i8x16_replace_lane(I8x16ReplaceLane, u8), visit_i16x8_extract_lane_s(I16x8ExtractLaneS, u8), visit_i16x8_extract_lane_u(I16x8ExtractLaneU, u8), visit_i16x8_replace_lane(I16x8ReplaceLane, u8), visit_i32x4_extract_lane(I32x4ExtractLane, u8), visit_i32x4_replace_lane(I32x4ReplaceLane, u8), diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index 85208bf..17343ee 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -152,12 +152,8 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { LocalCopy64(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value64>(&self.cf, *from)), LocalCopy128(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value128>(&self.cf, *from)), LocalCopyRef(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<ValueRef>(&self.cf, *from)), - I32AddLocals(a, b) => self.store.stack.values.push( - self.store.stack.values.local_get::<i32>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i32>(&self.cf, *b)), - )?, - I64AddLocals(a, b) => self.store.stack.values.push( - self.store.stack.values.local_get::<i64>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i64>(&self.cf, *b)), - )?, + I32AddLocals(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::<i32>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i32>(&self.cf, *b)))?, + I64AddLocals(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::<i64>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i64>(&self.cf, *b)))?, I32AddConst(c) => stack_op!(unary i32, |v| v.wrapping_add(*c)), I64AddConst(c) => stack_op!(unary i64, |v| v.wrapping_add(*c)), I32StoreLocalLocal(m, addr_local, value_local) => { @@ -394,6 +390,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { V128Bitselect => stack_op!(ternary Value128, |v1, v2, c| Value128::v128_bitselect(v1, v2, c)), V128AnyTrue => stack_op!(unary Value128 => i32, |v| v.v128_any_true() as i32), I8x16Swizzle => stack_op!(binary Value128, |a, s| a.i8x16_swizzle(s)), + I8x16RelaxedSwizzle => stack_op!(binary Value128, |a, s| a.i8x16_relaxed_swizzle(s)), V128Load(arg) => self.exec_mem_load::<Value128, 16, _>(arg.mem_addr(), arg.offset(), |v| v)?, V128Load8x8S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_s(v.to_le_bytes()))?, V128Load8x8U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_u(v.to_le_bytes()))?, @@ -582,6 +579,13 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { I8x16Shuffle(idx) => { let idx = self.func.data.v128_constants[*idx as usize].to_le_bytes(); stack_op!(binary Value128, |a, b| Value128::i8x16_shuffle(a, b, idx)) } I16x8Q15MulrSatS => stack_op!(binary Value128, |a, b| a.i16x8_q15mulr_sat_s(b)), I32x4DotI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_dot_i16x8_s(b)), + I8x16RelaxedLaneselect => stack_op!(ternary Value128, |v1, v2, c| Value128::i8x16_relaxed_laneselect(v1, v2, c)), + I16x8RelaxedLaneselect => stack_op!(ternary Value128, |v1, v2, c| Value128::i16x8_relaxed_laneselect(v1, v2, c)), + I32x4RelaxedLaneselect => stack_op!(ternary Value128, |v1, v2, c| Value128::i32x4_relaxed_laneselect(v1, v2, c)), + I64x2RelaxedLaneselect => stack_op!(ternary Value128, |v1, v2, c| Value128::i64x2_relaxed_laneselect(v1, v2, c)), + I16x8RelaxedQ15mulrS => stack_op!(binary Value128, |a, b| a.i16x8_relaxed_q15mulr_s(b)), + I16x8RelaxedDotI8x16I7x16S => stack_op!(binary Value128, |a, b| a.i16x8_relaxed_dot_i8x16_i7x16_s(b)), + I32x4RelaxedDotI8x16I7x16AddS => stack_op!(ternary Value128, |a, b, c| a.i32x4_relaxed_dot_i8x16_i7x16_add_s(b, c)), F32x4Ceil => stack_op!(simd_unary f32x4_ceil), F64x2Ceil => stack_op!(simd_unary f64x2_ceil), F32x4Floor => stack_op!(simd_unary f32x4_floor), @@ -612,6 +616,14 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { F32x4PMax => stack_op!(simd_binary f32x4_pmax), F64x2PMin => stack_op!(simd_binary f64x2_pmin), F64x2PMax => stack_op!(simd_binary f64x2_pmax), + F32x4RelaxedMadd => stack_op!(ternary Value128, |a, b, c| a.f32x4_relaxed_madd(b, c)), + F32x4RelaxedNmadd => stack_op!(ternary Value128, |a, b, c| a.f32x4_relaxed_nmadd(b, c)), + F64x2RelaxedMadd => stack_op!(ternary Value128, |a, b, c| a.f64x2_relaxed_madd(b, c)), + F64x2RelaxedNmadd => stack_op!(ternary Value128, |a, b, c| a.f64x2_relaxed_nmadd(b, c)), + F32x4RelaxedMin => stack_op!(binary Value128, |a, b| a.f32x4_relaxed_min(b)), + F32x4RelaxedMax => stack_op!(binary Value128, |a, b| a.f32x4_relaxed_max(b)), + F64x2RelaxedMin => stack_op!(binary Value128, |a, b| a.f64x2_relaxed_min(b)), + F64x2RelaxedMax => stack_op!(binary Value128, |a, b| a.f64x2_relaxed_max(b)), I32x4TruncSatF32x4S => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f32x4_s()), I32x4TruncSatF32x4U => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f32x4_u()), F32x4ConvertI32x4S => stack_op!(unary Value128, |v| v.f32x4_convert_i32x4_s()), @@ -622,6 +634,10 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { F64x2PromoteLowF32x4 => stack_op!(unary Value128, |v| v.f64x2_promote_low_f32x4()), I32x4TruncSatF64x2SZero => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f64x2_s_zero()), I32x4TruncSatF64x2UZero => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f64x2_u_zero()), + I32x4RelaxedTruncF32x4S => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f32x4_s()), + I32x4RelaxedTruncF32x4U => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f32x4_u()), + I32x4RelaxedTruncF64x2SZero => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f64x2_s_zero()), + I32x4RelaxedTruncF64x2UZero => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f64x2_u_zero()), }; self.cf.incr_instr_ptr(); diff --git a/crates/tinywasm/src/interpreter/value128.rs b/crates/tinywasm/src/interpreter/value128.rs index 9ccb7fc..c585b35 100644 --- a/crates/tinywasm/src/interpreter/value128.rs +++ b/crates/tinywasm/src/interpreter/value128.rs @@ -609,6 +609,11 @@ impl Value128 { Self::from_le_bytes(out) } + #[doc(alias = "i8x16.relaxed_swizzle")] + pub fn i8x16_relaxed_swizzle(self, s: Self) -> Self { + self.i8x16_swizzle(s) + } + #[doc(alias = "i8x16.shuffle")] pub fn i8x16_shuffle(a: Self, b: Self, idx: [u8; 16]) -> Self { let mut src = [0u8; 32]; @@ -938,6 +943,65 @@ impl Value128 { Self::from_i32x4(out) } + #[doc(alias = "i8x16.relaxed_laneselect")] + pub fn i8x16_relaxed_laneselect(v1: Self, v2: Self, c: Self) -> Self { + Self::v128_bitselect(v1, v2, c) + } + + #[doc(alias = "i16x8.relaxed_laneselect")] + pub fn i16x8_relaxed_laneselect(v1: Self, v2: Self, c: Self) -> Self { + Self::v128_bitselect(v1, v2, c) + } + + #[doc(alias = "i32x4.relaxed_laneselect")] + pub fn i32x4_relaxed_laneselect(v1: Self, v2: Self, c: Self) -> Self { + Self::v128_bitselect(v1, v2, c) + } + + #[doc(alias = "i64x2.relaxed_laneselect")] + pub fn i64x2_relaxed_laneselect(v1: Self, v2: Self, c: Self) -> Self { + Self::v128_bitselect(v1, v2, c) + } + + #[doc(alias = "i16x8.relaxed_q15mulr_s")] + pub fn i16x8_relaxed_q15mulr_s(self, rhs: Self) -> Self { + self.i16x8_q15mulr_sat_s(rhs) + } + + #[doc(alias = "i16x8.relaxed_dot_i8x16_i7x16_s")] + pub fn i16x8_relaxed_dot_i8x16_i7x16_s(self, rhs: Self) -> Self { + let a = self.as_i8x16(); + let b = rhs.as_i8x16(); + let mut out = [0i16; 8]; + + for (dst, (a_pair, b_pair)) in out.iter_mut().zip(a.chunks_exact(2).zip(b.chunks_exact(2))) { + let prod0 = (a_pair[0] as i16) * (b_pair[0] as i16); + let prod1 = (a_pair[1] as i16) * (b_pair[1] as i16); + *dst = prod0.wrapping_add(prod1); + } + + Self::from_i16x8(out) + } + + #[doc(alias = "i32x4.relaxed_dot_i8x16_i7x16_add_s")] + pub fn i32x4_relaxed_dot_i8x16_i7x16_add_s(self, rhs: Self, acc: Self) -> Self { + let a = self.as_i8x16(); + let b = rhs.as_i8x16(); + let c = acc.as_i32x4(); + let mut out = [0i32; 4]; + + for (i, dst) in out.iter_mut().enumerate() { + let base = i * 4; + let mut sum = 0i32; + for j in 0..4 { + sum = sum.wrapping_add((a[base + j] as i32).wrapping_mul(b[base + j] as i32)); + } + *dst = sum.wrapping_add(c[i]); + } + + Self::from_i32x4(out) + } + simd_cmp_mask!(i8x16_eq, "i8x16.eq", i8x16_eq, i8, 16, as_i8x16, from_i8x16, ==); simd_cmp_mask!(i16x8_eq, "i16x8.eq", i16x8_eq, i16, 8, as_i16x8, from_i16x8, ==); simd_cmp_mask!(i32x4_eq, "i32x4.eq", i32x4_eq, i32, 4, as_i32x4, from_i32x4, ==); @@ -1058,6 +1122,70 @@ impl Value128 { simd_float_binary!(f32x4_pmax, "f32x4.pmax", zip_f32x4, |a, b| if b > a { b } else { a }); simd_float_binary!(f64x2_pmax, "f64x2.pmax", zip_f64x2, |a, b| if b > a { b } else { a }); + #[doc(alias = "f32x4.relaxed_madd")] + pub fn f32x4_relaxed_madd(self, b: Self, c: Self) -> Self { + self.zip_f32x4(b, |x, y| canonicalize_simd_f32_nan(x * y)) + .zip_f32x4(c, |xy, z| canonicalize_simd_f32_nan(xy + z)) + } + + #[doc(alias = "f32x4.relaxed_nmadd")] + pub fn f32x4_relaxed_nmadd(self, b: Self, c: Self) -> Self { + self.zip_f32x4(b, |x, y| canonicalize_simd_f32_nan(-(x * y))) + .zip_f32x4(c, |neg_xy, z| canonicalize_simd_f32_nan(neg_xy + z)) + } + + #[doc(alias = "f64x2.relaxed_madd")] + pub fn f64x2_relaxed_madd(self, b: Self, c: Self) -> Self { + self.zip_f64x2(b, |x, y| canonicalize_simd_f64_nan(x * y)) + .zip_f64x2(c, |xy, z| canonicalize_simd_f64_nan(xy + z)) + } + + #[doc(alias = "f64x2.relaxed_nmadd")] + pub fn f64x2_relaxed_nmadd(self, b: Self, c: Self) -> Self { + self.zip_f64x2(b, |x, y| canonicalize_simd_f64_nan(-(x * y))) + .zip_f64x2(c, |neg_xy, z| canonicalize_simd_f64_nan(neg_xy + z)) + } + + #[doc(alias = "f32x4.relaxed_min")] + pub fn f32x4_relaxed_min(self, rhs: Self) -> Self { + self.f32x4_min(rhs) + } + + #[doc(alias = "f64x2.relaxed_min")] + pub fn f64x2_relaxed_min(self, rhs: Self) -> Self { + self.f64x2_min(rhs) + } + + #[doc(alias = "f32x4.relaxed_max")] + pub fn f32x4_relaxed_max(self, rhs: Self) -> Self { + self.f32x4_max(rhs) + } + + #[doc(alias = "f64x2.relaxed_max")] + pub fn f64x2_relaxed_max(self, rhs: Self) -> Self { + self.f64x2_max(rhs) + } + + #[doc(alias = "i32x4.relaxed_trunc_f32x4_s")] + pub fn i32x4_relaxed_trunc_f32x4_s(self) -> Self { + self.i32x4_trunc_sat_f32x4_s() + } + + #[doc(alias = "i32x4.relaxed_trunc_f32x4_u")] + pub fn i32x4_relaxed_trunc_f32x4_u(self) -> Self { + self.i32x4_trunc_sat_f32x4_u() + } + + #[doc(alias = "i32x4.relaxed_trunc_f64x2_s_zero")] + pub fn i32x4_relaxed_trunc_f64x2_s_zero(self) -> Self { + self.i32x4_trunc_sat_f64x2_s_zero() + } + + #[doc(alias = "i32x4.relaxed_trunc_f64x2_u_zero")] + pub fn i32x4_relaxed_trunc_f64x2_u_zero(self) -> Self { + self.i32x4_trunc_sat_f64x2_u_zero() + } + #[doc(alias = "i32x4.trunc_sat_f32x4_s")] pub fn i32x4_trunc_sat_f32x4_s(self) -> Self { let v = self.as_f32x4(); diff --git a/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv b/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv index a6e6228..6b89d8a 100644 --- a/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv +++ b/crates/tinywasm/tests/generated/wasm-relaxed-simd.csv @@ -1 +1 @@ -0.9.0-alpha.0,0,93,[{"name":"i16x8_relaxed_q15mulr_s.wast","passed":0,"failed":3},{"name":"i32x4_relaxed_trunc.wast","passed":0,"failed":17},{"name":"i8x16_relaxed_swizzle.wast","passed":0,"failed":6},{"name":"relaxed_dot_product.wast","passed":0,"failed":11},{"name":"relaxed_laneselect.wast","passed":0,"failed":12},{"name":"relaxed_madd_nmadd.wast","passed":0,"failed":19},{"name":"relaxed_min_max.wast","passed":0,"failed":25}] +0.9.0-alpha.0,93,0,[{"name":"i16x8_relaxed_q15mulr_s.wast","passed":3,"failed":0},{"name":"i32x4_relaxed_trunc.wast","passed":17,"failed":0},{"name":"i8x16_relaxed_swizzle.wast","passed":6,"failed":0},{"name":"relaxed_dot_product.wast","passed":11,"failed":0},{"name":"relaxed_laneselect.wast","passed":12,"failed":0},{"name":"relaxed_madd_nmadd.wast","passed":19,"failed":0},{"name":"relaxed_min_max.wast","passed":25,"failed":0}] diff --git a/crates/tinywasm/tests/test-wast.rs b/crates/tinywasm/tests/test-wast.rs index 40b0e04..ff2b2ed 100644 --- a/crates/tinywasm/tests/test-wast.rs +++ b/crates/tinywasm/tests/test-wast.rs @@ -1,7 +1,6 @@ use std::path::PathBuf; -use eyre::{Result, bail, eyre}; -use owo_colors::OwoColorize; +use eyre::{Result, bail}; use testsuite::TestSuite; mod testsuite; diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs index 6352730..d13c59b 100644 --- a/crates/tinywasm/tests/testsuite/run.rs +++ b/crates/tinywasm/tests/testsuite/run.rs @@ -410,7 +410,7 @@ impl TestSuite { AssertReturn { span, exec, results } => { info!("AssertReturn: {exec:?}"); - let expected = match convert_wastret(results.into_iter()) { + let expected_alternatives = match convert_wastret(results.into_iter()) { Err(err) => { test_group.add_result( &format!("AssertReturn(unsupported-{i})"), @@ -449,14 +449,19 @@ impl TestSuite { continue; } }; - let expected = expected.first().expect("expected global value"); - let module_global = module_global.attach_type(expected.val_type()); + let expected = expected_alternatives + .iter() + .filter_map(|alts| alts.first()) + .find(|exp| module_global.attach_type(exp.val_type()).eq_loose(exp)); - if !module_global.eq_loose(expected) { + if expected.is_none() { test_group.add_result( &format!("AssertReturn(unsupported-{i})"), span.linecol_in(wast_raw), - Err(eyre!("global value did not match: {:?} != {:?}", module_global, expected)), + Err(eyre!( + "global value did not match any expected alternative: {:?}", + module_global + )), ); continue; } @@ -493,20 +498,23 @@ impl TestSuite { e })?; - if outcomes.len() != expected.len() { + if !expected_alternatives.iter().any(|expected| expected.len() == outcomes.len()) { return Err(eyre!( "span: {:?} expected {} results, got {}", span, - expected.len(), + expected_alternatives.first().map_or(0, |v| v.len()), outcomes.len() )); } - outcomes.iter().zip(expected).enumerate().try_for_each(|(i, (outcome, exp))| { - (outcome.eq_loose(&exp)) - .then_some(()) - .ok_or_else(|| eyre!(" result {} did not match: {:?} != {:?}", i, outcome, exp)) - }) + if expected_alternatives.iter().any(|expected| { + expected.len() == outcomes.len() + && outcomes.iter().zip(expected.iter()).all(|(outcome, exp)| outcome.eq_loose(exp)) + }) { + Ok(()) + } else { + Err(eyre!("results did not match any expected alternative")) + } }); let res = res.map_err(|e| eyre!("test panicked: {:?}", try_downcast_panic(e))).and_then(|r| r); diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs index 4699d69..851fa20 100644 --- a/crates/tinywasm/tests/testsuite/util.rs +++ b/crates/tinywasm/tests/testsuite/util.rs @@ -87,8 +87,27 @@ pub fn convert_wastargs(args: Vec<wast::WastArg>) -> Result<Vec<tinywasm_types:: args.into_iter().map(|a| wastarg2tinywasmvalue(a)).collect() } -pub fn convert_wastret<'a>(args: impl Iterator<Item = wast::WastRet<'a>>) -> Result<Vec<tinywasm_types::WasmValue>> { - args.map(|a| wastret2tinywasmvalue(a)).collect() +pub fn convert_wastret<'a>( + args: impl Iterator<Item = wast::WastRet<'a>>, +) -> Result<Vec<Vec<tinywasm_types::WasmValue>>> { + let mut alternatives = vec![Vec::new()]; + + for arg in args { + let choices = wastret2tinywasmvalues(arg)?; + let mut next = Vec::with_capacity(alternatives.len() * choices.len()); + + for prefix in alternatives { + for choice in &choices { + let mut candidate = prefix.clone(); + candidate.push(*choice); + next.push(candidate); + } + } + + alternatives = next; + } + + Ok(alternatives) } fn wastarg2tinywasmvalue(arg: wast::WastArg) -> Result<tinywasm_types::WasmValue> { @@ -134,11 +153,20 @@ fn wast_i128_to_i128(i: wast::core::V128Pattern) -> i128 { i128::from_le_bytes(res.try_into().unwrap()) } -fn wastret2tinywasmvalue(ret: wast::WastRet) -> Result<tinywasm_types::WasmValue> { +fn wastret2tinywasmvalues(ret: wast::WastRet) -> Result<Vec<tinywasm_types::WasmValue>> { let wast::WastRet::Core(ret) = ret else { bail!("unsupported arg type"); }; + match ret { + wast::core::WastRetCore::Either(options) => { + options.into_iter().map(wastretcore2tinywasmvalue).collect::<Result<Vec<_>>>() + } + ret => Ok(vec![wastretcore2tinywasmvalue(ret)?]), + } +} + +fn wastretcore2tinywasmvalue(ret: wast::core::WastRetCore) -> Result<tinywasm_types::WasmValue> { use wast::core::WastRetCore::{F32, F64, I32, I64, RefExtern, RefFunc, RefNull, V128}; Ok(match ret { F32(f) => nanpattern2tinywasmvalue(f)?, diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index 83d0da8..59dc49f 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -237,18 +237,18 @@ pub enum Instruction { F32x4DemoteF64x2Zero, F64x2PromoteLowF32x4, // > Relaxed SIMD - // I8x16RelaxedSwizzle, - // I32x4RelaxedTruncF32x4S, I32x4RelaxedTruncF32x4U, - // I32x4RelaxedTruncF64x2SZero, I32x4RelaxedTruncF64x2UZero, - // F32x4RelaxedMadd, F32x4RelaxedNmadd, - // F64x2RelaxedMadd, F64x2RelaxedNmadd, - // I8x16RelaxedLaneselect, - // I16x8RelaxedLaneselect, - // I32x4RelaxedLaneselect, - // I64x2RelaxedLaneselect, - // F32x4RelaxedMin, F32x4RelaxedMax, - // F64x2RelaxedMin, F64x2RelaxedMax, - // I16x8RelaxedQ15mulrS, - // I16x8RelaxedDotI8x16I7x16S, - // I32x4RelaxedDotI8x16I7x16AddS + I8x16RelaxedSwizzle, + I32x4RelaxedTruncF32x4S, I32x4RelaxedTruncF32x4U, + I32x4RelaxedTruncF64x2SZero, I32x4RelaxedTruncF64x2UZero, + F32x4RelaxedMadd, F32x4RelaxedNmadd, + F64x2RelaxedMadd, F64x2RelaxedNmadd, + I8x16RelaxedLaneselect, + I16x8RelaxedLaneselect, + I32x4RelaxedLaneselect, + I64x2RelaxedLaneselect, + F32x4RelaxedMin, F32x4RelaxedMax, + F64x2RelaxedMin, F64x2RelaxedMax, + I16x8RelaxedQ15mulrS, + I16x8RelaxedDotI8x16I7x16S, + I32x4RelaxedDotI8x16I7x16AddS } |
