diff --git a/src/engine/CodeValidator.v3 b/src/engine/CodeValidator.v3 index cd7fb116a..bb1d49e14 100644 --- a/src/engine/CodeValidator.v3 +++ b/src/engine/CodeValidator.v3 @@ -1290,14 +1290,14 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e RESUME_THROW => { var ct = parser.readCont(); if (ct == null) return; + var thrown_tag = parser.readTagRef(); + if (thrown_tag == null) return; var handlers = parser.readSuspensionHandlers(); if (!err.ok()) return; popE(ValueTypes.Ref(true, ct)); ctlxfer.refR(ct.sig.params.length, handlers.length); - var thrown_tag = parser.readTagRef(); - if (thrown_tag == null) return; checkAndPopArgs(thrown_tag.fields); readAndCheckContHandlerTable(ct, handlers); diff --git a/src/engine/Sidetable.v3 b/src/engine/Sidetable.v3 index 4f9cfb0c2..d8e99392b 100644 --- a/src/engine/Sidetable.v3 +++ b/src/engine/Sidetable.v3 @@ -87,9 +87,9 @@ component Sidetables { var count = immptr.skip_catches(); size = count * Sidetable_CatchEntry.size; } - RESUME => size = computeResumeEntrySize(immptr); - RESUME_THROW => size = computeResumeEntrySize(immptr); - RESUME_THROW_REF => size = computeResumeEntrySize(immptr); + RESUME => size = computeResumeEntrySize(immptr, false); + RESUME_THROW => size = computeResumeEntrySize(immptr, true); + RESUME_THROW_REF => size = computeResumeEntrySize(immptr, false); BR_ON_NULL => size = Sidetable_BrEntry.size; BR_ON_NON_NULL => size = Sidetable_BrEntry.size; BR_ON_CAST => size = Sidetable_BrEntry.size; @@ -99,8 +99,9 @@ component Sidetables { return size; } // Compute the size (in bytes) of the sidetable entries for a resume opcode. - private def computeResumeEntrySize(immptr: CodePtr) -> int { + private def computeResumeEntrySize(immptr: CodePtr, has_tag: bool) -> int { immptr.skip_leb(); // skip continuation type index + if (has_tag) immptr.skip_leb(); // skip thrown tag index var count = immptr.skip_suspension_handlers(); return Sidetable_ResumeEntry.size + count * Sidetable_CatchEntry.size; } diff --git a/src/engine/compiler/SinglePassCompiler.v3 b/src/engine/compiler/SinglePassCompiler.v3 index 073480f8a..7e72a8cc8 100644 --- a/src/engine/compiler/SinglePassCompiler.v3 +++ b/src/engine/compiler/SinglePassCompiler.v3 @@ -1624,6 +1624,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl masm.emit_validate_and_consume_cont(contStack, cont); state.emitSaveAll(resolver, SpillMode.SAVE_AND_FREE_REGS); + masm.emit_mov_r_r(ValueKind.REF, regs.runtime_arg2, contStack); emit_compute_vsp(regs.vsp, state.sp); emit_spill_vsp(xenv.vsp); masm.emit_store_curstack_vsp(regs.vsp); @@ -1631,7 +1632,6 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl masm.emit_v3_set_X86_64Stack_rsp_r_r(regs.runtime_arg0, regs.sp); masm.emit_push_X86_64Stack_rsp_r_r(regs.runtime_arg0); emit_load_instance(regs.runtime_arg1); - masm.emit_mov_r_r(ValueKind.REF, regs.runtime_arg2, contStack); masm.emit_mov_r_i(regs.runtime_arg3, tag_id); masm.emit_call_runtime_RESUME_THROW(); dropN(u32.!(tag.fields.length)); @@ -1703,6 +1703,7 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl popFixedReg(regs.runtime_arg3); state.emitSaveAll(resolver, SpillMode.SAVE_AND_FREE_REGS); + masm.emit_mov_r_r(ValueKind.REF, regs.runtime_arg2, contStack); emit_compute_vsp(regs.vsp, state.sp); emit_spill_vsp(xenv.vsp); masm.emit_store_curstack_vsp(regs.vsp); @@ -1710,7 +1711,6 @@ class SinglePassCompiler(xenv: SpcExecEnv, masm: MacroAssembler, regAlloc: RegAl masm.emit_v3_set_X86_64Stack_rsp_r_r(regs.runtime_arg0, regs.sp); masm.emit_push_X86_64Stack_rsp_r_r(regs.runtime_arg0); emit_load_instance(regs.runtime_arg1); - masm.emit_mov_r_r(ValueKind.REF, regs.runtime_arg2, contStack); masm.emit_call_runtime_RESUME_THROW_REF(); // {RT.runtime_handle_resume_throw} returns a {Continuation} by pushing onto the stack // XXX: make RT function return {Continuation} directly diff --git a/src/engine/x86-64/X86_64Runtime.v3 b/src/engine/x86-64/X86_64Runtime.v3 index 965034801..0289a2f84 100644 --- a/src/engine/x86-64/X86_64Runtime.v3 +++ b/src/engine/x86-64/X86_64Runtime.v3 @@ -202,12 +202,12 @@ component X86_64Runtime { bottom.parent = prev.parent; bottom.parent_rsp_ptr.store(prev.parent_rsp_ptr.load()); // set up new stack configuration - prev.parent = null; - prev.parent_rsp_ptr.store(Pointer.NULL); curStack = X86_64Stack.!(target_stack); curStack.state_ = StackState.RUNNING; curStack.pushN(vals); curStack.push(Value.Cont(this_cont)); + prev.parent = null; + prev.parent_rsp_ptr.store(Pointer.NULL); // RT calls will pop off the top value of {curStack.rsp} on return; push a // value so that the return address is not popped off. curStack.pushRspPointer(Pointer.NULL); diff --git a/test/regress/ext:stack-switching/resume_throw11.bin.wast b/test/regress/ext:stack-switching/resume_throw11.bin.wast new file mode 100644 index 000000000..836a8b5a1 --- /dev/null +++ b/test/regress/ext:stack-switching/resume_throw11.bin.wast @@ -0,0 +1,11 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\92\80\80\80\00\05\60" + "\00\00\5d\00\60\01\7e\00\60\01\7f\00\60\00\01\7f" + "\03\83\80\80\80\00\02\00\04\0d\85\80\80\80\00\02" + "\00\02\00\03\07\88\80\80\80\00\01\04\6d\61\69\6e" + "\00\01\09\85\80\80\80\00\01\03\00\01\00\0a\9b\80" + "\80\80\00\02\82\80\80\80\00\00\0b\8e\80\80\80\00" + "\00\41\2a\d2\00\e0\01\e4\01\01\00\41\2a\0b" +) +(module instance) +(assert_exception (invoke "main")) diff --git a/test/regress/ext:stack-switching/resume_throw11.wast b/test/regress/ext:stack-switching/resume_throw11.wast new file mode 100644 index 000000000..1525c516e --- /dev/null +++ b/test/regress/ext:stack-switching/resume_throw11.wast @@ -0,0 +1,16 @@ +;; resume_throw naming a tag index != 0; the tag index precedes the handler vector. +(module + (type $f1 (func)) + (type $c1 (cont $f1)) + (tag $unused (param i64)) + (tag $t (param i32)) + (func $empty) (elem declare func $empty) + (func (export "main") (result i32) + (resume_throw + $c1 $t + (i32.const 42) (cont.new $c1 (ref.func $empty))) + i32.const 42 + ) +) + +(assert_exception (invoke "main")) diff --git a/test/regress/ext:stack-switching/resume_throw12.bin.wast b/test/regress/ext:stack-switching/resume_throw12.bin.wast new file mode 100644 index 000000000..6b85fd5ca --- /dev/null +++ b/test/regress/ext:stack-switching/resume_throw12.bin.wast @@ -0,0 +1,14 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\8a\80\80\80\00\03\60" + "\00\01\7f\5d\00\60\00\00\03\83\80\80\80\00\02\00" + "\00\0d\87\80\80\80\00\03\00\02\00\02\00\02\07\88" + "\80\80\80\00\01\04\6d\61\69\6e\00\01\09\85\80\80" + "\80\00\01\03\00\01\00\0a\c7\80\80\80\00\02\95\80" + "\80\80\00\00\02\40\1f\40\01\00\00\00\e2\01\0b\41" + "\7f\0f\0b\e2\02\41\0c\0b\a7\80\80\80\00\01\01\63" + "\01\02\63\01\d2\00\e0\01\e3\01\01\00\01\00\0f\0b" + "\21\00\02\63\01\20\00\e4\01\00\01\00\02\00\0f\0b" + "\1a\41\2a\0b" +) +(module instance) +(assert_return (invoke "main") (i32.const 0x2a)) diff --git a/test/regress/ext:stack-switching/resume_throw12.wast b/test/regress/ext:stack-switching/resume_throw12.wast new file mode 100644 index 000000000..c00979fce --- /dev/null +++ b/test/regress/ext:stack-switching/resume_throw12.wast @@ -0,0 +1,33 @@ +;; resume_throw carrying a suspension handler, which is encoded after the tag index. +(module + (type $f1 (func (result i32))) + (type $c1 (cont $f1)) + (tag $e) + (tag $f) + (tag $g) + (func $s (result i32) + (block $h + (try_table (catch $e $h) (suspend $f)) + (return (i32.const -1)) + ) + (suspend $g) + (i32.const 12) + ) + (elem declare func $s) + (func (export "main") (result i32) + (local $k (ref null $c1)) + (block $h1 (result (ref null $c1)) + (resume $c1 (on $f $h1) (cont.new $c1 (ref.func $s))) + (return) + ) + (local.set $k) + (block $h2 (result (ref null $c1)) + (resume_throw $c1 $e (on $g $h2) (local.get $k)) + (return) + ) + (drop) + (i32.const 42) + ) +) + +(assert_return (invoke "main") (i32.const 42)) diff --git a/test/regress/ext:stack-switching/resume_throw13.bin.wast b/test/regress/ext:stack-switching/resume_throw13.bin.wast new file mode 100644 index 000000000..490265a80 --- /dev/null +++ b/test/regress/ext:stack-switching/resume_throw13.bin.wast @@ -0,0 +1,14 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\8a\80\80\80\00\03\60" + "\00\01\7f\5d\00\60\00\00\03\83\80\80\80\00\02\00" + "\00\0d\87\80\80\80\00\03\00\02\00\02\00\02\07\88" + "\80\80\80\00\01\04\6d\61\69\6e\00\01\09\85\80\80" + "\80\00\01\03\00\01\00\0a\cc\80\80\80\00\02\95\80" + "\80\80\00\00\02\40\1f\40\01\00\02\00\e2\00\0b\41" + "\7f\0f\0b\e2\01\41\0c\0b\ac\80\80\80\00\01\01\63" + "\01\02\63\01\d2\00\e0\01\e3\01\01\00\00\00\0f\0b" + "\21\00\02\63\01\20\00\e4\01\02\01\00\01\00\0f\0b" + "\1a\02\7f\41\2a\0c\00\0b\0b" +) +(module instance) +(assert_return (invoke "main") (i32.const 0x2a)) diff --git a/test/regress/ext:stack-switching/resume_throw13.wast b/test/regress/ext:stack-switching/resume_throw13.wast new file mode 100644 index 000000000..2a27ce312 --- /dev/null +++ b/test/regress/ext:stack-switching/resume_throw13.wast @@ -0,0 +1,37 @@ +;; resume_throw with both a non-zero tag index and a handler, followed by a branch. +(module + (type $f1 (func (result i32))) + (type $c1 (cont $f1)) + (tag $f) + (tag $g) + (tag $e) + (func $s (result i32) + (block $h + (try_table (catch $e $h) (suspend $f)) + (return (i32.const -1)) + ) + (suspend $g) + (i32.const 12) + ) + (elem declare func $s) + (func (export "main") (result i32) + (local $k (ref null $c1)) + (block $h1 (result (ref null $c1)) + (resume $c1 (on $f $h1) (cont.new $c1 (ref.func $s))) + (return) + ) + (local.set $k) + (block $h2 (result (ref null $c1)) + (resume_throw $c1 $e (on $g $h2) (local.get $k)) + (return) + ) + (drop) + ;; the branch below reads a sidetable entry placed after the resume_throw entries + (block $done (result i32) + (i32.const 42) + (br $done) + ) + ) +) + +(assert_return (invoke "main") (i32.const 42)) diff --git a/test/regress/ext:stack-switching/resume_throw_regalloc0.bin.wast b/test/regress/ext:stack-switching/resume_throw_regalloc0.bin.wast new file mode 100644 index 000000000..aed6ec3e6 --- /dev/null +++ b/test/regress/ext:stack-switching/resume_throw_regalloc0.bin.wast @@ -0,0 +1,16 @@ +(module definition binary + "\00\61\73\6d\01\00\00\00\01\98\80\80\80\00\05\60" + "\00\01\7f\5d\00\60\02\7f\7f\00\60\00\02\7f\7f\60" + "\00\03\7f\7f\64\01\03\83\80\80\80\00\02\00\00\04" + "\85\80\80\80\00\01\63\01\00\01\0d\85\80\80\80\00" + "\02\00\02\00\02\07\88\80\80\80\00\01\04\6d\61\69" + "\6e\00\01\09\85\80\80\80\00\01\03\00\01\00\0a\dc" + "\80\80\80\00\02\8b\80\80\80\00\00\41\07\41\08\e2" + "\01\41\e3\00\0b\c6\80\80\80\00\02\01\7f\01\63\01" + "\02\7f\02\04\d2\00\e0\01\e3\01\01\00\01\00\0c\01" + "\0b\21\01\1a\1a\02\03\1f\03\01\00\00\00\20\00\41" + "\01\6a\20\00\41\02\6a\41\07\41\08\20\01\e4\01\00" + "\00\00\0b\0b\1a\1a\41\00\25\00\d1\1a\41\2a\0b\0b" +) +(module instance) +(assert_return (invoke "main") (i32.const 0x2a)) diff --git a/test/regress/ext:stack-switching/resume_throw_regalloc0.wast b/test/regress/ext:stack-switching/resume_throw_regalloc0.wast new file mode 100644 index 000000000..3bed399a8 --- /dev/null +++ b/test/regress/ext:stack-switching/resume_throw_regalloc0.wast @@ -0,0 +1,34 @@ +(module + (type $ft (func (result i32))) + (type $ct (cont $ft)) + (tag $cancel (param i32 i32)) + (tag $yield (param i32 i32)) + (table $t 1 (ref null $ct)) + + (func $worker (type $ft) (result i32) + (suspend $yield (i32.const 7) (i32.const 8)) + (i32.const 99)) + (elem declare func $worker) + + (func (export "main") (result i32) + (local $a i32) (local $k (ref null $ct)) + (block $done (result i32) + (block $on_yield (result i32 i32 (ref $ct)) + (resume $ct (on $yield $on_yield) + (cont.new $ct (ref.func $worker))) + (br $done)) + (local.set $k) (drop) (drop) + (block $on_cancel (result i32 i32) + (try_table (result i32 i32) (catch $cancel $on_cancel) + (i32.add (local.get $a) (i32.const 1)) + (i32.add (local.get $a) (i32.const 2)) + (i32.const 7) (i32.const 8) + (local.get $k) + (resume_throw $ct $cancel) + (unreachable))) + (drop) (drop) + (table.get $t (i32.const 0)) + (ref.is_null) (drop) + (i32.const 42))) +) +(assert_return (invoke "main") (i32.const 42))