Skip to content

Commit

Permalink
Optimize recursive calls (#341)
Browse files Browse the repository at this point in the history
* call by value instead of &mut because we can

* reuse local function frame instead of using the call stack

This significantly speeds up recursive calls and host function calls while not slowing down general execution.
  • Loading branch information
Robbepop authored Jan 18, 2022
1 parent 70faf34 commit 5b7ecc6
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 22 deletions.
12 changes: 0 additions & 12 deletions wasmi_v1/src/engine/call_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,18 +222,6 @@ impl CallStack {
Ok(())
}

pub fn push_pair(
&mut self,
first: FunctionFrame,
second: FunctionFrame,
) -> Result<(), TrapCode> {
if self.len() + 1 >= self.recursion_limit {
return Err(TrapCode::StackOverflow);
}
self.frames.extend([first, second]);
Ok(())
}

/// Pops the last [`FunctionFrame`] from the [`CallStack`] if any.
pub fn pop(&mut self) -> Option<FunctionFrame> {
self.frames.pop()
Expand Down
2 changes: 1 addition & 1 deletion wasmi_v1/src/engine/exec_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl<'engine, 'func> ExecutionContext<'engine, 'func> {
/// calls into another function or the function returns to its caller.
#[inline(always)]
pub fn execute_frame(
&mut self,
self,
mut ctx: impl AsContextMut,
) -> Result<FunctionExecutionOutcome, Trap> {
'outer: loop {
Expand Down
22 changes: 13 additions & 9 deletions wasmi_v1/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,29 +304,33 @@ impl EngineInner {
///
/// - If any of the executed instructions yield an error.
fn execute_until_done(&mut self, mut ctx: impl AsContextMut) -> Result<(), Trap> {
let mut function_frame = match self.call_stack.pop() {
Some(frame) => frame,
None => return Ok(()),
};
'outer: loop {
let mut function_frame = match self.call_stack.pop() {
Some(frame) => frame,
None => return Ok(()),
};
let result =
ExecutionContext::new(self, &mut function_frame)?.execute_frame(&mut ctx)?;
match result {
FunctionExecutionOutcome::Return => {
continue 'outer;
}
FunctionExecutionOutcome::Return => match self.call_stack.pop() {
Some(frame) => {
function_frame = frame;
continue 'outer;
}
None => return Ok(()),
},
FunctionExecutionOutcome::NestedCall(func) => {
match func.as_internal(ctx.as_context()) {
FuncEntityInternal::Wasm(wasm_func) => {
let nested_frame = FunctionFrame::new_wasm(func, wasm_func);
self.call_stack.push_pair(function_frame, nested_frame)?;
self.call_stack.push(function_frame)?;
function_frame = nested_frame;
}
FuncEntityInternal::Host(host_func) => {
// Note: We push the function context before calling the host function.
// If the VM is not resumable, it does no harm.
// If it is, we then save the context here.
let instance = function_frame.instance();
self.call_stack.push(function_frame)?;
let host_func = host_func.clone();
self.execute_host_func(&mut ctx, host_func, Some(instance))?;
}
Expand Down

0 comments on commit 5b7ecc6

Please sign in to comment.