Skip to content

Commit 9726f92

Browse files
committed
optimize sync-to-sync, guest-to-guest calls
We now omit recursive reentrance checks entirely when generating fused adapters for components which cannot possibly recursively reenter themselves, noting that components which only contain modules in leaf (sub)components fall into that category. However, we must still include a runtime global variable check-and-set to enforce the may-block rules for sync-typed tasks. With some more effort, we could also eliminate those for components statically known to never make blocking calls to intrinsics or imports. The implementation of the may-block check centers around a per-top-level-instance global variable called `task_may_block`, which we update each time we switch threads and tasks, as well as whenever `task.return` or `task.cancel` is called. This required shuffling some code around and creating a new `StoreOpaque::set_thread` function which encapsulates switching threads and updating `task_may_block` for the old and new instances.
1 parent 44522b1 commit 9726f92

File tree

23 files changed

+505
-304
lines changed

23 files changed

+505
-304
lines changed

crates/cranelift/src/compiler/component.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -732,14 +732,6 @@ impl<'a> TrampolineCompiler<'a> {
732732
|_, _| {},
733733
);
734734
}
735-
Trampoline::CheckBlocking => {
736-
self.translate_libcall(
737-
host::check_blocking,
738-
TrapSentinel::Falsy,
739-
WasmArgs::InRegisters,
740-
|_, _| {},
741-
);
742-
}
743735
Trampoline::EnterSyncCall => {
744736
self.translate_libcall(
745737
host::enter_sync_call,

crates/environ/src/component.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,6 @@ macro_rules! foreach_builtin_component_function {
188188
#[cfg(feature = "component-model-async")]
189189
error_context_transfer(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u64;
190190
#[cfg(feature = "component-model-async")]
191-
check_blocking(vmctx: vmctx) -> bool;
192-
#[cfg(feature = "component-model-async")]
193191
context_get(vmctx: vmctx, caller_instance: u32, slot: u32) -> u64;
194192
#[cfg(feature = "component-model-async")]
195193
context_set(vmctx: vmctx, caller_instance: u32, slot: u32, val: u32) -> bool;

crates/environ/src/component/dfg.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ pub enum CoreDef {
267267
InstanceFlags(RuntimeComponentInstanceIndex),
268268
Trampoline(TrampolineIndex),
269269
UnsafeIntrinsic(ModuleInternedTypeIndex, UnsafeIntrinsic),
270+
TaskMayBlock,
270271

271272
/// This is a special variant not present in `info::CoreDef` which
272273
/// represents that this definition refers to a fused adapter function. This
@@ -475,7 +476,6 @@ pub enum Trampoline {
475476
FutureTransfer,
476477
StreamTransfer,
477478
ErrorContextTransfer,
478-
CheckBlocking,
479479
EnterSyncCall,
480480
ExitSyncCall,
481481
ContextGet {
@@ -906,6 +906,7 @@ impl LinearizeDfg<'_> {
906906
}
907907
info::CoreDef::UnsafeIntrinsic(*i)
908908
}
909+
CoreDef::TaskMayBlock => info::CoreDef::TaskMayBlock,
909910
}
910911
}
911912

@@ -1157,7 +1158,6 @@ impl LinearizeDfg<'_> {
11571158
Trampoline::FutureTransfer => info::Trampoline::FutureTransfer,
11581159
Trampoline::StreamTransfer => info::Trampoline::StreamTransfer,
11591160
Trampoline::ErrorContextTransfer => info::Trampoline::ErrorContextTransfer,
1160-
Trampoline::CheckBlocking => info::Trampoline::CheckBlocking,
11611161
Trampoline::EnterSyncCall => info::Trampoline::EnterSyncCall,
11621162
Trampoline::ExitSyncCall => info::Trampoline::ExitSyncCall,
11631163
Trampoline::ContextGet { instance, slot } => info::Trampoline::ContextGet {

crates/environ/src/component/info.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,10 @@ pub enum CoreDef {
389389
Trampoline(TrampolineIndex),
390390
/// An intrinsic for compile-time builtins.
391391
UnsafeIntrinsic(UnsafeIntrinsic),
392+
/// Reference to a wasm global which represents a runtime-managed boolean
393+
/// indicating whether the currently-running task may perform a blocking
394+
/// operation.
395+
TaskMayBlock,
392396
}
393397

394398
impl<T> From<CoreExport<T>> for CoreDef
@@ -1105,10 +1109,6 @@ pub enum Trampoline {
11051109
/// component does not invalidate the handle in the original component.
11061110
ErrorContextTransfer,
11071111

1108-
/// An intrinsic used by FACT-generated modules to check whether an
1109-
/// async-typed function may be called via a sync lower.
1110-
CheckBlocking,
1111-
11121112
/// An intrinsic used by FACT-generated modules to check whether an instance
11131113
/// may be entered for a sync-to-sync call and push a task onto the stack if
11141114
/// so.
@@ -1246,7 +1246,6 @@ impl Trampoline {
12461246
FutureTransfer => format!("future-transfer"),
12471247
StreamTransfer => format!("stream-transfer"),
12481248
ErrorContextTransfer => format!("error-context-transfer"),
1249-
CheckBlocking => format!("check-blocking"),
12501249
EnterSyncCall => format!("enter-sync-call"),
12511250
ExitSyncCall => format!("exit-sync-call"),
12521251
ContextGet { .. } => format!("context-get"),

crates/environ/src/component/translate.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ pub struct Translator<'a, 'data> {
7979

8080
/// The top-level import name for Wasmtime's unsafe intrinsics, if any.
8181
unsafe_intrinsics_import: Option<&'a str>,
82+
83+
/// Whether this component contains any modules outside of leaf components.
84+
///
85+
/// We'll use this when generating fused adapters; if it's false, then we
86+
/// know that no guest-to-guest call can reenter a runtime instance
87+
/// recursively.
88+
has_nonleaf_modules: bool,
8289
}
8390

8491
/// Representation of the syntactic scope of a component meaning where it is
@@ -171,6 +178,13 @@ struct Translation<'data> {
171178
/// component has finished, e.g. for the `inline` pass, but beforehand this
172179
/// is set to `None`.
173180
types: Option<Types>,
181+
182+
/// Whether we've encountered a module while parsing this component
183+
/// (but ignoring modules found in subcomponents).
184+
saw_module: bool,
185+
186+
/// Whether we've encountered a subcomponent while parsing this component.
187+
saw_component: bool,
174188
}
175189

176190
// NB: the type information contained in `LocalInitializer` should always point
@@ -455,6 +469,7 @@ impl<'a, 'data> Translator<'a, 'data> {
455469
static_modules: Default::default(),
456470
scope_vec,
457471
unsafe_intrinsics_import: None,
472+
has_nonleaf_modules: false,
458473
}
459474
}
460475

@@ -606,6 +621,7 @@ impl<'a, 'data> Translator<'a, 'data> {
606621

607622
let known_func = match arg {
608623
CoreDef::InstanceFlags(_) => unreachable!("instance flags are not a function"),
624+
CoreDef::TaskMayBlock => unreachable!("task_may_block is not a function"),
609625

610626
// We could in theory inline these trampolines, so it could
611627
// potentially make sense to record that we know this
@@ -694,6 +710,8 @@ impl<'a, 'data> Translator<'a, 'data> {
694710
assert!(self.result.types.is_none());
695711
self.result.types = Some(self.validator.end(offset)?);
696712

713+
self.has_nonleaf_modules |= self.result.saw_module && self.result.saw_component;
714+
697715
// Exit the current lexical scope. If there is no parent (no
698716
// frame currently on the stack) then translation is finished.
699717
// Otherwise that means that a nested component has been
@@ -1193,6 +1211,7 @@ impl<'a, 'data> Translator<'a, 'data> {
11931211
parser,
11941212
unchecked_range,
11951213
} => {
1214+
self.result.saw_module = true;
11961215
let index = self.validator.types(0).unwrap().module_count();
11971216
self.validator.module_section(&unchecked_range)?;
11981217
let static_module_index = self.static_modules.next_key();
@@ -1238,6 +1257,7 @@ impl<'a, 'data> Translator<'a, 'data> {
12381257
parser,
12391258
unchecked_range,
12401259
} => {
1260+
self.result.saw_component = true;
12411261
self.validator.component_section(&unchecked_range)?;
12421262
self.lexical_scopes.push(LexicalScope {
12431263
parser: mem::replace(&mut self.parser, parser),

crates/environ/src/component/translate/adapt.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,11 @@ impl<'data> Translator<'_, 'data> {
205205
// the module using standard core wasm translation, and then fills out
206206
// the dfg metadata for each adapter.
207207
for (module_id, adapter_module) in state.adapter_modules.iter() {
208-
let mut module =
209-
fact::Module::new(self.types.types(), self.tunables.debug_adapter_modules);
208+
let mut module = fact::Module::new(
209+
self.types.types(),
210+
self.tunables.debug_adapter_modules,
211+
self.has_nonleaf_modules,
212+
);
210213
let mut names = Vec::with_capacity(adapter_module.adapters.len());
211214
for adapter in adapter_module.adapters.iter() {
212215
let name = format!("adapter{}", adapter.as_u32());
@@ -345,7 +348,6 @@ fn fact_import_to_core_def(
345348
fact::Import::ErrorContextTransfer => {
346349
simple_intrinsic(dfg::Trampoline::ErrorContextTransfer)
347350
}
348-
fact::Import::CheckBlocking => simple_intrinsic(dfg::Trampoline::CheckBlocking),
349351
fact::Import::EnterSyncCall => simple_intrinsic(dfg::Trampoline::EnterSyncCall),
350352
fact::Import::ExitSyncCall => simple_intrinsic(dfg::Trampoline::ExitSyncCall),
351353
}
@@ -453,7 +455,8 @@ impl PartitionAdapterModules {
453455
// These items can't transitively depend on an adapter
454456
dfg::CoreDef::Trampoline(_)
455457
| dfg::CoreDef::InstanceFlags(_)
456-
| dfg::CoreDef::UnsafeIntrinsic(..) => {}
458+
| dfg::CoreDef::UnsafeIntrinsic(..)
459+
| dfg::CoreDef::TaskMayBlock => {}
457460
}
458461
}
459462

crates/environ/src/component/translate/inline.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1592,7 +1592,7 @@ impl<'a> Inliner<'a> {
15921592
}
15931593
}
15941594

1595-
/// Translatees an `AdapterOptions` into a `CanonicalOptions` where
1595+
/// Translates an `AdapterOptions` into a `CanonicalOptions` where
15961596
/// memories/functions are inserted into the global initializer list for
15971597
/// use at runtime. This is only used for lowered host functions and lifted
15981598
/// functions exported to the host.

crates/environ/src/component/vmcomponent_offsets.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub struct VMComponentOffsets<P> {
6666
builtins: u32,
6767
vm_store_context: u32,
6868
flags: u32,
69+
task_may_block: u32,
6970
trampoline_func_refs: u32,
7071
intrinsic_func_refs: u32,
7172
lowerings: u32,
@@ -125,6 +126,7 @@ impl<P: PtrSize> VMComponentOffsets<P> {
125126
builtins: 0,
126127
vm_store_context: 0,
127128
flags: 0,
129+
task_may_block: 0,
128130
trampoline_func_refs: 0,
129131
intrinsic_func_refs: 0,
130132
lowerings: 0,
@@ -168,6 +170,7 @@ impl<P: PtrSize> VMComponentOffsets<P> {
168170
size(vm_store_context) = ret.ptr.size(),
169171
align(16),
170172
size(flags) = cmul(ret.num_runtime_component_instances, ret.ptr.size_of_vmglobal_definition()),
173+
size(task_may_block) = ret.ptr.size(),
171174
align(u32::from(ret.ptr.size())),
172175
size(trampoline_func_refs) = cmul(ret.num_trampolines, ret.ptr.size_of_vm_func_ref()),
173176
size(intrinsic_func_refs) = cmul(ret.num_unsafe_intrinsics, ret.ptr.size_of_vm_func_ref()),
@@ -215,6 +218,11 @@ impl<P: PtrSize> VMComponentOffsets<P> {
215218
self.flags + index.as_u32() * u32::from(self.ptr.size_of_vmglobal_definition())
216219
}
217220

221+
/// The offset of the `task_may_block` field.
222+
pub fn task_may_block(&self) -> u32 {
223+
self.task_may_block
224+
}
225+
218226
/// The offset of the `vm_store_context` field.
219227
#[inline]
220228
pub fn vm_store_context(&self) -> u32 {

crates/environ/src/fact.rs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ pub static PREPARE_CALL_FIXED_PARAMS: &[ValType] = &[
5454

5555
/// Representation of an adapter module.
5656
pub struct Module<'a> {
57+
might_recursively_reenter: bool,
58+
5759
/// Whether or not debug code is inserted into the adapters themselves.
5860
debug: bool,
5961
/// Type information from the creator of this `Module`
@@ -90,7 +92,6 @@ pub struct Module<'a> {
9092
imported_stream_transfer: Option<FuncIndex>,
9193
imported_error_context_transfer: Option<FuncIndex>,
9294

93-
imported_check_blocking: Option<FuncIndex>,
9495
imported_enter_sync_call: Option<FuncIndex>,
9596
imported_exit_sync_call: Option<FuncIndex>,
9697

@@ -104,6 +105,8 @@ pub struct Module<'a> {
104105
helper_worklist: Vec<(FunctionId, Helper)>,
105106

106107
exports: Vec<(u32, String)>,
108+
109+
task_may_block: Option<GlobalIndex>,
107110
}
108111

109112
struct AdapterData {
@@ -241,8 +244,13 @@ enum HelperLocation {
241244

242245
impl<'a> Module<'a> {
243246
/// Creates an empty module.
244-
pub fn new(types: &'a ComponentTypesBuilder, debug: bool) -> Module<'a> {
247+
pub fn new(
248+
types: &'a ComponentTypesBuilder,
249+
debug: bool,
250+
might_recursively_reenter: bool,
251+
) -> Module<'a> {
245252
Module {
253+
might_recursively_reenter,
246254
debug,
247255
types,
248256
core_types: Default::default(),
@@ -264,10 +272,10 @@ impl<'a> Module<'a> {
264272
imported_future_transfer: None,
265273
imported_stream_transfer: None,
266274
imported_error_context_transfer: None,
267-
imported_check_blocking: None,
268275
imported_enter_sync_call: None,
269276
imported_exit_sync_call: None,
270277
exports: Vec::new(),
278+
task_may_block: None,
271279
}
272280
}
273281

@@ -463,6 +471,25 @@ impl<'a> Module<'a> {
463471
idx
464472
}
465473

474+
fn import_task_may_block(&mut self) -> GlobalIndex {
475+
if let Some(task_may_block) = self.task_may_block {
476+
task_may_block
477+
} else {
478+
let task_may_block = self.import_global(
479+
"instance",
480+
"task_may_block",
481+
GlobalType {
482+
val_type: ValType::I32,
483+
mutable: true,
484+
shared: false,
485+
},
486+
CoreDef::TaskMayBlock,
487+
);
488+
self.task_may_block = Some(task_may_block);
489+
task_may_block
490+
}
491+
}
492+
466493
fn import_transcoder(&mut self, transcoder: transcode::Transcoder) -> FuncIndex {
467494
*self
468495
.imported_transcoders
@@ -720,17 +747,6 @@ impl<'a> Module<'a> {
720747
)
721748
}
722749

723-
fn import_check_blocking(&mut self) -> FuncIndex {
724-
self.import_simple(
725-
"async",
726-
"check-blocking",
727-
&[],
728-
&[],
729-
Import::CheckBlocking,
730-
|me| &mut me.imported_check_blocking,
731-
)
732-
}
733-
734750
fn import_enter_sync_call(&mut self) -> FuncIndex {
735751
self.import_simple(
736752
"async",
@@ -911,9 +927,6 @@ pub enum Import {
911927
/// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
912928
/// ownership of an `error-context`.
913929
ErrorContextTransfer,
914-
/// An intrinsic used by FACT-generated modules to check whether an
915-
/// async-typed function may be called via a sync lower.
916-
CheckBlocking,
917930
/// An intrinsic used by FACT-generated modules to check whether an instance
918931
/// may be entered for a sync-to-sync call and push a task onto the stack if
919932
/// so.

0 commit comments

Comments
 (0)