From 186362f25324ddbdbf7bf37aa3e4e073d990be62 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Thu, 27 Jul 2023 14:12:40 +0100 Subject: [PATCH 01/14] WIP (broken) --- cranelift/wasm/src/code_translator.rs | 46 +++++++++++++++++++-------- cranelift/wasm/src/environ/dummy.rs | 4 +-- cranelift/wasm/src/environ/spec.rs | 4 +-- crates/cranelift/src/func_environ.rs | 7 ++-- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs index c21d77accfb7..24068c66c447 100644 --- a/cranelift/wasm/src/code_translator.rs +++ b/cranelift/wasm/src/code_translator.rs @@ -2507,15 +2507,17 @@ pub fn translate_operator( let (original_contref, call_args) = state.peekn(arity + 1).split_last().unwrap(); let original_contobj = environ.typed_continuations_cont_ref_get_cont_obj(builder, *original_contref); - let call_arg_types = environ.continuation_arguments(*type_index).to_vec(); + + if call_args.len() > 0 { + let count = builder.ins().iconst(I32, call_args.len() as i64); + environ.typed_continuations_store_resume_args(builder, call_args, count, original_contobj); + } // Now, we generate the call instruction. let (base_addr, signal, tag) = environ.translate_resume( builder, state, - original_contobj, - &call_arg_types, - call_args, + original_contobj )?; // Description of results: // * The `base_addr` is the base address of VM context. @@ -2543,7 +2545,7 @@ pub fn translate_operator( canonicalise_brif(builder, is_zero, return_block, &[], suspend_block, &[]); // Next, build the suspend block. - let contref = { + let (contref, contobj) = { builder.switch_to_block(suspend_block); builder.seal_block(suspend_block); @@ -2566,7 +2568,7 @@ pub fn translate_operator( // We need to terminate this block before being allowed to switch to another one builder.ins().jump(switch_block, &[]); - contref + (contref, contobj) }; // Strategy: @@ -2609,19 +2611,36 @@ pub fn translate_operator( // the Switch structure and created the blocks it jumps // to. - let forwarding_case = + let forwarding_block = crate::translation_utils::resumetable_forwarding_block(builder, environ)?; + { + builder.switch_to_block(forwarding_block); + + // We suspend, thus deferring handling to the parent. + // We do nothing about tag *parameters, these remain unchanged within the + // payload buffer associcated with the whole VMContext. + environ.translate_suspend(builder, state, tag); + + // When reaching this point, the parent handler has just invoked `resume`. + // We propagate to the child (i.e., `contobj`). + + let _parent_contobj = environ.typed_continuations_load_continuation_object(builder, base_addr); + + // FIXME: swap tag return buffers of parent_contobj and contobj + // + environ.translate_resume(builder, state, contobj); + + + } // Switch block (where the actual switching logic is // emitted to). { builder.switch_to_block(switch_block); - switch.emit(builder, tag, forwarding_case); + switch.emit(builder, tag, forwarding_block); builder.seal_block(switch_block); - builder.switch_to_block(forwarding_case); - builder.seal_block(forwarding_case); - // TODO: emit effect forwarding logic. - builder.ins().trap(ir::TrapCode::UnreachableCodeReached); + builder.seal_block(forwarding_block); + // We can only seal the blocks we generated for each // tag now, after switch.emit ran. @@ -2660,7 +2679,8 @@ pub fn translate_operator( environ.typed_continuations_store_payloads(builder, ¶m_types, params); state.popn(param_count); - let vmctx = environ.translate_suspend(builder, state, *tag_index); + let tag_index_val = builder.ins().iconst(I32, *tag_index as i64); + let vmctx = environ.translate_suspend(builder, state, tag_index_val); let contobj = environ.typed_continuations_load_continuation_object(builder, vmctx); diff --git a/cranelift/wasm/src/environ/dummy.rs b/cranelift/wasm/src/environ/dummy.rs index f17f40a04adc..1741f1fcbdf6 100644 --- a/cranelift/wasm/src/environ/dummy.rs +++ b/cranelift/wasm/src/environ/dummy.rs @@ -711,8 +711,6 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ _builder: &mut FunctionBuilder, _state: &FuncTranslationState, _cont: ir::Value, - _call_arg_types: &[WasmType], - _call_args: &[ir::Value], ) -> WasmResult<(ir::Value, ir::Value, ir::Value)> { todo!() } @@ -731,7 +729,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ &mut self, _builder: &mut FunctionBuilder, _state: &FuncTranslationState, - _tag_index: u32, + _tag_index: ir::Value, ) -> ir::Value { todo!() } diff --git a/cranelift/wasm/src/environ/spec.rs b/cranelift/wasm/src/environ/spec.rs index 24e374543d6a..284119c3d0d5 100644 --- a/cranelift/wasm/src/environ/spec.rs +++ b/cranelift/wasm/src/environ/spec.rs @@ -644,8 +644,6 @@ pub trait FuncEnvironment: TargetEnvironment { builder: &mut FunctionBuilder, state: &FuncTranslationState, cont: ir::Value, - call_arg_types: &[wasmtime_types::WasmType], - call_args: &[ir::Value], ) -> WasmResult<(ir::Value, ir::Value, ir::Value)>; /// TODO(dhil): write documentation. @@ -662,7 +660,7 @@ pub trait FuncEnvironment: TargetEnvironment { &mut self, builder: &mut FunctionBuilder, state: &FuncTranslationState, - tag_index: u32, + tag_index: ir::Value, ) -> ir::Value; /// TODO diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index 8a9d0fa74899..bee4230471c6 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -2564,10 +2564,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m // // Second: Call the `resume` builtin - if call_args.len() > 0 { - let count = builder.ins().iconst(I32, call_args.len() as i64); - self.typed_continuations_store_resume_args(builder, call_args, count, contobj); - } + let (vmctx, result) = generate_builtin_call!(self, builder, resume, [contobj]); @@ -2601,7 +2598,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m &mut self, builder: &mut FunctionBuilder, _state: &FuncTranslationState, - tag_index: u32, + ir::Value, ) -> ir::Value { let tag_index = builder.ins().iconst(I32, tag_index as i64); From 85a47efc51df8fc7e76f186f4f80cfa79899bce5 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Thu, 27 Jul 2023 15:58:29 +0100 Subject: [PATCH 02/14] compiles --- cranelift/wasm/src/code_translator.rs | 2 +- crates/cranelift/src/func_environ.rs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs index 24068c66c447..44bbb756a375 100644 --- a/cranelift/wasm/src/code_translator.rs +++ b/cranelift/wasm/src/code_translator.rs @@ -2628,7 +2628,7 @@ pub fn translate_operator( // FIXME: swap tag return buffers of parent_contobj and contobj // - environ.translate_resume(builder, state, contobj); + environ.translate_resume(builder, state, contobj)?; } diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index bee4230471c6..932f421f1a3a 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -2553,8 +2553,6 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m builder: &mut FunctionBuilder, _state: &FuncTranslationState, contobj: ir::Value, - _call_arg_types: &[WasmType], - call_args: &[ir::Value], ) -> WasmResult<(ir::Value, ir::Value, ir::Value)> { // Strategy: // @@ -2598,9 +2596,8 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m &mut self, builder: &mut FunctionBuilder, _state: &FuncTranslationState, - ir::Value, + tag_index : ir::Value ) -> ir::Value { - let tag_index = builder.ins().iconst(I32, tag_index as i64); // Returns the vmctx return generate_builtin_call_no_return_val!(self, builder, suspend, [tag_index]); From fbd590561bc9cfb12c380e758816155a9963e367 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Thu, 27 Jul 2023 15:58:50 +0100 Subject: [PATCH 03/14] formatting --- cranelift/wasm/src/code_translator.rs | 20 ++++++++++---------- crates/cranelift/src/func_environ.rs | 5 +---- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs index 44bbb756a375..e931aa74ed73 100644 --- a/cranelift/wasm/src/code_translator.rs +++ b/cranelift/wasm/src/code_translator.rs @@ -2510,15 +2510,17 @@ pub fn translate_operator( if call_args.len() > 0 { let count = builder.ins().iconst(I32, call_args.len() as i64); - environ.typed_continuations_store_resume_args(builder, call_args, count, original_contobj); + environ.typed_continuations_store_resume_args( + builder, + call_args, + count, + original_contobj, + ); } // Now, we generate the call instruction. - let (base_addr, signal, tag) = environ.translate_resume( - builder, - state, - original_contobj - )?; + let (base_addr, signal, tag) = + environ.translate_resume(builder, state, original_contobj)?; // Description of results: // * The `base_addr` is the base address of VM context. // * The `signal` is an encoded boolean indicating whether @@ -2624,13 +2626,12 @@ pub fn translate_operator( // When reaching this point, the parent handler has just invoked `resume`. // We propagate to the child (i.e., `contobj`). - let _parent_contobj = environ.typed_continuations_load_continuation_object(builder, base_addr); + let _parent_contobj = + environ.typed_continuations_load_continuation_object(builder, base_addr); // FIXME: swap tag return buffers of parent_contobj and contobj // environ.translate_resume(builder, state, contobj)?; - - } // Switch block (where the actual switching logic is @@ -2641,7 +2642,6 @@ pub fn translate_operator( builder.seal_block(switch_block); builder.seal_block(forwarding_block); - // We can only seal the blocks we generated for each // tag now, after switch.emit ran. for case_block in case_blocks { diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index 932f421f1a3a..7e5fbc456e1c 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -2562,8 +2562,6 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m // // Second: Call the `resume` builtin - - let (vmctx, result) = generate_builtin_call!(self, builder, resume, [contobj]); // The result encodes whether the return happens via ordinary @@ -2596,9 +2594,8 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m &mut self, builder: &mut FunctionBuilder, _state: &FuncTranslationState, - tag_index : ir::Value + tag_index: ir::Value, ) -> ir::Value { - // Returns the vmctx return generate_builtin_call_no_return_val!(self, builder, suspend, [tag_index]); } From c767680a6a2bac1788b877e8706da81e0a8281b3 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Fri, 28 Jul 2023 17:58:16 +0100 Subject: [PATCH 04/14] back edge to new resume block --- cranelift/wasm/src/code_translator.rs | 78 +++++++++++++++++---------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs index e931aa74ed73..9bf4c655f09e 100644 --- a/cranelift/wasm/src/code_translator.rs +++ b/cranelift/wasm/src/code_translator.rs @@ -2517,34 +2517,56 @@ pub fn translate_operator( original_contobj, ); } - - // Now, we generate the call instruction. - let (base_addr, signal, tag) = - environ.translate_resume(builder, state, original_contobj)?; - // Description of results: - // * The `base_addr` is the base address of VM context. - // * The `signal` is an encoded boolean indicating whether - // the `resume` returned ordinarily or via a suspend - // instruction. - // * The `tag` is the index of the control tag supplied to - // suspend (only valid if `signal` is 1). - // Pop the `resume_args` off the stack. state.popn(arity + 1); - // Now, construct blocks for the three continuations: - // 1) `resume` returned normally. - // 2) `resume` returned via a suspend. - // 3) `resume` is forwarding (TODO) - - // Test the signal bit. - let is_zero = builder.ins().icmp_imm(IntCC::Equal, signal, 0); + let resume_block = builder.create_block(); let return_block = crate::translation_utils::return_block(builder, environ)?; let suspend_block = crate::translation_utils::suspend_block(builder, environ)?; let switch_block = builder.create_block(); - // Jump to the return block if the signal is 0, otherwise - // jump to the suspend block. - canonicalise_brif(builder, is_zero, return_block, &[], suspend_block, &[]); + + builder.ins().jump(resume_block, &[original_contobj]); + + let (base_addr, tag) = { + builder.switch_to_block(resume_block); + builder.append_block_param(resume_block, environ.pointer_type()); + + // The continuation object to actually call resume on + let resume_contobj = builder.block_params(resume_block)[0]; + + // Now, we generate the call instruction. + let (base_addr, signal, tag) = + environ.translate_resume(builder, state, resume_contobj)?; + // Description of results: + // * The `base_addr` is the base address of VM context. + // * The `signal` is an encoded boolean indicating whether + // the `resume` returned ordinarily or via a suspend + // instruction. + // * The `tag` is the index of the control tag supplied to + // suspend (only valid if `signal` is 1). + + // Now, construct blocks for the three continuations: + // 1) `resume` returned normally. + // 2) `resume` returned via a suspend. + // 3) `resume` is forwarding (TODO) + + // Test the signal bit. + let is_zero = builder.ins().icmp_imm(IntCC::Equal, signal, 0); + + // Jump to the return block if the signal is 0, otherwise + // jump to the suspend block. + canonicalise_brif( + builder, + is_zero, + return_block, + &[resume_contobj], + suspend_block, + &[], + ); + + // We do not seal this block, yet, because the effect forwarding block has a back edge to it + (base_addr, tag) + }; // Next, build the suspend block. let (contref, contobj) = { @@ -2625,13 +2647,11 @@ pub fn translate_operator( // When reaching this point, the parent handler has just invoked `resume`. // We propagate to the child (i.e., `contobj`). - let _parent_contobj = environ.typed_continuations_load_continuation_object(builder, base_addr); - // FIXME: swap tag return buffers of parent_contobj and contobj - // - environ.translate_resume(builder, state, contobj)?; + builder.ins().jump(resume_block, &[contobj]); + builder.seal_block(resume_block); } // Switch block (where the actual switching logic is @@ -2652,14 +2672,18 @@ pub fn translate_operator( // Now, finish the return block. { builder.switch_to_block(return_block); + builder.append_block_param(return_block, environ.pointer_type()); builder.seal_block(return_block); + // The continuation object that resume was called on + let resumed_contobj = builder.block_params(return_block)[0]; + // Load and push the results. let returns = environ.continuation_returns(*type_index).to_vec(); let values = environ.typed_continuations_load_return_values( builder, &returns, - original_contobj, + resumed_contobj, ); // The continuation has returned and all `ContinuationReferences` From d0cea005eb473876b11ba58f1df93ed812bb8227 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Fri, 28 Jul 2023 18:26:43 +0100 Subject: [PATCH 05/14] forwarding1.wast --- .../typed-continuations/cont_forwarding.txt | 22 +++++++++ .../typed-continuations/cont_forwarding1.wast | 46 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/misc_testsuite/typed-continuations/cont_forwarding.txt create mode 100644 tests/misc_testsuite/typed-continuations/cont_forwarding1.wast diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding.txt b/tests/misc_testsuite/typed-continuations/cont_forwarding.txt new file mode 100644 index 000000000000..48924b6b9746 --- /dev/null +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding.txt @@ -0,0 +1,22 @@ +Tests: + +1: +Simple forwarding, no payloads, no param or return values on function, immediately resumed by handler + +2. +Like 1, but with param and return values on functions (but no tag payloads) + +3. +Tag has parameters (but no return values) + +4. +Tag has param and return values + +5. +Continuation is not immediately resumed, instead we run a different continuation in the meantime + +6. +The resumed continuation suspends again, using the same tag + +7. +The resumed continuation suspends again, using a different tag diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding1.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding1.wast new file mode 100644 index 000000000000..1d37a0382d10 --- /dev/null +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding1.wast @@ -0,0 +1,46 @@ +(module + + (type $unit_to_unit (func)) + (type $ct (cont $unit_to_unit)) + + (type $g2_res_type (func (result (ref $ct)))) + (type $g2_res_type_ct (cont $g2_res_type)) + + (tag $e1) + (tag $e2) + + (global $marker (mut i32) (i32.const 0)) + + (func $update_marker (param $x i32) + (i32.add (global.get $marker) (i32.const 1)) + (i32.mul (local.get $x)) + (global.set $marker)) + + (func $g1 + (call $update_marker (i32.const 2)) + (suspend $e1) + (call $update_marker (i32.const 3))) + (elem declare func $g1) + + ;; Calls $g1 as continuation, but only handles e2 rather than e1 + (func $g2 + (block $on_e2 (result (ref $ct)) + (call $update_marker (i32.const 5)) + (resume $ct (tag $e2 $on_e2) (cont.new $ct (ref.func $g1))) + (return)) + (unreachable)) + (elem declare func $g2) + + (func $g3 + (block $on_e1 (result (ref $ct)) + (call $update_marker (i32.const 7)) + (resume $ct (tag $e1 $on_e1) (cont.new $ct (ref.func $g2))) + (unreachable)) + (call $update_marker (i32.const 11)) + (resume $ct)) + + (func $test (export "test") (result i32) + (call $g3) + (global.get $marker))) + +(assert_return (invoke "test") (i32.const 2742)) From 167e3cc26d9359c980c607d28d527e47cf19e475 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Fri, 28 Jul 2023 18:48:31 +0100 Subject: [PATCH 06/14] forwarding.wast --- .../typed-continuations/cont_forwarding2.wast | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tests/misc_testsuite/typed-continuations/cont_forwarding2.wast diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding2.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding2.wast new file mode 100644 index 000000000000..b3ca7a282948 --- /dev/null +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding2.wast @@ -0,0 +1,47 @@ +(module + + (type $int_to_int (func (param i32) (result i32))) + (type $unit_to_int (func (result i32))) + (type $ct0 (cont $int_to_int)) + (type $ct1 (cont $unit_to_int)) + + (tag $e1) + (tag $e2) + + ;;(global $marker (mut i32) (i32.const 0)) + + ;; (func $update_marker (param $x i32) + ;; (i32.add (global.get $marker) (i32.const 1)) + ;; (i32.mul (local.get $x)) + ;; (global.set $marker)) + + (func $g1 (param $x i32) (result i32) + (suspend $e1) + (i32.add (local.get $x) (i32.const 1))) + (elem declare func $g1) + + ;; Calls $g1 as continuation, but only handles e2 rather than e1 + (func $g2 (param $x i32) (result i32) + (block $on_e2 (result (ref $ct1)) + ;;(call $update_marker (i32.const 5)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (i32.add (i32.const 1)) + (return)) + (unreachable)) + (elem declare func $g2) + + (func $g3 (param $x i32) (result i32) + (block $on_e1 (result (ref $ct1)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) + (unreachable)) + (resume $ct1) + (i32.add (i32.const 1)) + ) + + (func $test (export "test") (result i32) + (call $g3 (i32.const 1)) + )) + +(assert_return (invoke "test") (i32.const 6)) From 046730b378adf0826c2897f9c77f916c21b6dfb8 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Mon, 31 Jul 2023 15:50:04 +0100 Subject: [PATCH 07/14] implement support for tag return values when forwarding --- cranelift/wasm/src/code_translator.rs | 9 +++++++-- cranelift/wasm/src/environ/dummy.rs | 10 ++++++++++ cranelift/wasm/src/environ/spec.rs | 8 ++++++++ crates/cranelift/src/func_environ.rs | 14 ++++++++++++++ crates/environ/src/builtin.rs | 6 ++++++ crates/runtime/src/continuation.rs | 15 +++++++++++++++ crates/runtime/src/libcalls.rs | 11 +++++++++++ 7 files changed, 71 insertions(+), 2 deletions(-) diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs index 9bf4c655f09e..c54aea7a032a 100644 --- a/cranelift/wasm/src/code_translator.rs +++ b/cranelift/wasm/src/code_translator.rs @@ -2646,9 +2646,14 @@ pub fn translate_operator( environ.translate_suspend(builder, state, tag); // When reaching this point, the parent handler has just invoked `resume`. - // We propagate to the child (i.e., `contobj`). - let _parent_contobj = + // We propagate the tag return values to the child (i.e., `contobj`). + let parent_contobj = environ.typed_continuations_load_continuation_object(builder, base_addr); + environ.typed_continuations_forward_tag_return_values( + builder, + parent_contobj, + contobj, + ); builder.ins().jump(resume_block, &[contobj]); builder.seal_block(resume_block); diff --git a/cranelift/wasm/src/environ/dummy.rs b/cranelift/wasm/src/environ/dummy.rs index 1741f1fcbdf6..de93a6f288c4 100644 --- a/cranelift/wasm/src/environ/dummy.rs +++ b/cranelift/wasm/src/environ/dummy.rs @@ -767,6 +767,16 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ todo!() } + /// TODO + fn typed_continuations_forward_tag_return_values( + &mut self, + _builder: &mut FunctionBuilder, + _parent_contobj: ir::Value, + _child_contobj: ir::Value, + ) { + todo!() + } + fn typed_continuations_store_resume_args( &mut self, _builder: &mut FunctionBuilder, diff --git a/cranelift/wasm/src/environ/spec.rs b/cranelift/wasm/src/environ/spec.rs index 284119c3d0d5..e3c81aa3b282 100644 --- a/cranelift/wasm/src/environ/spec.rs +++ b/cranelift/wasm/src/environ/spec.rs @@ -692,6 +692,14 @@ pub trait FuncEnvironment: TargetEnvironment { valtypes: &[wasmtime_types::WasmType], ) -> std::vec::Vec; + /// TODO + fn typed_continuations_forward_tag_return_values( + &mut self, + builder: &mut FunctionBuilder, + parent_contobj: ir::Value, + child_contobj: ir::Value, + ); + /// TODO fn typed_continuations_store_payloads( &mut self, diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index 7e5fbc456e1c..39fde06d2a09 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -2692,6 +2692,20 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m values } + fn typed_continuations_forward_tag_return_values( + &mut self, + builder: &mut FunctionBuilder, + parent_contobj: ir::Value, + child_contobj: ir::Value, + ) { + generate_builtin_call_no_return_val!( + self, + builder, + cont_obj_forward_tag_return_values_buffer, + [parent_contobj, child_contobj] + ); + } + /// TODO fn typed_continuations_cont_ref_get_cont_obj( &mut self, diff --git a/crates/environ/src/builtin.rs b/crates/environ/src/builtin.rs index 84e416496a77..427b5d6913fa 100644 --- a/crates/environ/src/builtin.rs +++ b/crates/environ/src/builtin.rs @@ -106,6 +106,12 @@ macro_rules! foreach_builtin_function { cont_obj_get_tag_return_values_buffer(vmctx: vmctx, contobj: pointer, expected_count : i32) -> pointer; /// Deallocated the tag return value buffer within the continuation object. cont_obj_deallocate_tag_return_values_buffer(vmctx: vmctx, contobj: pointer); + /// Sets the tag return values of `child_contobj` to those of `parent_contobj`. + /// This is implemented by exchanging the pointers to the underlying buffers. + /// `child_contobj` must not currently have a tag return value buffer. + /// `parent_contobj` may or may not have one. + cont_obj_forward_tag_return_values_buffer(vmctx: vmctx, parent_contobj: pointer, child_contobj : pointer); + /// TODO drop_cont_obj(vmctx: vmctx, contobj: pointer); diff --git a/crates/runtime/src/continuation.rs b/crates/runtime/src/continuation.rs index a38136dc9a20..65c91c1c022c 100644 --- a/crates/runtime/src/continuation.rs +++ b/crates/runtime/src/continuation.rs @@ -174,6 +174,21 @@ pub fn cont_obj_get_tag_return_values_buffer( return payloads.data; } +/// TODO +pub fn cont_obj_forward_tag_return_values_buffer( + parent: *mut ContinuationObject, + child: *mut ContinuationObject, +) { + let parent = unsafe { parent.as_mut().unwrap() }; + let child = unsafe { child.as_mut().unwrap() }; + assert!(parent.state == State::Invoked); + assert!(child.state == State::Invoked); + + assert!(child.tag_return_values.is_none()); + + child.tag_return_values = parent.tag_return_values.take() +} + /// TODO pub fn cont_obj_deallocate_tag_return_values_buffer(obj: *mut ContinuationObject) { let obj = unsafe { obj.as_mut().unwrap() }; diff --git a/crates/runtime/src/libcalls.rs b/crates/runtime/src/libcalls.rs index 77e1bc05131a..739f75fd1663 100644 --- a/crates/runtime/src/libcalls.rs +++ b/crates/runtime/src/libcalls.rs @@ -861,6 +861,17 @@ fn cont_obj_get_tag_return_values_buffer( ) as *mut u8 } +fn cont_obj_forward_tag_return_values_buffer( + _instance: &mut Instance, + parent_contobj: *mut u8, + child_contobj: *mut u8, +) { + crate::continuation::cont_obj_forward_tag_return_values_buffer( + parent_contobj as *mut crate::continuation::ContinuationObject, + child_contobj as *mut crate::continuation::ContinuationObject, + ); +} + fn cont_obj_deallocate_tag_return_values_buffer(_instance: &mut Instance, contobj: *mut u8) { crate::continuation::cont_obj_deallocate_tag_return_values_buffer( contobj as *mut crate::continuation::ContinuationObject, From 5cbf3e0bb0cd099daabb8e97bd8fde9d629da974 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Mon, 31 Jul 2023 15:57:16 +0100 Subject: [PATCH 08/14] new test --- .../typed-continuations/cont_forwarding.txt | 2 +- .../typed-continuations/cont_forwarding3.wast | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/misc_testsuite/typed-continuations/cont_forwarding3.wast diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding.txt b/tests/misc_testsuite/typed-continuations/cont_forwarding.txt index 48924b6b9746..109c7454a380 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding.txt +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding.txt @@ -7,7 +7,7 @@ Simple forwarding, no payloads, no param or return values on function, immediate Like 1, but with param and return values on functions (but no tag payloads) 3. -Tag has parameters (but no return values) +Tag has payloads (param and return values) 4. Tag has param and return values diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding3.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding3.wast new file mode 100644 index 000000000000..db02e8495fca --- /dev/null +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding3.wast @@ -0,0 +1,45 @@ +(module + + (type $int_to_int (func (param i32) (result i32))) + (type $unit_to_int (func (result i32))) + (type $ct0 (cont $int_to_int)) + (type $ct1 (cont $unit_to_int)) + + (tag $e1 (param i32) (result i32)) + (tag $e2) + + (func $g1 (param $x i32) (result i32) + (i32.add (local.get $x) (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1))) + (elem declare func $g1) + + ;; Calls $g1 as continuation, but only handles e2 rather than e1 + (func $g2 (param $x i32) (result i32) + (block $on_e2 (result (ref $ct1)) + ;;(call $update_marker (i32.const 5)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (i32.add (i32.const 1)) + (return)) + (unreachable)) + (elem declare func $g2) + + (func $g3 (param $x i32) (result i32) + (local $k (ref $ct0)) + (block $on_e1 (result i32 (ref $ct0)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) + (unreachable)) + (local.set $k) + (i32.add (i32.const 1)) + (local.get $k) + (resume $ct0) + (i32.add (i32.const 1)) + ) + + (func $test (export "test") (result i32) + (call $g3 (i32.const 1)) + )) + +(assert_return (invoke "test") (i32.const 8)) From e9c328c7a4b7818818049ce0556d4331a7e33f26 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Wed, 2 Aug 2023 16:14:08 +0100 Subject: [PATCH 09/14] update comments on tests --- .../misc_testsuite/typed-continuations/cont_forwarding.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding.txt b/tests/misc_testsuite/typed-continuations/cont_forwarding.txt index 109c7454a380..e732ede9f9f3 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding.txt +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding.txt @@ -10,13 +10,10 @@ Like 1, but with param and return values on functions (but no tag payloads) Tag has payloads (param and return values) 4. -Tag has param and return values - -5. Continuation is not immediately resumed, instead we run a different continuation in the meantime -6. +5. The resumed continuation suspends again, using the same tag -7. +6. The resumed continuation suspends again, using a different tag From 4afc15dfcfb57b39570ccf8bb96f0e9b2fdaf10a Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Thu, 3 Aug 2023 16:04:29 +0100 Subject: [PATCH 10/14] two more tests --- .../typed-continuations/cont_forwarding.txt | 9 ++- .../typed-continuations/cont_forwarding4.wast | 56 +++++++++++++++++++ .../typed-continuations/cont_forwarding5.wast | 55 ++++++++++++++++++ 3 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 tests/misc_testsuite/typed-continuations/cont_forwarding4.wast create mode 100644 tests/misc_testsuite/typed-continuations/cont_forwarding5.wast diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding.txt b/tests/misc_testsuite/typed-continuations/cont_forwarding.txt index e732ede9f9f3..3a16d9faab36 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding.txt +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding.txt @@ -10,10 +10,13 @@ Like 1, but with param and return values on functions (but no tag payloads) Tag has payloads (param and return values) 4. -Continuation is not immediately resumed, instead we run a different continuation in the meantime +Continuation is not immediately resumed, instead we run a different continuation in the meantime. 5. -The resumed continuation suspends again, using the same tag +Continuation is not immediately resumed, we pass it to a different one as an argument, increasing the length of the chain by adding yet another useless handler. 6. -The resumed continuation suspends again, using a different tag +The resumed continuation suspends again, using the same tag. We install a new handler at the outermost level, which should be forwarded to correctly. + +7. +The resumed continuation suspends again, using a different tag (the "inner" one). We install a new handler for the inner tag, which should have no effect diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding4.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding4.wast new file mode 100644 index 000000000000..63ebeb115d16 --- /dev/null +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding4.wast @@ -0,0 +1,56 @@ +(module + + (type $int_to_int (func (param i32) (result i32))) + (type $unit_to_int (func (result i32))) + (type $ct0 (cont $int_to_int)) + (type $ct1 (cont $unit_to_int)) + + (tag $e1 (param i32) (result i32)) + (tag $e2) + + (func $g1 (param $x i32) (result i32) + (i32.add (local.get $x) (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1))) + (elem declare func $g1) + + ;; Calls $g1 as continuation, but only handles e2 rather than e1 + (func $g2 (param $x i32) (result i32) + (block $on_e2 (result (ref $ct1)) + ;;(call $update_marker (i32.const 5)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (i32.add (i32.const 1)) + (return)) + (unreachable)) + (elem declare func $g2) + + (func $g3 (param $x i32) (result i32) + (local $k1 (ref $ct0)) + (local $k2 (ref $ct0)) + (block $on_e1 (result i32 (ref $ct0)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) + (unreachable)) + (local.set $k1) + (i32.add (i32.const 1)) + + ;; We run another continuation before resuming $k1 + (block $on_e1_2 (param i32) (result i32 (ref $ct0)) + (resume $ct0 (tag $e1 $on_e1_2) (cont.new $ct0 (ref.func $g1))) + (unreachable)) + (local.set $k2) + (i32.add (i32.const 1)) + (resume $ct0 (local.get $k2)) + (i32.add (i32.const 1)) + + ;; Now finally resume $k1 + (resume $ct0 (local.get $k1)) + (i32.add (i32.const 1)) + ) + + (func $test (export "test") (result i32) + (call $g3 (i32.const 1)) + )) + +(assert_return (invoke "test") (i32.const 12)) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding5.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding5.wast new file mode 100644 index 000000000000..cfa911f7edcf --- /dev/null +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding5.wast @@ -0,0 +1,55 @@ +(module + + (type $int_to_int (func (param i32) (result i32))) + (type $unit_to_int (func (result i32))) + (type $ct0 (cont $int_to_int)) + (type $ct1 (cont $unit_to_int)) + + (type $ct1_to_int (func (param (ref $ct1)) (result i32))) + (type $ct2 (cont $ct1_to_int)) + + (tag $e1 (param i32) (result i32)) + (tag $e2) + + (func $g1 (param $x i32) (result i32) + (i32.add (local.get $x) (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1))) + (elem declare func $g1) + + ;; Calls $g1 as continuation, but only handles e2 rather than e1 + (func $g2 (param $x i32) (result i32) + (block $on_e2 (result (ref $ct1)) + ;;(call $update_marker (i32.const 5)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (i32.add (i32.const 1)) + (return)) + (unreachable)) + (elem declare func $g2) + + (func $g3 (param $k (ref $ct1)) (result i32) + (resume $ct1 (local.get $k))) + (elem declare func $g3) + + (func $g4 (param $x i32) (result i32) + (local $k1 (ref $ct0)) + (local $k2 (ref $ct1)) + (block $on_e1 (result i32 (ref $ct0)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) + (unreachable)) + (local.set $k1) + (i32.add (i32.const 1)) + (cont.bind $ct0 $ct1 (local.get $k1)) + (local.set $k2) + + ;; We resume $k1 by running it from within another continuation + (resume $ct2 (local.get $k2) (cont.new $ct2 (ref.func $g3))) + (i32.add (i32.const 1))) + + (func $test (export "test") (result i32) + (call $g4 (i32.const 1)) + )) + +(assert_return (invoke "test") (i32.const 8)) From ad3044ea7c4a767fd486465e597c855e29feba1b Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Sat, 19 Aug 2023 20:41:16 +0100 Subject: [PATCH 11/14] three new tests --- .../typed-continuations/cont_forwarding6.wast | 62 ++++++++++++++++ .../typed-continuations/cont_forwarding7.wast | 61 ++++++++++++++++ .../typed-continuations/cont_forwarding8.wast | 70 +++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 tests/misc_testsuite/typed-continuations/cont_forwarding6.wast create mode 100644 tests/misc_testsuite/typed-continuations/cont_forwarding7.wast create mode 100644 tests/misc_testsuite/typed-continuations/cont_forwarding8.wast diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding6.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding6.wast new file mode 100644 index 000000000000..84b7908b39f1 --- /dev/null +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding6.wast @@ -0,0 +1,62 @@ +;; The resumed continuation suspends again (from the same inner function), using the same tag. +;; We install a new handler at the outermost level, which should be forwarded to correctly. + +(module + + (type $int_to_int (func (param i32) (result i32))) + (type $unit_to_int (func (result i32))) + (type $ct0 (cont $int_to_int)) + (type $ct1 (cont $unit_to_int)) + + (type $ct1_to_int (func (param (ref $ct1)) (result i32))) + (type $ct2 (cont $ct1_to_int)) + + (tag $e1 (param i32) (result i32)) + (tag $e2) + + (func $g1 (param $x i32) (result i32) + (i32.add (local.get $x) (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1))) + (elem declare func $g1) + + ;; Calls $g1 as continuation, but only handles e2 rather than e1 + (func $g2 (param $x i32) (result i32) + (block $on_e2 (result (ref $ct1)) + ;;(call $update_marker (i32.const 5)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (i32.add (i32.const 1)) + (return)) + (unreachable)) + (elem declare func $g2) + + (func $g3 (param $x i32) (result i32) + (local $k1 (ref $ct0)) + (block $on_e1 (result i32 (ref $ct0)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) + (unreachable)) + (local.set $k1) + (i32.add (i32.const 1)) + + ;; g1 will suspend again. We install a new handler for $e1 + (block $on_e1_2 (param i32) (result i32 (ref $ct0)) + (resume $ct0 (tag $e1 $on_e1_2) (local.get $k1)) + (unreachable)) + (local.set $k1) + (i32.add (i32.const 1)) + + (resume $ct0 (local.get $k1)) + (i32.add (i32.const 1)) + + ) + + (func $test (export "test") (result i32) + (call $g3 (i32.const 1)) + ) + ) + +(assert_return (invoke "test") (i32.const 10)) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding7.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding7.wast new file mode 100644 index 000000000000..d0a4dfa57cae --- /dev/null +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding7.wast @@ -0,0 +1,61 @@ +;; The resumed continuation suspends again (but not from the same function), using the same tag. +;; We use the same outer handler in a loop + +(module + + (type $int_to_int (func (param i32) (result i32))) + (type $unit_to_int (func (result i32))) + (type $ct0 (cont $int_to_int)) + (type $ct1 (cont $unit_to_int)) + + (type $ct1_to_int (func (param (ref $ct1)) (result i32))) + (type $ct2 (cont $ct1_to_int)) + + (tag $e1 (param i32) (result i32)) + (tag $e2) + + (func $g1 (param $x i32) (result i32) + (i32.add (local.get $x) (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1))) + (elem declare func $g1) + + ;; Calls $g1 as continuation, but only handles e2 rather than e1 + (func $g2 (param $x i32) (result i32) + (block $on_e2 (result (ref $ct1)) + ;;(call $update_marker (i32.const 5)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (i32.add (i32.const 1)) + ;; suspend to $e1 again, this time from the direct child of the handler of $e1 + (suspend $e1) + (i32.add (i32.const 1)) + (return)) + (unreachable)) + (elem declare func $g2) + + (func $g3 (param $x i32) (result i32) + (local $k1 (ref $ct0)) + (local.get $x) + (cont.new $ct0 (ref.func $g2)) + + (loop $loop (param i32 (ref $ct0)) + (block $on_e1 (param i32 (ref $ct0)) (result i32 (ref $ct0)) + (resume $ct0 (tag $e1 $on_e1)) + (return)) + (local.set $k1) + (i32.add (i32.const 1)) + (local.get $k1) + (br $loop)) + (unreachable) + + ) + + (func $test (export "test") (result i32) + (call $g3 (i32.const 1)) + ) + ) + +(assert_return (invoke "test") (i32.const 10)) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding8.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding8.wast new file mode 100644 index 000000000000..668f724c3b7a --- /dev/null +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding8.wast @@ -0,0 +1,70 @@ +;; The resumed continuation suspends again, to the same and different tags. +;; We use the same outer handler in a loop + +(module + + (type $int_to_int (func (param i32) (result i32))) + (type $unit_to_int (func (result i32))) + (type $ct0 (cont $int_to_int)) + (type $ct1 (cont $unit_to_int)) + + (type $ct1_to_int (func (param (ref $ct1)) (result i32))) + (type $ct2 (cont $ct1_to_int)) + + (tag $e1 (param i32) (result i32)) + (tag $e2 (param i32) (result i32)) + + (func $g1 (param $x i32) (result i32) + (i32.add (local.get $x) (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1)) + (suspend $e2) + (i32.add (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1)) + ) + (elem declare func $g1) + + ;; Calls $g1 as continuation, but only handles e2 rather than e1 + (func $g2 (param $x i32) (result i32) + (local $k1 (ref $ct0)) + (block $on_e2 (result i32 (ref $ct0)) + ;;(call $update_marker (i32.const 5)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (unreachable)) + (local.set $k1) + + ;; we expect to suspend to $e2 eventually, at which point we install a new handler, while keeping the + ;; "outer" one for $e1 intact. + (i32.add (i32.const 1)) + (block $on_e2_2 (param i32) (result i32 (ref $ct0)) + (resume $ct0 (tag $e2 $on_e2_2) (local.get $k1)) + (i32.add (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1)) + (return)) + (unreachable)) + (elem declare func $g2) + + (func $g3 (param $x i32) (result i32) + (local $k1 (ref $ct0)) + (local.get $x) + (cont.new $ct0 (ref.func $g2)) + + (loop $loop (param i32 (ref $ct0)) + (block $on_e1 (param i32 (ref $ct0)) (result i32 (ref $ct0)) + (resume $ct0 (tag $e1 $on_e1)) + (return)) + (local.set $k1) + (i32.add (i32.const 1)) + (local.get $k1) + (br $loop)) + (unreachable)) + + (func $test (export "test") (result i32) + (call $g3 (i32.const 1)) + ) + ) + +(assert_return (invoke "test") (i32.const 12)) From 53b5b8ba64a4a790ff085a49707154654ef3fc27 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Sat, 19 Aug 2023 20:50:50 +0100 Subject: [PATCH 12/14] test formatting --- .../typed-continuations/cont_forwarding.txt | 22 ------- .../typed-continuations/cont_forwarding1.wast | 25 ++++---- .../typed-continuations/cont_forwarding2.wast | 26 +++------ .../typed-continuations/cont_forwarding3.wast | 39 +++++++------ .../typed-continuations/cont_forwarding4.wast | 49 ++++++++-------- .../typed-continuations/cont_forwarding5.wast | 48 ++++++++-------- .../typed-continuations/cont_forwarding6.wast | 57 ++++++++----------- .../typed-continuations/cont_forwarding7.wast | 54 ++++++++---------- .../typed-continuations/cont_forwarding8.wast | 30 ++++------ 9 files changed, 150 insertions(+), 200 deletions(-) delete mode 100644 tests/misc_testsuite/typed-continuations/cont_forwarding.txt diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding.txt b/tests/misc_testsuite/typed-continuations/cont_forwarding.txt deleted file mode 100644 index 3a16d9faab36..000000000000 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding.txt +++ /dev/null @@ -1,22 +0,0 @@ -Tests: - -1: -Simple forwarding, no payloads, no param or return values on function, immediately resumed by handler - -2. -Like 1, but with param and return values on functions (but no tag payloads) - -3. -Tag has payloads (param and return values) - -4. -Continuation is not immediately resumed, instead we run a different continuation in the meantime. - -5. -Continuation is not immediately resumed, we pass it to a different one as an argument, increasing the length of the chain by adding yet another useless handler. - -6. -The resumed continuation suspends again, using the same tag. We install a new handler at the outermost level, which should be forwarded to correctly. - -7. -The resumed continuation suspends again, using a different tag (the "inner" one). We install a new handler for the inner tag, which should have no effect diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding1.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding1.wast index 1d37a0382d10..7a997fa3d7e9 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding1.wast +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding1.wast @@ -1,3 +1,6 @@ +;; Simple forwarding, no payloads, no param or return values on +;; function, immediately resumed by handler + (module (type $unit_to_unit (func)) @@ -24,20 +27,20 @@ ;; Calls $g1 as continuation, but only handles e2 rather than e1 (func $g2 - (block $on_e2 (result (ref $ct)) - (call $update_marker (i32.const 5)) - (resume $ct (tag $e2 $on_e2) (cont.new $ct (ref.func $g1))) - (return)) - (unreachable)) + (block $on_e2 (result (ref $ct)) + (call $update_marker (i32.const 5)) + (resume $ct (tag $e2 $on_e2) (cont.new $ct (ref.func $g1))) + (return)) + (unreachable)) (elem declare func $g2) (func $g3 - (block $on_e1 (result (ref $ct)) - (call $update_marker (i32.const 7)) - (resume $ct (tag $e1 $on_e1) (cont.new $ct (ref.func $g2))) - (unreachable)) - (call $update_marker (i32.const 11)) - (resume $ct)) + (block $on_e1 (result (ref $ct)) + (call $update_marker (i32.const 7)) + (resume $ct (tag $e1 $on_e1) (cont.new $ct (ref.func $g2))) + (unreachable)) + (call $update_marker (i32.const 11)) + (resume $ct)) (func $test (export "test") (result i32) (call $g3) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding2.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding2.wast index b3ca7a282948..285edf8b1761 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding2.wast +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding2.wast @@ -1,3 +1,5 @@ +;; Like previous test, but with param and return values on functions (but no tag payloads) + (module (type $int_to_int (func (param i32) (result i32))) @@ -8,13 +10,6 @@ (tag $e1) (tag $e2) - ;;(global $marker (mut i32) (i32.const 0)) - - ;; (func $update_marker (param $x i32) - ;; (i32.add (global.get $marker) (i32.const 1)) - ;; (i32.mul (local.get $x)) - ;; (global.set $marker)) - (func $g1 (param $x i32) (result i32) (suspend $e1) (i32.add (local.get $x) (i32.const 1))) @@ -23,7 +18,6 @@ ;; Calls $g1 as continuation, but only handles e2 rather than e1 (func $g2 (param $x i32) (result i32) (block $on_e2 (result (ref $ct1)) - ;;(call $update_marker (i32.const 5)) (i32.add (local.get $x) (i32.const 1)) (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) (i32.add (i32.const 1)) @@ -32,16 +26,14 @@ (elem declare func $g2) (func $g3 (param $x i32) (result i32) - (block $on_e1 (result (ref $ct1)) - (i32.add (local.get $x) (i32.const 1)) - (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) - (unreachable)) - (resume $ct1) - (i32.add (i32.const 1)) - ) + (block $on_e1 (result (ref $ct1)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) + (unreachable)) + (resume $ct1) + (i32.add (i32.const 1))) (func $test (export "test") (result i32) - (call $g3 (i32.const 1)) - )) + (call $g3 (i32.const 1)))) (assert_return (invoke "test") (i32.const 6)) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding3.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding3.wast index db02e8495fca..d8439a485dff 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding3.wast +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding3.wast @@ -1,3 +1,5 @@ +;; Like previous test, but tag has payloads (param and return values) + (module (type $int_to_int (func (param i32) (result i32))) @@ -16,30 +18,27 @@ ;; Calls $g1 as continuation, but only handles e2 rather than e1 (func $g2 (param $x i32) (result i32) - (block $on_e2 (result (ref $ct1)) - ;;(call $update_marker (i32.const 5)) - (i32.add (local.get $x) (i32.const 1)) - (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) - (i32.add (i32.const 1)) - (return)) - (unreachable)) + (block $on_e2 (result (ref $ct1)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (i32.add (i32.const 1)) + (return)) + (unreachable)) (elem declare func $g2) (func $g3 (param $x i32) (result i32) - (local $k (ref $ct0)) - (block $on_e1 (result i32 (ref $ct0)) - (i32.add (local.get $x) (i32.const 1)) - (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) - (unreachable)) - (local.set $k) - (i32.add (i32.const 1)) - (local.get $k) - (resume $ct0) - (i32.add (i32.const 1)) - ) + (local $k (ref $ct0)) + (block $on_e1 (result i32 (ref $ct0)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) + (unreachable)) + (local.set $k) + (i32.add (i32.const 1)) + (local.get $k) + (resume $ct0) + (i32.add (i32.const 1))) (func $test (export "test") (result i32) - (call $g3 (i32.const 1)) - )) + (call $g3 (i32.const 1)))) (assert_return (invoke "test") (i32.const 8)) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding4.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding4.wast index 63ebeb115d16..9e53ff4c6b7a 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding4.wast +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding4.wast @@ -1,3 +1,5 @@ +;; Continuation is not immediately resumed, instead we run a different continuation in the meantime. + (module (type $int_to_int (func (param i32) (result i32))) @@ -17,7 +19,6 @@ ;; Calls $g1 as continuation, but only handles e2 rather than e1 (func $g2 (param $x i32) (result i32) (block $on_e2 (result (ref $ct1)) - ;;(call $update_marker (i32.const 5)) (i32.add (local.get $x) (i32.const 1)) (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) (i32.add (i32.const 1)) @@ -26,31 +27,29 @@ (elem declare func $g2) (func $g3 (param $x i32) (result i32) - (local $k1 (ref $ct0)) - (local $k2 (ref $ct0)) - (block $on_e1 (result i32 (ref $ct0)) - (i32.add (local.get $x) (i32.const 1)) - (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) - (unreachable)) - (local.set $k1) - (i32.add (i32.const 1)) - - ;; We run another continuation before resuming $k1 - (block $on_e1_2 (param i32) (result i32 (ref $ct0)) - (resume $ct0 (tag $e1 $on_e1_2) (cont.new $ct0 (ref.func $g1))) - (unreachable)) - (local.set $k2) - (i32.add (i32.const 1)) - (resume $ct0 (local.get $k2)) - (i32.add (i32.const 1)) - - ;; Now finally resume $k1 - (resume $ct0 (local.get $k1)) - (i32.add (i32.const 1)) - ) + (local $k1 (ref $ct0)) + (local $k2 (ref $ct0)) + (block $on_e1 (result i32 (ref $ct0)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) + (unreachable)) + (local.set $k1) + (i32.add (i32.const 1)) + + ;; We run another continuation before resuming $k1 + (block $on_e1_2 (param i32) (result i32 (ref $ct0)) + (resume $ct0 (tag $e1 $on_e1_2) (cont.new $ct0 (ref.func $g1))) + (unreachable)) + (local.set $k2) + (i32.add (i32.const 1)) + (resume $ct0 (local.get $k2)) + (i32.add (i32.const 1)) + + ;; Now finally resume $k1 + (resume $ct0 (local.get $k1)) + (i32.add (i32.const 1))) (func $test (export "test") (result i32) - (call $g3 (i32.const 1)) - )) + (call $g3 (i32.const 1)))) (assert_return (invoke "test") (i32.const 12)) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding5.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding5.wast index cfa911f7edcf..28b344d00a3e 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding5.wast +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding5.wast @@ -1,3 +1,7 @@ +;; Continuation is not immediately resumed, we pass it to a different one as an +;; argument, increasing the length of the chain by adding yet another useless +;; handler. + (module (type $int_to_int (func (param i32) (result i32))) @@ -19,13 +23,12 @@ ;; Calls $g1 as continuation, but only handles e2 rather than e1 (func $g2 (param $x i32) (result i32) - (block $on_e2 (result (ref $ct1)) - ;;(call $update_marker (i32.const 5)) - (i32.add (local.get $x) (i32.const 1)) - (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) - (i32.add (i32.const 1)) - (return)) - (unreachable)) + (block $on_e2 (result (ref $ct1)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (i32.add (i32.const 1)) + (return)) + (unreachable)) (elem declare func $g2) (func $g3 (param $k (ref $ct1)) (result i32) @@ -33,23 +36,22 @@ (elem declare func $g3) (func $g4 (param $x i32) (result i32) - (local $k1 (ref $ct0)) - (local $k2 (ref $ct1)) - (block $on_e1 (result i32 (ref $ct0)) - (i32.add (local.get $x) (i32.const 1)) - (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) - (unreachable)) - (local.set $k1) - (i32.add (i32.const 1)) - (cont.bind $ct0 $ct1 (local.get $k1)) - (local.set $k2) - - ;; We resume $k1 by running it from within another continuation - (resume $ct2 (local.get $k2) (cont.new $ct2 (ref.func $g3))) - (i32.add (i32.const 1))) + (local $k1 (ref $ct0)) + (local $k2 (ref $ct1)) + (block $on_e1 (result i32 (ref $ct0)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) + (unreachable)) + (local.set $k1) + (i32.add (i32.const 1)) + (cont.bind $ct0 $ct1 (local.get $k1)) + (local.set $k2) + + ;; We resume $k1 by running it from within another continuation + (resume $ct2 (local.get $k2) (cont.new $ct2 (ref.func $g3))) + (i32.add (i32.const 1))) (func $test (export "test") (result i32) - (call $g4 (i32.const 1)) - )) + (call $g4 (i32.const 1)))) (assert_return (invoke "test") (i32.const 8)) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding6.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding6.wast index 84b7908b39f1..ed9ec85698ac 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding6.wast +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding6.wast @@ -8,9 +8,6 @@ (type $ct0 (cont $int_to_int)) (type $ct1 (cont $unit_to_int)) - (type $ct1_to_int (func (param (ref $ct1)) (result i32))) - (type $ct2 (cont $ct1_to_int)) - (tag $e1 (param i32) (result i32)) (tag $e2) @@ -24,39 +21,35 @@ ;; Calls $g1 as continuation, but only handles e2 rather than e1 (func $g2 (param $x i32) (result i32) - (block $on_e2 (result (ref $ct1)) - ;;(call $update_marker (i32.const 5)) - (i32.add (local.get $x) (i32.const 1)) - (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) - (i32.add (i32.const 1)) - (return)) - (unreachable)) + (block $on_e2 (result (ref $ct1)) + ;;(call $update_marker (i32.const 5)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (i32.add (i32.const 1)) + (return)) + (unreachable)) (elem declare func $g2) (func $g3 (param $x i32) (result i32) - (local $k1 (ref $ct0)) - (block $on_e1 (result i32 (ref $ct0)) - (i32.add (local.get $x) (i32.const 1)) - (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) - (unreachable)) - (local.set $k1) - (i32.add (i32.const 1)) - - ;; g1 will suspend again. We install a new handler for $e1 - (block $on_e1_2 (param i32) (result i32 (ref $ct0)) - (resume $ct0 (tag $e1 $on_e1_2) (local.get $k1)) - (unreachable)) - (local.set $k1) - (i32.add (i32.const 1)) - - (resume $ct0 (local.get $k1)) - (i32.add (i32.const 1)) - - ) + (local $k1 (ref $ct0)) + (block $on_e1 (result i32 (ref $ct0)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e1 $on_e1) (cont.new $ct0 (ref.func $g2))) + (unreachable)) + (local.set $k1) + (i32.add (i32.const 1)) + + ;; g1 will suspend again. We install a new handler for $e1 + (block $on_e1_2 (param i32) (result i32 (ref $ct0)) + (resume $ct0 (tag $e1 $on_e1_2) (local.get $k1)) + (unreachable)) + (local.set $k1) + (i32.add (i32.const 1)) + + (resume $ct0 (local.get $k1)) + (i32.add (i32.const 1))) (func $test (export "test") (result i32) - (call $g3 (i32.const 1)) - ) - ) + (call $g3 (i32.const 1)))) (assert_return (invoke "test") (i32.const 10)) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding7.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding7.wast index d0a4dfa57cae..a3cd792207a2 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding7.wast +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding7.wast @@ -8,9 +8,6 @@ (type $ct0 (cont $int_to_int)) (type $ct1 (cont $unit_to_int)) - (type $ct1_to_int (func (param (ref $ct1)) (result i32))) - (type $ct2 (cont $ct1_to_int)) - (tag $e1 (param i32) (result i32)) (tag $e2) @@ -24,38 +21,33 @@ ;; Calls $g1 as continuation, but only handles e2 rather than e1 (func $g2 (param $x i32) (result i32) - (block $on_e2 (result (ref $ct1)) - ;;(call $update_marker (i32.const 5)) - (i32.add (local.get $x) (i32.const 1)) - (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) - (i32.add (i32.const 1)) - ;; suspend to $e1 again, this time from the direct child of the handler of $e1 - (suspend $e1) - (i32.add (i32.const 1)) - (return)) - (unreachable)) + (block $on_e2 (result (ref $ct1)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (i32.add (i32.const 1)) + ;; suspend to $e1 again, this time from the direct child of the handler of $e1 + (suspend $e1) + (i32.add (i32.const 1)) + (return)) + (unreachable)) (elem declare func $g2) (func $g3 (param $x i32) (result i32) - (local $k1 (ref $ct0)) - (local.get $x) - (cont.new $ct0 (ref.func $g2)) - - (loop $loop (param i32 (ref $ct0)) - (block $on_e1 (param i32 (ref $ct0)) (result i32 (ref $ct0)) - (resume $ct0 (tag $e1 $on_e1)) - (return)) - (local.set $k1) - (i32.add (i32.const 1)) - (local.get $k1) - (br $loop)) - (unreachable) - - ) + (local $k1 (ref $ct0)) + (local.get $x) + (cont.new $ct0 (ref.func $g2)) + + (loop $loop (param i32 (ref $ct0)) + (block $on_e1 (param i32 (ref $ct0)) (result i32 (ref $ct0)) + (resume $ct0 (tag $e1 $on_e1)) + (return)) + (local.set $k1) + (i32.add (i32.const 1)) + (local.get $k1) + (br $loop)) + (unreachable)) (func $test (export "test") (result i32) - (call $g3 (i32.const 1)) - ) - ) + (call $g3 (i32.const 1)))) (assert_return (invoke "test") (i32.const 10)) diff --git a/tests/misc_testsuite/typed-continuations/cont_forwarding8.wast b/tests/misc_testsuite/typed-continuations/cont_forwarding8.wast index 668f724c3b7a..044ad0f2064e 100644 --- a/tests/misc_testsuite/typed-continuations/cont_forwarding8.wast +++ b/tests/misc_testsuite/typed-continuations/cont_forwarding8.wast @@ -6,10 +6,6 @@ (type $int_to_int (func (param i32) (result i32))) (type $unit_to_int (func (result i32))) (type $ct0 (cont $int_to_int)) - (type $ct1 (cont $unit_to_int)) - - (type $ct1_to_int (func (param (ref $ct1)) (result i32))) - (type $ct2 (cont $ct1_to_int)) (tag $e1 (param i32) (result i32)) (tag $e2 (param i32) (result i32)) @@ -21,29 +17,27 @@ (suspend $e2) (i32.add (i32.const 1)) (suspend $e1) - (i32.add (i32.const 1)) - ) + (i32.add (i32.const 1))) (elem declare func $g1) ;; Calls $g1 as continuation, but only handles e2 rather than e1 (func $g2 (param $x i32) (result i32) (local $k1 (ref $ct0)) - (block $on_e2 (result i32 (ref $ct0)) - ;;(call $update_marker (i32.const 5)) - (i32.add (local.get $x) (i32.const 1)) - (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) - (unreachable)) + (block $on_e2 (result i32 (ref $ct0)) + (i32.add (local.get $x) (i32.const 1)) + (resume $ct0 (tag $e2 $on_e2) (cont.new $ct0 (ref.func $g1))) + (unreachable)) (local.set $k1) ;; we expect to suspend to $e2 eventually, at which point we install a new handler, while keeping the ;; "outer" one for $e1 intact. (i32.add (i32.const 1)) (block $on_e2_2 (param i32) (result i32 (ref $ct0)) - (resume $ct0 (tag $e2 $on_e2_2) (local.get $k1)) - (i32.add (i32.const 1)) - (suspend $e1) - (i32.add (i32.const 1)) - (return)) + (resume $ct0 (tag $e2 $on_e2_2) (local.get $k1)) + (i32.add (i32.const 1)) + (suspend $e1) + (i32.add (i32.const 1)) + (return)) (unreachable)) (elem declare func $g2) @@ -63,8 +57,6 @@ (unreachable)) (func $test (export "test") (result i32) - (call $g3 (i32.const 1)) - ) - ) + (call $g3 (i32.const 1)))) (assert_return (invoke "test") (i32.const 12)) From dc6586bb7e54fc0b00d2e019c18a5e6f7c57fca0 Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Sat, 19 Aug 2023 20:55:16 +0100 Subject: [PATCH 13/14] return block does not need to get continuation as block parameter --- cranelift/wasm/src/code_translator.rs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs index c54aea7a032a..b7163090e13c 100644 --- a/cranelift/wasm/src/code_translator.rs +++ b/cranelift/wasm/src/code_translator.rs @@ -2527,7 +2527,7 @@ pub fn translate_operator( builder.ins().jump(resume_block, &[original_contobj]); - let (base_addr, tag) = { + let (base_addr, tag, resumed_contobj) = { builder.switch_to_block(resume_block); builder.append_block_param(resume_block, environ.pointer_type()); @@ -2548,24 +2548,17 @@ pub fn translate_operator( // Now, construct blocks for the three continuations: // 1) `resume` returned normally. // 2) `resume` returned via a suspend. - // 3) `resume` is forwarding (TODO) + // 3) `resume` is forwarding // Test the signal bit. let is_zero = builder.ins().icmp_imm(IntCC::Equal, signal, 0); // Jump to the return block if the signal is 0, otherwise // jump to the suspend block. - canonicalise_brif( - builder, - is_zero, - return_block, - &[resume_contobj], - suspend_block, - &[], - ); + canonicalise_brif(builder, is_zero, return_block, &[], suspend_block, &[]); // We do not seal this block, yet, because the effect forwarding block has a back edge to it - (base_addr, tag) + (base_addr, tag, resume_contobj) }; // Next, build the suspend block. @@ -2677,12 +2670,8 @@ pub fn translate_operator( // Now, finish the return block. { builder.switch_to_block(return_block); - builder.append_block_param(return_block, environ.pointer_type()); builder.seal_block(return_block); - // The continuation object that resume was called on - let resumed_contobj = builder.block_params(return_block)[0]; - // Load and push the results. let returns = environ.continuation_returns(*type_index).to_vec(); let values = environ.typed_continuations_load_return_values( From 53bd5a98a5ffe02c33a852ddadb699962ebda04f Mon Sep 17 00:00:00 2001 From: Frank Emrich Date: Sun, 20 Aug 2023 10:29:34 +0100 Subject: [PATCH 14/14] fix env.rs --- cranelift/filetests/src/test_wasm/env.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/cranelift/filetests/src/test_wasm/env.rs b/cranelift/filetests/src/test_wasm/env.rs index 802a07619db9..0fcbae9b1aca 100644 --- a/cranelift/filetests/src/test_wasm/env.rs +++ b/cranelift/filetests/src/test_wasm/env.rs @@ -705,11 +705,8 @@ impl<'a> FuncEnvironment for FuncEnv<'a> { builder: &mut cranelift_frontend::FunctionBuilder, state: &cranelift_wasm::FuncTranslationState, cont: ir::Value, - call_arg_types: &[wasmtime_types::WasmType], - call_args: &[ir::Value], ) -> cranelift_wasm::WasmResult<(ir::Value, ir::Value, ir::Value)> { - self.inner - .translate_resume(builder, state, cont, call_arg_types, call_args) + self.inner.translate_resume(builder, state, cont) } /// TODO(dhil): write documentation. @@ -729,7 +726,7 @@ impl<'a> FuncEnvironment for FuncEnv<'a> { &mut self, builder: &mut cranelift_frontend::FunctionBuilder, state: &cranelift_wasm::FuncTranslationState, - tag_index: u32, + tag_index: ir::Value, ) -> ir::Value { return self.inner.translate_suspend(builder, state, tag_index); } @@ -850,4 +847,18 @@ impl<'a> FuncEnvironment for FuncEnv<'a> { .inner .typed_continuations_load_tag_return_values(builder, contobj, valtypes); } + + /// TODO + fn typed_continuations_forward_tag_return_values( + &mut self, + builder: &mut cranelift_frontend::FunctionBuilder, + parent_contobj: ir::Value, + child_contobj: ir::Value, + ) { + return self.inner.typed_continuations_forward_tag_return_values( + builder, + parent_contobj, + child_contobj, + ); + } }