Skip to content
This repository was archived by the owner on Oct 3, 2025. It is now read-only.

Commit 9218c95

Browse files
chore: improve value stack
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
1 parent cfcd1f7 commit 9218c95

File tree

10 files changed

+191
-45
lines changed

10 files changed

+191
-45
lines changed

benchmarks/benches/fibonacci.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,21 @@ fn run_native_recursive(n: i32) -> i32 {
4646

4747
const FIBONACCI: &[u8] = include_bytes!("../../examples/rust/out/fibonacci.wasm");
4848
fn criterion_benchmark(c: &mut Criterion) {
49-
{
50-
let mut group = c.benchmark_group("fibonacci");
51-
group.bench_function("native", |b| b.iter(|| run_native(black_box(60))));
52-
group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(FIBONACCI, black_box(60), "fibonacci")));
53-
group.bench_function("wasmi", |b| b.iter(|| run_wasmi(FIBONACCI, black_box(60), "fibonacci")));
54-
group.bench_function("wasmer", |b| b.iter(|| run_wasmer(FIBONACCI, black_box(60), "fibonacci")));
55-
}
49+
// {
50+
// let mut group = c.benchmark_group("fibonacci");
51+
// group.bench_function("native", |b| b.iter(|| run_native(black_box(60))));
52+
// group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(FIBONACCI, black_box(60), "fibonacci")));
53+
// group.bench_function("wasmi", |b| b.iter(|| run_wasmi(FIBONACCI, black_box(60), "fibonacci")));
54+
// group.bench_function("wasmer", |b| b.iter(|| run_wasmer(FIBONACCI, black_box(60), "fibonacci")));
55+
// }
5656

5757
{
5858
let mut group = c.benchmark_group("fibonacci-recursive");
5959
group.measurement_time(std::time::Duration::from_secs(5));
60-
group.bench_function("native", |b| b.iter(|| run_native_recursive(black_box(26))));
60+
// group.bench_function("native", |b| b.iter(|| run_native_recursive(black_box(26))));
6161
group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(FIBONACCI, black_box(26), "fibonacci_recursive")));
62-
group.bench_function("wasmi", |b| b.iter(|| run_wasmi(FIBONACCI, black_box(26), "fibonacci_recursive")));
63-
group.bench_function("wasmer", |b| b.iter(|| run_wasmer(FIBONACCI, black_box(26), "fibonacci_recursive")));
62+
// group.bench_function("wasmi", |b| b.iter(|| run_wasmi(FIBONACCI, black_box(26), "fibonacci_recursive")));
63+
// group.bench_function("wasmer", |b| b.iter(|| run_wasmer(FIBONACCI, black_box(26), "fibonacci_recursive")));
6464
}
6565
}
6666

crates/parser/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ tinywasm-types={version="0.7.0", path="../types", default-features=false}
1616
default=["std", "logging"]
1717
logging=["log"]
1818
std=["tinywasm-types/std", "wasmparser/std"]
19+
nightly=[]

crates/tinywasm/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ std=["tinywasm-parser?/std", "tinywasm-types/std"]
3434
parser=["tinywasm-parser"]
3535
archive=["tinywasm-types/archive"]
3636
simd=[]
37-
nightly=[]
37+
nightly=["tinywasm-parser?/nightly"]
3838

3939
[[test]]
4040
name="test-mvp"

crates/tinywasm/src/boxvec.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use crate::unlikely;
2+
use alloc::{borrow::Cow, boxed::Box, vec};
3+
use core::ops::RangeBounds;
4+
5+
// A Vec-like type that doesn't deallocate memory when popping elements.
6+
#[derive(Debug)]
7+
pub(crate) struct BoxVec<T> {
8+
pub(crate) data: Box<[T]>,
9+
pub(crate) end: usize,
10+
}
11+
12+
impl<T: Copy + Default> BoxVec<T> {
13+
#[inline(always)]
14+
pub(crate) fn with_capacity(capacity: usize) -> Self {
15+
Self { data: vec![T::default(); capacity].into_boxed_slice(), end: 0 }
16+
}
17+
18+
#[inline(always)]
19+
pub(crate) fn push(&mut self, value: T) {
20+
assert!(self.end <= self.data.len(), "stack overflow");
21+
self.data[self.end] = value;
22+
self.end += 1;
23+
}
24+
25+
#[inline(always)]
26+
pub(crate) fn pop(&mut self) -> Option<T> {
27+
assert!(self.end <= self.data.len(), "invalid stack state (should be impossible)");
28+
if unlikely(self.end == 0) {
29+
None
30+
} else {
31+
self.end -= 1;
32+
Some(self.data[self.end])
33+
}
34+
}
35+
36+
#[inline(always)]
37+
pub(crate) fn len(&self) -> usize {
38+
self.end
39+
}
40+
41+
#[inline(always)]
42+
pub(crate) fn extend_from_slice(&mut self, values: &[T]) {
43+
let new_end = self.end + values.len();
44+
assert!(new_end <= self.data.len(), "stack overflow");
45+
self.data[self.end..new_end].copy_from_slice(values);
46+
self.end = new_end;
47+
}
48+
49+
#[inline(always)]
50+
pub(crate) fn last_mut(&mut self) -> Option<&mut T> {
51+
assert!(self.end <= self.data.len(), "invalid stack state (should be impossible)");
52+
if unlikely(self.end == 0) {
53+
None
54+
} else {
55+
Some(&mut self.data[self.end - 1])
56+
}
57+
}
58+
59+
#[inline(always)]
60+
pub(crate) fn last(&self) -> Option<&T> {
61+
assert!(self.end <= self.data.len(), "invalid stack state (should be impossible)");
62+
if unlikely(self.end == 0) {
63+
None
64+
} else {
65+
Some(&self.data[self.end - 1])
66+
}
67+
}
68+
69+
#[inline(always)]
70+
pub(crate) fn drain(&mut self, range: impl RangeBounds<usize>) -> Cow<'_, [T]> {
71+
let start = match range.start_bound() {
72+
core::ops::Bound::Included(&start) => start,
73+
core::ops::Bound::Excluded(&start) => start + 1,
74+
core::ops::Bound::Unbounded => 0,
75+
};
76+
let end = match range.end_bound() {
77+
core::ops::Bound::Included(&end) => end + 1,
78+
core::ops::Bound::Excluded(&end) => end,
79+
core::ops::Bound::Unbounded => self.end,
80+
};
81+
82+
assert!(start <= end);
83+
assert!(end <= self.end);
84+
85+
if end == self.end {
86+
self.end = start;
87+
return Cow::Borrowed(&self.data[start..end]);
88+
}
89+
90+
let drain = self.data[start..end].to_vec();
91+
self.data.copy_within(end..self.end, start);
92+
self.end -= end - start;
93+
Cow::Owned(drain)
94+
}
95+
}
96+
97+
impl<T> core::ops::Index<usize> for BoxVec<T> {
98+
type Output = T;
99+
100+
#[inline(always)]
101+
fn index(&self, index: usize) -> &T {
102+
&self.data[index]
103+
}
104+
}
105+
106+
impl<T> core::ops::Index<core::ops::Range<usize>> for BoxVec<T> {
107+
type Output = [T];
108+
109+
#[inline(always)]
110+
fn index(&self, index: core::ops::Range<usize>) -> &[T] {
111+
&self.data[index]
112+
}
113+
}
114+
115+
impl<T> core::ops::IndexMut<usize> for BoxVec<T> {
116+
#[inline(always)]
117+
fn index_mut(&mut self, index: usize) -> &mut T {
118+
&mut self.data[index]
119+
}
120+
}

crates/tinywasm/src/func.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ impl FuncHandle {
5959
};
6060

6161
// 6. Let f be the dummy frame
62-
let call_frame_params = params.iter().map(|v| RawWasmValue::from(*v));
63-
let call_frame = CallFrame::new(wasm_func.clone(), func_inst.owner, call_frame_params, 0);
62+
let call_frame_params = params.iter().map(|v| RawWasmValue::from(*v)).collect::<Vec<_>>();
63+
let call_frame = CallFrame::new(wasm_func.clone(), func_inst.owner, &call_frame_params, 0);
6464

6565
// 7. Push the frame f to the call stack
6666
// & 8. Push the values to the stack (Not needed since the call frame owns the values)

crates/tinywasm/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ pub use module::Module;
100100
pub use reference::*;
101101
pub use store::*;
102102

103+
mod boxvec;
103104
mod func;
104105
mod imports;
105106
mod instance;

crates/tinywasm/src/runtime/interpreter/mod.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ impl<'store, 'stack> Executor<'store, 'stack> {
6464
}
6565

6666
#[inline(always)]
67-
pub(crate) fn exec_next(&mut self) -> Result<ControlFlow<()>> {
67+
fn exec_next(&mut self) -> Result<ControlFlow<()>> {
6868
use tinywasm_types::Instruction::*;
6969
match self.cf.fetch_instr() {
70-
Nop => cold(),
70+
Nop => self.exec_noop(),
7171
Unreachable => self.exec_unreachable()?,
7272

73-
Drop => self.stack.values.pop().map(|_| ())?,
73+
Drop => self.exec_drop()?,
7474
Select(_valtype) => self.exec_select()?,
7575

7676
Call(v) => return self.exec_call_direct(*v),
@@ -80,7 +80,7 @@ impl<'store, 'stack> Executor<'store, 'stack> {
8080
Else(end_offset) => self.exec_else(*end_offset)?,
8181
Loop(args, end) => self.enter_block(self.cf.instr_ptr, *end, BlockType::Loop, *args),
8282
Block(args, end) => self.enter_block(self.cf.instr_ptr, *end, BlockType::Block, *args),
83-
Br(v) => break_to!(*v, self),
83+
Br(v) => return self.exec_br(*v),
8484
BrIf(v) => return self.exec_br_if(*v),
8585
BrTable(default, len) => return self.exec_brtable(*default, *len),
8686
Return => return self.exec_return(),
@@ -310,7 +310,6 @@ impl<'store, 'stack> Executor<'store, 'stack> {
310310
I32StoreLocal { local, const_i32, offset, mem_addr } => {
311311
self.exec_i32_store_local(*local, *const_i32, *offset, *mem_addr)?
312312
}
313-
314313
i => {
315314
cold();
316315
return Err(Error::UnsupportedFeature(format!("unimplemented instruction: {:?}", i)));
@@ -344,6 +343,13 @@ impl<'store, 'stack> Executor<'store, 'stack> {
344343
Ok(())
345344
}
346345

346+
#[inline(always)]
347+
fn exec_br(&mut self, to: u32) -> Result<ControlFlow<()>> {
348+
break_to!(to, self);
349+
self.cf.instr_ptr += 1;
350+
Ok(ControlFlow::Continue(()))
351+
}
352+
347353
#[inline(always)]
348354
fn exec_br_if(&mut self, to: u32) -> Result<ControlFlow<()>> {
349355
let val: i32 = self.stack.values.pop()?.into();
@@ -396,6 +402,9 @@ impl<'store, 'stack> Executor<'store, 'stack> {
396402
Err(Error::Trap(Trap::Unreachable))
397403
}
398404

405+
#[inline(always)]
406+
fn exec_noop(&self) {}
407+
399408
#[inline(always)]
400409
fn exec_ref_is_null(&mut self) -> Result<()> {
401410
self.stack.values.replace_top(|val| ((i32::from(val) == -1) as i32).into())
@@ -551,18 +560,24 @@ impl<'store, 'stack> Executor<'store, 'stack> {
551560
let table_idx = self.module.resolve_table_addr(table_index);
552561
let table = self.store.get_table(table_idx)?;
553562
let delta: i32 = self.stack.values.pop()?.into();
554-
let prev_size = table.borrow().size() as i32;
563+
let prev_size = table.borrow().size();
555564
table.borrow_mut().grow_to_fit((prev_size + delta) as usize)?;
556565
self.stack.values.push(prev_size.into());
557566
Ok(())
558567
}
559568

560569
#[inline(always)]
561-
fn exec_table_fill(&mut self, table_index: u32) -> Result<()> {
570+
fn exec_table_fill(&mut self, _table_index: u32) -> Result<()> {
562571
// TODO: implement
563572
Ok(())
564573
}
565574

575+
#[inline(always)]
576+
fn exec_drop(&mut self) -> Result<()> {
577+
self.stack.values.pop()?;
578+
Ok(())
579+
}
580+
566581
#[inline(always)]
567582
fn exec_select(&mut self) -> Result<()> {
568583
let cond: i32 = self.stack.values.pop()?.into();
@@ -695,7 +710,7 @@ impl<'store, 'stack> Executor<'store, 'stack> {
695710
#[inline(always)]
696711
fn exec_call(&mut self, wasm_func: Rc<WasmFunction>, owner: ModuleInstanceAddr) -> Result<ControlFlow<()>> {
697712
let params = self.stack.values.pop_n_rev(wasm_func.ty.params.len())?;
698-
let new_call_frame = CallFrame::new(wasm_func, owner, params, self.stack.blocks.len() as u32);
713+
let new_call_frame = CallFrame::new(wasm_func, owner, &params, self.stack.blocks.len() as u32);
699714
self.cf.instr_ptr += 1; // skip the call instruction
700715
self.stack.call_stack.push(core::mem::replace(&mut self.cf, new_call_frame))?;
701716
self.module.swap_with(self.cf.module_addr, self.store);

crates/tinywasm/src/runtime/stack/call_stack.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl CallFrame {
103103
pub(crate) fn new(
104104
wasm_func_inst: Rc<WasmFunction>,
105105
owner: ModuleInstanceAddr,
106-
params: impl ExactSizeIterator<Item = RawWasmValue>,
106+
params: &[RawWasmValue],
107107
block_ptr: u32,
108108
) -> Self {
109109
let locals = {

0 commit comments

Comments
 (0)