Skip to content

Commit 9435b78

Browse files
committed
wasm-interp: Implement EHv4
1 parent 92a25fd commit 9435b78

File tree

10 files changed

+158
-70
lines changed

10 files changed

+158
-70
lines changed

include/wabt/interp/interp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ struct LocalDesc {
295295
struct CatchDesc {
296296
Index tag_index;
297297
u32 offset;
298+
bool ref = false;
298299
};
299300

300301
// Handlers for a catch-less `try` or `try-catch` block are included in the
@@ -313,6 +314,7 @@ struct HandlerDesc {
313314
// Local stack heights at the handler site that need to be restored.
314315
u32 values;
315316
u32 exceptions;
317+
bool catch_all_ref = false;
316318
};
317319

318320
struct FuncDesc {

include/wabt/shared-validator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class SharedValidator {
213213
Result OnThrow(const Location&, Var tag_var);
214214
Result OnThrowRef(const Location&);
215215
Result OnTry(const Location&, Type sig_type);
216-
Result BeginTryTable(const Location&);
216+
Result BeginTryTable(const Location&, Type sig_type);
217217
Result OnTryTableCatch(const Location&, const TableCatch&);
218218
Result EndTryTable(const Location&, Type sig_type);
219219
Result OnUnary(const Location&, Opcode);

include/wabt/type-checker.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,9 @@ class TypeChecker {
131131
Result OnThrowRef();
132132
Result OnTry(const TypeVector& param_types, const TypeVector& result_types);
133133
Result OnTryTableCatch(const TypeVector& sig, Index);
134-
Result OnTryTable(const TypeVector& param_types,
135-
const TypeVector& result_types);
134+
Result BeginTryTable(const TypeVector& param_types);
135+
Result EndTryTable(const TypeVector& param_types,
136+
const TypeVector& result_types);
136137
Result OnUnary(Opcode);
137138
Result OnUnreachable();
138139
Result EndFunction();

src/interp/binary-reader-interp.cc

Lines changed: 77 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ Result BinaryReaderInterp::BeginInitExpr(FuncDesc* func) {
649649
label_stack_.clear();
650650
func_ = func;
651651
func_->code_offset = istream_.end();
652-
Type type = func->type.results[0];
652+
Type type = func_->type.results[0];
653653
CHECK_RESULT(validator_.BeginInitExpr(GetLocation(), type));
654654
// Push implicit init func label (equivalent to return).
655655
PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset);
@@ -1068,6 +1068,11 @@ Result BinaryReaderInterp::OnEndExpr() {
10681068
HandlerDesc& desc = func_->handlers[local_label->handler_desc_index];
10691069
desc.try_end_offset = istream_.end();
10701070
assert(desc.catches.size() == 0);
1071+
} else if (label_type == LabelType::TryTable) {
1072+
// TryTable blocks need a try_end_offset
1073+
Label* local_label = TopLabel();
1074+
HandlerDesc& desc = func_->handlers[local_label->handler_desc_index];
1075+
desc.try_end_offset = istream_.end();
10711076
} else if (label_type == LabelType::Catch) {
10721077
istream_.EmitCatchDrop(1);
10731078
}
@@ -1553,63 +1558,77 @@ Result BinaryReaderInterp::OnTryExpr(Type sig_type) {
15531558

15541559
Result BinaryReaderInterp::OnTryTableExpr(Type sig_type,
15551560
const RawCatchVector& catches) {
1556-
CHECK_RESULT(validator_.BeginTryTable(GetLocation()));
1557-
1558-
assert(false && "NYI");
1559-
1560-
//// from loop
1561-
// PushLabel(LabelKind::Block, istream_.end());
1562-
// return Result::Ok;
1563-
1564-
//// from brtable
1565-
// Index drop_count, keep_count, catch_drop_count;
1566-
// istream_.Emit(Opcode::BrTable, num_targets);
1567-
1568-
// for (Index i = 0; i < num_targets; ++i) {
1569-
// Index depth = target_depths[i];
1570-
// CHECK_RESULT(
1571-
// validator_.OnBrTableTarget(GetLocation(), Var(depth,
1572-
// GetLocation())));
1573-
// CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count));
1574-
// CHECK_RESULT(validator_.GetCatchCount(depth, &catch_drop_count));
1575-
// // Emit DropKeep directly (instead of using EmitDropKeep) so the
1576-
// // instruction has a fixed size. Same for CatchDrop as well.
1577-
// istream_.Emit(Opcode::InterpDropKeep, drop_count, keep_count);
1578-
// istream_.Emit(Opcode::InterpCatchDrop, catch_drop_count);
1579-
// EmitBr(depth, 0, 0, 0);
1580-
// }
1581-
// CHECK_RESULT(validator_.OnBrTableTarget(
1582-
// GetLocation(), Var(default_target_depth, GetLocation())));
1583-
// CHECK_RESULT(
1584-
// GetBrDropKeepCount(default_target_depth, &drop_count, &keep_count));
1585-
// CHECK_RESULT(
1586-
// validator_.GetCatchCount(default_target_depth, &catch_drop_count));
1587-
//// The default case doesn't need a fixed size, since it is never jumped
1588-
/// over.
1589-
// istream_.EmitDropKeep(drop_count, keep_count);
1590-
// istream_.Emit(Opcode::InterpCatchDrop, catch_drop_count);
1591-
// EmitBr(default_target_depth, 0, 0, 0);
1592-
1593-
// CHECK_RESULT(validator_.EndBrTable(GetLocation()));
1594-
// return Result::Ok;
1595-
1596-
//// from try
1597-
// u32 exn_stack_height;
1598-
// CHECK_RESULT(
1599-
// validator_.GetCatchCount(label_stack_.size() - 1, &exn_stack_height));
1600-
// u32 value_stack_height = validator_.type_stack_size();
1601-
// CHECK_RESULT(validator_.OnTry(GetLocation(), sig_type));
1602-
//// Push a label that tracks mapping of exn -> catch
1603-
// PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset,
1604-
// func_->handlers.size());
1605-
// func_->handlers.push_back(HandlerDesc{HandlerKind::Catch,
1606-
// istream_.end(),
1607-
// Istream::kInvalidOffset,
1608-
// {},
1609-
// {Istream::kInvalidOffset},
1610-
// value_stack_height,
1611-
// exn_stack_height});
1612-
return Result::Error;
1561+
// we can just emit the catch handlers beforehand, so long as we skip over
1562+
// them when entering the try.
1563+
CHECK_RESULT(validator_.BeginTryTable(GetLocation(), sig_type));
1564+
1565+
u32 exn_stack_height;
1566+
CHECK_RESULT(
1567+
validator_.GetCatchCount(label_stack_.size() - 1, &exn_stack_height));
1568+
// NOTE: *NOT* GetLocalCount. we don't count the parameters, as they're not
1569+
// part of the frame.
1570+
u32 value_stack_height = validator_.type_stack_size() + local_count_;
1571+
1572+
HandlerDesc desc = HandlerDesc{HandlerKind::Catch,
1573+
Istream::kInvalidOffset,
1574+
Istream::kInvalidOffset,
1575+
{},
1576+
{Istream::kInvalidOffset},
1577+
value_stack_height,
1578+
exn_stack_height};
1579+
1580+
istream_.Emit(Opcode::Br);
1581+
auto offset = istream_.EmitFixupU32();
1582+
1583+
bool has_catch_all = false;
1584+
for (const auto& raw_catch : catches) {
1585+
TableCatch catch_;
1586+
catch_.kind = raw_catch.kind;
1587+
catch_.tag = Var(raw_catch.tag, GetLocation());
1588+
catch_.target = Var(raw_catch.depth, GetLocation());
1589+
CHECK_RESULT(validator_.OnTryTableCatch(GetLocation(), catch_));
1590+
// stop emitting handlers after catch_all - but we must still validate the
1591+
// handlers we don't emit
1592+
if (has_catch_all) {
1593+
continue;
1594+
}
1595+
if (catch_.IsCatchAll()) {
1596+
has_catch_all = true;
1597+
desc.catch_all_ref = catch_.IsRef();
1598+
desc.catch_all_offset = istream_.end();
1599+
} else {
1600+
desc.catches.push_back(CatchDesc{raw_catch.tag, istream_.end(), catch_.IsRef()});
1601+
}
1602+
// we can't use GetBrDropKeepCount because we're not in a real block.
1603+
SharedValidator::Label* vlabel;
1604+
CHECK_RESULT(validator_.GetLabel(raw_catch.depth, &vlabel));
1605+
// we keep the exception's results.
1606+
// (this has already been validated, above)
1607+
Index keep_count = vlabel->br_types().size();
1608+
// we drop everything between the current block and the br target.
1609+
// (we have already taken the TryTable block parameters into account, in
1610+
// BeginTryTable)
1611+
Index drop_count = validator_.type_stack_size() - vlabel->type_stack_limit;
1612+
Index catch_drop_count;
1613+
// we use the regular catch count
1614+
CHECK_RESULT(validator_.GetCatchCount(raw_catch.depth, &catch_drop_count));
1615+
// but increment, as we are semantically in a catch
1616+
catch_drop_count++;
1617+
EmitBr(raw_catch.depth, drop_count, keep_count, catch_drop_count);
1618+
}
1619+
1620+
CHECK_RESULT(validator_.EndTryTable(GetLocation(), sig_type));
1621+
1622+
desc.try_start_offset = istream_.end();
1623+
1624+
// as usual, the label is pushed after the catch handlers
1625+
PushLabel(LabelKind::Try, Istream::kInvalidOffset, Istream::kInvalidOffset,
1626+
func_->handlers.size());
1627+
func_->handlers.push_back(std::move(desc));
1628+
1629+
istream_.ResolveFixupU32(offset);
1630+
1631+
return Result::Ok;
16131632
}
16141633

16151634
Result BinaryReaderInterp::OnCatchExpr(Index tag_index) {

src/interp/interp.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2609,6 +2609,7 @@ RunResult Thread::DoThrow(Exception::Ptr exn) {
26092609
Tag::Ptr exn_tag{store_, exn->tag()};
26102610
bool popped_frame = false;
26112611
bool had_catch_all = false;
2612+
bool target_exnref = false;
26122613

26132614
// DoThrow is responsible for unwinding the stack at the point at which an
26142615
// exception is thrown, and also branching to the appropriate catch within
@@ -2646,6 +2647,7 @@ RunResult Thread::DoThrow(Exception::Ptr exn) {
26462647
target_offset = _catch.offset;
26472648
target_values = (*iter).values;
26482649
target_exceptions = (*iter).exceptions;
2650+
target_exnref = _catch.ref;
26492651
goto found_handler;
26502652
}
26512653
}
@@ -2654,6 +2656,7 @@ RunResult Thread::DoThrow(Exception::Ptr exn) {
26542656
target_values = (*iter).values;
26552657
target_exceptions = (*iter).exceptions;
26562658
had_catch_all = true;
2659+
target_exnref = handler.catch_all_ref;
26572660
goto found_handler;
26582661
}
26592662
}
@@ -2691,6 +2694,9 @@ RunResult Thread::DoThrow(Exception::Ptr exn) {
26912694
if (!had_catch_all) {
26922695
PushValues(exn_tag->type().signature, exn->args());
26932696
}
2697+
if (target_exnref) {
2698+
Push(exn.ref());
2699+
}
26942700
return RunResult::Ok;
26952701
}
26962702

src/shared-validator.cc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,8 +1211,12 @@ Result SharedValidator::OnTry(const Location& loc, Type sig_type) {
12111211
return result;
12121212
}
12131213

1214-
Result SharedValidator::BeginTryTable(const Location& loc) {
1214+
Result SharedValidator::BeginTryTable(const Location& loc, Type sig_type) {
12151215
Result result = CheckInstr(Opcode::TryTable, loc);
1216+
TypeVector param_types, result_types;
1217+
result |= CheckBlockSignature(loc, Opcode::TryTable, sig_type, &param_types,
1218+
&result_types);
1219+
result |= typechecker_.BeginTryTable(param_types);
12161220
return result;
12171221
}
12181222

@@ -1237,7 +1241,7 @@ Result SharedValidator::EndTryTable(const Location& loc, Type sig_type) {
12371241
TypeVector param_types, result_types;
12381242
result |= CheckBlockSignature(loc, Opcode::TryTable, sig_type, &param_types,
12391243
&result_types);
1240-
result |= typechecker_.OnTryTable(param_types, result_types);
1244+
result |= typechecker_.EndTryTable(param_types, result_types);
12411245
return result;
12421246
}
12431247

src/type-checker.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,11 @@ Result TypeChecker::OnTry(const TypeVector& param_types,
890890
return result;
891891
}
892892

893+
Result TypeChecker::BeginTryTable(const TypeVector& param_types) {
894+
Result result = PopAndCheckSignature(param_types, "try_table");
895+
return result;
896+
}
897+
893898
Result TypeChecker::OnTryTableCatch(const TypeVector& sig, Index depth) {
894899
Result result = Result::Ok;
895900
Label* label;
@@ -903,12 +908,11 @@ Result TypeChecker::OnTryTableCatch(const TypeVector& sig, Index depth) {
903908
return result;
904909
}
905910

906-
Result TypeChecker::OnTryTable(const TypeVector& param_types,
907-
const TypeVector& result_types) {
908-
Result result = PopAndCheckSignature(param_types, "try_table");
911+
Result TypeChecker::EndTryTable(const TypeVector& param_types,
912+
const TypeVector& result_types) {
909913
PushLabel(LabelType::TryTable, param_types, result_types);
910914
PushTypes(param_types);
911-
return result;
915+
return Result::Ok;
912916
}
913917

914918
Result TypeChecker::OnUnary(Opcode opcode) {

src/validator.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ Result Validator::EndTryExpr(TryExpr* expr) {
568568
}
569569

570570
Result Validator::BeginTryTableExpr(TryTableExpr* expr) {
571-
result_ |= validator_.BeginTryTable(expr->loc);
571+
result_ |= validator_.BeginTryTable(expr->loc, GetDeclarationType(expr->block.decl));
572572
for (const TableCatch& catch_ : expr->catches) {
573573
result_ |= validator_.OnTryTableCatch(expr->loc, catch_);
574574
}
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
;;; TOOL: run-interp-spec
22
;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/throw_ref.wast
33
;;; ARGS*: --enable-exceptions
4-
;;; SKIP: NYI
4+
(;; STDOUT ;;;
5+
out/test/spec/exception-handling/throw_ref.wast:99: assert_exception passed
6+
out/test/spec/exception-handling/throw_ref.wast:101: assert_exception passed
7+
out/test/spec/exception-handling/throw_ref.wast:104: assert_exception passed
8+
out/test/spec/exception-handling/throw_ref.wast:106: assert_exception passed
9+
out/test/spec/exception-handling/throw_ref.wast:108: assert_exception passed
10+
out/test/spec/exception-handling/throw_ref.wast:109: assert_exception passed
11+
out/test/spec/exception-handling/throw_ref.wast:115: assert_exception passed
12+
out/test/spec/exception-handling/throw_ref.wast:117: assert_invalid passed:
13+
out/test/spec/exception-handling/throw_ref/throw_ref.1.wasm:0000018: error: type mismatch in throw_ref, expected [exnref] but got []
14+
0000018: error: OnThrowRefExpr callback failed
15+
out/test/spec/exception-handling/throw_ref.wast:118: assert_invalid passed:
16+
out/test/spec/exception-handling/throw_ref/throw_ref.2.wasm:000001a: error: type mismatch in throw_ref, expected [exnref] but got []
17+
000001a: error: OnThrowRefExpr callback failed
18+
15/15 tests passed.
19+
;;; STDOUT ;;)
Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,41 @@
11
;;; TOOL: run-interp-spec
22
;;; STDIN_FILE: third_party/testsuite/proposals/exception-handling/try_table.wast
33
;;; ARGS*: --enable-exceptions --enable-tail-call
4-
;;; SKIP: NYI
4+
(;; STDOUT ;;;
5+
out/test/spec/exception-handling/try_table.wast:246: assert_trap passed: unreachable executed
6+
out/test/spec/exception-handling/try_table.wast:249: assert_trap passed: integer divide by zero
7+
out/test/spec/exception-handling/try_table.wast:253: assert_exception passed
8+
out/test/spec/exception-handling/try_table.wast:257: assert_exception passed
9+
out/test/spec/exception-handling/try_table.wast:294: assert_exception passed
10+
out/test/spec/exception-handling/try_table.wast:295: assert_exception passed
11+
out/test/spec/exception-handling/try_table.wast:322: assert_malformed passed:
12+
out/test/spec/exception-handling/try_table/try_table.3.wat:1:16: error: unexpected token "catch_all", expected an instr.
13+
(module (func (catch_all)))
14+
^^^^^^^^^
15+
out/test/spec/exception-handling/try_table.wast:327: assert_malformed passed:
16+
out/test/spec/exception-handling/try_table/try_table.4.wat:1:25: error: unexpected token "catch", expected an instr.
17+
(module (tag $e) (func (catch $e)))
18+
^^^^^
19+
out/test/spec/exception-handling/try_table.wast:342: assert_invalid passed:
20+
out/test/spec/exception-handling/try_table/try_table.6.wasm:000001c: error: type mismatch in try table, expected [i32] but got []
21+
000001c: error: OnEndExpr callback failed
22+
out/test/spec/exception-handling/try_table.wast:346: assert_invalid passed:
23+
out/test/spec/exception-handling/try_table/try_table.7.wasm:000001e: error: type mismatch in try table, expected [i32] but got [i64]
24+
000001e: error: OnEndExpr callback failed
25+
out/test/spec/exception-handling/try_table.wast:351: assert_invalid passed:
26+
out/test/spec/exception-handling/try_table/try_table.8.wasm:0000022: error: catch signature doesn't match target: expected [exnref], got []
27+
0000022: error: OnTryTableExpr callback failed
28+
out/test/spec/exception-handling/try_table.wast:355: assert_invalid passed:
29+
out/test/spec/exception-handling/try_table/try_table.9.wasm:0000026: error: catch signature doesn't match target: expected [], got [exnref]
30+
0000026: error: OnTryTableExpr callback failed
31+
out/test/spec/exception-handling/try_table.wast:359: assert_invalid passed:
32+
out/test/spec/exception-handling/try_table/try_table.10.wasm:000001c: error: catch signature doesn't match target: expected [exnref], got []
33+
000001c: error: OnTryTableExpr callback failed
34+
out/test/spec/exception-handling/try_table.wast:363: assert_invalid passed:
35+
out/test/spec/exception-handling/try_table/try_table.11.wasm:000001d: error: catch signature doesn't match target: expected [], got [exnref]
36+
000001d: error: OnTryTableExpr callback failed
37+
out/test/spec/exception-handling/try_table.wast:367: assert_invalid passed:
38+
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]
39+
0000028: error: OnTryTableExpr callback failed
40+
51/51 tests passed.
41+
;;; STDOUT ;;)

0 commit comments

Comments
 (0)