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
5 changes: 5 additions & 0 deletions cranelift/codegen/src/isa/x64/inst/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,8 @@ pub(crate) fn emit(
in_payload0,
out_payload0,
} => {
let stack_map = state.take_stack_map();

// Note that we do not emit anything for preserving and restoring
// ordinary registers here: That's taken care of by regalloc for us,
// since we marked this instruction as clobbering all registers.
Expand Down Expand Up @@ -643,6 +645,9 @@ pub(crate) fn emit(
asm::inst::jmpq_m::new(tmp1.to_reg()).emit(sink, info, state);

sink.bind_label(resume, state.ctrl_plane_mut());
if let Some(s) = stack_map {
sink.push_user_stack_map(state, sink.cur_offset(), s);
}
}

Inst::JmpKnown { dst } => uncond_jmp(sink, *dst),
Expand Down
4 changes: 3 additions & 1 deletion cranelift/codegen/src/isa/x64/inst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1467,7 +1467,9 @@ impl MachInst for Inst {

fn is_safepoint(&self) -> bool {
match self {
Inst::CallKnown { .. } | Inst::CallUnknown { .. } => true,
Inst::CallKnown { .. } | Inst::CallUnknown { .. } | Inst::StackSwitchBasic { .. } => {
true
}
_ => false,
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
test compile
set opt_level=speed
set stack_switch_model=basic
target x86_64

;; A stack switch is a GC safepoint. Its stack map is associated with the
;; resume PC saved in the outgoing control context.
function %switch_with_stack_map(i64, i64, i64) -> i64 {
ss0 = explicit_slot 4

block0(v0: i64, v1: i64, v2: i64):
v3 = stack_switch v0, v1, v2, stack_map=[i32 @ ss0+0]
return v3
}
3 changes: 3 additions & 0 deletions crates/cranelift/src/alias_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ enum VmType {
BuiltinFunctionsArray,
ComponentBuiltinFunctionsArray,
HostValRaw,
// Keep new variants at the end: `VmType`'s discriminant participates in
// stable alias-region IDs printed by disassembly tests.
VMPayloads,
}

/// A key that uniquely identifies an alias region across an entire compilation.
Expand Down
50 changes: 42 additions & 8 deletions crates/cranelift/src/func_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ use wasmtime_environ::{
};
use wasmtime_environ::{FUNCREF_INIT_BIT, FUNCREF_MASK};

/// Function-local stack slots backing a continuation's
/// `VMPayloads::values`.
///
/// The values and their GC-reference markers are logically one
/// payload descriptor but use distinct stack slots so that Cranelift
/// can assign them distinct alias regions. The marker slot is only
/// allocated when this function contains a stack switching site whose
/// payloads may contain GC references.
#[derive(Clone, Copy)]
pub(crate) struct VMPayloadStackSlots {
pub(crate) values: ir::StackSlot,
pub(crate) gc_ref_markers: Option<ir::StackSlot>,
}

#[derive(Copy, Clone, Debug)]
pub(crate) enum Extension {
Sign,
Expand Down Expand Up @@ -216,10 +230,12 @@ pub struct FuncEnvironment<'module_environment> {
/// current stack's `handler_list` field.
stack_switching_handler_list_buffer: Option<ir::StackSlot>,

/// Used by the stack switching feature. If set, we have a allocated a
/// slot on this function's stack to be used for the
/// current continuation's `values` field.
stack_switching_values_buffer: Option<ir::StackSlot>,
/// Used by the stack switching feature. If set, these are the stack slots
/// backing the current continuation's `values` field.
stack_switching_values_storage: Option<VMPayloadStackSlots>,

/// Reusable storage for `get_interned_contref` builtin.
stack_switching_cont_ref_result_storage: Option<ir::StackSlot>,

/// The stack-slot used for exposing Wasm state via debug
/// instrumentation, if any, and the builder containing its metadata.
Expand Down Expand Up @@ -300,7 +316,8 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
stack_limit_at_function_entry: None,

stack_switching_handler_list_buffer: None,
stack_switching_values_buffer: None,
stack_switching_values_storage: None,
stack_switching_cont_ref_result_storage: None,

state_slot: None,
next_srcloc: ir::SourceLoc::default(),
Expand All @@ -313,6 +330,18 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
}
}

/// Returns the cached continuation-reference stack slot, creating it with
/// `data` if necessary.
pub(crate) fn get_or_create_contref_stack_slot(
&mut self,
builder: &mut FunctionBuilder<'_>,
data: ir::StackSlotData,
) -> ir::StackSlot {
*self
.stack_switching_cont_ref_result_storage
.get_or_insert_with(|| builder.create_sized_stack_slot(data))
}

/// Consume the branch hint for the instruction at module-relative `offset`
/// (i.e. `builder.srcloc().bits()`), if any. The lazy decoder only moves
/// forward, making this O(n) over a function body.
Expand Down Expand Up @@ -5287,8 +5316,9 @@ impl FuncEnvironment<'_> {
builder: &mut FunctionBuilder<'_>,
contobj: ir::Value,
args: &[ir::Value],
arg_types: &[WasmValType],
) -> ir::Value {
stack_switching::instructions::translate_cont_bind(self, builder, contobj, args)
stack_switching::instructions::translate_cont_bind(self, builder, contobj, args, arg_types)
}

pub fn translate_cont_new(
Expand Down Expand Up @@ -5368,13 +5398,15 @@ impl FuncEnvironment<'_> {
builder: &mut FunctionBuilder<'_>,
tag_index: u32,
suspend_args: &[ir::Value],
tag_return_types: &[ir::Type],
suspend_arg_types: &[WasmValType],
tag_return_types: &[WasmValType],
) -> WasmResult<Vec<ir::Value>> {
stack_switching::instructions::translate_suspend(
self,
builder,
tag_index,
suspend_args,
suspend_arg_types,
tag_return_types,
)
}
Expand All @@ -5386,14 +5418,16 @@ impl FuncEnvironment<'_> {
tag_index: u32,
contobj: ir::Value,
switch_args: &[ir::Value],
return_types: &[ir::Type],
switch_arg_types: &[WasmValType],
return_types: &[WasmValType],
) -> WasmResult<Vec<ir::Value>> {
stack_switching::instructions::translate_switch(
self,
builder,
tag_index,
contobj,
switch_args,
switch_arg_types,
return_types,
)
}
Expand Down
103 changes: 97 additions & 6 deletions crates/cranelift/src/func_environ/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use crate::TRAP_ARRAY_OUT_OF_BOUNDS;
use crate::bounds_checks::BoundsCheck;
use crate::func_environ::{CheckedEntity, Extension, FuncEnvironment};
use crate::func_environ::{CheckedEntity, Extension, FuncEnvironment, stack_switching::fatpointer};
use crate::translate::{
Heap, HeapData, MemoryKind, StructFieldsVec, TargetEnvironment, VmctxLoadChain,
};
Expand Down Expand Up @@ -377,10 +377,7 @@ pub fn read_field_at_addr(
.call(get_interned_func_ref, &[vmctx, func_ref_id, expected_ty]);
builder.func.dfg.first_result(call_inst)
}
WasmHeapTopType::Cont => {
// TODO(#10248) GC integration for stack switching
return stack_switching_unsupported();
}
WasmHeapTopType::Cont => read_cont_ref_at_addr(func_env, builder, addr, flags)?,
},
},
};
Expand Down Expand Up @@ -431,6 +428,94 @@ pub fn intern_func_ref(
Ok(builder.ins().ireduce(ir::types::I32, func_ref_id))
}

fn intern_cont_ref(
func_env: &mut FuncEnvironment<'_>,
builder: &mut FunctionBuilder<'_>,
ref_type: WasmRefType,
contobj: ir::Value,
) -> ir::Value {
assert_eq!(ref_type.heap_type.top(), WasmHeapTopType::Cont);

if ref_type.heap_type == WasmHeapType::NoCont {
let zero = builder.ins().iconst(ir::types::I32, 0);

if !ref_type.nullable {
builder.ins().trapz(zero, TRAP_INTERNAL_ASSERT);
}

return zero;
}

let (revision, contref) = fatpointer::deconstruct(func_env, &mut builder.cursor(), contobj);
let vmctx = func_env.vmctx_val(&mut builder.cursor());
let intern = func_env
.builtin_functions
.intern_contref_for_gc_heap(builder.func);
let call = builder.ins().call(intern, &[vmctx, contref, revision]);
let id = builder.func.dfg.first_result(call);
builder.ins().ireduce(ir::types::I32, id)
}

fn read_cont_ref_at_addr(
func_env: &mut FuncEnvironment<'_>,
builder: &mut FunctionBuilder<'_>,
addr: ir::Value,
flags: ir::MemFlagsData,
) -> WasmResult<ir::Value> {
let id = builder.ins().load(ir::types::I32, flags, addr, 0);

// We either create or fetch an existing a stack slot to hold the
// continuation values (16 bytes), and pass the address of this
// slot as the out parameter to the builtin.
let pointer_type = func_env.pointer_type();
let pointer_bytes = pointer_type.bytes();
let fatpointer_bytes = fatpointer::bytes(func_env);

let slot = func_env.get_or_create_contref_stack_slot(
builder,
ir::StackSlotData::new(
ir::StackSlotKind::ExplicitSlot,
fatpointer_bytes,
u8::try_from(pointer_bytes.trailing_zeros()).unwrap(),
),
);
let out_result = builder.ins().stack_addr(pointer_type, slot, 0);

let vmctx = func_env.vmctx_val(&mut builder.cursor());
let get = func_env
.builtin_functions
.get_interned_contref(builder.func);
builder.ins().call(get, &[vmctx, id, out_result]);

let region = func_env.alias_regions.stack_slot_region(builder.func, slot);
let flags = ir::MemFlagsData::trusted().with_alias_region(Some(region));
let contref = builder.ins().load(pointer_type, flags, out_result, 0);
let revision = builder.ins().load(
pointer_type,
flags,
out_result,
i32::try_from(pointer_bytes).unwrap(),
);
Comment on lines +492 to +498

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these offsets use constants defined in wasmtime-environ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the PtrSize::size?

Ok(fatpointer::construct(
func_env,
&mut builder.cursor(),
revision,
contref,
))
}

fn write_cont_ref_at_addr(
func_env: &mut FuncEnvironment<'_>,
builder: &mut FunctionBuilder<'_>,
ref_type: WasmRefType,
flags: ir::MemFlagsData,
field_addr: ir::Value,
contobj: ir::Value,
) {
let id = intern_cont_ref(func_env, builder, ref_type, contobj);
builder.ins().store(flags, id, field_addr, 0);
}

fn write_func_ref_at_addr(
func_env: &mut FuncEnvironment<'_>,
builder: &mut FunctionBuilder<'_>,
Expand Down Expand Up @@ -487,7 +572,9 @@ pub fn write_field_at_addr(
func_env, builder, r, field_addr, new_val, flags,
)?;
}
WasmHeapTopType::Cont => return stack_switching_unsupported(),
WasmHeapTopType::Cont => {
write_cont_ref_at_addr(func_env, builder, r, flags, field_addr, new_val)
}
},
WasmStorageType::Val(_) => {
assert_eq!(
Expand Down Expand Up @@ -527,6 +614,10 @@ fn default_value(
}
WasmValType::Ref(r) => {
assert!(r.nullable);
if r.heap_type.top() == WasmHeapTopType::Cont {
let zero = cursor.ins().iconst(func_env.pointer_type(), 0);
return fatpointer::construct(func_env, cursor, zero, zero);
}
let (ty, needs_stack_map) = func_env.reference_type(r.heap_type);

// NB: The collector doesn't need to know about null references.
Expand Down
4 changes: 3 additions & 1 deletion crates/cranelift/src/func_environ/gc/copying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,9 @@ impl GcCompiler for CopyingCompiler {
// store the reference directly.
unbarriered_store_gc_ref(builder, r.heap_type, field_addr, val, flags)?;
}
WasmHeapTopType::Cont => return super::stack_switching_unsupported(),
WasmHeapTopType::Cont => {
write_field_at_addr(func_env, builder, ty, field_addr, val)?
}
},
WasmStorageType::I8 => {
assert_eq!(builder.func.dfg.value_type(val), ir::types::I32);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use cranelift_codegen::ir;
use cranelift_codegen::ir::InstBuilder;

/// Returns the platform size of a fat pointer.
pub fn bytes(env: &crate::func_environ::FuncEnvironment) -> u32 {
2 * env.pointer_type().bytes()
}

/// Returns the Cranelift type used to represent all of the following:
/// - wasm values of type `(ref null $ct)` and `(ref $ct)`
/// - equivalently: runtime values of type `Option<VMContObj>` and `VMContObj`
Expand Down Expand Up @@ -34,7 +39,7 @@ pub(crate) fn deconstruct<'a>(
/// Constructs a continuation object from a given contref and revision pointer.
/// The contref_addr may be 0, to indicate that we want to build a wasm null reference.
pub(crate) fn construct<'a>(
env: &mut crate::func_environ::FuncEnvironment<'a>,
env: &crate::func_environ::FuncEnvironment<'a>,
pos: &mut cranelift_codegen::cursor::FuncCursor,
revision_counter: ir::Value,
contref_addr: ir::Value,
Expand Down
Loading
Loading