Skip to content

Commit 17e15e8

Browse files
committed
perf: hold the value stack in the executor while it runs
The executor takes the store's value stack when it is created and gives it back when it is dropped, and around host calls, which read their arguments from the store and may call back into wasm. Every handler receives the executor as a `&mut`, so a stack access reached through it needs one load fewer (the store pointer), and the compiler knows that no store to a slot or to memory changes a stack's height, so it can keep the heights in registers within a handler.
1 parent 869d691 commit 17e15e8

5 files changed

Lines changed: 367 additions & 270 deletions

File tree

‎crates/tinywasm/src/interpreter/executor/instructions.rs‎

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ macro_rules! exec_op {
66
<$ty>::stack_push(value_stack, $expr?);
77
Ok(())
88
}
9-
exec_binary_fallible(&mut $executor.store.value_stack)?;
9+
exec_binary_fallible(&mut $executor.value_stack)?;
1010
}};
1111
($executor:ident; unary $from:ty => $to:ty, |$v:ident| $expr:expr) => {{
1212
fn exec_unary(value_stack: &mut ValueStack) {
1313
let $v = <$from>::stack_pop(value_stack);
1414
<$to>::stack_push(value_stack, $expr);
1515
}
16-
exec_unary(&mut $executor.store.value_stack);
16+
exec_unary(&mut $executor.value_stack);
1717
}};
1818
($executor:ident; binary $from:ty => $to:ty, |$lhs:ident, $rhs:ident| $expr:expr) => {{
1919
exec_op!($executor; binary $from, $from => $to, |$lhs, $rhs| $expr)
@@ -24,7 +24,7 @@ macro_rules! exec_op {
2424
let $lhs = <$lhs_ty>::stack_pop(value_stack);
2525
<$res>::stack_push(value_stack, $expr);
2626
}
27-
exec_binary(&mut $executor.store.value_stack);
27+
exec_binary(&mut $executor.value_stack);
2828
}};
2929
($executor:ident; ternary $from:ty => $to:ty, |$a:ident, $b:ident, $c:ident| $expr:expr) => {{
3030
fn exec_ternary(value_stack: &mut ValueStack) {
@@ -33,7 +33,7 @@ macro_rules! exec_op {
3333
let $a = <$from>::stack_pop(value_stack);
3434
<$to>::stack_push(value_stack, $expr);
3535
}
36-
exec_ternary(&mut $executor.store.value_stack);
36+
exec_ternary(&mut $executor.value_stack);
3737
}};
3838
}
3939

@@ -43,15 +43,15 @@ macro_rules! instruction_handlers {
4343
($emit:ident) => {
4444
$emit! { executor, instr_ptr, dispatch_next, dispatch_flow;
4545
Unreachable => { return cold!(Err(Trap::Unreachable.into())); },
46-
Drop32 => { _ = Value32::stack_pop(&mut executor.store.value_stack)},
47-
Drop64 => { _ = Value64::stack_pop(&mut executor.store.value_stack)},
48-
Drop128 => { _ = Value128::stack_pop(&mut executor.store.value_stack)},
49-
Select32 => Value32::stack_select(&mut executor.store.value_stack),
50-
Select64 => Value64::stack_select(&mut executor.store.value_stack),
51-
Select128 => Value128::stack_select(&mut executor.store.value_stack),
46+
Drop32 => { _ = Value32::stack_pop(&mut executor.value_stack)},
47+
Drop64 => { _ = Value64::stack_pop(&mut executor.value_stack)},
48+
Drop128 => { _ = Value128::stack_pop(&mut executor.value_stack)},
49+
Select32 => Value32::stack_select(&mut executor.value_stack),
50+
Select64 => Value64::stack_select(&mut executor.value_stack),
51+
Select128 => Value128::stack_select(&mut executor.value_stack),
5252
SelectStore32(idx) => executor.exec_select_store::<Value32, 4>(idx.resolve(&executor.func.data))?,
5353
SelectStore64(idx) => executor.exec_select_store::<Value64, 8>(idx.resolve(&executor.func.data))?,
54-
SelectMulti(counts) => executor.store.value_stack.select_multi(*counts),
54+
SelectMulti(counts) => executor.value_stack.select_multi(*counts),
5555
Call(v) => dispatch_flow!(executor.exec_call_direct(*v, instr_ptr + 1)?),
5656
CallSelf => { executor.exec_call_self(instr_ptr + 1)?; dispatch_next!(0); },
5757
CallIndirect(idx) => dispatch_flow!(executor.exec_call_indirect::<false>(*idx, instr_ptr + 1)?),
@@ -63,10 +63,10 @@ macro_rules! instruction_handlers {
6363
Throw(tag) => dispatch_flow!(executor.exec_throw(*tag, instr_ptr)?),
6464
ThrowRef => dispatch_flow!(executor.exec_throw_ref(instr_ptr)?),
6565
Jump(ip) => dispatch_next!(*ip as usize),
66-
JumpIfZero32(ip) => if i32::stack_pop(&mut executor.store.value_stack) == 0 { dispatch_next!(*ip as usize) },
67-
JumpIfNonZero32(ip) => if i32::stack_pop(&mut executor.store.value_stack) != 0 { dispatch_next!(*ip as usize) },
68-
JumpIfZero64(ip) => if i64::stack_pop(&mut executor.store.value_stack) == 0 { dispatch_next!(*ip as usize) },
69-
JumpIfNonZero64(ip) => if i64::stack_pop(&mut executor.store.value_stack) != 0 { dispatch_next!(*ip as usize) },
66+
JumpIfZero32(ip) => if i32::stack_pop(&mut executor.value_stack) == 0 { dispatch_next!(*ip as usize) },
67+
JumpIfNonZero32(ip) => if i32::stack_pop(&mut executor.value_stack) != 0 { dispatch_next!(*ip as usize) },
68+
JumpIfZero64(ip) => if i64::stack_pop(&mut executor.value_stack) == 0 { dispatch_next!(*ip as usize) },
69+
JumpIfNonZero64(ip) => if i64::stack_pop(&mut executor.value_stack) != 0 { dispatch_next!(*ip as usize) },
7070
JumpIfRefNull(ip) => { let ip = *ip; if executor.exec_jump_if_ref::<true>() { dispatch_next!(ip as usize) } },
7171
JumpIfRefNonNull(ip) => { let ip = *ip; if executor.exec_jump_if_ref::<false>() { dispatch_next!(ip as usize) } },
7272
BrOnCast(idx) => if let Some(ip) = executor.exec_br_on_cast::<false>(*idx) { dispatch_next!(ip) },
@@ -91,33 +91,33 @@ macro_rules! instruction_handlers {
9191
JumpCmpLocalConst64(packed) => if let Some(ip) = executor.exec_jump_cmp_local_const64(*packed) { dispatch_next!(ip) },
9292
JumpCmpLocalLocal32(packed) => if let Some(ip) = executor.exec_jump_cmp_local_local32(*packed) { dispatch_next!(ip) },
9393
JumpCmpLocalLocal64(packed) => if let Some(ip) = executor.exec_jump_cmp_local_local64(*packed) { dispatch_next!(ip) },
94-
DropKeep32 { base, keep } => executor.store.value_stack.stack_32.truncate_keep((executor.cf.stack_base().s32 + u32::from(*base)) as usize, *keep as usize),
95-
DropKeep64 { base, keep } => executor.store.value_stack.stack_64.truncate_keep((executor.cf.stack_base().s64 + u32::from(*base)) as usize, *keep as usize),
96-
DropKeep128 { base, keep } => executor.store.value_stack.stack_128.truncate_keep((executor.cf.stack_base().s128 + u32::from(*base)) as usize, *keep as usize),
94+
DropKeep32 { base, keep } => executor.value_stack.stack_32.truncate_keep((executor.cf.stack_base().s32 + u32::from(*base)) as usize, *keep as usize),
95+
DropKeep64 { base, keep } => executor.value_stack.stack_64.truncate_keep((executor.cf.stack_base().s64 + u32::from(*base)) as usize, *keep as usize),
96+
DropKeep128 { base, keep } => executor.value_stack.stack_128.truncate_keep((executor.cf.stack_base().s128 + u32::from(*base)) as usize, *keep as usize),
9797
BranchTable(idx) => dispatch_next!(executor.exec_branch_table(*idx)),
9898
Return => dispatch_flow!(executor.exec_return()),
9999
ReturnVoid => dispatch_flow!(executor.exec_return_void()),
100100
Return32 => dispatch_flow!(executor.exec_return_32()),
101101
Return64 => dispatch_flow!(executor.exec_return_64()),
102102
Return128 => dispatch_flow!(executor.exec_return_128()),
103-
LocalGet32(local_index) => Value32::local_push(&mut executor.store.value_stack, &executor.cf, *local_index),
104-
LocalGet64(local_index) => Value64::local_push(&mut executor.store.value_stack, &executor.cf, *local_index),
105-
LocalGet128(local_index) => Value128::local_push(&mut executor.store.value_stack, &executor.cf, *local_index),
103+
LocalGet32(local_index) => Value32::local_push(&mut executor.value_stack, &executor.cf, *local_index),
104+
LocalGet64(local_index) => Value64::local_push(&mut executor.value_stack, &executor.cf, *local_index),
105+
LocalGet128(local_index) => Value128::local_push(&mut executor.value_stack, &executor.cf, *local_index),
106106
LocalSet32(local_index) => executor.exec_local_set_pop::<Value32>(*local_index),
107107
LocalSet64(local_index) => executor.exec_local_set_pop::<Value64>(*local_index),
108108
LocalSet128(local_index) => executor.exec_local_set_pop::<Value128>(*local_index),
109-
LocalCopy32(from, to) => Value32::local_copy(&mut executor.store.value_stack, &executor.cf, *from, *to),
110-
LocalCopy64(from, to) => Value64::local_copy(&mut executor.store.value_stack, &executor.cf, *from, *to),
111-
LocalCopy128(from, to) => Value128::local_copy(&mut executor.store.value_stack, &executor.cf, *from, *to),
112-
AddConst32(c) => { i32::stack_update(&mut executor.store.value_stack, |value| value.wrapping_add(*c)); },
113-
AndConst32(c) => { i32::stack_update(&mut executor.store.value_stack, |value| value & *c); },
114-
XorConst32(c) => { i32::stack_update(&mut executor.store.value_stack, |value| value ^ *c); },
115-
ShrUConst32(c) => { u32::stack_update(&mut executor.store.value_stack, |value| value.wrapping_shr(*c as u32)); },
116-
AddConst64(idx) => { let rhs = idx.resolve(&executor.func.data).value(); i64::stack_update(&mut executor.store.value_stack, |value| value.wrapping_add(rhs)); },
117-
BinOpStackConst32(op, rhs) => { u32::stack_update(&mut executor.store.value_stack, |lhs| op.exec(lhs, *rhs as u32)); },
118-
BinOpStackConst64(packed) => { let rhs = packed.index.resolve(&executor.func.data).value() as u64; u64::stack_update(&mut executor.store.value_stack, |lhs| packed.op.exec(lhs, rhs)); },
119-
IncLocal32(arg) => { i32::local_update(&mut executor.store.value_stack, &executor.cf, arg.local, |value| value.wrapping_add(arg.value)); },
120-
IncLocal64(packed) => { let rhs = packed.index.resolve(&executor.func.data).value(); i64::local_update(&mut executor.store.value_stack, &executor.cf, packed.op, |value| value.wrapping_add(rhs)); },
109+
LocalCopy32(from, to) => Value32::local_copy(&mut executor.value_stack, &executor.cf, *from, *to),
110+
LocalCopy64(from, to) => Value64::local_copy(&mut executor.value_stack, &executor.cf, *from, *to),
111+
LocalCopy128(from, to) => Value128::local_copy(&mut executor.value_stack, &executor.cf, *from, *to),
112+
AddConst32(c) => { i32::stack_update(&mut executor.value_stack, |value| value.wrapping_add(*c)); },
113+
AndConst32(c) => { i32::stack_update(&mut executor.value_stack, |value| value & *c); },
114+
XorConst32(c) => { i32::stack_update(&mut executor.value_stack, |value| value ^ *c); },
115+
ShrUConst32(c) => { u32::stack_update(&mut executor.value_stack, |value| value.wrapping_shr(*c as u32)); },
116+
AddConst64(idx) => { let rhs = idx.resolve(&executor.func.data).value(); i64::stack_update(&mut executor.value_stack, |value| value.wrapping_add(rhs)); },
117+
BinOpStackConst32(op, rhs) => { u32::stack_update(&mut executor.value_stack, |lhs| op.exec(lhs, *rhs as u32)); },
118+
BinOpStackConst64(packed) => { let rhs = packed.index.resolve(&executor.func.data).value() as u64; u64::stack_update(&mut executor.value_stack, |lhs| packed.op.exec(lhs, rhs)); },
119+
IncLocal32(arg) => { i32::local_update(&mut executor.value_stack, &executor.cf, arg.local, |value| value.wrapping_add(arg.value)); },
120+
IncLocal64(packed) => { let rhs = packed.index.resolve(&executor.func.data).value(); i64::local_update(&mut executor.value_stack, &executor.cf, packed.op, |value| value.wrapping_add(rhs)); },
121121
I32Add3 => exec_op!(executor; ternary i32 => i32, |a, b, c| a.wrapping_add(b).wrapping_add(c)),
122122
I64Add3 => exec_op!(executor; ternary i64 => i64, |a, b, c| a.wrapping_add(b).wrapping_add(c)),
123123
MulAccLocal32(acc) => executor.exec_mul_acc_local::<i32>(*acc, i32::wrapping_mul, i32::wrapping_add),
@@ -160,9 +160,9 @@ macro_rules! instruction_handlers {
160160
BinOpGlobalConst32(packed) => { let v = packed.index.resolve(&executor.func.data); executor.exec_binop_global_const::<Value32>(v.a(), v.b(), packed.op)?; },
161161
BinOpGlobalConst64(packed) => { let v = packed.index.resolve(&executor.func.data); executor.exec_binop_global_const::<Value64>(v.a(), v.b(), packed.op)?; },
162162
BinOpGlobalConst128(packed) => { let v = packed.index.resolve(&executor.func.data); let rhs = Value128(v.b().resolve(&executor.func.data).value()); executor.exec_binop_global_const::<Value128>(v.a(), rhs, packed.op)?; },
163-
SetLocalConst32(arg) => i32::local_set(&mut executor.store.value_stack, &executor.cf, arg.local, arg.value),
164-
SetLocalConst64(packed) => { let v = packed.index.resolve(&executor.func.data).value(); i64::local_set(&mut executor.store.value_stack, &executor.cf, packed.op, v); },
165-
SetLocalConst128(packed) => Value128::local_set(&mut executor.store.value_stack, &executor.cf, packed.op, Value128(packed.index.resolve(&executor.func.data).value())),
163+
SetLocalConst32(arg) => i32::local_set(&mut executor.value_stack, &executor.cf, arg.local, arg.value),
164+
SetLocalConst64(packed) => { let v = packed.index.resolve(&executor.func.data).value(); i64::local_set(&mut executor.value_stack, &executor.cf, packed.op, v); },
165+
SetLocalConst128(packed) => Value128::local_set(&mut executor.value_stack, &executor.cf, packed.op, Value128(packed.index.resolve(&executor.func.data).value())),
166166
IncMemoryLocal32(arg) => executor.exec_inc_memory_local::<i32, 4>(arg.memory_arg_idx, arg.local1, #[inline(always)] |v| v.wrapping_add(1))?,
167167
IncMemoryLocal64(arg) => executor.exec_inc_memory_local::<i64, 8>(arg.memory_arg_idx, arg.local1, #[inline(always)] |v| v.wrapping_add(1))?,
168168
StoreLocalLocal32(arg) => executor.exec_store_local_local::<u32, 4>(arg.memory_arg_idx, arg.local1, arg.local2)?,
@@ -202,10 +202,10 @@ macro_rules! instruction_handlers {
202202
GlobalTee32(global_index) => executor.exec_global_tee::<Value32>(*global_index),
203203
GlobalTee64(global_index) => executor.exec_global_tee::<Value64>(*global_index),
204204
GlobalTee128(global_index) => executor.exec_global_tee::<Value128>(*global_index),
205-
Const32(val) => i32::stack_push(&mut executor.store.value_stack, *val),
206-
Const64Imm(val) => i64::stack_push(&mut executor.store.value_stack, i64::from(*val)),
207-
Const64(idx) => i64::stack_push(&mut executor.store.value_stack, idx.resolve(&executor.func.data).value()),
208-
Const128Imm(val) => Value128::stack_push(&mut executor.store.value_stack, Value128(u128::from(*val).to_le_bytes())),
205+
Const32(val) => i32::stack_push(&mut executor.value_stack, *val),
206+
Const64Imm(val) => i64::stack_push(&mut executor.value_stack, i64::from(*val)),
207+
Const64(idx) => i64::stack_push(&mut executor.value_stack, idx.resolve(&executor.func.data).value()),
208+
Const128Imm(val) => Value128::stack_push(&mut executor.value_stack, Value128(u128::from(*val).to_le_bytes())),
209209
I64Eqz => exec_op!(executor; unary i64 => i32, |v| i32::from(v == 0)),
210210
I32Eqz => exec_op!(executor; unary i32 => i32, |v| i32::from(v == 0)),
211211
I32Eq => exec_op!(executor; binary i32 => i32, |a, b| i32::from(a == b)),
@@ -290,8 +290,8 @@ macro_rules! instruction_handlers {
290290
I64Popcnt => exec_op!(executor; unary i64 => i64, |v| i64::from(v.count_ones())),
291291

292292
// Reference types
293-
RefFunc(func_idx) => ValueRef::stack_push(&mut executor.store.value_stack, ValueRef::from_category_addr(executor.module.resolve_func_addr(*func_idx))),
294-
RefNull(_) => ValueRef::stack_push(&mut executor.store.value_stack, ValueRef::NULL),
293+
RefFunc(func_idx) => ValueRef::stack_push(&mut executor.value_stack, ValueRef::from_category_addr(executor.module.resolve_func_addr(*func_idx))),
294+
RefNull(_) => ValueRef::stack_push(&mut executor.value_stack, ValueRef::NULL),
295295
RefIsNull => executor.exec_ref_is_null()?,
296296
RefAsNonNull => executor.exec_ref_as_non_null()?,
297297
RefI31 => exec_op!(executor; unary i32 => ValueRef, |v| ValueRef::from_i31(v)),
@@ -460,7 +460,7 @@ macro_rules! instruction_handlers {
460460
V128Store64Lane(arg) => executor.exec_mem_store_lane::<i64, 8>(*arg)?,
461461
V128Load32Zero(idx) => executor.exec_mem_load::<i32, 4, Value128>(idx.resolve(&executor.func.data), |v| Value128::from_i32x4([v, 0, 0, 0]))?,
462462
V128Load64Zero(idx) => executor.exec_mem_load::<i64, 8, Value128>(idx.resolve(&executor.func.data), |v| Value128::from_i64x2([v, 0]))?,
463-
Const128(arg) => Value128::stack_push(&mut executor.store.value_stack, Value128(arg.resolve(&executor.func.data).value())),
463+
Const128(arg) => Value128::stack_push(&mut executor.value_stack, Value128(arg.resolve(&executor.func.data).value())),
464464
I8x16ExtractLaneS(lane) => executor.exec_simd_extract_lane::<i32>(*lane, |v, lane| v.extract_lane_i8(lane) as i32)?,
465465
I8x16ExtractLaneU(lane) => executor.exec_simd_extract_lane::<i32>(*lane, |v, lane| v.extract_lane_u8(lane) as i32)?,
466466
I16x8ExtractLaneS(lane) => executor.exec_simd_extract_lane::<i32>(*lane, |v, lane| v.extract_lane_i16(lane) as i32)?,

0 commit comments

Comments
 (0)