diff options
author | Soni L. <EnderMoneyMod@gmail.com> | 2024-11-20 19:19:56 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-20 14:19:56 -0800 |
commit | b09caf643e08ec056769a39e91c65e0bafa90551 (patch) | |
tree | 727654d04c6550b392afc98a5a9677c1a3fec747 | |
parent | a0b7abef00b59eeafed58c774195189425d020b0 (diff) | |
download | wabt-b09caf643e08ec056769a39e91c65e0bafa90551.tar.gz wabt-b09caf643e08ec056769a39e91c65e0bafa90551.tar.bz2 wabt-b09caf643e08ec056769a39e91c65e0bafa90551.zip |
interp: Implement EHv4 (#2512)
Continuation of #2470
-rw-r--r-- | include/wabt/interp/interp.h | 2 | ||||
-rw-r--r-- | src/interp/binary-reader-interp.cc | 88 | ||||
-rw-r--r-- | src/interp/interp.cc | 17 | ||||
-rw-r--r-- | src/interp/istream.cc | 4 | ||||
-rw-r--r-- | test/spec/exception-handling/throw_ref.txt | 19 | ||||
-rw-r--r-- | test/spec/exception-handling/try_table.txt | 41 |
6 files changed, 167 insertions, 4 deletions
diff --git a/include/wabt/interp/interp.h b/include/wabt/interp/interp.h index 821a5391..9ce7b181 100644 --- a/include/wabt/interp/interp.h +++ b/include/wabt/interp/interp.h @@ -296,6 +296,7 @@ struct LocalDesc { struct CatchDesc { Index tag_index; u32 offset; + bool ref = false; }; // Handlers for a catch-less `try` or `try-catch` block are included in the @@ -314,6 +315,7 @@ struct HandlerDesc { // Local stack heights at the handler site that need to be restored. u32 values; u32 exceptions; + bool catch_all_ref = false; }; struct FuncDesc { diff --git a/src/interp/binary-reader-interp.cc b/src/interp/binary-reader-interp.cc index 1d3daf8e..7e9b8d5e 100644 --- a/src/interp/binary-reader-interp.cc +++ b/src/interp/binary-reader-interp.cc @@ -241,7 +241,10 @@ class BinaryReaderInterp : public BinaryReaderNop { Result OnTableInitExpr(Index segment_index, Index table_index) override; Result OnTernaryExpr(Opcode opcode) override; Result OnThrowExpr(Index tag_index) override; + Result OnThrowRefExpr() override; Result OnTryExpr(Type sig_type) override; + Result OnTryTableExpr(Type sig_type, + const CatchClauseVector& catches) override; Result OnUnreachableExpr() override; Result EndFunctionBody(Index index) override; Result OnSimdLaneOpExpr(Opcode opcode, uint64_t value) override; @@ -1072,6 +1075,11 @@ Result BinaryReaderInterp::OnEndExpr() { HandlerDesc& desc = func_->handlers[local_label->handler_desc_index]; desc.try_end_offset = istream_.end(); assert(desc.catches.size() == 0); + } else if (label_type == LabelType::TryTable) { + // TryTable blocks need a try_end_offset + Label* local_label = TopLabel(); + HandlerDesc& desc = func_->handlers[local_label->handler_desc_index]; + desc.try_end_offset = istream_.end(); } else if (label_type == LabelType::Catch) { istream_.EmitCatchDrop(1); } @@ -1518,6 +1526,12 @@ Result BinaryReaderInterp::OnThrowExpr(Index tag_index) { return Result::Ok; } +Result BinaryReaderInterp::OnThrowRefExpr() { + CHECK_RESULT(validator_.OnThrowRef(GetLocation())); + istream_.Emit(Opcode::ThrowRef); + return Result::Ok; +} + Result BinaryReaderInterp::OnRethrowExpr(Index depth) { Index catch_depth; CHECK_RESULT(validator_.OnRethrow(GetLocation(), Var(depth, GetLocation()))); @@ -1549,6 +1563,80 @@ Result BinaryReaderInterp::OnTryExpr(Type sig_type) { return Result::Ok; } +Result BinaryReaderInterp::OnTryTableExpr(Type sig_type, + const CatchClauseVector& catches) { + // we can just emit the catch handlers beforehand, so long as we skip over + // them when entering the try. + CHECK_RESULT(validator_.BeginTryTable(GetLocation(), sig_type)); + + u32 exn_stack_height; + CHECK_RESULT( + validator_.GetCatchCount(label_stack_.size() - 1, &exn_stack_height)); + // NOTE: *NOT* GetLocalCount. we don't count the parameters, as they're not + // part of the frame. + u32 value_stack_height = validator_.type_stack_size() + local_count_; + + HandlerDesc desc = + HandlerDesc{HandlerKind::Catch, Istream::kInvalidOffset, + Istream::kInvalidOffset, {}, + {Istream::kInvalidOffset}, value_stack_height, + exn_stack_height}; + + istream_.Emit(Opcode::Br); + auto offset = istream_.EmitFixupU32(); + + bool has_catch_all = false; + for (const auto& raw_catch : catches) { + TableCatch catch_; + catch_.kind = raw_catch.kind; + catch_.tag = Var(raw_catch.tag, GetLocation()); + catch_.target = Var(raw_catch.depth, GetLocation()); + CHECK_RESULT(validator_.OnTryTableCatch(GetLocation(), catch_)); + // stop emitting handlers after catch_all - but we must still validate the + // handlers we don't emit + if (has_catch_all) { + continue; + } + if (catch_.IsCatchAll()) { + has_catch_all = true; + desc.catch_all_ref = catch_.IsRef(); + desc.catch_all_offset = istream_.end(); + } else { + desc.catches.push_back( + CatchDesc{raw_catch.tag, istream_.end(), catch_.IsRef()}); + } + // we can't use GetBrDropKeepCount because we're not in a real block. + SharedValidator::Label* vlabel; + CHECK_RESULT(validator_.GetLabel(raw_catch.depth, &vlabel)); + // we keep the exception's results. + // (this has already been validated, above) + Index keep_count = vlabel->br_types().size(); + // we drop everything between the current block and the br target. + // (we have already taken the TryTable block parameters into account, in + // BeginTryTable) + Index drop_count = validator_.type_stack_size() - vlabel->type_stack_limit; + Index catch_drop_count; + // we use the regular catch count + CHECK_RESULT(validator_.GetCatchCount(raw_catch.depth, &catch_drop_count)); + // but increment, as we are semantically in a catch + catch_drop_count++; + EmitBr(raw_catch.depth, drop_count, keep_count, catch_drop_count); + } + + CHECK_RESULT(validator_.EndTryTable(GetLocation(), sig_type)); + + desc.try_start_offset = istream_.end(); + + // as usual, the label is pushed after the catch handlers + PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset, + func_->handlers.size()); + func_->handlers.push_back(std::move(desc)); + + istream_.ResolveFixupU32(offset); + + return Result::Ok; +} + Result BinaryReaderInterp::OnCatchExpr(Index tag_index) { CHECK_RESULT( validator_.OnCatch(GetLocation(), Var(tag_index, GetLocation()), false)); diff --git a/src/interp/interp.cc b/src/interp/interp.cc index 800d81f3..f8fc5da9 100644 --- a/src/interp/interp.cc +++ b/src/interp/interp.cc @@ -1959,6 +1959,14 @@ RunResult Thread::StepInternal(Trap::Ptr* out_trap) { exceptions_[exceptions_.size() - exn_index - 1]}; return DoThrow(exn); } + case O::ThrowRef: { + Ref ref = Pop<Ref>(); + assert(store_.HasValueType(ref, ValueType::ExnRef)); + // FIXME better error message? + TRAP_IF(ref == Ref::Null, "expected exnref, got null"); + Exception::Ptr exn = store_.UnsafeGet<Exception>(ref); + return DoThrow(exn); + } // The following opcodes are either never generated or should never be // executed. @@ -1973,13 +1981,12 @@ RunResult Thread::StepInternal(Trap::Ptr* out_trap) { case O::CallRef: case O::Try: + case O::TryTable: case O::Catch: case O::CatchAll: case O::Delegate: case O::InterpData: case O::Invalid: - case O::TryTable: - case O::ThrowRef: WABT_UNREACHABLE; break; } @@ -2616,6 +2623,7 @@ RunResult Thread::DoThrow(Exception::Ptr exn) { Tag::Ptr exn_tag{store_, exn->tag()}; bool popped_frame = false; bool had_catch_all = false; + bool target_exnref = false; // DoThrow is responsible for unwinding the stack at the point at which an // exception is thrown, and also branching to the appropriate catch within @@ -2653,6 +2661,7 @@ RunResult Thread::DoThrow(Exception::Ptr exn) { target_offset = _catch.offset; target_values = (*iter).values; target_exceptions = (*iter).exceptions; + target_exnref = _catch.ref; goto found_handler; } } @@ -2661,6 +2670,7 @@ RunResult Thread::DoThrow(Exception::Ptr exn) { target_values = (*iter).values; target_exceptions = (*iter).exceptions; had_catch_all = true; + target_exnref = handler.catch_all_ref; goto found_handler; } } @@ -2698,6 +2708,9 @@ found_handler: if (!had_catch_all) { PushValues(exn_tag->type().signature, exn->args()); } + if (target_exnref) { + Push(exn.ref()); + } return RunResult::Ok; } diff --git a/src/interp/istream.cc b/src/interp/istream.cc index 25627ba2..65e3c15d 100644 --- a/src/interp/istream.cc +++ b/src/interp/istream.cc @@ -124,6 +124,7 @@ Instr Istream::Read(Offset* offset) const { case Opcode::Nop: case Opcode::Return: case Opcode::Unreachable: + case Opcode::ThrowRef: case Opcode::RefNull: // 0 immediates, 0 operands. instr.kind = InstrKind::Imm_0_Op_0; @@ -793,9 +794,8 @@ Instr Istream::Read(Offset* offset) const { case Opcode::Invalid: case Opcode::Loop: case Opcode::Try: - case Opcode::ReturnCall: case Opcode::TryTable: - case Opcode::ThrowRef: + case Opcode::ReturnCall: // Not used. break; } diff --git a/test/spec/exception-handling/throw_ref.txt b/test/spec/exception-handling/throw_ref.txt new file mode 100644 index 00000000..eed75517 --- /dev/null +++ b/test/spec/exception-handling/throw_ref.txt @@ -0,0 +1,19 @@ +;;; TOOL: run-interp-spec +;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/throw_ref.wast +;;; ARGS*: --enable-exceptions +(;; STDOUT ;;; +out/test/spec/exception-handling/throw_ref.wast:99: assert_exception passed +out/test/spec/exception-handling/throw_ref.wast:101: assert_exception passed +out/test/spec/exception-handling/throw_ref.wast:104: assert_exception passed +out/test/spec/exception-handling/throw_ref.wast:106: assert_exception passed +out/test/spec/exception-handling/throw_ref.wast:108: assert_exception passed +out/test/spec/exception-handling/throw_ref.wast:109: assert_exception passed +out/test/spec/exception-handling/throw_ref.wast:115: assert_exception passed +out/test/spec/exception-handling/throw_ref.wast:117: assert_invalid passed: + out/test/spec/exception-handling/throw_ref/throw_ref.1.wasm:0000018: error: type mismatch in throw_ref, expected [exnref] but got [] + 0000018: error: OnThrowRefExpr callback failed +out/test/spec/exception-handling/throw_ref.wast:118: assert_invalid passed: + out/test/spec/exception-handling/throw_ref/throw_ref.2.wasm:000001a: error: type mismatch in throw_ref, expected [exnref] but got [] + 000001a: error: OnThrowRefExpr callback failed +15/15 tests passed. +;;; STDOUT ;;) diff --git a/test/spec/exception-handling/try_table.txt b/test/spec/exception-handling/try_table.txt new file mode 100644 index 00000000..6e3414bb --- /dev/null +++ b/test/spec/exception-handling/try_table.txt @@ -0,0 +1,41 @@ +;;; TOOL: run-interp-spec +;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/try_table.wast +;;; ARGS*: --enable-exceptions --enable-tail-call +(;; STDOUT ;;; +out/test/spec/exception-handling/try_table.wast:261: assert_trap passed: unreachable executed +out/test/spec/exception-handling/try_table.wast:264: assert_trap passed: integer divide by zero +out/test/spec/exception-handling/try_table.wast:268: assert_exception passed +out/test/spec/exception-handling/try_table.wast:272: assert_exception passed +out/test/spec/exception-handling/try_table.wast:310: assert_exception passed +out/test/spec/exception-handling/try_table.wast:311: assert_exception passed +out/test/spec/exception-handling/try_table.wast:340: assert_malformed passed: + out/test/spec/exception-handling/try_table/try_table.3.wat:1:16: error: unexpected token "catch_all", expected an instr. + (module (func (catch_all))) + ^^^^^^^^^ +out/test/spec/exception-handling/try_table.wast:345: assert_malformed passed: + out/test/spec/exception-handling/try_table/try_table.4.wat:1:25: error: unexpected token "catch", expected an instr. + (module (tag $e) (func (catch $e))) + ^^^^^ +out/test/spec/exception-handling/try_table.wast:360: assert_invalid passed: + out/test/spec/exception-handling/try_table/try_table.6.wasm:000001c: error: type mismatch in try table, expected [i32] but got [] + 000001c: error: OnEndExpr callback failed +out/test/spec/exception-handling/try_table.wast:364: assert_invalid passed: + out/test/spec/exception-handling/try_table/try_table.7.wasm:000001e: error: type mismatch in try table, expected [i32] but got [i64] + 000001e: error: OnEndExpr callback failed +out/test/spec/exception-handling/try_table.wast:369: assert_invalid passed: + out/test/spec/exception-handling/try_table/try_table.8.wasm:0000022: error: catch signature doesn't match target: expected [exnref], got [] + 0000022: error: OnTryTableExpr callback failed +out/test/spec/exception-handling/try_table.wast:373: assert_invalid passed: + out/test/spec/exception-handling/try_table/try_table.9.wasm:0000026: error: catch signature doesn't match target: expected [], got [exnref] + 0000026: error: OnTryTableExpr callback failed +out/test/spec/exception-handling/try_table.wast:377: assert_invalid passed: + out/test/spec/exception-handling/try_table/try_table.10.wasm:000001c: error: catch signature doesn't match target: expected [exnref], got [] + 000001c: error: OnTryTableExpr callback failed +out/test/spec/exception-handling/try_table.wast:381: assert_invalid passed: + out/test/spec/exception-handling/try_table/try_table.11.wasm:000001d: error: catch signature doesn't match target: expected [], got [exnref] + 000001d: error: OnTryTableExpr callback failed +out/test/spec/exception-handling/try_table.wast:385: assert_invalid passed: + out/test/spec/exception-handling/try_table/try_table.12.wasm:0000028: error: catch signature doesn't match target: expected [i64, exnref], got [i32, exnref] + 0000028: error: OnTryTableExpr callback failed +53/53 tests passed. +;;; STDOUT ;;) |