Skip to content

Commit 50f7340

Browse files
committed
perf: borrow instruction stream across tail dispatch
1 parent f0bcaba commit 50f7340

2 files changed

Lines changed: 104 additions & 9 deletions

File tree

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use super::*;
33
struct Unbudgeted;
44
struct Bounded;
55

6-
type UnbudgetedHandler = for<'store> fn(&mut Executor<'store>, usize, Instruction) -> ExecResult<()>;
6+
type UnbudgetedHandler =
7+
for<'store> fn(&mut Executor<'store>, &[Instruction], FuncAddr, usize, Instruction) -> ExecResult<()>;
78
type BoundedHandler = for<'store> fn(&mut Executor<'store>, usize, Instruction, u32) -> ExecResult<()>;
89

910
#[cold]
@@ -29,22 +30,30 @@ macro_rules! define_unbudgeted_tail_dispatch {
2930
#[allow(non_snake_case, unreachable_code, unused_imports, unused_macros, unused_variables)]
3031
fn $variant(
3132
$executor: &mut Executor<'_>,
33+
instructions: &[Instruction],
34+
func_addr: FuncAddr,
3235
$instr_ptr: usize,
3336
instruction: Instruction,
3437
) -> ExecResult<()> {
3538
macro_rules! $dispatch_next {
3639
($next_instr_ptr:expr) => {{
3740
let next_instr_ptr = $next_instr_ptr;
38-
let instruction = $executor.func.instructions[next_instr_ptr];
41+
let instruction = instructions[next_instr_ptr];
3942
let handler = Self::handler_for(instruction.opcode());
40-
become handler($executor, next_instr_ptr, instruction);
43+
become handler($executor, instructions, func_addr, next_instr_ptr, instruction);
4144
}};
4245
}
4346
macro_rules! $dispatch_flow {
4447
($flow:expr) => {{
4548
match $flow.next_instr_ptr() {
46-
Some(next_instr_ptr) => $dispatch_next!(next_instr_ptr),
47-
None => return cold!(Ok(())),
49+
Some(next_instr_ptr) => {
50+
if $executor.cf.func_addr != func_addr {
51+
$executor.cf.instr_ptr = next_instr_ptr;
52+
return Ok(());
53+
}
54+
$dispatch_next!(next_instr_ptr)
55+
},
56+
None => return cold!({ $executor.completed = true; Ok(()) }),
4857
}
4958
}};
5059
}
@@ -142,10 +151,18 @@ impl Bounded {
142151
impl<'store> Executor<'store> {
143152
#[inline(always)]
144153
pub(crate) fn run_to_completion(mut self) -> Result<()> {
145-
let instr_ptr = self.cf.instr_ptr;
146-
let instruction = self.func.instructions[instr_ptr];
147-
let handler = Unbudgeted::handler_for(instruction.opcode());
148-
Ok(handler(&mut self, instr_ptr, instruction)?)
154+
loop {
155+
let func = self.func.clone();
156+
let instructions = &func.instructions;
157+
let func_addr = self.cf.func_addr;
158+
let instr_ptr = self.cf.instr_ptr;
159+
let instruction = instructions[instr_ptr];
160+
let handler = Unbudgeted::handler_for(instruction.opcode());
161+
handler(&mut self, instructions, func_addr, instr_ptr, instruction)?;
162+
if self.completed {
163+
return Ok(());
164+
}
165+
}
149166
}
150167

151168
#[cfg(feature = "std")]
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#![cfg(feature = "nightly-tail-calls")]
2+
3+
use tinywasm::{ExecProgress, HostFunction, Imports, ModuleInstance, Result, Store};
4+
5+
const ADDER: &str = r#"
6+
(module
7+
(import "host" "bump" (func $bump (param i32) (result i32)))
8+
(func (export "add") (param i32) (result i32)
9+
local.get 0
10+
call $bump
11+
i32.const 2
12+
i32.add))
13+
"#;
14+
15+
const CALLER: &str = r#"
16+
(module
17+
(type $unary (func (param i32) (result i32)))
18+
(import "adder" "add" (func $add (type $unary)))
19+
(table 1 funcref)
20+
(elem (i32.const 0) $add)
21+
(func $local (param i32) (result i32)
22+
local.get 0
23+
i32.const 4
24+
i32.add)
25+
(func (export "main") (param i32) (result i32)
26+
(local $acc i32)
27+
(local $i i32)
28+
local.get 0
29+
local.set $acc
30+
(loop $again
31+
local.get $acc
32+
i32.const 0
33+
call_indirect (type $unary)
34+
call $local
35+
call $add
36+
local.set $acc
37+
local.get $i
38+
i32.const 1
39+
i32.add
40+
local.tee $i
41+
i32.const 64
42+
i32.lt_u
43+
br_if $again)
44+
local.get $acc))
45+
"#;
46+
47+
#[test]
48+
fn linked_calls_preserve_instruction_stream_across_function_switches() -> Result<()> {
49+
let adder = tinywasm::parse_bytes(&wat::parse_str(ADDER).unwrap())?;
50+
let caller = tinywasm::parse_bytes(&wat::parse_str(CALLER).unwrap())?;
51+
let mut store = Store::default();
52+
53+
let mut adder_imports = Imports::new();
54+
adder_imports.define("host", "bump", HostFunction::from(|_ctx, value: i32| -> Result<i32> { Ok(value + 1) }));
55+
let adder_instance = ModuleInstance::instantiate(&mut store, &adder, Some(&adder_imports))?;
56+
57+
let mut caller_imports = Imports::new();
58+
caller_imports.link_module("adder", adder_instance)?;
59+
let caller_instance = ModuleInstance::instantiate(&mut store, &caller, Some(&caller_imports))?;
60+
let main = caller_instance.func::<i32, i32>(&store, "main")?;
61+
62+
assert_eq!(main.call(&mut store, 32)?, 672);
63+
64+
let mut resumable = main.call_resumable(&mut store, 32)?;
65+
let mut suspended = false;
66+
loop {
67+
match resumable.resume_with_fuel(8)? {
68+
ExecProgress::Completed(value) => {
69+
assert_eq!(value, 672);
70+
break;
71+
}
72+
ExecProgress::Suspended => suspended = true,
73+
}
74+
}
75+
assert!(suspended);
76+
77+
Ok(())
78+
}

0 commit comments

Comments
 (0)