Skip to content

Commit

Permalink
wasm-interp: Implement EHv4
Browse files Browse the repository at this point in the history
  • Loading branch information
SoniEx2 committed Oct 8, 2024
1 parent 9fe9fad commit ad9a8a6
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 70 deletions.
2 changes: 2 additions & 0 deletions include/wabt/interp/interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,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
Expand All @@ -313,6 +314,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 {
Expand Down
2 changes: 1 addition & 1 deletion include/wabt/shared-validator.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ class SharedValidator {
Result OnThrow(const Location&, Var tag_var);
Result OnThrowRef(const Location&);
Result OnTry(const Location&, Type sig_type);
Result BeginTryTable(const Location&);
Result BeginTryTable(const Location&, Type sig_type);
Result OnTryTableCatch(const Location&, const TableCatch&);
Result EndTryTable(const Location&, Type sig_type);
Result OnUnary(const Location&, Opcode);
Expand Down
5 changes: 3 additions & 2 deletions include/wabt/type-checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,9 @@ class TypeChecker {
Result OnThrowRef();
Result OnTry(const TypeVector& param_types, const TypeVector& result_types);
Result OnTryTableCatch(const TypeVector& sig, Index);
Result OnTryTable(const TypeVector& param_types,
const TypeVector& result_types);
Result BeginTryTable(const TypeVector& param_types);
Result EndTryTable(const TypeVector& param_types,
const TypeVector& result_types);
Result OnUnary(Opcode);
Result OnUnreachable();
Result EndFunction();
Expand Down
135 changes: 77 additions & 58 deletions src/interp/binary-reader-interp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ Result BinaryReaderInterp::BeginInitExpr(FuncDesc* func) {
label_stack_.clear();
func_ = func;
func_->code_offset = istream_.end();
Type type = func->type.results[0];
Type type = func_->type.results[0];
CHECK_RESULT(validator_.BeginInitExpr(GetLocation(), type));
// Push implicit init func label (equivalent to return).
PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset);
Expand Down Expand Up @@ -1068,6 +1068,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);
}
Expand Down Expand Up @@ -1553,63 +1558,77 @@ Result BinaryReaderInterp::OnTryExpr(Type sig_type) {

Result BinaryReaderInterp::OnTryTableExpr(Type sig_type,
const RawCatchVector& catches) {
CHECK_RESULT(validator_.BeginTryTable(GetLocation()));

assert(false && "NYI");

//// from loop
// PushLabel(LabelKind::Block, istream_.end());
// return Result::Ok;

//// from brtable
// Index drop_count, keep_count, catch_drop_count;
// istream_.Emit(Opcode::BrTable, num_targets);

// for (Index i = 0; i < num_targets; ++i) {
// Index depth = target_depths[i];
// CHECK_RESULT(
// validator_.OnBrTableTarget(GetLocation(), Var(depth,
// GetLocation())));
// CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
// CHECK_RESULT(validator_.GetCatchCount(depth, &catch_drop_count));
// // Emit DropKeep directly (instead of using EmitDropKeep) so the
// // instruction has a fixed size. Same for CatchDrop as well.
// istream_.Emit(Opcode::InterpDropKeep, drop_count, keep_count);
// istream_.Emit(Opcode::InterpCatchDrop, catch_drop_count);
// EmitBr(depth, 0, 0, 0);
// }
// CHECK_RESULT(validator_.OnBrTableTarget(
// GetLocation(), Var(default_target_depth, GetLocation())));
// CHECK_RESULT(
// GetBrDropKeepCount(default_target_depth, &drop_count, &keep_count));
// CHECK_RESULT(
// validator_.GetCatchCount(default_target_depth, &catch_drop_count));
//// The default case doesn't need a fixed size, since it is never jumped
/// over.
// istream_.EmitDropKeep(drop_count, keep_count);
// istream_.Emit(Opcode::InterpCatchDrop, catch_drop_count);
// EmitBr(default_target_depth, 0, 0, 0);

// CHECK_RESULT(validator_.EndBrTable(GetLocation()));
// return Result::Ok;

//// from try
// u32 exn_stack_height;
// CHECK_RESULT(
// validator_.GetCatchCount(label_stack_.size() - 1, &exn_stack_height));
// u32 value_stack_height = validator_.type_stack_size();
// CHECK_RESULT(validator_.OnTry(GetLocation(), sig_type));
//// Push a label that tracks mapping of exn -> catch
// PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset,
// func_->handlers.size());
// func_->handlers.push_back(HandlerDesc{HandlerKind::Catch,
// istream_.end(),
// Istream::kInvalidOffset,
// {},
// {Istream::kInvalidOffset},
// value_stack_height,
// exn_stack_height});
return Result::Error;
// 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) {
Expand Down
6 changes: 6 additions & 0 deletions src/interp/interp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2609,6 +2609,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
Expand Down Expand Up @@ -2646,6 +2647,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;
}
}
Expand All @@ -2654,6 +2656,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;
}
}
Expand Down Expand Up @@ -2691,6 +2694,9 @@ RunResult Thread::DoThrow(Exception::Ptr exn) {
if (!had_catch_all) {
PushValues(exn_tag->type().signature, exn->args());
}
if (target_exnref) {
Push(exn.ref());
}
return RunResult::Ok;
}

Expand Down
8 changes: 6 additions & 2 deletions src/shared-validator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1211,8 +1211,12 @@ Result SharedValidator::OnTry(const Location& loc, Type sig_type) {
return result;
}

Result SharedValidator::BeginTryTable(const Location& loc) {
Result SharedValidator::BeginTryTable(const Location& loc, Type sig_type) {
Result result = CheckInstr(Opcode::TryTable, loc);
TypeVector param_types, result_types;
result |= CheckBlockSignature(loc, Opcode::TryTable, sig_type, &param_types,
&result_types);
result |= typechecker_.BeginTryTable(param_types);
return result;
}

Expand All @@ -1237,7 +1241,7 @@ Result SharedValidator::EndTryTable(const Location& loc, Type sig_type) {
TypeVector param_types, result_types;
result |= CheckBlockSignature(loc, Opcode::TryTable, sig_type, &param_types,
&result_types);
result |= typechecker_.OnTryTable(param_types, result_types);
result |= typechecker_.EndTryTable(param_types, result_types);
return result;
}

Expand Down
12 changes: 8 additions & 4 deletions src/type-checker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,11 @@ Result TypeChecker::OnTry(const TypeVector& param_types,
return result;
}

Result TypeChecker::BeginTryTable(const TypeVector& param_types) {
Result result = PopAndCheckSignature(param_types, "try_table");
return result;
}

Result TypeChecker::OnTryTableCatch(const TypeVector& sig, Index depth) {
Result result = Result::Ok;
Label* label;
Expand All @@ -903,12 +908,11 @@ Result TypeChecker::OnTryTableCatch(const TypeVector& sig, Index depth) {
return result;
}

Result TypeChecker::OnTryTable(const TypeVector& param_types,
const TypeVector& result_types) {
Result result = PopAndCheckSignature(param_types, "try_table");
Result TypeChecker::EndTryTable(const TypeVector& param_types,
const TypeVector& result_types) {
PushLabel(LabelType::TryTable, param_types, result_types);
PushTypes(param_types);
return result;
return Result::Ok;
}

Result TypeChecker::OnUnary(Opcode opcode) {
Expand Down
2 changes: 1 addition & 1 deletion src/validator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ Result Validator::EndTryExpr(TryExpr* expr) {
}

Result Validator::BeginTryTableExpr(TryTableExpr* expr) {
result_ |= validator_.BeginTryTable(expr->loc);
result_ |= validator_.BeginTryTable(expr->loc, GetDeclarationType(expr->block.decl));
for (const TableCatch& catch_ : expr->catches) {
result_ |= validator_.OnTryTableCatch(expr->loc, catch_);
}
Expand Down
17 changes: 16 additions & 1 deletion test/spec/exception-handling/throw_ref.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
;;; TOOL: run-interp-spec
;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/throw_ref.wast
;;; ARGS*: --enable-exceptions
;;; SKIP: NYI
(;; 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 ;;)
39 changes: 38 additions & 1 deletion test/spec/exception-handling/try_table.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,41 @@
;;; TOOL: run-interp-spec
;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/try_table.wast
;;; ARGS*: --enable-exceptions --enable-tail-call
;;; SKIP: NYI
(;; STDOUT ;;;
out/test/spec/exception-handling/try_table.wast:246: assert_trap passed: unreachable executed
out/test/spec/exception-handling/try_table.wast:249: assert_trap passed: integer divide by zero
out/test/spec/exception-handling/try_table.wast:253: assert_exception passed
out/test/spec/exception-handling/try_table.wast:257: assert_exception passed
out/test/spec/exception-handling/try_table.wast:294: assert_exception passed
out/test/spec/exception-handling/try_table.wast:295: assert_exception passed
out/test/spec/exception-handling/try_table.wast:322: 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:327: 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:342: 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:346: 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:351: 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:355: 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:359: 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:363: 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:367: 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
51/51 tests passed.
;;; STDOUT ;;)

0 comments on commit ad9a8a6

Please sign in to comment.