From b7c3b6ab8890951b88b5c399ac055461faaee597 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 18 Oct 2022 13:59:03 +0200 Subject: [PATCH 1/8] add benchmarks to test root call overhead with different amounts of parameters and results --- crates/wasmi/benches/benches.rs | 212 ++++++++++++++++++++++++- crates/wasmi/benches/wat/bare_call.wat | 30 ++++ 2 files changed, 234 insertions(+), 8 deletions(-) create mode 100644 crates/wasmi/benches/wat/bare_call.wat diff --git a/crates/wasmi/benches/benches.rs b/crates/wasmi/benches/benches.rs index b0a8358bcf..df16128263 100644 --- a/crates/wasmi/benches/benches.rs +++ b/crates/wasmi/benches/benches.rs @@ -11,7 +11,7 @@ use criterion::{criterion_group, criterion_main, Bencher, Criterion}; use std::{slice, time::Duration}; use wasmi as v1; use wasmi::core::Value; -use wasmi_core::{F32, F64}; +use wasmi_core::{F32, F64, ValueType}; const WASM_KERNEL: &str = "benches/wasm/wasm_kernel/target/wasm32-unknown-unknown/release/wasm_kernel.wasm"; @@ -48,6 +48,14 @@ criterion_group! { bench_execute_regex_redux, bench_execute_count_until, bench_execute_trunc_f2i, + bench_execute_typed_bare_call_0, + bench_execute_typed_bare_call_1, + bench_execute_typed_bare_call_4, + bench_execute_typed_bare_call_16, + bench_execute_bare_call_0, + bench_execute_bare_call_1, + bench_execute_bare_call_4, + bench_execute_bare_call_16, bench_execute_global_bump, bench_execute_fac_recursive, bench_execute_fac_opt, @@ -273,6 +281,196 @@ fn bench_execute_trunc_f2i(c: &mut Criterion) { }); } +fn bench_execute_typed_bare_call_0(c: &mut Criterion) { + const REPETITIONS: usize = 20_000; + c.bench_function("execute/bare_call_0/typed", |b| { + let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/bare_call.wat")); + let bare_call = instance + .get_export(&store, "bare_call_0") + .and_then(v1::Extern::into_func) + .unwrap(); + let bare_call = bare_call.typed::<(), ()>(&store).unwrap(); + b.iter(|| { + for _ in 0..REPETITIONS { + bare_call + .call(&mut store, ()) + .unwrap(); + } + }) + }); +} + +fn bench_execute_typed_bare_call_1(c: &mut Criterion) { + const REPETITIONS: usize = 20_000; + c.bench_function("execute/bare_call_1/typed", |b| { + let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/bare_call.wat")); + let bare_call = instance + .get_export(&store, "bare_call_1") + .and_then(v1::Extern::into_func) + .unwrap(); + let bare_call = bare_call.typed::(&store).unwrap(); + b.iter(|| { + for _ in 0..REPETITIONS { + let _ = bare_call + .call(&mut store, 0) + .unwrap(); + } + }) + }); +} + +fn bench_execute_typed_bare_call_4(c: &mut Criterion) { + const REPETITIONS: usize = 20_000; + type InOut = (i32, i64, F32, F64); + c.bench_function("execute/bare_call_4/typed", |b| { + let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/bare_call.wat")); + let bare_call = instance + .get_export(&store, "bare_call_4") + .and_then(v1::Extern::into_func) + .unwrap(); + let bare_call = bare_call.typed::(&store).unwrap(); + b.iter(|| { + for _ in 0..REPETITIONS { + let _ = bare_call + .call(&mut store, (0, 0, F32::from(0.0), F64::from(0.0))) + .unwrap(); + } + }) + }); +} + +fn bench_execute_typed_bare_call_16(c: &mut Criterion) { + const REPETITIONS: usize = 20_000; + type InOut = ( + i32, i64, F32, F64, + i32, i64, F32, F64, + i32, i64, F32, F64, + i32, i64, F32, F64, + ); + c.bench_function("execute/bare_call_16/typed", |b| { + let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/bare_call.wat")); + let bare_call = instance + .get_export(&store, "bare_call_16") + .and_then(v1::Extern::into_func) + .unwrap(); + let bare_call = bare_call.typed::(&store).unwrap(); + b.iter(|| { + for _ in 0..REPETITIONS { + let _ = bare_call + .call(&mut store, ( + 0, 0, F32::from(0.0), F64::from(0.0), + 0, 0, F32::from(0.0), F64::from(0.0), + 0, 0, F32::from(0.0), F64::from(0.0), + 0, 0, F32::from(0.0), F64::from(0.0), + )) + .unwrap(); + } + }) + }); +} + +fn bench_execute_bare_call_0(c: &mut Criterion) { + const REPETITIONS: usize = 20_000; + c.bench_function("execute/bare_call_0", |b| { + let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/bare_call.wat")); + let bare_call = instance + .get_export(&store, "bare_call_0") + .and_then(v1::Extern::into_func) + .unwrap(); + let params = &[]; + let results = &mut []; + b.iter(|| { + for _ in 0..REPETITIONS { + bare_call + .call(&mut store, params, results) + .unwrap(); + } + }) + }); +} + +fn bench_execute_bare_call_1(c: &mut Criterion) { + const REPETITIONS: usize = 20_000; + c.bench_function("execute/bare_call_1", |b| { + let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/bare_call.wat")); + let bare_call = instance + .get_export(&store, "bare_call_1") + .and_then(v1::Extern::into_func) + .unwrap(); + let params = &[Value::I32(0)]; + let results = &mut [Value::I32(0)]; + b.iter(|| { + for _ in 0..REPETITIONS { + bare_call + .call(&mut store, params, results) + .unwrap(); + } + }) + }); +} + +fn bench_execute_bare_call_4(c: &mut Criterion) { + const REPETITIONS: usize = 20_000; + c.bench_function("execute/bare_call_4", |b| { + let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/bare_call.wat")); + let bare_call = instance + .get_export(&store, "bare_call_4") + .and_then(v1::Extern::into_func) + .unwrap(); + let params = &[ + Value::default(ValueType::I32), + Value::default(ValueType::I64), + Value::default(ValueType::F32), + Value::default(ValueType::F64), + ]; + let results = &mut [Value::I32(0); 4]; + b.iter(|| { + for _ in 0..REPETITIONS { + bare_call + .call(&mut store, params, results) + .unwrap(); + } + }) + }); +} + +fn bench_execute_bare_call_16(c: &mut Criterion) { + const REPETITIONS: usize = 20_000; + c.bench_function("execute/bare_call_16", |b| { + let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/bare_call.wat")); + let bare_call = instance + .get_export(&store, "bare_call_16") + .and_then(v1::Extern::into_func) + .unwrap(); + let params = &[ + Value::default(ValueType::I32), + Value::default(ValueType::I64), + Value::default(ValueType::F32), + Value::default(ValueType::F64), + Value::default(ValueType::I32), + Value::default(ValueType::I64), + Value::default(ValueType::F32), + Value::default(ValueType::F64), + Value::default(ValueType::I32), + Value::default(ValueType::I64), + Value::default(ValueType::F32), + Value::default(ValueType::F64), + Value::default(ValueType::I32), + Value::default(ValueType::I64), + Value::default(ValueType::F32), + Value::default(ValueType::F64), + ]; + let results = &mut [Value::I32(0); 16]; + b.iter(|| { + for _ in 0..REPETITIONS { + bare_call + .call(&mut store, params, results) + .unwrap(); + } + }) + }); +} + fn bench_execute_global_bump(c: &mut Criterion) { const BUMP_AMOUNT: i32 = 100_000; c.bench_function("execute/global_bump", |b| { @@ -326,9 +524,8 @@ fn bench_execute_fac_opt(c: &mut Criterion) { }); } -const RECURSIVE_DEPTH: i32 = 8000; - fn bench_execute_recursive_ok(c: &mut Criterion) { + const RECURSIVE_DEPTH: i32 = 8000; c.bench_function("execute/recursive_ok", |b| { let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/recursive_ok.wat")); let bench_call = instance @@ -346,11 +543,10 @@ fn bench_execute_recursive_ok(c: &mut Criterion) { }); } -const RECURSIVE_SCAN_DEPTH: i32 = 8000; -const RECURSIVE_SCAN_EXPECTED: i32 = - ((RECURSIVE_SCAN_DEPTH * RECURSIVE_SCAN_DEPTH) + RECURSIVE_SCAN_DEPTH) / 2; - fn bench_execute_recursive_scan(c: &mut Criterion) { + const RECURSIVE_SCAN_DEPTH: i32 = 8000; + const RECURSIVE_SCAN_EXPECTED: i32 = + ((RECURSIVE_SCAN_DEPTH * RECURSIVE_SCAN_DEPTH) + RECURSIVE_SCAN_DEPTH) / 2; c.bench_function("execute/recursive_scan", |b| { let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/recursive_scan.wat")); @@ -362,7 +558,7 @@ fn bench_execute_recursive_scan(c: &mut Criterion) { b.iter(|| { bench_call - .call(&mut store, &[Value::I32(RECURSIVE_DEPTH)], &mut result) + .call(&mut store, &[Value::I32(RECURSIVE_SCAN_DEPTH)], &mut result) .unwrap(); assert_eq!(result, [Value::I32(RECURSIVE_SCAN_EXPECTED)]); }) diff --git a/crates/wasmi/benches/wat/bare_call.wat b/crates/wasmi/benches/wat/bare_call.wat new file mode 100644 index 0000000000..7748a6b166 --- /dev/null +++ b/crates/wasmi/benches/wat/bare_call.wat @@ -0,0 +1,30 @@ +(module + (func (export "bare_call_0") (param) (result)) + (func (export "bare_call_1") (param i32) (result i32) local.get 0) + (func (export "bare_call_4") (param i32 i64 f32 f64) (result i32 i64 f32 f64) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + ) + (func (export "bare_call_16") + (param i32 i64 f32 f64 i32 i64 f32 f64 i32 i64 f32 f64 i32 i64 f32 f64) + (result i32 i64 f32 f64 i32 i64 f32 f64 i32 i64 f32 f64 i32 i64 f32 f64) + local.get 0 + local.get 1 + local.get 2 + local.get 3 + local.get 4 + local.get 5 + local.get 6 + local.get 7 + local.get 8 + local.get 9 + local.get 10 + local.get 11 + local.get 12 + local.get 13 + local.get 14 + local.get 15 + ) +) From df9947005e47f3b0d77178e33729def4ebd58de1 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 18 Oct 2022 14:06:56 +0200 Subject: [PATCH 2/8] optimize TypedFunc::call at cost of Func::call --- crates/wasmi/benches/benches.rs | 73 ++++++++++++++++----------- crates/wasmi/src/engine/mod.rs | 36 ++----------- crates/wasmi/src/engine/traits.rs | 78 +++++++++++++---------------- crates/wasmi/src/func/into_func.rs | 55 ++++++++++++++++++++ crates/wasmi/src/func/mod.rs | 3 ++ crates/wasmi/src/func/typed_func.rs | 30 +++-------- 6 files changed, 150 insertions(+), 125 deletions(-) diff --git a/crates/wasmi/benches/benches.rs b/crates/wasmi/benches/benches.rs index df16128263..4822c90724 100644 --- a/crates/wasmi/benches/benches.rs +++ b/crates/wasmi/benches/benches.rs @@ -11,7 +11,7 @@ use criterion::{criterion_group, criterion_main, Bencher, Criterion}; use std::{slice, time::Duration}; use wasmi as v1; use wasmi::core::Value; -use wasmi_core::{F32, F64, ValueType}; +use wasmi_core::{ValueType, F32, F64}; const WASM_KERNEL: &str = "benches/wasm/wasm_kernel/target/wasm32-unknown-unknown/release/wasm_kernel.wasm"; @@ -292,9 +292,7 @@ fn bench_execute_typed_bare_call_0(c: &mut Criterion) { let bare_call = bare_call.typed::<(), ()>(&store).unwrap(); b.iter(|| { for _ in 0..REPETITIONS { - bare_call - .call(&mut store, ()) - .unwrap(); + bare_call.call(&mut store, ()).unwrap(); } }) }); @@ -311,9 +309,7 @@ fn bench_execute_typed_bare_call_1(c: &mut Criterion) { let bare_call = bare_call.typed::(&store).unwrap(); b.iter(|| { for _ in 0..REPETITIONS { - let _ = bare_call - .call(&mut store, 0) - .unwrap(); + let _ = bare_call.call(&mut store, 0).unwrap(); } }) }); @@ -342,10 +338,22 @@ fn bench_execute_typed_bare_call_4(c: &mut Criterion) { fn bench_execute_typed_bare_call_16(c: &mut Criterion) { const REPETITIONS: usize = 20_000; type InOut = ( - i32, i64, F32, F64, - i32, i64, F32, F64, - i32, i64, F32, F64, - i32, i64, F32, F64, + i32, + i64, + F32, + F64, + i32, + i64, + F32, + F64, + i32, + i64, + F32, + F64, + i32, + i64, + F32, + F64, ); c.bench_function("execute/bare_call_16/typed", |b| { let (mut store, instance) = load_instance_from_wat(include_bytes!("wat/bare_call.wat")); @@ -357,12 +365,27 @@ fn bench_execute_typed_bare_call_16(c: &mut Criterion) { b.iter(|| { for _ in 0..REPETITIONS { let _ = bare_call - .call(&mut store, ( - 0, 0, F32::from(0.0), F64::from(0.0), - 0, 0, F32::from(0.0), F64::from(0.0), - 0, 0, F32::from(0.0), F64::from(0.0), - 0, 0, F32::from(0.0), F64::from(0.0), - )) + .call( + &mut store, + ( + 0, + 0, + F32::from(0.0), + F64::from(0.0), + 0, + 0, + F32::from(0.0), + F64::from(0.0), + 0, + 0, + F32::from(0.0), + F64::from(0.0), + 0, + 0, + F32::from(0.0), + F64::from(0.0), + ), + ) .unwrap(); } }) @@ -381,9 +404,7 @@ fn bench_execute_bare_call_0(c: &mut Criterion) { let results = &mut []; b.iter(|| { for _ in 0..REPETITIONS { - bare_call - .call(&mut store, params, results) - .unwrap(); + bare_call.call(&mut store, params, results).unwrap(); } }) }); @@ -401,9 +422,7 @@ fn bench_execute_bare_call_1(c: &mut Criterion) { let results = &mut [Value::I32(0)]; b.iter(|| { for _ in 0..REPETITIONS { - bare_call - .call(&mut store, params, results) - .unwrap(); + bare_call.call(&mut store, params, results).unwrap(); } }) }); @@ -426,9 +445,7 @@ fn bench_execute_bare_call_4(c: &mut Criterion) { let results = &mut [Value::I32(0); 4]; b.iter(|| { for _ in 0..REPETITIONS { - bare_call - .call(&mut store, params, results) - .unwrap(); + bare_call.call(&mut store, params, results).unwrap(); } }) }); @@ -463,9 +480,7 @@ fn bench_execute_bare_call_16(c: &mut Criterion) { let results = &mut [Value::I32(0); 16]; b.iter(|| { for _ in 0..REPETITIONS { - bare_call - .call(&mut store, params, results) - .unwrap(); + bare_call.call(&mut store, params, results).unwrap(); } }) }); diff --git a/crates/wasmi/src/engine/mod.rs b/crates/wasmi/src/engine/mod.rs index 78b3047ac3..f0518aae5a 100644 --- a/crates/wasmi/src/engine/mod.rs +++ b/crates/wasmi/src/engine/mod.rs @@ -285,23 +285,19 @@ impl EngineInner { Results: CallResults, { self.initialize_args(params); - let signature = match func.as_internal(ctx.as_context()) { + match func.as_internal(ctx.as_context()) { FuncEntityInternal::Wasm(wasm_func) => { - let signature = wasm_func.signature(); let mut frame = self.stack.call_wasm_root(wasm_func, &self.code_map)?; let mut cache = InstanceCache::from(frame.instance()); self.execute_wasm_func(ctx.as_context_mut(), &mut frame, &mut cache)?; - signature } FuncEntityInternal::Host(host_func) => { - let signature = host_func.signature(); let host_func = host_func.clone(); self.stack .call_host_root(ctx.as_context_mut(), host_func, &self.func_types)?; - signature } }; - let results = self.write_results_back(signature, results); + let results = self.write_results_back(results); Ok(results) } @@ -311,9 +307,7 @@ impl EngineInner { Params: CallParams, { self.stack.clear(); - for param in params.feed_params() { - self.stack.values.push(param); - } + self.stack.values.extend(params.call_params()); } /// Writes the results of the function execution back into the `results` buffer. @@ -325,31 +319,11 @@ impl EngineInner { /// # Panics /// /// - If the `results` buffer length does not match the remaining amount of stack values. - fn write_results_back( - &mut self, - func_type: DedupFuncType, - results: Results, - ) -> ::Results + fn write_results_back(&mut self, results: Results) -> ::Results where Results: CallResults, { - let result_types = self.func_types.resolve_func_type(func_type).results(); - assert_eq!( - self.stack.values.len(), - results.len_results(), - "expected {} values on the stack after function execution but found {}", - results.len_results(), - self.stack.values.len(), - ); - assert_eq!(results.len_results(), result_types.len()); - results.feed_results( - self.stack - .values - .drain() - .iter() - .zip(result_types) - .map(|(raw_value, value_type)| raw_value.with_type(*value_type)), - ) + results.call_results(self.stack.values.drain()) } /// Executes the top most Wasm function on the [`Stack`] until the [`Stack`] is empty. diff --git a/crates/wasmi/src/engine/traits.rs b/crates/wasmi/src/engine/traits.rs index 6c907c63fd..b16535367b 100644 --- a/crates/wasmi/src/engine/traits.rs +++ b/crates/wasmi/src/engine/traits.rs @@ -1,5 +1,6 @@ use crate::core::Value; use core::{iter, slice}; +use wasmi_core::UntypedValue; /// Types implementing this trait may be used as parameters for function execution. /// @@ -12,33 +13,45 @@ use core::{iter, slice}; /// [`Engine`]: [`crate::Engine`] pub trait CallParams { /// The iterator over the parameter values. - type Params: Iterator; - - /// Returns the number of given parameter values. - /// - /// # Note - /// - /// Used by the [`Engine`] to determine how many parameters are received. - /// - /// [`Engine`]: [`crate::Engine`] - fn len_params(&self) -> usize; + type Params: ExactSizeIterator; /// Feeds the parameter values from the caller. - fn feed_params(self) -> Self::Params; + fn call_params(self) -> Self::Params; } impl<'a> CallParams for &'a [Value] { - type Params = iter::Copied>; + type Params = CallParamsValueIter<'a>; - fn len_params(&self) -> usize { - self.len() + #[inline] + fn call_params(self) -> Self::Params { + CallParamsValueIter { + iter: self.iter().copied(), + } } +} + +/// An iterator over the [`UntypedValue`] call parameters. +#[derive(Debug)] +pub struct CallParamsValueIter<'a> { + iter: iter::Copied>, +} - fn feed_params(self) -> Self::Params { - self.iter().copied() +impl<'a> Iterator for CallParamsValueIter<'a> { + type Item = UntypedValue; + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(UntypedValue::from) } } +impl ExactSizeIterator for CallParamsValueIter<'_> {} + /// Types implementing this trait may be used as results for function execution. /// /// # Note @@ -53,43 +66,22 @@ pub trait CallResults { /// The type of the returned results value. type Results; - /// Returns the number of expected result values. - /// - /// # Note - /// - /// Used by the [`Engine`] to determine how many results are expected. - /// - /// [`Engine`]: [`crate::Engine`] - fn len_results(&self) -> usize; - /// Feeds the result values back to the caller. /// /// # Panics /// /// If the given `results` do not match the expected amount. - fn feed_results(self, results: T) -> Self::Results - where - T: IntoIterator, - T::IntoIter: ExactSizeIterator; + fn call_results(self, results: &[UntypedValue]) -> Self::Results; } impl<'a> CallResults for &'a mut [Value] { type Results = Self; - fn len_results(&self) -> usize { - self.len() - } - - fn feed_results(self, results: T) -> Self::Results - where - T: IntoIterator, - T::IntoIter: ExactSizeIterator, - { - let results = results.into_iter(); - assert_eq!(self.len_results(), results.len()); - for (dst, src) in self.iter_mut().zip(results) { - *dst = src; - } + fn call_results(self, results: &[UntypedValue]) -> Self::Results { + assert_eq!(self.len(), results.len()); + self.iter_mut().zip(results).for_each(|(dst, src)| { + *dst = src.with_type(dst.value_type()); + }); self } } diff --git a/crates/wasmi/src/func/into_func.rs b/crates/wasmi/src/func/into_func.rs index 079aaaadff..d655c5644d 100644 --- a/crates/wasmi/src/func/into_func.rs +++ b/crates/wasmi/src/func/into_func.rs @@ -210,18 +210,35 @@ pub trait WasmTypeList: DecodeUntypedSlice + EncodeUntypedSlice + Sized { /// The iterator type of the sequence of [`Value`]. type ValuesIter: ExactSizeIterator + DoubleEndedIterator + FusedIterator; + type UntypedValues: IntoIterator + + AsRef<[UntypedValue]> + + AsMut<[UntypedValue]>; + + /// The iterator type of the sequence of [`Value`]. + type UntypedValuesIter: ExactSizeIterator + + DoubleEndedIterator + + FusedIterator; + /// Returns an array representing the [`ValueType`] sequence of `Self`. fn value_types() -> Self::Types; /// Returns an array representing the [`Value`] sequence of `self`. fn values(self) -> Self::Values; + /// Returns an array representing the [`UntypedValue`] sequence of `self`. + fn untyped_values(self) -> Self::UntypedValues; + /// Consumes the [`Value`] iterator and creates `Self` if possible. /// /// Returns `None` if construction of `Self` is impossible. fn from_values(values: T) -> Option where T: Iterator; + + /// Consumes the [`UntypedValue`] iterator and creates `Self` if possible. + /// + /// Returns `None` if construction of `Self` is impossible. + fn from_untyped_values(values: &[UntypedValue]) -> Option; } impl WasmTypeList for T1 @@ -234,6 +251,8 @@ where type TypesIter = array::IntoIter; type Values = [Value; 1]; type ValuesIter = array::IntoIter; + type UntypedValues = [UntypedValue; 1]; + type UntypedValuesIter = array::IntoIter; #[inline] fn value_types() -> Self::Types { @@ -245,6 +264,11 @@ where [>::into(self)] } + #[inline] + fn untyped_values(self) -> Self::UntypedValues { + [>::into(self)] + } + fn from_values(mut values: T) -> Option where T: Iterator, @@ -258,6 +282,13 @@ where } Some(value) } + + fn from_untyped_values(values: &[UntypedValue]) -> Option { + if values.len() != 1 { + return None; + } + Some(values[0].into()) + } } macro_rules! impl_wasm_type_list { @@ -274,13 +305,17 @@ macro_rules! impl_wasm_type_list { type TypesIter = array::IntoIter; type Values = [Value; $n]; type ValuesIter = array::IntoIter; + type UntypedValues = [UntypedValue; $n]; + type UntypedValuesIter = array::IntoIter; + #[inline] fn value_types() -> Self::Types { [$( <$tuple as WasmType>::value_type() ),*] } + #[inline] #[allow(non_snake_case)] fn values(self) -> Self::Values { let ($($tuple,)*) = self; @@ -289,6 +324,15 @@ macro_rules! impl_wasm_type_list { ),*] } + #[inline] + #[allow(non_snake_case)] + fn untyped_values(self) -> Self::UntypedValues { + let ($($tuple,)*) = self; + [$( + <$tuple as Into>::into($tuple) + ),*] + } + fn from_values(mut values: T) -> Option where T: Iterator, @@ -304,6 +348,17 @@ macro_rules! impl_wasm_type_list { } Some(result) } + + #[inline] + #[allow(non_snake_case)] + fn from_untyped_values(values: &[UntypedValue]) -> Option { + if let [$($tuple),*] = *values { + return Some( + ( $( Into::into($tuple), )* ) + ) + } + None + } } }; } diff --git a/crates/wasmi/src/func/mod.rs b/crates/wasmi/src/func/mod.rs index df7cacdcdb..d053805b89 100644 --- a/crates/wasmi/src/func/mod.rs +++ b/crates/wasmi/src/func/mod.rs @@ -302,6 +302,9 @@ impl Func { if expected_outputs.len() != outputs.len() { return Err(FuncError::MismatchingResults { func: *self }).map_err(Into::into); } + for (output, output_type) in outputs.into_iter().zip(expected_outputs) { + *output = Value::default(*output_type); + } // Note: Cloning an [`Engine`] is intentionally a cheap operation. ctx.as_context().store.engine().clone().execute_func( ctx.as_context_mut(), diff --git a/crates/wasmi/src/func/typed_func.rs b/crates/wasmi/src/func/typed_func.rs index f6fd9320c1..531f634e2f 100644 --- a/crates/wasmi/src/func/typed_func.rs +++ b/crates/wasmi/src/func/typed_func.rs @@ -1,13 +1,12 @@ use super::{into_func::WasmTypeList, Func, FuncError}; use crate::{ - core::Value, engine::{CallParams, CallResults}, AsContext, AsContextMut, Error, }; use core::{fmt, fmt::Debug, marker::PhantomData}; -use wasmi_core::Trap; +use wasmi_core::{Trap, UntypedValue}; /// A typed [`Func`] instance. /// @@ -113,14 +112,11 @@ impl CallParams for Params where Params: WasmParams, { - type Params = ::ValuesIter; + type Params = ::UntypedValuesIter; - fn len_params(&self) -> usize { - ::LEN - } - - fn feed_params(self) -> Self::Params { - ::values(self).into_iter() + #[inline] + fn call_params(self) -> Self::Params { + ::untyped_values(self).into_iter() } } @@ -159,19 +155,9 @@ where { type Results = Results; - fn len_results(&self) -> usize { - ::LEN - } - - fn feed_results(self, results: T) -> Self::Results - where - T: IntoIterator, - T::IntoIter: ExactSizeIterator, - { - let results = results.into_iter(); - assert_eq!(self.len_results(), results.len()); - ::from_values(results) - .expect("unable to construct typed results from value iterator") + fn call_results(self, results: &[UntypedValue]) -> Self::Results { + ::from_untyped_values(results) + .expect("unable to construct typed results from call results") } } From b3c5ed58ed40d185f89c180599777d1ae2f62669 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 18 Oct 2022 14:24:03 +0200 Subject: [PATCH 3/8] remove WasmTypeList::{values, from_values} trait methods --- crates/wasmi/src/func/into_func.rs | 54 ------------------------------ 1 file changed, 54 deletions(-) diff --git a/crates/wasmi/src/func/into_func.rs b/crates/wasmi/src/func/into_func.rs index d655c5644d..cd1a271c86 100644 --- a/crates/wasmi/src/func/into_func.rs +++ b/crates/wasmi/src/func/into_func.rs @@ -222,19 +222,9 @@ pub trait WasmTypeList: DecodeUntypedSlice + EncodeUntypedSlice + Sized { /// Returns an array representing the [`ValueType`] sequence of `Self`. fn value_types() -> Self::Types; - /// Returns an array representing the [`Value`] sequence of `self`. - fn values(self) -> Self::Values; - /// Returns an array representing the [`UntypedValue`] sequence of `self`. fn untyped_values(self) -> Self::UntypedValues; - /// Consumes the [`Value`] iterator and creates `Self` if possible. - /// - /// Returns `None` if construction of `Self` is impossible. - fn from_values(values: T) -> Option - where - T: Iterator; - /// Consumes the [`UntypedValue`] iterator and creates `Self` if possible. /// /// Returns `None` if construction of `Self` is impossible. @@ -259,30 +249,11 @@ where [::value_type()] } - #[inline] - fn values(self) -> Self::Values { - [>::into(self)] - } - #[inline] fn untyped_values(self) -> Self::UntypedValues { [>::into(self)] } - fn from_values(mut values: T) -> Option - where - T: Iterator, - { - let value: T1 = values.next().and_then(Value::try_into)?; - if values.next().is_some() { - // Note: If the iterator yielded more items than - // necessary we create no value from this procedure - // as it is likely a bug. - return None; - } - Some(value) - } - fn from_untyped_values(values: &[UntypedValue]) -> Option { if values.len() != 1 { return None; @@ -315,15 +286,6 @@ macro_rules! impl_wasm_type_list { ),*] } - #[inline] - #[allow(non_snake_case)] - fn values(self) -> Self::Values { - let ($($tuple,)*) = self; - [$( - <$tuple as Into>::into($tuple) - ),*] - } - #[inline] #[allow(non_snake_case)] fn untyped_values(self) -> Self::UntypedValues { @@ -333,22 +295,6 @@ macro_rules! impl_wasm_type_list { ),*] } - fn from_values(mut values: T) -> Option - where - T: Iterator, - { - let result = ($( - values.next().and_then(Value::try_into::<$tuple>)?, - )*); - if values.next().is_some() { - // Note: If the iterator yielded more items than - // necessary we create no value from this procedure - // as it is likely a bug. - return None - } - Some(result) - } - #[inline] #[allow(non_snake_case)] fn from_untyped_values(values: &[UntypedValue]) -> Option { From 32f7df0e94c430f79fc889698a4be9953c8693e8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 18 Oct 2022 14:24:34 +0200 Subject: [PATCH 4/8] refactor WasmTypeList::from_untyped_values impl for a single T --- crates/wasmi/src/func/into_func.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/wasmi/src/func/into_func.rs b/crates/wasmi/src/func/into_func.rs index cd1a271c86..8b0a94db93 100644 --- a/crates/wasmi/src/func/into_func.rs +++ b/crates/wasmi/src/func/into_func.rs @@ -254,11 +254,12 @@ where [>::into(self)] } + #[inline] fn from_untyped_values(values: &[UntypedValue]) -> Option { - if values.len() != 1 { - return None; + if let [value] = *values { + return Some(value.into()); } - Some(values[0].into()) + return None; } } From 420d50ca7d0309ec876ddcac5b6e2dbaa54f22c3 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 18 Oct 2022 14:25:22 +0200 Subject: [PATCH 5/8] remove WasmTypeList::{Values, ValuesIter} assoc types --- crates/wasmi/src/func/into_func.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/crates/wasmi/src/func/into_func.rs b/crates/wasmi/src/func/into_func.rs index 8b0a94db93..d291e807dd 100644 --- a/crates/wasmi/src/func/into_func.rs +++ b/crates/wasmi/src/func/into_func.rs @@ -202,14 +202,6 @@ pub trait WasmTypeList: DecodeUntypedSlice + EncodeUntypedSlice + Sized { /// The iterator type of the sequence of [`ValueType`]. type TypesIter: ExactSizeIterator + DoubleEndedIterator + FusedIterator; - /// The [`Value`] sequence as array. - type Values: IntoIterator - + AsRef<[Value]> - + AsMut<[Value]>; - - /// The iterator type of the sequence of [`Value`]. - type ValuesIter: ExactSizeIterator + DoubleEndedIterator + FusedIterator; - type UntypedValues: IntoIterator + AsRef<[UntypedValue]> + AsMut<[UntypedValue]>; @@ -239,8 +231,6 @@ where type Types = [ValueType; 1]; type TypesIter = array::IntoIter; - type Values = [Value; 1]; - type ValuesIter = array::IntoIter; type UntypedValues = [UntypedValue; 1]; type UntypedValuesIter = array::IntoIter; @@ -275,8 +265,6 @@ macro_rules! impl_wasm_type_list { type Types = [ValueType; $n]; type TypesIter = array::IntoIter; - type Values = [Value; $n]; - type ValuesIter = array::IntoIter; type UntypedValues = [UntypedValue; $n]; type UntypedValuesIter = array::IntoIter; From cba6eee8a031ea8ee81cb2018615ec24794093cb Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 18 Oct 2022 14:28:21 +0200 Subject: [PATCH 6/8] rename some WasmTypeList trait methods --- crates/wasmi/src/func/into_func.rs | 44 +++++++++++++++-------------- crates/wasmi/src/func/typed_func.rs | 10 +++---- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/crates/wasmi/src/func/into_func.rs b/crates/wasmi/src/func/into_func.rs index d291e807dd..3ee75be83c 100644 --- a/crates/wasmi/src/func/into_func.rs +++ b/crates/wasmi/src/func/into_func.rs @@ -69,8 +69,8 @@ macro_rules! impl_into_func { #[allow(non_snake_case)] fn into_func(self) -> (FuncType, HostFuncTrampoline) { let signature = FuncType::new( - ::value_types(), - ::value_types(), + ::types(), + ::types(), ); let trampoline = HostFuncTrampoline::new( move |caller: Caller, params_results: FuncParams| -> Result { @@ -197,30 +197,32 @@ pub trait WasmTypeList: DecodeUntypedSlice + EncodeUntypedSlice + Sized { /// The [`ValueType`] sequence as array. type Types: IntoIterator + AsRef<[ValueType]> - + AsMut<[ValueType]>; + + AsMut<[ValueType]> + + Copy + + Clone; /// The iterator type of the sequence of [`ValueType`]. type TypesIter: ExactSizeIterator + DoubleEndedIterator + FusedIterator; - type UntypedValues: IntoIterator + type Values: IntoIterator + AsRef<[UntypedValue]> - + AsMut<[UntypedValue]>; + + AsMut<[UntypedValue]> + + Copy + + Clone; /// The iterator type of the sequence of [`Value`]. - type UntypedValuesIter: ExactSizeIterator - + DoubleEndedIterator - + FusedIterator; + type ValuesIter: ExactSizeIterator + DoubleEndedIterator + FusedIterator; /// Returns an array representing the [`ValueType`] sequence of `Self`. - fn value_types() -> Self::Types; + fn types() -> Self::Types; /// Returns an array representing the [`UntypedValue`] sequence of `self`. - fn untyped_values(self) -> Self::UntypedValues; + fn values(self) -> Self::Values; /// Consumes the [`UntypedValue`] iterator and creates `Self` if possible. /// /// Returns `None` if construction of `Self` is impossible. - fn from_untyped_values(values: &[UntypedValue]) -> Option; + fn from_values(values: &[UntypedValue]) -> Option; } impl WasmTypeList for T1 @@ -231,21 +233,21 @@ where type Types = [ValueType; 1]; type TypesIter = array::IntoIter; - type UntypedValues = [UntypedValue; 1]; - type UntypedValuesIter = array::IntoIter; + type Values = [UntypedValue; 1]; + type ValuesIter = array::IntoIter; #[inline] - fn value_types() -> Self::Types { + fn types() -> Self::Types { [::value_type()] } #[inline] - fn untyped_values(self) -> Self::UntypedValues { + fn values(self) -> Self::Values { [>::into(self)] } #[inline] - fn from_untyped_values(values: &[UntypedValue]) -> Option { + fn from_values(values: &[UntypedValue]) -> Option { if let [value] = *values { return Some(value.into()); } @@ -265,11 +267,11 @@ macro_rules! impl_wasm_type_list { type Types = [ValueType; $n]; type TypesIter = array::IntoIter; - type UntypedValues = [UntypedValue; $n]; - type UntypedValuesIter = array::IntoIter; + type Values = [UntypedValue; $n]; + type ValuesIter = array::IntoIter; #[inline] - fn value_types() -> Self::Types { + fn types() -> Self::Types { [$( <$tuple as WasmType>::value_type() ),*] @@ -277,7 +279,7 @@ macro_rules! impl_wasm_type_list { #[inline] #[allow(non_snake_case)] - fn untyped_values(self) -> Self::UntypedValues { + fn values(self) -> Self::Values { let ($($tuple,)*) = self; [$( <$tuple as Into>::into($tuple) @@ -286,7 +288,7 @@ macro_rules! impl_wasm_type_list { #[inline] #[allow(non_snake_case)] - fn from_untyped_values(values: &[UntypedValue]) -> Option { + fn from_values(values: &[UntypedValue]) -> Option { if let [$($tuple),*] = *values { return Some( ( $( Into::into($tuple), )* ) diff --git a/crates/wasmi/src/func/typed_func.rs b/crates/wasmi/src/func/typed_func.rs index 531f634e2f..80f8a2cee4 100644 --- a/crates/wasmi/src/func/typed_func.rs +++ b/crates/wasmi/src/func/typed_func.rs @@ -68,8 +68,8 @@ where let func_type = func.func_type(&ctx); let (expected_params, expected_results) = func_type.params_results(); let (actual_params, actual_results) = ( - ::value_types(), - ::value_types(), + ::types(), + ::types(), ); if actual_params.as_ref() != expected_params { return Err(Error::Func(FuncError::MismatchingParameters { func })); @@ -112,11 +112,11 @@ impl CallParams for Params where Params: WasmParams, { - type Params = ::UntypedValuesIter; + type Params = ::ValuesIter; #[inline] fn call_params(self) -> Self::Params { - ::untyped_values(self).into_iter() + ::values(self).into_iter() } } @@ -156,7 +156,7 @@ where type Results = Results; fn call_results(self, results: &[UntypedValue]) -> Self::Results { - ::from_untyped_values(results) + ::from_values(results) .expect("unable to construct typed results from call results") } } From aea1c71650a830f80d3ae0d49849a964ed313523 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 18 Oct 2022 14:36:11 +0200 Subject: [PATCH 7/8] apply clippy suggestions --- crates/wasmi/src/func/into_func.rs | 2 +- crates/wasmi/src/func/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/wasmi/src/func/into_func.rs b/crates/wasmi/src/func/into_func.rs index 3ee75be83c..327ea5936e 100644 --- a/crates/wasmi/src/func/into_func.rs +++ b/crates/wasmi/src/func/into_func.rs @@ -251,7 +251,7 @@ where if let [value] = *values { return Some(value.into()); } - return None; + None } } diff --git a/crates/wasmi/src/func/mod.rs b/crates/wasmi/src/func/mod.rs index d053805b89..ee63edce30 100644 --- a/crates/wasmi/src/func/mod.rs +++ b/crates/wasmi/src/func/mod.rs @@ -302,7 +302,7 @@ impl Func { if expected_outputs.len() != outputs.len() { return Err(FuncError::MismatchingResults { func: *self }).map_err(Into::into); } - for (output, output_type) in outputs.into_iter().zip(expected_outputs) { + for (output, output_type) in outputs.iter_mut().zip(expected_outputs) { *output = Value::default(*output_type); } // Note: Cloning an [`Engine`] is intentionally a cheap operation. From 4b5ae7cc949fa7a871875614e48450955903e2bd Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Tue, 18 Oct 2022 15:00:34 +0200 Subject: [PATCH 8/8] replace raw for loop with for_each --- crates/wasmi/src/func/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/wasmi/src/func/mod.rs b/crates/wasmi/src/func/mod.rs index ee63edce30..63d1cd0566 100644 --- a/crates/wasmi/src/func/mod.rs +++ b/crates/wasmi/src/func/mod.rs @@ -302,9 +302,10 @@ impl Func { if expected_outputs.len() != outputs.len() { return Err(FuncError::MismatchingResults { func: *self }).map_err(Into::into); } - for (output, output_type) in outputs.iter_mut().zip(expected_outputs) { - *output = Value::default(*output_type); - } + outputs + .iter_mut() + .zip(expected_outputs.iter().copied().map(Value::default)) + .for_each(|(output, expected_output)| *output = expected_output); // Note: Cloning an [`Engine`] is intentionally a cheap operation. ctx.as_context().store.engine().clone().execute_func( ctx.as_context_mut(),