Skip to content

Commit 99116d4

Browse files
committed
implement host function calls in return_call[_indirect]
1 parent da7bdbb commit 99116d4

File tree

3 files changed

+138
-15
lines changed

3 files changed

+138
-15
lines changed

crates/wasmi/src/engine/executor/handler/exec.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use crate::{
3434
resolve_memory,
3535
resolve_table,
3636
resolve_table_mut,
37+
return_call_host,
3738
return_call_wasm,
3839
set_global,
3940
update_instance,
@@ -285,22 +286,23 @@ pub fn return_call_imported(
285286
) -> Done {
286287
let (_, crate::ir::decode::ReturnCallImported { params, func }) = unsafe { decode_op(ip) };
287288
let func = fetch_func(instance, func);
288-
let func = resolve_func(state.store, &func);
289-
let (callee_ip, sp, mem0, mem0_len, instance) = match func {
289+
let func_entity = resolve_func(state.store, &func);
290+
let (callee_ip, sp, new_instance) = match func_entity {
290291
FuncEntity::Wasm(func) => {
291292
let wasm_func = func.func_body();
292293
let callee_instance = *func.instance();
293294
let callee_instance = resolve_instance(state.store, &callee_instance).into();
294295
let (callee_ip, callee_sp) =
295296
return_call_wasm(state, params, wasm_func, Some(instance))?;
296-
let (instance, mem0, mem0_len) =
297-
update_instance(state.store, instance, callee_instance, mem0, mem0_len);
298-
(callee_ip, callee_sp, mem0, mem0_len, instance)
297+
(callee_ip, callee_sp, callee_instance)
299298
}
300-
FuncEntity::Host(_host_func) => {
301-
todo!()
299+
FuncEntity::Host(host_func) => {
300+
let host_func = *host_func;
301+
return_call_host(state, func, host_func, params, instance)?
302302
}
303303
};
304+
let (instance, mem0, mem0_len) =
305+
update_instance(state.store, instance, new_instance, mem0, mem0_len);
304306
dispatch!(state, callee_ip, sp, mem0, mem0_len, instance)
305307
}
306308

@@ -323,22 +325,23 @@ pub fn return_call_indirect(
323325
) = unsafe { decode_op(ip) };
324326
let func =
325327
resolve_indirect_func(index, table, func_type, state, sp, instance).into_control()?;
326-
let func = resolve_func(state.store, &func);
327-
let (callee_ip, sp, mem0, mem0_len, instance) = match func {
328+
let func_entity = resolve_func(state.store, &func);
329+
let (callee_ip, sp, callee_instance) = match func_entity {
328330
FuncEntity::Wasm(func) => {
329331
let wasm_func = func.func_body();
330332
let callee_instance = *func.instance();
331333
let callee_instance: Inst = resolve_instance(state.store, &callee_instance).into();
332334
let (callee_ip, callee_sp) =
333335
return_call_wasm(state, params, wasm_func, Some(instance))?;
334-
let (instance, mem0, mem0_len) =
335-
update_instance(state.store, instance, callee_instance, mem0, mem0_len);
336-
(callee_ip, callee_sp, mem0, mem0_len, instance)
336+
(callee_ip, callee_sp, callee_instance)
337337
}
338-
FuncEntity::Host(_func) => {
339-
todo!()
338+
FuncEntity::Host(host_func) => {
339+
let host_func = *host_func;
340+
return_call_host(state, func, host_func, params, instance)?
340341
}
341342
};
343+
let (instance, mem0, mem0_len) =
344+
update_instance(state.store, instance, callee_instance, mem0, mem0_len);
342345
dispatch!(state, callee_ip, sp, mem0, mem0_len, instance)
343346
}
344347

crates/wasmi/src/engine/executor/handler/state.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use crate::{
22
core::{ReadAs, UntypedVal, WriteAs},
33
engine::{
44
executor::{
5-
handler::{dispatch::ExecutionOutcome, utils::extract_mem0},
5+
handler::{
6+
dispatch::{Control, ExecutionOutcome},
7+
utils::extract_mem0,
8+
},
69
CodeMap,
710
},
811
utils::unreachable_unchecked,
@@ -252,6 +255,8 @@ pub struct Stack {
252255
frames: CallStack,
253256
}
254257

258+
type ReturnCallHost = Control<(Ip, Sp, Inst), Sp>;
259+
255260
impl Stack {
256261
pub fn new(config: &StackConfig) -> Self {
257262
Self {
@@ -276,6 +281,17 @@ impl Stack {
276281
self.values.capacity()
277282
}
278283

284+
pub fn return_prepare_host_frame<'a>(
285+
&'a mut self,
286+
callee_params: BoundedSlotSpan,
287+
results_len: u16,
288+
caller_instance: Inst,
289+
) -> Result<(ReturnCallHost, FuncInOut<'a>), TrapCode> {
290+
let (callee_start, caller) = self.frames.return_prepare_host_frame(caller_instance);
291+
self.values
292+
.return_prepare_host_frame(caller, callee_start, callee_params, results_len)
293+
}
294+
279295
pub fn prepare_host_frame<'a>(
280296
&'a mut self,
281297
caller_ip: Option<Ip>,
@@ -376,6 +392,57 @@ impl ValueStack {
376392
}
377393
}
378394

395+
/// # Note
396+
///
397+
/// In the following code, `callee` represents the called host function frame
398+
/// and `caller` represents the caller of the caller of the host function, a.k.a.
399+
/// the caller's caller.
400+
fn return_prepare_host_frame<'a>(
401+
&'a mut self,
402+
caller: Option<(Ip, usize, Inst)>,
403+
callee_start: usize,
404+
callee_params: BoundedSlotSpan,
405+
results_len: u16,
406+
) -> Result<(ReturnCallHost, FuncInOut<'a>), TrapCode> {
407+
let caller_start = caller.map(|(_, start, _)| start).unwrap_or(0);
408+
let params_offset = usize::from(u16::from(callee_params.span().head()));
409+
let params_len = usize::from(callee_params.len());
410+
let results_len = usize::from(results_len);
411+
let callee_size = params_len.max(results_len);
412+
if callee_size == 0 {
413+
let sp = match caller {
414+
Some(_) if caller_start != callee_start => self.sp(caller_start),
415+
_ => Sp::dangling(),
416+
};
417+
let inout = FuncInOut::new(&mut [], 0, 0);
418+
let control = match caller {
419+
Some((ip, _, instance)) => ReturnCallHost::Continue((ip, sp, instance)),
420+
None => ReturnCallHost::Break(sp),
421+
};
422+
return Ok((control, inout));
423+
}
424+
let Some(params_start) = callee_start.checked_add(params_offset) else {
425+
return Err(TrapCode::StackOverflow);
426+
};
427+
let Some(params_end) = params_start.checked_add(params_len) else {
428+
return Err(TrapCode::StackOverflow);
429+
};
430+
self.cells
431+
.copy_within(params_start..params_end, callee_start);
432+
let Some(callee_end) = callee_start.checked_add(callee_size) else {
433+
return Err(TrapCode::StackOverflow);
434+
};
435+
self.cells.resize(callee_end, UntypedVal::default());
436+
let caller_sp = self.sp(caller_start);
437+
let cells = &mut self.cells[callee_start..];
438+
let inout = FuncInOut::new(cells, params_len, results_len);
439+
let control = match caller {
440+
Some((ip, _, instance)) => ReturnCallHost::Continue((ip, caller_sp, instance)),
441+
None => ReturnCallHost::Break(caller_sp),
442+
};
443+
Ok((control, inout))
444+
}
445+
379446
fn prepare_host_frame<'a>(
380447
&'a mut self,
381448
caller_start: usize,
@@ -504,6 +571,26 @@ impl CallStack {
504571
self.top_start()
505572
}
506573

574+
/// # Note
575+
///
576+
/// In the following code, `callee` represents the called host function frame
577+
/// and `caller` represents the caller of the caller of the host function, a.k.a.
578+
/// the caller's caller.
579+
pub fn return_prepare_host_frame(
580+
&mut self,
581+
callee_instance: Inst,
582+
) -> (usize, Option<(Ip, usize, Inst)>) {
583+
let callee_start = self.top_start();
584+
let caller = match self.pop() {
585+
Some((ip, start, instance)) => {
586+
let instance = instance.unwrap_or(callee_instance);
587+
Some((ip, start, instance))
588+
}
589+
None => None,
590+
};
591+
(callee_start, caller)
592+
}
593+
507594
#[inline(always)]
508595
fn push(
509596
&mut self,

crates/wasmi/src/engine/executor/handler/utils.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,39 @@ pub fn call_host(
429429
Control::Continue(sp)
430430
}
431431

432+
pub fn return_call_host(
433+
state: &mut VmState,
434+
func: Func,
435+
host_func: HostFuncEntity,
436+
params: BoundedSlotSpan,
437+
instance: Inst,
438+
) -> Control<(Ip, Sp, Inst), Break> {
439+
debug_assert_eq!(params.len(), host_func.len_params());
440+
let trampoline = *host_func.trampoline();
441+
let (control, params_results) = state
442+
.stack
443+
.return_prepare_host_frame(params, host_func.len_results(), instance)
444+
.into_control()?;
445+
match state
446+
.store
447+
.call_host_func(trampoline, Some(instance), params_results, CallHooks::Call)
448+
{
449+
Ok(()) => {}
450+
Err(StoreError::External(error)) => {
451+
done!(state, DoneReason::host_error(error, func, params.span()))
452+
}
453+
Err(StoreError::Internal(error)) => unsafe {
454+
unreachable_unchecked!(
455+
"internal interpreter error while executing host function: {error}"
456+
)
457+
},
458+
}
459+
match control {
460+
Control::Continue((ip, sp, instance)) => Control::Continue((ip, sp, instance)),
461+
Control::Break(sp) => done!(state, DoneReason::Return(sp)),
462+
}
463+
}
464+
432465
pub fn call_wasm_or_host(
433466
state: &mut VmState,
434467
caller_ip: Ip,

0 commit comments

Comments
 (0)