Skip to content

Commit

Permalink
refactor task/stream/future handle lifting/lowering
Browse files Browse the repository at this point in the history
This addresses a couple of issues:

- Previously, we were passing task/stream/future/error-context reps directly to
  instances while keeping track of which instance had access to which rep.  That
  worked fine in that there was no way to forge access to inaccessible reps, but
  it leaked information about what other instances were doing.  Now we maintain
  per-instance waitable and error-context tables which map the reps to and from
  the handles which the instance sees.

- The `no_std` build was broken due to use of `HashMap` in
  `runtime::vm::component`, which is now fixed.

Note that we use one single table per instance for all tasks, streams, and
futures.  This is partly necessary because, when async events are delivered to
the guest, it wouldn't have enough context to know which stream or future we're
talking about if each unique stream and future type had its own table.  So at
minimum, we need to use the same table for all streams (regardless of payload
type), and likewise for futures.  Also, per
WebAssembly/component-model#395 (comment),
the plan is to move towards a shared table for all resource types as well, so
this moves us in that direction.

Signed-off-by: Joel Dice <joel.dice@fermyon.com>
  • Loading branch information
dicej committed Nov 21, 2024
1 parent a5c2bce commit 83eb855
Show file tree
Hide file tree
Showing 14 changed files with 395 additions and 177 deletions.
35 changes: 29 additions & 6 deletions crates/cranelift/src/compiler/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,26 @@ impl<'a> TrampolineCompiler<'a> {
self.translate_task_backpressure_call(*instance)
}
Trampoline::TaskReturn => self.translate_task_return_call(),
Trampoline::TaskWait { async_, memory } => {
self.translate_task_wait_or_poll_call(*async_, *memory, self.offsets.task_wait())
}
Trampoline::TaskPoll { async_, memory } => {
self.translate_task_wait_or_poll_call(*async_, *memory, self.offsets.task_poll())
}
Trampoline::TaskWait {
instance,
async_,
memory,
} => self.translate_task_wait_or_poll_call(
*instance,
*async_,
*memory,
self.offsets.task_wait(),
),
Trampoline::TaskPoll {
instance,
async_,
memory,
} => self.translate_task_wait_or_poll_call(
*instance,
*async_,
*memory,
self.offsets.task_poll(),
),
Trampoline::TaskYield { async_ } => self.translate_task_yield_call(*async_),
Trampoline::SubtaskDrop { instance } => self.translate_subtask_drop_call(*instance),
Trampoline::StreamNew { ty } => self.translate_future_or_stream_call(
Expand Down Expand Up @@ -283,6 +297,7 @@ impl<'a> TrampolineCompiler<'a> {
self.offsets.async_exit(),
Some(*callback),
vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::new(pointer_type),
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::new(ir::types::I32),
Expand Down Expand Up @@ -1391,6 +1406,7 @@ impl<'a> TrampolineCompiler<'a> {

fn translate_task_wait_or_poll_call(
&mut self,
caller_instance: RuntimeComponentInstanceIndex,
async_: bool,
memory: RuntimeMemoryIndex,
offset: u32,
Expand All @@ -1413,6 +1429,13 @@ impl<'a> TrampolineCompiler<'a> {
let mut host_sig = ir::Signature::new(CallConv::triple_default(self.isa.triple()));
host_sig.params.push(ir::AbiParam::new(pointer_type));

host_sig.params.push(ir::AbiParam::new(ir::types::I32));
host_args.push(
self.builder
.ins()
.iconst(ir::types::I32, i64::from(caller_instance.as_u32())),
);

host_sig.params.push(ir::AbiParam::new(ir::types::I8));
host_args.push(
self.builder
Expand Down
16 changes: 14 additions & 2 deletions crates/environ/src/component/dfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,12 @@ pub enum Trampoline {
},
TaskReturn,
TaskWait {
instance: RuntimeComponentInstanceIndex,
async_: bool,
memory: MemoryId,
},
TaskPoll {
instance: RuntimeComponentInstanceIndex,
async_: bool,
memory: MemoryId,
},
Expand Down Expand Up @@ -756,11 +758,21 @@ impl LinearizeDfg<'_> {
instance: *instance,
},
Trampoline::TaskReturn => info::Trampoline::TaskReturn,
Trampoline::TaskWait { async_, memory } => info::Trampoline::TaskWait {
Trampoline::TaskWait {
instance,
async_,
memory,
} => info::Trampoline::TaskWait {
instance: *instance,
async_: *async_,
memory: self.runtime_memory(*memory),
},
Trampoline::TaskPoll { async_, memory } => info::Trampoline::TaskPoll {
Trampoline::TaskPoll {
instance,
async_,
memory,
} => info::Trampoline::TaskPoll {
instance: *instance,
async_: *async_,
memory: self.runtime_memory(*memory),
},
Expand Down
4 changes: 4 additions & 0 deletions crates/environ/src/component/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,8 @@ pub enum Trampoline {
/// A `task.wait` intrinsic, which waits for at least one outstanding async
/// task/stream/future to make progress, returning the first such event.
TaskWait {
/// The specific component instance which is calling the intrinsic.
instance: RuntimeComponentInstanceIndex,
/// If `true`, indicates the caller instance maybe reentered.
async_: bool,
/// Memory to use when storing the event.
Expand All @@ -693,6 +695,8 @@ pub enum Trampoline {
/// task/stream/future has made progress. Unlike `task.wait`, this does not
/// block and may return nothing if no such event has occurred.
TaskPoll {
/// The specific component instance which is calling the intrinsic.
instance: RuntimeComponentInstanceIndex,
/// If `true`, indicates the caller instance maybe reentered.
async_: bool,
/// Memory to use when storing the event.
Expand Down
2 changes: 2 additions & 0 deletions crates/environ/src/component/translate/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,7 @@ impl<'a> Inliner<'a> {
let index = self.result.trampolines.push((
*func,
dfg::Trampoline::TaskWait {
instance: frame.instance,
async_: *async_,
memory,
},
Expand All @@ -712,6 +713,7 @@ impl<'a> Inliner<'a> {
let index = self.result.trampolines.push((
*func,
dfg::Trampoline::TaskPoll {
instance: frame.instance,
async_: *async_,
memory,
},
Expand Down
1 change: 1 addition & 0 deletions crates/environ/src/fact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ impl<'a> Module<'a> {
"async",
"exit-call",
&[
ValType::I32,
ValType::FUNCREF,
ValType::I32,
ValType::I32,
Expand Down
10 changes: 10 additions & 0 deletions crates/environ/src/fact/trampoline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,10 @@ impl Compiler<'_, '_> {
adapter.callee.as_u32(),
format!("[adapter-callee]{}", adapter.name),
));

self.instruction(I32Const(
i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
));
self.instruction(RefFunc(adapter.callee.as_u32()));
self.instruction(I32Const(
i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
Expand Down Expand Up @@ -481,6 +485,9 @@ impl Compiler<'_, '_> {
adapter.callee.as_u32(),
format!("[adapter-callee]{}", adapter.name),
));
self.instruction(I32Const(
i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
));
self.instruction(RefFunc(adapter.callee.as_u32()));
self.instruction(I32Const(
i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
Expand Down Expand Up @@ -531,6 +538,9 @@ impl Compiler<'_, '_> {
adapter.callee.as_u32(),
format!("[adapter-callee]{}", adapter.name),
));
self.instruction(I32Const(
i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
));
self.instruction(RefFunc(adapter.callee.as_u32()));
self.instruction(I32Const(
i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
Expand Down
Loading

0 comments on commit 83eb855

Please sign in to comment.