Skip to content

Commit 3a0c4f0

Browse files
committed
Skip {enter,exit}-sync-call for "thread-transparent" adapters
Today, every sync adapter calls `enter-sync-call`, then does its lifting and lowering of arguments and reesults, and then calls `exit-sync-call` afterwards. The `{enter,exit}-sync-call` helpers save and restore the old thread's TLS context and create the new thread's TLS context. For sync-to-sync calls, we inline these helpers and do their work lazily via the `VMDeferredThread` machinery. But even so, creating a lazy `VMDeferredThread` can be pretty expensive if the adapter's callee is just doing like a single load or store or has been boiled away into returning a constant value. Therefore, this commit introduces an analysis to find "thread-transparent" components. These are components that do not `canon lower` any component model intrinsic to access the thread state, and therefore *cannot* read or write that state. When we are compiling adapters whose callee is thread-transparent, we don't even need to `{enter,exit}-sync-call` at all because the callee will not read/write its thread state, so we don't need to save and restore the current thread state, we can just leave it in place.
1 parent 3df636a commit 3a0c4f0

44 files changed

Lines changed: 4520 additions & 150 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/cranelift/src/compiler/component.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,7 @@ impl<'a> TrampolineCompiler<'a> {
11891189
}
11901190

11911191
if self.compiler.tunables.concurrency_support {
1192-
Some(self.enter_sync_call_inline(instance, def.instance))
1192+
Some(self.enter_sync_call_inline(def.instance))
11931193
} else {
11941194
None
11951195
}
@@ -1286,14 +1286,9 @@ impl<'a> TrampolineCompiler<'a> {
12861286
/// otherwise do eagerly.
12871287
fn enter_sync_call_inline(
12881288
&mut self,
1289-
caller_instance: RuntimeComponentInstanceIndex,
12901289
callee_instance: RuntimeComponentInstanceIndex,
12911290
) -> ir::StackSlot {
12921291
let vmctx = self.caller_vmctx();
1293-
let caller_instance = self
1294-
.builder
1295-
.ins()
1296-
.iconst(ir::types::I32, i64::from(caller_instance.as_u32()));
12971292
let callee_async = self.builder.ins().iconst(ir::types::I32, 0);
12981293
let callee_instance = self
12991294
.builder
@@ -1304,7 +1299,6 @@ impl<'a> TrampolineCompiler<'a> {
13041299
&mut self.alias_regions,
13051300
vmctx,
13061301
crate::component_sync_call::EnterArgs {
1307-
caller_instance,
13081302
callee_async,
13091303
callee_instance,
13101304
},

crates/cranelift/src/component_sync_call.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ use wasmtime_environ::{GetPtrSize, NUM_COMPONENT_CONTEXT_SLOTS, PtrSize};
2929
/// `VMDeferredThread`, to be replayed by the host if it ever has to promote the
3030
/// deferred thread into a real one.
3131
pub struct EnterArgs {
32-
/// The component instance performing the call.
33-
pub caller_instance: ir::Value,
3432
/// Whether the callee is async-lifted, as an `i32` boolean.
3533
pub callee_async: ir::Value,
3634
/// The component instance being called into.
@@ -79,11 +77,6 @@ where
7977
.store(&mut builder.cursor(), slot_addr, parent);
8078

8179
// Record the deferred `enter_sync_call` arguments.
82-
alias_regions.vm_deferred_thread().caller_instance().store(
83-
&mut builder.cursor(),
84-
slot_addr,
85-
args.caller_instance,
86-
);
8780
alias_regions.vm_deferred_thread().callee_async().store(
8881
&mut builder.cursor(),
8982
slot_addr,

crates/cranelift/src/func_environ.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2076,18 +2076,17 @@ impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> {
20762076
/// defers the heavyweight task bookkeeping the `enter_sync_call` libcall
20772077
/// would otherwise do eagerly.
20782078
///
2079-
/// `real_call_args` is `[callee_vmctx, caller_vmctx, caller_instance,
2080-
/// callee_async, callee_instance]`.
2079+
/// `real_call_args` is `[callee_vmctx, caller_vmctx, callee_async,
2080+
/// callee_instance]`.
20812081
fn lower_fact_enter_sync_call(&mut self, real_call_args: &[ir::Value]) -> CallRets {
20822082
let vmctx = self.env.vmctx_val(&mut self.builder.cursor());
20832083
let slot = crate::component_sync_call::enter(
20842084
self.builder,
20852085
&mut self.env.alias_regions,
20862086
vmctx,
20872087
crate::component_sync_call::EnterArgs {
2088-
caller_instance: real_call_args[2],
2089-
callee_async: real_call_args[3],
2090-
callee_instance: real_call_args[4],
2088+
callee_async: real_call_args[2],
2089+
callee_instance: real_call_args[3],
20912090
},
20922091
);
20932092

crates/environ/src/component.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ macro_rules! foreach_builtin_component_function {
9797
resource_transfer_own(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u64;
9898
resource_transfer_borrow(vmctx: vmctx, src_idx: u32, src_table: u32, dst_table: u32) -> u64;
9999

100-
enter_sync_call(vmctx: vmctx, caller_instance: u32, callee_async: u32, callee_instance: u32) -> bool;
100+
enter_sync_call(vmctx: vmctx, callee_async: u32, callee_instance: u32) -> bool;
101101
exit_sync_call(vmctx: vmctx) -> bool;
102102

103103
#[cfg(feature = "component-model-async")]

crates/environ/src/component/dfg.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ pub struct ComponentDfg {
150150
/// Interned map of id-to-`CanonicalOptions`, or all sets-of-options used by
151151
/// this component.
152152
pub options: Intern<OptionsId, CanonicalOptions>,
153+
154+
/// The thread-transparency analysis for this component.
155+
pub transparency: ThreadTransparency,
153156
}
154157

155158
/// Possible side effects that are possible with instantiating this component.

crates/environ/src/component/translate.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ use wasmparser::{Chunk, ComponentExternName, Encoding, Parser, Payload, Validato
2323
mod adapt;
2424
pub use self::adapt::*;
2525
mod inline;
26+
mod thread_transparency;
27+
pub use self::thread_transparency::ThreadTransparency;
2628

2729
/// Structure used to translate a component and parse it.
2830
pub struct Translator<'a, 'data> {

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,14 @@ impl<'data> Translator<'_, 'data> {
211211
let mut names = Vec::with_capacity(adapter_module.adapters.len());
212212
for adapter in adapter_module.adapters.iter() {
213213
let name = format!("adapter{}", adapter.as_u32());
214-
module.adapt(&name, &component.adapters[*adapter]);
214+
let adapter = &component.adapters[*adapter];
215+
module.adapt(
216+
&name,
217+
adapter,
218+
component
219+
.transparency
220+
.adapter_is_transparent(self.types.types(), adapter),
221+
);
215222
names.push(name);
216223
}
217224
let wasm = module.encode();

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

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ pub(super) fn run(
130130
// the root frame which are then used for recording the exports of the
131131
// component.
132132
inliner.result.num_runtime_component_instances += 1;
133+
inliner.result.transparency.push_root_instance(index);
133134
let frame = InlinerFrame::new(index, result, ComponentClosure::default(), args, None);
134135
let resources_snapshot = types.resources_mut().clone();
135136
let mut frames = vec![(frame, resources_snapshot)];
@@ -185,8 +186,8 @@ struct Inliner<'a> {
185186
/// incrementally processed via the `initializers` list here. Note that the
186187
/// inliner frames are stored on the heap to avoid recursion based on user
187188
/// input.
188-
struct InlinerFrame<'a> {
189-
instance: RuntimeComponentInstanceIndex,
189+
pub(super) struct InlinerFrame<'a> {
190+
pub(super) instance: RuntimeComponentInstanceIndex,
190191

191192
/// The remaining initializers to process when instantiating this component.
192193
initializers: std::slice::Iter<'a, LocalInitializer<'a>>,
@@ -215,7 +216,7 @@ struct InlinerFrame<'a> {
215216
modules: PrimaryMap<ModuleIndex, ModuleDef<'a>>,
216217

217218
// component model index spaces
218-
component_funcs: PrimaryMap<ComponentFuncIndex, ComponentFuncDef<'a>>,
219+
pub(super) component_funcs: PrimaryMap<ComponentFuncIndex, ComponentFuncDef<'a>>,
219220
module_instances: PrimaryMap<ModuleInstanceIndex, ModuleInstanceDef<'a>>,
220221
component_instances: PrimaryMap<ComponentInstanceIndex, ComponentInstanceDef<'a>>,
221222
components: PrimaryMap<ComponentIndex, ComponentDef<'a>>,
@@ -258,7 +259,7 @@ struct ComponentClosure<'a> {
258259
/// values and so this is used to ensure that we primarily only deal with
259260
/// individual functions and modules instead of synthetic instances.
260261
#[derive(Clone, PartialEq, Hash, Eq)]
261-
struct ImportPath<'a> {
262+
pub(super) struct ImportPath<'a> {
262263
index: ImportIndex,
263264
path: Vec<Cow<'a, str>>,
264265
}
@@ -268,7 +269,7 @@ struct ImportPath<'a> {
268269
/// This is the "value" of an item defined within a component and is used to
269270
/// represent both imports and exports.
270271
#[derive(Clone)]
271-
enum ComponentItemDef<'a> {
272+
pub(super) enum ComponentItemDef<'a> {
272273
Component(ComponentDef<'a>),
273274
Instance(ComponentInstanceDef<'a>),
274275
Func(ComponentFuncDef<'a>),
@@ -277,7 +278,7 @@ enum ComponentItemDef<'a> {
277278
}
278279

279280
#[derive(Clone)]
280-
enum ModuleDef<'a> {
281+
pub(super) enum ModuleDef<'a> {
281282
/// A core wasm module statically defined within the original component.
282283
///
283284
/// The `StaticModuleIndex` indexes into the `static_modules` map in the
@@ -309,7 +310,7 @@ enum ModuleInstanceDef<'a> {
309310
}
310311

311312
#[derive(Clone)]
312-
enum ComponentFuncDef<'a> {
313+
pub(super) enum ComponentFuncDef<'a> {
313314
/// A compile-time builtin intrinsic.
314315
UnsafeIntrinsic(UnsafeIntrinsic),
315316

@@ -328,7 +329,7 @@ enum ComponentFuncDef<'a> {
328329
}
329330

330331
#[derive(Clone)]
331-
enum ComponentInstanceDef<'a> {
332+
pub(super) enum ComponentInstanceDef<'a> {
332333
/// The `__wasmtime_intrinsics` instance that exports all of our
333334
/// compile-time builtin intrinsics.
334335
Intrinsics,
@@ -356,7 +357,7 @@ enum ComponentInstanceDef<'a> {
356357
}
357358

358359
#[derive(Clone)]
359-
struct ComponentDef<'a> {
360+
pub(super) struct ComponentDef<'a> {
360361
index: StaticComponentIndex,
361362
closure: ComponentClosure<'a>,
362363
}
@@ -437,6 +438,11 @@ impl<'a> Inliner<'a> {
437438
use LocalInitializer::*;
438439

439440
let (frame, _) = frames.last_mut().unwrap();
441+
442+
self.result
443+
.transparency
444+
.process_initializer(types, frame, initializer);
445+
440446
match initializer {
441447
// When a component imports an item the actual definition of the
442448
// item is looked up here (not at runtime) via its name. The
@@ -1297,13 +1303,18 @@ impl<'a> Inliner<'a> {
12971303
self.result.num_runtime_component_instances,
12981304
);
12991305
self.result.num_runtime_component_instances += 1;
1306+
let args = args
1307+
.iter()
1308+
.map(|(name, item)| Ok((*name, frame.item(*item, types)?)))
1309+
.collect::<Result<HashMap<_, _>>>()?;
1310+
1311+
self.result.transparency.push_instance(index, &args);
1312+
13001313
let frame = InlinerFrame::new(
13011314
index,
13021315
&self.nested_components[component.index],
13031316
component.closure.clone(),
1304-
args.iter()
1305-
.map(|(name, item)| Ok((*name, frame.item(*item, types)?)))
1306-
.collect::<Result<_>>()?,
1317+
args,
13071318
Some(*ty),
13081319
);
13091320
return Ok(Some(frame));

0 commit comments

Comments
 (0)