Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions crates/cranelift/src/alias_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,17 @@ where
}
}

/// The Cranelift type of a pointer on the target being compiled for.
pub fn pointer_type(&self) -> ir::Type {
self.pointer_type
}

/// The pointer size of the target being compiled for, used to compute the
/// layout of Wasmtime's vmctx types.
pub fn ptr_size(&self) -> &Offsets::Ptr {
self.offsets.get_ptr_size()
}

/// Get the alias region for accesses into the GC heap.
pub fn gc_heap_region(&mut self, func: &mut ir::Function) -> ir::AliasRegion {
self.region(func, AliasRegionKey::GcHeap)
Expand Down
145 changes: 86 additions & 59 deletions crates/cranelift/src/compiler/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ use cranelift_codegen::ir::condcodes::IntCC;
use cranelift_codegen::ir::{self, InstBuilder, Value};
use cranelift_codegen::isa::{CallConv, TargetIsa};
use cranelift_frontend::FunctionBuilder;
use wasmtime_environ::GetPtrSize;
use wasmtime_environ::error::{Result, bail};
use wasmtime_environ::{
Abi, BuiltinFunctionIndex, CompiledFunctionBody, EntityRef, FuncKey, HostCall, PanicOnOom as _,
TrapSentinel, Tunables, WasmFuncType, WasmValType, component::*,
Abi, BuiltinFunctionIndex, CompiledFunctionBody, EntityRef, FuncKey, GetPtrSize, HostCall,
PanicOnOom as _, TrapSentinel, Tunables, WasmFuncType, WasmValType, component::*,
fact::PREPARE_CALL_FIXED_PARAMS,
};

Expand Down Expand Up @@ -1136,8 +1135,10 @@ impl<'a> TrampolineCompiler<'a> {
// old_may_block = load.i32 vmctx+$may_block_offset
// store 0, vmctx+$may_block_offset
//
// ;; call enter_sync_call, but only if the component instances
// ;; differ and concurrency is enabled
// ;; enter a sync call, but only if the component instances
// ;; differ and concurrency is enabled. This pushes an on-stack
// ;; `VMDeferredThread` and zeroes the live context slots; see
// ;; `enter_sync_call` below.
// ...
//
// ;; ============================================================
Expand All @@ -1149,16 +1150,17 @@ impl<'a> TrampolineCompiler<'a> {
// dtor = load.ptr vmctx+$offset
// func_addr = load.ptr dtor+$offset
// callee_vmctx = load.ptr dtor+$offset
// call_indirect func_addr, callee_vmctx, vmctx, rep
// ;; ============================================================
//
// ;; restore old value of may_block
// store old_may_block, vmctx+$may_block_offset
// call_indirect func_addr, callee_vmctx, vmctx, rep
//
// ;; if needed, call exit_sync_call
// ;; and restore the caller's slots afterwards
// store saved0, vmstore+$context_slot0
// ...
// ;; ============================================================
//
// ;; if needed, restore the old value of may_block
// ;; if needed, exit the sync call entered above and restore the
// ;; old value of may_block
// ...
// store old_may_block, vmctx+$may_block_offset
//
// jump return_block
Expand Down Expand Up @@ -1191,51 +1193,32 @@ impl<'a> TrampolineCompiler<'a> {
self.builder.switch_to_block(run_destructor_block);

// If this is a component-defined resource, the `may_leave` flag must be
// checked. Additionally, if concurrency is enabled, the `may_block`
// field must be updated and `enter_sync_call` called. Note though that
// all of that may be elided if the resource table resides in the same
// component instance that defined the resource as the component is
// calling itself.
let old_may_block = if let Some(def) = resource_def {
// checked. Additionally, if concurrency is enabled, the `may_block`
// field must be updated and a sync call entered.
let entered_sync_call = if has_destructor && let Some(def) = resource_def {
// Skip the may-leave check for self-owned resources.
if self.types[resource].unwrap_concrete_instance() != def.instance {
self.check_may_leave_instance(self.types[resource].unwrap_concrete_instance());
}

if self.compiler.tunables.concurrency_support {
// Stash the old value of `may_block` and then set it to false.
let old_may_block = self
.alias_regions
.vmcomponent()
.task_may_block()
.readonly()
.load(&mut self.builder.cursor(), vmctx);
let zero = self.builder.ins().iconst(ir::types::I32, i64::from(0));
self.alias_regions.vmcomponent().task_may_block().store(
&mut self.builder.cursor(),
vmctx,
zero,
);
if self.compiler.tunables.concurrency_support {
// Stash the old value of `may_block` and then set it to false.
let old_may_block = self
.alias_regions
.vmcomponent()
.task_may_block()
.readonly()
.load(&mut self.builder.cursor(), vmctx);
let zero = self.builder.ins().iconst(ir::types::I32, i64::from(0));
self.alias_regions.vmcomponent().task_may_block().store(
&mut self.builder.cursor(),
vmctx,
zero,
);

// Call `enter_sync_call`
//
// FIXME: Apply the optimizations described in #12311.
let host_args = vec![
vmctx,
self.builder
.ins()
.iconst(ir::types::I32, i64::from(instance.as_u32())),
self.builder.ins().iconst(ir::types::I32, i64::from(0)),
self.builder
.ins()
.iconst(ir::types::I32, i64::from(def.instance.as_u32())),
];
let call = self.call_libcall(vmctx, host::enter_sync_call, &host_args);
let result = self.builder.func.dfg.inst_results(call).get(0).copied();
self.raise_if_host_trapped(result.unwrap());

Some(old_may_block)
} else {
None
}
let slot = self.enter_sync_call_inline(instance, def.instance);

Some((old_may_block, slot))
} else {
None
}
Expand Down Expand Up @@ -1315,13 +1298,8 @@ impl<'a> TrampolineCompiler<'a> {
self.builder.seal_block(continuation);
}

if let Some(old_may_block) = old_may_block {
// Call `exit_sync_call`
//
// FIXME: Apply the optimizations described in #12311.
let call = self.call_libcall(vmctx, host::exit_sync_call, &[vmctx]);
let result = self.builder.func.dfg.inst_results(call).get(0).copied();
self.raise_if_host_trapped(result.unwrap());
if let Some((old_may_block, slot)) = entered_sync_call {
self.exit_sync_call_inline(vmctx, slot);

// Restore the old value of `may_block`
self.alias_regions.vmcomponent().task_may_block().store(
Expand All @@ -1339,6 +1317,55 @@ impl<'a> TrampolineCompiler<'a> {
self.abi_store_results(&[]);
}

/// Translates `enter-sync-call` around a resource destructor, deferring the
/// heavyweight task bookkeeping the `enter_sync_call` libcall would
/// otherwise do eagerly.
fn enter_sync_call_inline(
&mut self,
caller_instance: RuntimeComponentInstanceIndex,
callee_instance: RuntimeComponentInstanceIndex,
) -> ir::StackSlot {
let vmctx = self.caller_vmctx();
let caller_instance = self
.builder
.ins()
.iconst(ir::types::I32, i64::from(caller_instance.as_u32()));
let callee_async = self.builder.ins().iconst(ir::types::I32, 0);
let callee_instance = self
.builder
.ins()
.iconst(ir::types::I32, i64::from(callee_instance.as_u32()));
crate::component_sync_call::enter(
&mut self.builder,
&mut self.alias_regions,
vmctx,
crate::component_sync_call::EnterArgs {
caller_instance,
callee_async,
callee_instance,
},
)
}

/// Translates `exit-sync-call`, the counterpart to `enter_sync_call` above.
fn exit_sync_call_inline(&mut self, vmctx: ir::Value, slot: ir::StackSlot) {
// Note that the helper here wants a core wasm vmctx, not a component
// one like `vmctx` is in this function.
let caller_vmctx = self.caller_vmctx();
let slow = crate::component_sync_call::exit(
&mut self.builder,
&mut self.alias_regions,
caller_vmctx,
slot,
);

let call = self.call_libcall(vmctx, host::exit_sync_call, &[vmctx]);
let result = self.builder.func.dfg.inst_results(call).get(0).copied();
self.raise_if_host_trapped(result.unwrap());

slow.finish(&mut self.builder);
}

fn load_optional_memory(
&mut self,
vmctx: ir::Value,
Expand Down
Loading
Loading