summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSoni L. <EnderMoneyMod@gmail.com>2024-11-20 19:19:56 -0300
committerGitHub <noreply@github.com>2024-11-20 14:19:56 -0800
commitb09caf643e08ec056769a39e91c65e0bafa90551 (patch)
tree727654d04c6550b392afc98a5a9677c1a3fec747
parenta0b7abef00b59eeafed58c774195189425d020b0 (diff)
downloadwabt-b09caf643e08ec056769a39e91c65e0bafa90551.tar.gz
wabt-b09caf643e08ec056769a39e91c65e0bafa90551.tar.bz2
wabt-b09caf643e08ec056769a39e91c65e0bafa90551.zip
interp: Implement EHv4 (#2512)
Continuation of #2470
-rw-r--r--include/wabt/interp/interp.h2
-rw-r--r--src/interp/binary-reader-interp.cc88
-rw-r--r--src/interp/interp.cc17
-rw-r--r--src/interp/istream.cc4
-rw-r--r--test/spec/exception-handling/throw_ref.txt19
-rw-r--r--test/spec/exception-handling/try_table.txt41
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 ;;)