Skip to content

Commit a631d20

Browse files
posbornefrank-emrichdhil
authored
cranelift: stack-switching support (#11003)
* cranelift: stack-switching support This initial commit represents the "pr2" base commit with minimal merge conflicts resolved. Due to OOB conflicts, this commit is not functional as-is, but using it as a base in order to allow for easier reviewing of the delta from this commit to what will be used for the PR against upstream. Co-authored-by: Daniel Hillerström <[email protected]> Co-authored-by: Paul Osborne <[email protected]> * cranelift: stack-switching updates pass 1 This first set of changes updates the base pr in order to compiled and pass basic checks (compile, clippy, fmt) with the biggest part of the change being to eliminate injection of tracing/assertions in JIT'ed code. * cranelift: stack-switching: restore original visibility for a few func_environ members * cranelift: stack-switching conditional compilation At this point, the only bit we really branch on is what we do in order to avoid problems tying into wasmtime_environ. This is basd on the approach and macro used by the gc code for converting presence/absence of the cranelift feature flag to cranelift compile time. This is a bit of a half-measure for now as we still compile most stack-switching code in cranelift, but this does enough to avoid causing problems with missing definitions in wasmtime_environ. * cranelift: avoid "as" casts in stack-switching Replace either with infallible From or fallible, panicing TryFrom alternatives where required. * cranelift: cleanup stack-switching control_effect signatures After removing emission of runtime trace logging and assertions, there were several unused parameters. Remove those from the ControlEffect signatures completely. * cranelift: rename stack-switching VMArray to VMHostArray This matches a change to the mirrored runtime type in the upstream changes. * stack-switching: fix typo Co-authored-by: Daniel Hillerström <[email protected]> * stack-switching: used Index impl for get_stack_slot_data * stack-switching: use smallvec over vec in several cases * stack-switching: avoid resumetable naming confusion * stack-switching: cleanup unused params from unchecked_get_continuation The extra parameters here used to be used for emitting runtime assertions, but with those gone we just had unused params and lifetimes, clean those out. * stack_switching: simplify store_data_entries assertion * stack-switching: simplify translate_table_{grow,fill} control flow * stack-switching: remove translate_resume_throw stub There's already a stub elsewhere and this is not called, when exceptions are added and it is time to revisit, this method can be restored. * stack-switching: compute control_context_size based on target triple * stack-switching: VMHostArrayRef updates Rename VMHostArray -> VMHostArrayRef Change impl to compute address with offset upfront rather than on each load. * stack-switching: move cranelift code to live under func_environ This matches the directory structure for gc and aids in visibility for a few members required by stack-switching code in cranelift. * stack-switching: formatting fix * stack-switching: reduce visibility on a few additional items * stack-switching: simplify contobj fatptr con/de-struction * stack-switching: add disas tests to cover new instructions * stack-switching: fix layout of VMContObj In the course of the various runtime updates, the layout of the runtime VMContObj got switched around. This resulted in failures when doing certain table operations on continuations. This change fixes that layout problem and adds some tests with offsets to avoid the problem. Due to the way that we interact with the VMContObj in cranelift, we don't use these offsets outside of the tests. * Fix formatting of merge conflict resolution * cranelift: remove ir::function::get_stack_slot_data This method isn't required as sized_stack_slots is already pub. * stack-switching: reduce visibility of a couple func_environ methods * stack-switching: define VMContObj as two words This change migrates VMContObj and its usages in cranelift and runtime to work with the VMContObj fat pointer as two words in order to better target different architectures (still gated to x86_64 for now). To support this, a size type was plumbed into the builtins function signature types (as is done for component types) that maps to usize. * fixup! stack-switching: define VMContObj as two words * stack-switching: add stub Val::ContRef This type is not fully complete until continuation/gc integration is revisited (#10248) but without these changes, test cases are now failing on panics as we need some representation of continuation references in the runtime Val enumeration. Runtime errors with TODO notes are added for the stubbed code paths to revisit later. * fixup! stack-switching: add stub Val::ContRef * fixup! stack-switching: add stub Val::ContRef * fixup! stack-switching: define VMContObj as two words prtest:full * stack-switching: don't conflate host and target pointer sizes Disas tests were failing on i686 targeting x86_64 as the size of the host pointer was leaking into what we were using to do codegen in a few paths. This patch is a bit of a hack as it seems like using a generic <T> for T: *mut u8 (as an example) is a bit questionable. To keep things small, I do a hacky typecheck to map pointers to the target pointer size here. * stack-switching: VMHostArray entry sizes based off env PtrSize Revisiting the previous commit with an approach that should be less brittle. --------- Co-authored-by: Frank Emrich <[email protected]> Co-authored-by: Daniel Hillerström <[email protected]>
1 parent 5764da5 commit a631d20

File tree

31 files changed

+3582
-102
lines changed

31 files changed

+3582
-102
lines changed

cranelift/codegen/src/isa/x64/abi.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
101101
mut args: ArgsAccumulator,
102102
) -> CodegenResult<(u32, Option<usize>)> {
103103
let is_fastcall = call_conv == CallConv::WindowsFastcall;
104+
let is_tail = call_conv == CallConv::Tail;
104105

105106
let mut next_gpr = 0;
106107
let mut next_vreg = 0;
@@ -181,6 +182,11 @@ impl ABIMachineSpec for X64ABIMachineSpec {
181182
// This is consistent with LLVM's behavior, and is needed for
182183
// some uses of Cranelift (e.g., the rustc backend).
183184
//
185+
// - Otherwise, if the calling convention is Tail, we behave as in
186+
// the previous case, even if `enable_llvm_abi_extensions` is not
187+
// set in the flags: This is a custom calling convention defined
188+
// by Cranelift, LLVM doesn't know about it.
189+
//
184190
// - Otherwise, both SysV and Fastcall specify behavior (use of
185191
// vector register, a register pair, or passing by reference
186192
// depending on the case), but for simplicity, we will just panic if
@@ -194,6 +200,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
194200
if param.value_type.bits() > 64
195201
&& !(param.value_type.is_vector() || param.value_type.is_float())
196202
&& !flags.enable_llvm_abi_extensions()
203+
&& !is_tail
197204
{
198205
panic!(
199206
"i128 args/return values not supported unless LLVM ABI extensions are enabled"

crates/c-api/src/val.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ impl wasm_val_t {
9898
Val::ExternRef(_) => crate::abort("creating a wasm_val_t from an externref"),
9999
Val::ExnRef(_) => crate::abort("creating a wasm_val_t from an exnref"),
100100
Val::V128(_) => crate::abort("creating a wasm_val_t from a v128"),
101+
Val::ContRef(_) => crate::abort("creating a wasm_val_t from a contref"),
101102
}
102103
}
103104

@@ -258,6 +259,7 @@ impl wasmtime_val_t {
258259
v128: val.as_u128().to_le_bytes(),
259260
},
260261
},
262+
Val::ContRef(_) => crate::abort("contrefs not yet supported in C API (#10248)"),
261263
}
262264
}
263265

crates/cranelift/src/func_environ.rs

Lines changed: 206 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod gc;
2+
pub(crate) mod stack_switching;
23

34
use crate::compiler::Compiler;
45
use crate::translate::{
@@ -171,6 +172,16 @@ pub struct FuncEnvironment<'module_environment> {
171172
/// always present even if this is a "leaf" function, as we have to call
172173
/// into the host to trap when signal handlers are disabled.
173174
pub(crate) stack_limit_at_function_entry: Option<ir::GlobalValue>,
175+
176+
/// Used by the stack switching feature. If set, we have a allocated a
177+
/// slot on this function's stack to be used for the
178+
/// current stack's `handler_list` field.
179+
stack_switching_handler_list_buffer: Option<ir::StackSlot>,
180+
181+
/// Used by the stack switching feature. If set, we have a allocated a
182+
/// slot on this function's stack to be used for the
183+
/// current continuation's `values` field.
184+
stack_switching_values_buffer: Option<ir::StackSlot>,
174185
}
175186

176187
impl<'module_environment> FuncEnvironment<'module_environment> {
@@ -224,6 +235,9 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
224235
translation,
225236

226237
stack_limit_at_function_entry: None,
238+
239+
stack_switching_handler_list_buffer: None,
240+
stack_switching_values_buffer: None,
227241
}
228242
}
229243

@@ -1958,8 +1972,6 @@ impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> {
19581972
return CheckIndirectCallTypeSignature::StaticTrap;
19591973
}
19601974

1961-
WasmHeapType::Cont | WasmHeapType::ConcreteCont(_) | WasmHeapType::NoCont => todo!(), // FIXME: #10248 stack switching support.
1962-
19631975
// Engine-indexed types don't show up until runtime and it's a Wasm
19641976
// validation error to perform a call through a non-function table,
19651977
// so these cases are dynamically not reachable.
@@ -1977,6 +1989,9 @@ impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> {
19771989
| WasmHeapType::Exn
19781990
| WasmHeapType::ConcreteExn(_)
19791991
| WasmHeapType::NoExn
1992+
| WasmHeapType::Cont
1993+
| WasmHeapType::ConcreteCont(_)
1994+
| WasmHeapType::NoCont
19801995
| WasmHeapType::None => {
19811996
unreachable!()
19821997
}
@@ -2267,7 +2282,9 @@ impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environm
22672282
let needs_stack_map = match wasm_ty.top() {
22682283
WasmHeapTopType::Extern | WasmHeapTopType::Any | WasmHeapTopType::Exn => true,
22692284
WasmHeapTopType::Func => false,
2270-
WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support.
2285+
// TODO(#10248) Once continuations can be stored on the GC heap, we
2286+
// will need stack maps for continuation objects.
2287+
WasmHeapTopType::Cont => false,
22712288
};
22722289
(ty, needs_stack_map)
22732290
}
@@ -2320,22 +2337,32 @@ impl FuncEnvironment<'_> {
23202337
let mut pos = builder.cursor();
23212338
let table = self.table(table_index);
23222339
let ty = table.ref_type.heap_type;
2323-
let grow = if ty.is_vmgcref_type() {
2324-
gc::builtins::table_grow_gc_ref(self, &mut pos.func)?
2325-
} else {
2326-
debug_assert_eq!(ty.top(), WasmHeapTopType::Func);
2327-
self.builtin_functions.table_grow_func_ref(&mut pos.func)
2328-
};
2329-
23302340
let (table_vmctx, defined_table_index) =
23312341
self.table_vmctx_and_defined_index(&mut pos, table_index);
2332-
23332342
let index_type = table.idx_type;
23342343
let delta = self.cast_index_to_i64(&mut pos, delta, index_type);
2335-
let call_inst = pos
2336-
.ins()
2337-
.call(grow, &[table_vmctx, defined_table_index, delta, init_value]);
2338-
let result = pos.func.dfg.first_result(call_inst);
2344+
2345+
let mut args: SmallVec<[_; 6]> = smallvec![table_vmctx, defined_table_index, delta];
2346+
let grow = match ty.top() {
2347+
WasmHeapTopType::Extern | WasmHeapTopType::Any | WasmHeapTopType::Exn => {
2348+
args.push(init_value);
2349+
gc::builtins::table_grow_gc_ref(self, pos.func)?
2350+
}
2351+
WasmHeapTopType::Func => {
2352+
args.push(init_value);
2353+
self.builtin_functions.table_grow_func_ref(pos.func)
2354+
}
2355+
WasmHeapTopType::Cont => {
2356+
let (revision, contref) =
2357+
stack_switching::fatpointer::deconstruct(self, &mut pos, init_value);
2358+
args.extend_from_slice(&[contref, revision]);
2359+
stack_switching::builtins::table_grow_cont_obj(self, pos.func)?
2360+
}
2361+
};
2362+
2363+
let call_inst = pos.ins().call(grow, &args);
2364+
let result = builder.func.dfg.first_result(call_inst);
2365+
23392366
Ok(self.convert_pointer_to_index_type(builder.cursor(), result, index_type, false))
23402367
}
23412368

@@ -2367,7 +2394,15 @@ impl FuncEnvironment<'_> {
23672394
}
23682395

23692396
// Continuation types.
2370-
WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support.
2397+
WasmHeapTopType::Cont => {
2398+
let (elem_addr, flags) = table_data.prepare_table_addr(self, builder, index);
2399+
Ok(builder.ins().load(
2400+
stack_switching::fatpointer::fatpointer_type(self),
2401+
flags,
2402+
elem_addr,
2403+
0,
2404+
))
2405+
}
23712406
}
23722407
}
23732408

@@ -2415,7 +2450,11 @@ impl FuncEnvironment<'_> {
24152450
}
24162451

24172452
// Continuation types.
2418-
WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support.
2453+
WasmHeapTopType::Cont => {
2454+
let (elem_addr, flags) = table_data.prepare_table_addr(self, builder, index);
2455+
builder.ins().store(flags, value, elem_addr, 0);
2456+
Ok(())
2457+
}
24192458
}
24202459
}
24212460

@@ -2429,21 +2468,31 @@ impl FuncEnvironment<'_> {
24292468
) -> WasmResult<()> {
24302469
let mut pos = builder.cursor();
24312470
let table = self.table(table_index);
2432-
let index_type = table.idx_type;
2433-
let dst = self.cast_index_to_i64(&mut pos, dst, index_type);
2434-
let len = self.cast_index_to_i64(&mut pos, len, index_type);
24352471
let ty = table.ref_type.heap_type;
2436-
let libcall = if ty.is_vmgcref_type() {
2437-
gc::builtins::table_fill_gc_ref(self, &mut pos.func)?
2438-
} else {
2439-
debug_assert_eq!(ty.top(), WasmHeapTopType::Func);
2440-
self.builtin_functions.table_fill_func_ref(&mut pos.func)
2441-
};
2442-
2472+
let dst = self.cast_index_to_i64(&mut pos, dst, table.idx_type);
2473+
let len = self.cast_index_to_i64(&mut pos, len, table.idx_type);
24432474
let (table_vmctx, table_index) = self.table_vmctx_and_defined_index(&mut pos, table_index);
24442475

2445-
pos.ins()
2446-
.call(libcall, &[table_vmctx, table_index, dst, val, len]);
2476+
let mut args: SmallVec<[_; 6]> = smallvec![table_vmctx, table_index, dst];
2477+
let libcall = match ty.top() {
2478+
WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => {
2479+
args.push(val);
2480+
gc::builtins::table_fill_gc_ref(self, &mut pos.func)?
2481+
}
2482+
WasmHeapTopType::Func => {
2483+
args.push(val);
2484+
self.builtin_functions.table_fill_func_ref(&mut pos.func)
2485+
}
2486+
WasmHeapTopType::Cont => {
2487+
let (revision, contref) =
2488+
stack_switching::fatpointer::deconstruct(self, &mut pos, val);
2489+
args.extend_from_slice(&[contref, revision]);
2490+
stack_switching::builtins::table_fill_cont_obj(self, &mut pos.func)?
2491+
}
2492+
};
2493+
2494+
args.push(len);
2495+
builder.ins().call(libcall, &args);
24472496

24482497
Ok(())
24492498
}
@@ -2795,7 +2844,10 @@ impl FuncEnvironment<'_> {
27952844
WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => {
27962845
pos.ins().iconst(types::I32, 0)
27972846
}
2798-
WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support.
2847+
WasmHeapTopType::Cont => {
2848+
let zero = pos.ins().iconst(self.pointer_type(), 0);
2849+
stack_switching::fatpointer::construct(self, &mut pos, zero, zero)
2850+
}
27992851
})
28002852
}
28012853

@@ -2811,9 +2863,18 @@ impl FuncEnvironment<'_> {
28112863
return Ok(pos.ins().iconst(ir::types::I32, 0));
28122864
}
28132865

2814-
let byte_is_null =
2815-
pos.ins()
2816-
.icmp_imm(cranelift_codegen::ir::condcodes::IntCC::Equal, value, 0);
2866+
let byte_is_null = match ty.heap_type.top() {
2867+
WasmHeapTopType::Cont => {
2868+
let (_revision, contref) =
2869+
stack_switching::fatpointer::deconstruct(self, &mut pos, value);
2870+
pos.ins()
2871+
.icmp_imm(cranelift_codegen::ir::condcodes::IntCC::Equal, contref, 0)
2872+
}
2873+
_ => pos
2874+
.ins()
2875+
.icmp_imm(cranelift_codegen::ir::condcodes::IntCC::Equal, value, 0),
2876+
};
2877+
28172878
Ok(pos.ins().uextend(ir::types::I32, byte_is_null))
28182879
}
28192880

@@ -3556,6 +3617,118 @@ impl FuncEnvironment<'_> {
35563617
self.isa.triple().architecture == target_lexicon::Architecture::X86_64
35573618
}
35583619

3620+
pub fn translate_cont_bind(
3621+
&mut self,
3622+
builder: &mut FunctionBuilder<'_>,
3623+
contobj: ir::Value,
3624+
args: &[ir::Value],
3625+
) -> ir::Value {
3626+
stack_switching::instructions::translate_cont_bind(self, builder, contobj, args)
3627+
}
3628+
3629+
pub fn translate_cont_new(
3630+
&mut self,
3631+
builder: &mut FunctionBuilder<'_>,
3632+
func: ir::Value,
3633+
arg_types: &[WasmValType],
3634+
return_types: &[WasmValType],
3635+
) -> WasmResult<ir::Value> {
3636+
stack_switching::instructions::translate_cont_new(
3637+
self,
3638+
builder,
3639+
func,
3640+
arg_types,
3641+
return_types,
3642+
)
3643+
}
3644+
3645+
pub fn translate_resume(
3646+
&mut self,
3647+
builder: &mut FunctionBuilder<'_>,
3648+
type_index: u32,
3649+
contobj: ir::Value,
3650+
resume_args: &[ir::Value],
3651+
resumetable: &[(u32, Option<ir::Block>)],
3652+
) -> WasmResult<Vec<ir::Value>> {
3653+
stack_switching::instructions::translate_resume(
3654+
self,
3655+
builder,
3656+
type_index,
3657+
contobj,
3658+
resume_args,
3659+
resumetable,
3660+
)
3661+
}
3662+
3663+
pub fn translate_suspend(
3664+
&mut self,
3665+
builder: &mut FunctionBuilder<'_>,
3666+
tag_index: u32,
3667+
suspend_args: &[ir::Value],
3668+
tag_return_types: &[ir::Type],
3669+
) -> Vec<ir::Value> {
3670+
stack_switching::instructions::translate_suspend(
3671+
self,
3672+
builder,
3673+
tag_index,
3674+
suspend_args,
3675+
tag_return_types,
3676+
)
3677+
}
3678+
3679+
/// Translates switch instructions.
3680+
pub fn translate_switch(
3681+
&mut self,
3682+
builder: &mut FunctionBuilder,
3683+
tag_index: u32,
3684+
contobj: ir::Value,
3685+
switch_args: &[ir::Value],
3686+
return_types: &[ir::Type],
3687+
) -> WasmResult<Vec<ir::Value>> {
3688+
stack_switching::instructions::translate_switch(
3689+
self,
3690+
builder,
3691+
tag_index,
3692+
contobj,
3693+
switch_args,
3694+
return_types,
3695+
)
3696+
}
3697+
3698+
pub fn continuation_arguments(&self, index: TypeIndex) -> &[WasmValType] {
3699+
let idx = self.module.types[index].unwrap_module_type_index();
3700+
self.types[self.types[idx]
3701+
.unwrap_cont()
3702+
.clone()
3703+
.unwrap_module_type_index()]
3704+
.unwrap_func()
3705+
.params()
3706+
}
3707+
3708+
pub fn continuation_returns(&self, index: TypeIndex) -> &[WasmValType] {
3709+
let idx = self.module.types[index].unwrap_module_type_index();
3710+
self.types[self.types[idx]
3711+
.unwrap_cont()
3712+
.clone()
3713+
.unwrap_module_type_index()]
3714+
.unwrap_func()
3715+
.returns()
3716+
}
3717+
3718+
pub fn tag_params(&self, tag_index: TagIndex) -> &[WasmValType] {
3719+
let idx = self.module.tags[tag_index].signature;
3720+
self.types[idx.unwrap_module_type_index()]
3721+
.unwrap_func()
3722+
.params()
3723+
}
3724+
3725+
pub fn tag_returns(&self, tag_index: TagIndex) -> &[WasmValType] {
3726+
let idx = self.module.tags[tag_index].signature;
3727+
self.types[idx.unwrap_module_type_index()]
3728+
.unwrap_func()
3729+
.returns()
3730+
}
3731+
35593732
pub fn use_x86_blendv_for_relaxed_laneselect(&self, ty: Type) -> bool {
35603733
self.isa.has_x86_blendv_lowering(ty)
35613734
}
@@ -4138,17 +4311,3 @@ fn index_type_to_ir_type(index_type: IndexType) -> ir::Type {
41384311
IndexType::I64 => I64,
41394312
}
41404313
}
4141-
4142-
/// TODO(10248) This is removed in the next stack switching PR. It stops the
4143-
/// compiler from complaining about the stack switching libcalls being dead
4144-
/// code.
4145-
#[cfg(feature = "stack-switching")]
4146-
#[allow(
4147-
dead_code,
4148-
reason = "Dummy function to suppress more dead code warnings"
4149-
)]
4150-
pub fn use_stack_switching_libcalls() {
4151-
let _ = BuiltinFunctions::cont_new;
4152-
let _ = BuiltinFunctions::table_grow_cont_obj;
4153-
let _ = BuiltinFunctions::table_fill_cont_obj;
4154-
}

0 commit comments

Comments
 (0)