summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/gen-s-parser.py4
-rw-r--r--src/gen-s-parser.inc47
-rw-r--r--src/ir/ExpressionAnalyzer.cpp2
-rw-r--r--src/ir/ExpressionManipulator.cpp2
-rw-r--r--src/ir/LocalStructuralDominance.cpp8
-rw-r--r--src/ir/ReFinalize.cpp2
-rw-r--r--src/ir/branch-utils.h14
-rw-r--r--src/ir/cost.h5
-rw-r--r--src/ir/effects.h10
-rw-r--r--src/ir/possible-contents.cpp5
-rw-r--r--src/ir/properties.h6
-rw-r--r--src/ir/subtype-exprs.h2
-rw-r--r--src/parser/parsers.h5
-rw-r--r--src/passes/Print.cpp34
-rw-r--r--src/passes/TypeGeneralizing.cpp2
-rw-r--r--src/wasm-binary.h12
-rw-r--r--src/wasm-builder.h25
-rw-r--r--src/wasm-delegations-fields.def48
-rw-r--r--src/wasm-delegations.def2
-rw-r--r--src/wasm-interpreter.h2
-rw-r--r--src/wasm-ir-builder.h2
-rw-r--r--src/wasm-s-parser.h3
-rw-r--r--src/wasm-stack.h11
-rw-r--r--src/wasm.h45
-rw-r--r--src/wasm/parsing.cpp5
-rw-r--r--src/wasm/wasm-binary.cpp69
-rw-r--r--src/wasm/wasm-ir-builder.cpp4
-rw-r--r--src/wasm/wasm-s-parser.cpp68
-rw-r--r--src/wasm/wasm-stack.cpp30
-rw-r--r--src/wasm/wasm-validator.cpp13
-rw-r--r--src/wasm/wasm.cpp41
-rw-r--r--src/wasm2js.h8
-rw-r--r--test/binaryen.js/exception-handling.js.txt4
-rw-r--r--test/binaryen.js/kitchen-sink.js.txt66
-rw-r--r--test/lit/basic/exception-handling.wast651
35 files changed, 1185 insertions, 72 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py
index 0dfe220b5..f716302d0 100755
--- a/scripts/gen-s-parser.py
+++ b/scripts/gen-s-parser.py
@@ -554,8 +554,10 @@ instructions = [
#
# exception handling instructions
("try", "makeTry(s)"),
+ ("try_table", "makeTryTable(s)"),
("throw", "makeThrow(s)"),
("rethrow", "makeRethrow(s)"),
+ ("throw_ref", "makeThrowRef(s)"),
# Multivalue pseudoinstructions
("tuple.make", "makeTupleMake(s)"),
("tuple.extract", "makeTupleExtract(s)"),
@@ -714,7 +716,7 @@ def instruction_parser(new_parser=False):
inst_length = 0
for inst, expr in instructions:
if new_parser and inst in {"block", "loop", "if", "try", "then",
- "else"}:
+ "else", "try_table"}:
# These are either control flow handled manually or not real
# instructions. Skip them.
continue
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index bc0a03dd3..2c284d561 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -3393,15 +3393,31 @@ switch (buf[0]) {
case 'e':
if (op == "then"sv) { return makeThenOrElse(s); }
goto parse_error;
- case 'r':
- if (op == "throw"sv) { return makeThrow(s); }
+ case 'r': {
+ switch (buf[5]) {
+ case '\0':
+ if (op == "throw"sv) { return makeThrow(s); }
+ goto parse_error;
+ case '_':
+ if (op == "throw_ref"sv) { return makeThrowRef(s); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
+ default: goto parse_error;
+ }
+ }
+ case 'r': {
+ switch (buf[3]) {
+ case '\0':
+ if (op == "try"sv) { return makeTry(s); }
+ goto parse_error;
+ case '_':
+ if (op == "try_table"sv) { return makeTryTable(s); }
goto parse_error;
default: goto parse_error;
}
}
- case 'r':
- if (op == "try"sv) { return makeTry(s); }
- goto parse_error;
case 'u': {
switch (buf[6]) {
case 'd':
@@ -8646,12 +8662,23 @@ switch (buf[0]) {
default: goto parse_error;
}
}
- case 'h':
- if (op == "throw"sv) {
- CHECK_ERR(makeThrow(ctx, pos));
- return Ok{};
+ case 'h': {
+ switch (buf[5]) {
+ case '\0':
+ if (op == "throw"sv) {
+ CHECK_ERR(makeThrow(ctx, pos));
+ return Ok{};
+ }
+ goto parse_error;
+ case '_':
+ if (op == "throw_ref"sv) {
+ CHECK_ERR(makeThrowRef(ctx, pos));
+ return Ok{};
+ }
+ goto parse_error;
+ default: goto parse_error;
}
- goto parse_error;
+ }
case 'u': {
switch (buf[6]) {
case 'd':
diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp
index 62ad3ed3e..a75abd6ce 100644
--- a/src/ir/ExpressionAnalyzer.cpp
+++ b/src/ir/ExpressionAnalyzer.cpp
@@ -205,7 +205,9 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left,
}
#define DELEGATE_FIELD_INT_ARRAY(id, field) COMPARE_LIST(field)
+#define DELEGATE_FIELD_INT_VECTOR(id, field) COMPARE_LIST(field)
#define DELEGATE_FIELD_NAME_VECTOR(id, field) COMPARE_LIST(field)
+#define DELEGATE_FIELD_TYPE_VECTOR(id, field) COMPARE_LIST(field)
#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) \
if (castLeft->field.is() != castRight->field.is()) { \
diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp
index 9232dddf2..4cac87a01 100644
--- a/src/ir/ExpressionManipulator.cpp
+++ b/src/ir/ExpressionManipulator.cpp
@@ -94,8 +94,10 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) {
#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) COPY_VECTOR(field)
#define DELEGATE_FIELD_NAME_VECTOR(id, field) COPY_VECTOR(field)
+#define DELEGATE_FIELD_TYPE_VECTOR(id, field) COPY_VECTOR(field)
#define DELEGATE_FIELD_INT_ARRAY(id, field) COPY_ARRAY(field)
+#define DELEGATE_FIELD_INT_VECTOR(id, field) COPY_VECTOR(field)
#include "wasm-delegations-fields.def"
diff --git a/src/ir/LocalStructuralDominance.cpp b/src/ir/LocalStructuralDominance.cpp
index 183ca7b3f..37cd7f4ba 100644
--- a/src/ir/LocalStructuralDominance.cpp
+++ b/src/ir/LocalStructuralDominance.cpp
@@ -207,6 +207,14 @@ LocalStructuralDominance::LocalStructuralDominance(Function* func,
currp = &curr->cast<Try>()->body;
continue;
}
+ case Expression::Id::TryTableId: {
+ self->pushTask(Scanner::doEndScope, currp);
+ // Just call the task immediately.
+ doBeginScope(self, currp);
+ // Immediately continue in the try_table.
+ currp = &curr->cast<TryTable>()->body;
+ continue;
+ }
default: {
// Control flow structures have been handled. This is an expression,
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
index 3d74b1422..65f5c4724 100644
--- a/src/ir/ReFinalize.cpp
+++ b/src/ir/ReFinalize.cpp
@@ -127,8 +127,10 @@ void ReFinalize::visitTableGrow(TableGrow* curr) { curr->finalize(); }
void ReFinalize::visitTableFill(TableFill* curr) { curr->finalize(); }
void ReFinalize::visitTableCopy(TableCopy* curr) { curr->finalize(); }
void ReFinalize::visitTry(Try* curr) { curr->finalize(); }
+void ReFinalize::visitTryTable(TryTable* curr) { curr->finalize(); }
void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); }
void ReFinalize::visitRethrow(Rethrow* curr) { curr->finalize(); }
+void ReFinalize::visitThrowRef(ThrowRef* curr) { curr->finalize(); }
void ReFinalize::visitNop(Nop* curr) { curr->finalize(); }
void ReFinalize::visitUnreachable(Unreachable* curr) { curr->finalize(); }
void ReFinalize::visitPop(Pop* curr) { curr->finalize(); }
diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h
index 9598e2830..3c0275701 100644
--- a/src/ir/branch-utils.h
+++ b/src/ir/branch-utils.h
@@ -75,6 +75,13 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) {
func(name, sw->value ? sw->value->type : Type::none);
} else if (auto* br = expr->dynCast<BrOn>()) {
func(name, br->getSentType());
+ } else if (auto* tt = expr->dynCast<TryTable>()) {
+ for (Index i = 0; i < tt->catchTags.size(); i++) {
+ auto dest = tt->catchDests[i];
+ if (dest == name) {
+ func(name, tt->sentTypes[i]);
+ }
+ }
} else {
assert(expr->is<Try>() || expr->is<Rethrow>()); // delegate or rethrow
}
@@ -82,7 +89,8 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) {
}
// Similar to operateOnScopeNameUses, but also passes in the expression that is
-// sent if the branch is taken. nullptr is given if there is no value.
+// sent if the branch is taken. nullptr is given if there is no value or there
+// is a value but it is not known statically.
template<typename T>
void operateOnScopeNameUsesAndSentValues(Expression* expr, T func) {
operateOnScopeNameUses(expr, [&](Name& name) {
@@ -94,6 +102,10 @@ void operateOnScopeNameUsesAndSentValues(Expression* expr, T func) {
func(name, sw->value);
} else if (auto* br = expr->dynCast<BrOn>()) {
func(name, br->ref);
+ } else if (auto* tt = expr->dynCast<TryTable>()) {
+ // The values are supplied by throwing instructions, so we are unable to
+ // know what they will be here.
+ func(name, nullptr);
} else {
assert(expr->is<Try>() || expr->is<Rethrow>()); // delegate or rethrow
}
diff --git a/src/ir/cost.h b/src/ir/cost.h
index d3a483535..0e87317a6 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -584,6 +584,10 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
// We assume no exception will be thrown in most cases
return visit(curr->body);
}
+ CostType visitTryTable(TryTable* curr) {
+ // We assume no exception will be thrown in most cases
+ return visit(curr->body);
+ }
CostType visitThrow(Throw* curr) {
CostType ret = Unacceptable;
for (auto* child : curr->operands) {
@@ -592,6 +596,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
return ret;
}
CostType visitRethrow(Rethrow* curr) { return Unacceptable; }
+ CostType visitThrowRef(ThrowRef* curr) { return Unacceptable; }
CostType visitTupleMake(TupleMake* curr) {
CostType ret = 0;
for (auto* child : curr->operands) {
diff --git a/src/ir/effects.h b/src/ir/effects.h
index 0e45ec70c..1156cd823 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -706,6 +706,11 @@ private:
parent.delegateTargets.insert(curr->delegateTarget);
}
}
+ void visitTryTable(TryTable* curr) {
+ for (auto name : curr->catchDests) {
+ parent.breakTargets.insert(name);
+ }
+ }
void visitThrow(Throw* curr) {
if (parent.tryDepth == 0) {
parent.throws_ = true;
@@ -715,6 +720,11 @@ private:
if (parent.tryDepth == 0) {
parent.throws_ = true;
}
+ }
+ void visitThrowRef(ThrowRef* curr) {
+ if (parent.tryDepth == 0) {
+ parent.throws_ = true;
+ }
// traps when the arg is null
parent.implicitTrap = true;
}
diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp
index e7e0cd4fd..67b33553d 100644
--- a/src/ir/possible-contents.cpp
+++ b/src/ir/possible-contents.cpp
@@ -1152,6 +1152,10 @@ struct InfoCollector
#endif
}
}
+ void visitTryTable(TryTable* curr) {
+ // TODO: optimize when possible
+ addRoot(curr);
+ }
void visitThrow(Throw* curr) {
auto& operands = curr->operands;
if (!isRelevant(operands)) {
@@ -1165,6 +1169,7 @@ struct InfoCollector
}
}
void visitRethrow(Rethrow* curr) {}
+ void visitThrowRef(ThrowRef* curr) {}
void visitTupleMake(TupleMake* curr) {
if (isRelevant(curr->type)) {
diff --git a/src/ir/properties.h b/src/ir/properties.h
index 914a26919..301f15e16 100644
--- a/src/ir/properties.h
+++ b/src/ir/properties.h
@@ -59,7 +59,7 @@ inline bool isSymmetric(Binary* binary) {
inline bool isControlFlowStructure(Expression* curr) {
return curr->is<Block>() || curr->is<If>() || curr->is<Loop>() ||
- curr->is<Try>();
+ curr->is<Try>() || curr->is<TryTable>();
}
// Check if an expression is a control flow construct with a name, which implies
@@ -475,8 +475,8 @@ inline bool isResultFallthrough(Expression* curr) {
// unreachable, for example, but then there is no meaningful answer to give
// anyhow.
return curr->is<LocalSet>() || curr->is<Block>() || curr->is<If>() ||
- curr->is<Loop>() || curr->is<Try>() || curr->is<Select>() ||
- curr->is<Break>();
+ curr->is<Loop>() || curr->is<Try>() || curr->is<TryTable>() ||
+ curr->is<Select>() || curr->is<Break>();
}
inline bool canEmitSelectWithArms(Expression* ifTrue, Expression* ifFalse) {
diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h
index dc9ea1432..86805f88a 100644
--- a/src/ir/subtype-exprs.h
+++ b/src/ir/subtype-exprs.h
@@ -212,6 +212,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
self()->noteSubtype(body, curr);
}
}
+ void visitTryTable(TryTable* curr) { self()->noteSubtype(curr->body, curr); }
void visitThrow(Throw* curr) {
Type params = self()->getModule()->getTag(curr->tag)->sig.params;
assert(params.size() == curr->operands.size());
@@ -220,6 +221,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
}
}
void visitRethrow(Rethrow* curr) {}
+ void visitThrowRef(ThrowRef* curr) {}
void visitTupleMake(TupleMake* curr) {}
void visitTupleExtract(TupleExtract* curr) {}
void visitRefI31(RefI31* curr) {}
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
index 54644cafc..7f4005c3d 100644
--- a/src/parser/parsers.h
+++ b/src/parser/parsers.h
@@ -118,6 +118,7 @@ template<typename Ctx> Result<> makeTableFill(Ctx&, Index);
template<typename Ctx> Result<> makeTableCopy(Ctx&, Index);
template<typename Ctx> Result<> makeThrow(Ctx&, Index);
template<typename Ctx> Result<> makeRethrow(Ctx&, Index);
+template<typename Ctx> Result<> makeThrowRef(Ctx&, Index);
template<typename Ctx> Result<> makeTupleMake(Ctx&, Index);
template<typename Ctx> Result<> makeTupleExtract(Ctx&, Index);
template<typename Ctx> Result<> makeTupleDrop(Ctx&, Index);
@@ -1525,6 +1526,10 @@ template<typename Ctx> Result<> makeRethrow(Ctx& ctx, Index pos) {
return ctx.makeRethrow(pos, *label);
}
+template<typename Ctx> Result<> makeThrowRef(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
template<typename Ctx> Result<> makeTupleMake(Ctx& ctx, Index pos) {
auto arity = tupleArity(ctx);
CHECK_ERR(arity);
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 3feebd121..71181566d 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -299,6 +299,7 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
void visitIf(If* curr);
void visitLoop(Loop* curr);
void visitTry(Try* curr);
+ void visitTryTable(TryTable* curr);
void maybePrintUnreachableReplacement(Expression* curr, Type type);
void maybePrintUnreachableOrNullReplacement(Expression* curr, Type type);
void visitCallRef(CallRef* curr) {
@@ -1974,6 +1975,25 @@ struct PrintExpressionContents
printBlockType(Signature(Type::none, curr->type));
}
}
+ void visitTryTable(TryTable* curr) {
+ printMedium(o, "try_table");
+ if (curr->type.isConcrete()) {
+ o << ' ';
+ printBlockType(Signature(Type::none, curr->type));
+ }
+ for (Index i = 0; i < curr->catchTags.size(); i++) {
+ o << " (";
+ if (curr->catchTags[i]) {
+ printMedium(o, curr->catchRefs[i] ? "catch_ref " : "catch ");
+ printName(curr->catchTags[i], o);
+ o << ' ';
+ } else {
+ printMedium(o, curr->catchRefs[i] ? "catch_all_ref " : "catch_all ");
+ }
+ printName(curr->catchDests[i], o);
+ o << ')';
+ }
+ }
void visitThrow(Throw* curr) {
printMedium(o, "throw ");
printName(curr->tag, o);
@@ -1982,6 +2002,7 @@ struct PrintExpressionContents
printMedium(o, "rethrow ");
printName(curr->target, o);
}
+ void visitThrowRef(ThrowRef* curr) { printMedium(o, "throw_ref "); }
void visitNop(Nop* curr) { printMinor(o, "nop"); }
void visitUnreachable(Unreachable* curr) { printMinor(o, "unreachable"); }
void visitPop(Pop* curr) {
@@ -2728,6 +2749,19 @@ void PrintSExpression::visitTry(Try* curr) {
}
}
+void PrintSExpression::visitTryTable(TryTable* curr) {
+ controlFlowDepth++;
+ o << '(';
+ printExpressionContents(curr);
+ incIndent();
+ maybePrintImplicitBlock(curr->body, true);
+ decIndent();
+ if (full) {
+ o << " ;; end if";
+ }
+ controlFlowDepth--;
+}
+
void PrintSExpression::maybePrintUnreachableReplacement(Expression* curr,
Type type) {
// See the parallel function
diff --git a/src/passes/TypeGeneralizing.cpp b/src/passes/TypeGeneralizing.cpp
index 6b9df6510..d167b89a9 100644
--- a/src/passes/TypeGeneralizing.cpp
+++ b/src/passes/TypeGeneralizing.cpp
@@ -499,8 +499,10 @@ struct TransferFn : OverriddenVisitor<TransferFn> {
}
void visitTry(Try* curr) { WASM_UNREACHABLE("TODO"); }
+ void visitTryTable(TryTable* curr) { WASM_UNREACHABLE("TODO"); }
void visitThrow(Throw* curr) { WASM_UNREACHABLE("TODO"); }
void visitRethrow(Rethrow* curr) { WASM_UNREACHABLE("TODO"); }
+ void visitThrowRef(ThrowRef* curr) { WASM_UNREACHABLE("TODO"); }
void visitTupleMake(TupleMake* curr) { WASM_UNREACHABLE("TODO"); }
void visitTupleExtract(TupleExtract* curr) { WASM_UNREACHABLE("TODO"); }
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 9e68ca330..84720865e 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1170,11 +1170,17 @@ enum ASTNodes {
// exception handling opcodes
Try = 0x06,
- Catch = 0x07,
- CatchAll = 0x19,
+ Catch_P3 = 0x07, // Old Phase 3 'catch'
+ CatchAll_P3 = 0x19, // Old Phase 3 'catch_all'
Delegate = 0x18,
Throw = 0x08,
Rethrow = 0x09,
+ TryTable = 0x1f,
+ Catch = 0x00,
+ CatchRef = 0x01,
+ CatchAll = 0x02,
+ CatchAllRef = 0x03,
+ ThrowRef = 0x0a,
// typed function references opcodes
@@ -1909,8 +1915,10 @@ public:
void visitTableGet(TableGet* curr);
void visitTableSet(TableSet* curr);
void visitTryOrTryInBlock(Expression*& out);
+ void visitTryTable(TryTable* curr);
void visitThrow(Throw* curr);
void visitRethrow(Rethrow* curr);
+ void visitThrowRef(ThrowRef* curr);
void visitCallRef(CallRef* curr);
void visitRefAsCast(RefCast* curr, uint32_t code);
void visitRefAs(RefAs* curr, uint8_t code);
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 2f379b4e1..fdb489c33 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -846,6 +846,31 @@ public:
Try* makeTry(Name name, Expression* body, Name delegateTarget, Type type) {
return makeTry(name, body, {}, {}, delegateTarget, type, true);
}
+ TryTable* makeTryTable(Expression* body,
+ const std::vector<Name>& catchTags,
+ const std::vector<Name>& catchDests,
+ const std::vector<bool>& catchRefs) {
+ auto* ret = wasm.allocator.alloc<TryTable>();
+ ret->body = body;
+ ret->catchTags.set(catchTags);
+ ret->catchDests.set(catchDests);
+ ret->catchRefs.set(catchRefs);
+ ret->finalize(&wasm);
+ return ret;
+ }
+ TryTable* makeTryTable(Expression* body,
+ const std::vector<Name>& catchTags,
+ const std::vector<Name>& catchDests,
+ const std::vector<bool>& catchRefs,
+ Type type) {
+ auto* ret = wasm.allocator.alloc<TryTable>();
+ ret->body = body;
+ ret->catchTags.set(catchTags);
+ ret->catchDests.set(catchDests);
+ ret->catchRefs.set(catchRefs);
+ ret->finalize(type, &wasm);
+ return ret;
+ }
Throw* makeThrow(Tag* tag, const std::vector<Expression*>& args) {
return makeThrow(tag->name, args);
}
diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def
index d7567b38d..3f8072445 100644
--- a/src/wasm-delegations-fields.def
+++ b/src/wasm-delegations-fields.def
@@ -52,6 +52,10 @@
// DELEGATE_FIELD_INT_ARRAY(id, field) - called for a std::array of fixed size
// of integer values (like a SIMD mask). If this is not defined, and
// DELEGATE_GET_FIELD is, then DELEGATE_FIELD_INT is called on them.
+
+// DELEGATE_FIELD_INT_VECTOR(id, field) - called for a variable-sized vector
+// of integer values. If this is not defined, and DELEGATE_GET_FIELD is, then
+// DELEGATE_FIELD_INT is called on them.
//
// DELEGATE_FIELD_LITERAL(id, field) - called for a Literal.
//
@@ -85,6 +89,10 @@
//
// DELEGATE_FIELD_TYPE(id, field) - called for a Type.
//
+// DELEGATE_FIELD_TYPE_VECTOR(id, field) - called for a variable-sized vector
+// of Types. If this is not defined, and DELEGATE_GET_FIELD is, then
+// DELEGATE_FIELD_TYPE is called on them.
+//
// DELEGATE_FIELD_HEAPTYPE(id, field) - called for a HeapType.
//
// DELEGATE_FIELD_ADDRESS(id, field) - called for an Address.
@@ -131,6 +139,17 @@
#endif
#endif
+#ifndef DELEGATE_FIELD_INT_VECTOR
+#ifdef DELEGATE_GET_FIELD
+#define DELEGATE_FIELD_INT_VECTOR(id, field) \
+ for (Index i = 0; i < (DELEGATE_GET_FIELD(id, field)).size(); i++) { \
+ DELEGATE_FIELD_INT(id, field[i]); \
+ }
+#else
+#error please define DELEGATE_FIELD_INT_VECTOR(id, field)
+#endif
+#endif
+
#ifndef DELEGATE_FIELD_LITERAL
#error please define DELEGATE_FIELD_LITERAL(id, field)
#endif
@@ -190,6 +209,17 @@
#error please define DELEGATE_FIELD_TYPE(id, field)
#endif
+#ifndef DELEGATE_FIELD_TYPE_VECTOR
+#ifdef DELEGATE_GET_FIELD
+#define DELEGATE_FIELD_TYPE_VECTOR(id, field) \
+ for (Index i = 0; i < (DELEGATE_GET_FIELD(id, field)).size(); i++) { \
+ DELEGATE_FIELD_TYPE(id, field[i]); \
+ }
+#else
+#error please define DELEGATE_FIELD_TYPE_VECTOR(id, field)
+#endif
+#endif
+
#ifndef DELEGATE_FIELD_HEAPTYPE
#error please define DELEGATE_FIELD_HEAPTYPE(id, field)
#endif
@@ -596,6 +626,16 @@ switch (DELEGATE_ID) {
DELEGATE_END(Try);
break;
}
+ case Expression::Id::TryTableId: {
+ DELEGATE_START(TryTable);
+ DELEGATE_FIELD_TYPE_VECTOR(TryTable, sentTypes);
+ DELEGATE_FIELD_INT_VECTOR(TryTable, catchRefs);
+ DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(TryTable, catchDests);
+ DELEGATE_FIELD_NAME_KIND_VECTOR(TryTable, catchTags, ModuleItemKind::Tag);
+ DELEGATE_FIELD_CHILD(TryTable, body);
+ DELEGATE_END(TryTable);
+ break;
+ }
case Expression::Id::ThrowId: {
DELEGATE_START(Throw);
DELEGATE_FIELD_CHILD_VECTOR(Throw, operands);
@@ -609,6 +649,12 @@ switch (DELEGATE_ID) {
DELEGATE_END(Rethrow);
break;
}
+ case Expression::Id::ThrowRefId: {
+ DELEGATE_START(ThrowRef);
+ DELEGATE_FIELD_CHILD(ThrowRef, exnref);
+ DELEGATE_END(ThrowRef);
+ break;
+ }
case Expression::Id::NopId: {
DELEGATE_START(Nop);
DELEGATE_END(Nop);
@@ -909,6 +955,7 @@ switch (DELEGATE_ID) {
#undef DELEGATE_FIELD_CHILD_VECTOR
#undef DELEGATE_FIELD_INT
#undef DELEGATE_FIELD_INT_ARRAY
+#undef DELEGATE_FIELD_INT_VECTOR
#undef DELEGATE_FIELD_LITERAL
#undef DELEGATE_FIELD_NAME
#undef DELEGATE_FIELD_NAME_VECTOR
@@ -918,6 +965,7 @@ switch (DELEGATE_ID) {
#undef DELEGATE_FIELD_NAME_KIND
#undef DELEGATE_FIELD_NAME_KIND_VECTOR
#undef DELEGATE_FIELD_TYPE
+#undef DELEGATE_FIELD_TYPE_VECTOR
#undef DELEGATE_FIELD_HEAPTYPE
#undef DELEGATE_FIELD_ADDRESS
#undef DELEGATE_GET_FIELD
diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def
index 903b19bf7..e486c490f 100644
--- a/src/wasm-delegations.def
+++ b/src/wasm-delegations.def
@@ -65,8 +65,10 @@ DELEGATE(TableGrow);
DELEGATE(TableFill);
DELEGATE(TableCopy);
DELEGATE(Try);
+DELEGATE(TryTable);
DELEGATE(Throw);
DELEGATE(Rethrow);
+DELEGATE(ThrowRef);
DELEGATE(TupleMake);
DELEGATE(TupleExtract);
DELEGATE(RefI31);
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 3599def90..eb0a1274a 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1394,6 +1394,7 @@ public:
Flow visitTableFill(TableFill* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitTableCopy(TableCopy* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); }
+ Flow visitTryTable(TryTable* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitThrow(Throw* curr) {
NOTE_ENTER("Throw");
Literals arguments;
@@ -1411,6 +1412,7 @@ public:
WASM_UNREACHABLE("throw");
}
Flow visitRethrow(Rethrow* curr) { WASM_UNREACHABLE("unimp"); }
+ Flow visitThrowRef(ThrowRef* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitRefI31(RefI31* curr) {
NOTE_ENTER("RefI31");
Flow flow = visit(curr->value);
diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h
index 398cd4f9a..dcb25efa7 100644
--- a/src/wasm-ir-builder.h
+++ b/src/wasm-ir-builder.h
@@ -155,8 +155,10 @@ public:
[[nodiscard]] Result<> makeTableFill(Name table);
[[nodiscard]] Result<> makeTableCopy(Name destTable, Name srcTable);
[[nodiscard]] Result<> makeTry(Name label, Type type);
+ // [[nodiscard]] Result<> makeTryTable();
[[nodiscard]] Result<> makeThrow(Name tag);
[[nodiscard]] Result<> makeRethrow(Index label);
+ // [[nodiscard]] Result<> makeThrowRef();
[[nodiscard]] Result<> makeTupleMake(uint32_t arity);
[[nodiscard]] Result<> makeTupleExtract(uint32_t arity, uint32_t index);
[[nodiscard]] Result<> makeTupleDrop(uint32_t arity);
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 446f2a292..7921378de 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -284,9 +284,10 @@ private:
Expression* makeTableFill(Element& s);
Expression* makeTableCopy(Element& s);
Expression* makeTry(Element& s);
- Expression* makeTryOrCatchBody(Element& s, Type type, bool isTry);
+ Expression* makeTryTable(Element& s);
Expression* makeThrow(Element& s);
Expression* makeRethrow(Element& s);
+ Expression* makeThrowRef(Element& s);
Expression* makeTupleMake(Element& s);
Expression* makeTupleExtract(Element& s);
Expression* makeTupleDrop(Element& s);
diff --git a/src/wasm-stack.h b/src/wasm-stack.h
index 1f66212ad..a195f1a84 100644
--- a/src/wasm-stack.h
+++ b/src/wasm-stack.h
@@ -170,6 +170,7 @@ public:
void visitIf(If* curr);
void visitLoop(Loop* curr);
void visitTry(Try* curr);
+ void visitTryTable(TryTable* curr);
protected:
Function* func = nullptr;
@@ -404,6 +405,16 @@ template<typename SubType> void BinaryenIRWriter<SubType>::visitTry(Try* curr) {
}
}
+template<typename SubType>
+void BinaryenIRWriter<SubType>::visitTryTable(TryTable* curr) {
+ emit(curr);
+ visitPossibleBlockContents(curr->body);
+ emitScopeEnd(curr);
+ if (curr->type == Type::unreachable) {
+ emitUnreachable();
+ }
+}
+
// Binaryen IR to binary writer
class BinaryenIRToBinaryWriter
: public BinaryenIRWriter<BinaryenIRToBinaryWriter> {
diff --git a/src/wasm.h b/src/wasm.h
index 1b01278b0..576a0d0f1 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -42,6 +42,8 @@
namespace wasm {
+class Module;
+
// An index in a wasm module
using Index = uint32_t;
@@ -701,8 +703,10 @@ public:
TableFillId,
TableCopyId,
TryId,
+ TryTableId,
ThrowId,
RethrowId,
+ ThrowRefId,
TupleMakeId,
TupleExtractId,
RefI31Id,
@@ -1452,6 +1456,7 @@ public:
void finalize();
};
+// 'try' from the old (Phase 3) EH proposal
class Try : public SpecificExpression<Expression::TryId> {
public:
Try(MixedArena& allocator) : catchTags(allocator), catchBodies(allocator) {}
@@ -1471,6 +1476,35 @@ public:
void finalize(Type type_);
};
+// 'try_table' from the new EH proposal
+class TryTable : public SpecificExpression<Expression::TryTableId> {
+public:
+ TryTable(MixedArena& allocator)
+ : catchTags(allocator), catchDests(allocator), catchRefs(allocator),
+ sentTypes(allocator) {}
+
+ Expression* body;
+
+ // Tag names. Empty names (Name()) for catch_all and catch_all_ref
+ ArenaVector<Name> catchTags;
+ // catches' destination blocks
+ ArenaVector<Name> catchDests;
+ // true for catch_ref and catch_all_ref
+ ArenaVector<bool> catchRefs;
+
+ bool hasCatchAll() const;
+
+ // When 'Module*' parameter is given, we cache catch tags' types into
+ // 'sentTypes' array, so that the types can be accessed in other analyses
+ // without accessing the module.
+ void finalize(Module* wasm = nullptr);
+ void finalize(Type type_, Module* wasm = nullptr);
+
+ // Caches tags' types in the catch clauses in order not to query the module
+ // every time we query the sent types
+ ArenaVector<Type> sentTypes;
+};
+
class Throw : public SpecificExpression<Expression::ThrowId> {
public:
Throw(MixedArena& allocator) : operands(allocator) {}
@@ -1481,6 +1515,7 @@ public:
void finalize();
};
+// 'rethrow' from the old (Phase 3) EH proposal
class Rethrow : public SpecificExpression<Expression::RethrowId> {
public:
Rethrow(MixedArena& allocator) {}
@@ -1490,6 +1525,16 @@ public:
void finalize();
};
+// 'throw_ref' from the new EH proposal
+class ThrowRef : public SpecificExpression<Expression::ThrowRefId> {
+public:
+ ThrowRef(MixedArena& allocator) {}
+
+ Expression* exnref;
+
+ void finalize();
+};
+
class TupleMake : public SpecificExpression<Expression::TupleMakeId> {
public:
TupleMake(MixedArena& allocator) : operands(allocator) {}
diff --git a/src/wasm/parsing.cpp b/src/wasm/parsing.cpp
index c6134922a..1606a2dd1 100644
--- a/src/wasm/parsing.cpp
+++ b/src/wasm/parsing.cpp
@@ -84,10 +84,11 @@ Name UniqueNameMapper::sourceToUnique(Name sName) {
return DELEGATE_CALLER_TARGET;
}
if (labelMappings.find(sName) == labelMappings.end()) {
- throw ParseException("bad label in sourceToUnique");
+ throw ParseException("bad label in sourceToUnique: " + sName.toString());
}
if (labelMappings[sName].empty()) {
- throw ParseException("use of popped label in sourceToUnique");
+ throw ParseException("use of popped label in sourceToUnique: " +
+ sName.toString());
}
return labelMappings[sName].back();
}
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index be1c27d03..bf14efeb1 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -3010,7 +3010,7 @@ void WasmBinaryReader::processExpressions() {
}
auto peek = input[pos];
if (peek == BinaryConsts::End || peek == BinaryConsts::Else ||
- peek == BinaryConsts::Catch || peek == BinaryConsts::CatchAll ||
+ peek == BinaryConsts::Catch_P3 || peek == BinaryConsts::CatchAll_P3 ||
peek == BinaryConsts::Delegate) {
BYN_TRACE("== processExpressions finished with unreachable"
<< std::endl);
@@ -3955,8 +3955,8 @@ BinaryConsts::ASTNodes WasmBinaryReader::readExpression(Expression*& curr) {
}
break;
case BinaryConsts::Else:
- case BinaryConsts::Catch:
- case BinaryConsts::CatchAll: {
+ case BinaryConsts::Catch_P3:
+ case BinaryConsts::CatchAll_P3: {
curr = nullptr;
if (DWARF && currFunction) {
assert(!controlFlowStack.empty());
@@ -4014,12 +4014,18 @@ BinaryConsts::ASTNodes WasmBinaryReader::readExpression(Expression*& curr) {
case BinaryConsts::Try:
visitTryOrTryInBlock(curr);
break;
+ case BinaryConsts::TryTable:
+ visitTryTable((curr = allocator.alloc<TryTable>())->cast<TryTable>());
+ break;
case BinaryConsts::Throw:
visitThrow((curr = allocator.alloc<Throw>())->cast<Throw>());
break;
case BinaryConsts::Rethrow:
visitRethrow((curr = allocator.alloc<Rethrow>())->cast<Rethrow>());
break;
+ case BinaryConsts::ThrowRef:
+ visitThrowRef((curr = allocator.alloc<ThrowRef>())->cast<ThrowRef>());
+ break;
case BinaryConsts::MemorySize: {
auto size = allocator.alloc<MemorySize>();
curr = size;
@@ -6917,9 +6923,9 @@ void WasmBinaryReader::visitTryOrTryInBlock(Expression*& out) {
// here, then do that later.
std::vector<Index> tagIndexes;
- while (lastSeparator == BinaryConsts::Catch ||
- lastSeparator == BinaryConsts::CatchAll) {
- if (lastSeparator == BinaryConsts::Catch) {
+ while (lastSeparator == BinaryConsts::Catch_P3 ||
+ lastSeparator == BinaryConsts::CatchAll_P3) {
+ if (lastSeparator == BinaryConsts::Catch_P3) {
auto index = getU32LEB();
if (index >= wasm.tags.size()) {
throwError("bad tag index");
@@ -7030,6 +7036,51 @@ void WasmBinaryReader::visitTryOrTryInBlock(Expression*& out) {
breakTargetNames.erase(catchLabel);
}
+void WasmBinaryReader::visitTryTable(TryTable* curr) {
+ BYN_TRACE("zz node: TryTable\n");
+
+ // For simplicity of implementation, like if scopes, we create a hidden block
+ // within each try-body, and let branches target those inner blocks instead.
+ curr->type = getType();
+ auto numCatches = getU32LEB();
+ // We cannot immediately update tagRefs in the loop below, as catchTags is
+ // being grown, an so references would get invalidated. Store the indexes
+ // here, then do that later.
+ std::vector<Index> tagIndexes;
+
+ for (size_t i = 0; i < numCatches; i++) {
+ uint8_t code = getInt8();
+ if (code == BinaryConsts::Catch || code == BinaryConsts::CatchRef) {
+ auto index = getU32LEB();
+ if (index >= wasm.tags.size()) {
+ throwError("bad tag index");
+ }
+ tagIndexes.push_back(index);
+ auto* tag = wasm.tags[index].get();
+ curr->catchTags.push_back(tag->name);
+ } else {
+ tagIndexes.push_back(-1); // unused
+ curr->catchTags.push_back(Name());
+ }
+ curr->catchDests.push_back(getBreakTarget(getU32LEB()).name);
+ curr->catchRefs.push_back(code == BinaryConsts::CatchRef ||
+ code == BinaryConsts::CatchAllRef);
+ }
+
+ for (Index i = 0; i < tagIndexes.size(); i++) {
+ if (curr->catchTags[i]) {
+ // We don't know the final name yet.
+ tagRefs[tagIndexes[i]].push_back(&curr->catchTags[i]);
+ }
+ }
+
+ // catch_*** clauses should refer to block labels without entering the try
+ // scope. So we do this after reading catch clauses.
+ startControlFlow(curr);
+ curr->body = getBlockOrSingleton(curr->type);
+ curr->finalize(curr->type, &wasm);
+}
+
void WasmBinaryReader::visitThrow(Throw* curr) {
BYN_TRACE("zz node: Throw\n");
auto index = getU32LEB();
@@ -7058,6 +7109,12 @@ void WasmBinaryReader::visitRethrow(Rethrow* curr) {
curr->finalize();
}
+void WasmBinaryReader::visitThrowRef(ThrowRef* curr) {
+ BYN_TRACE("zz node: ThrowRef\n");
+ curr->exnref = popNonVoidExpression();
+ curr->finalize();
+}
+
void WasmBinaryReader::visitCallRef(CallRef* curr) {
BYN_TRACE("zz node: CallRef\n");
curr->target = popNonVoidExpression();
diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp
index 0877666a4..3cfe0ea30 100644
--- a/src/wasm/wasm-ir-builder.cpp
+++ b/src/wasm/wasm-ir-builder.cpp
@@ -1337,6 +1337,8 @@ Result<> IRBuilder::makeTry(Name label, Type type) {
return visitTryStart(tryy, label);
}
+// Result<> IRBuilder::makeTryTable() {}
+
Result<> IRBuilder::makeThrow(Name tag) {
Throw curr(wasm.allocator);
curr.tag = tag;
@@ -1353,6 +1355,8 @@ Result<> IRBuilder::makeRethrow(Index label) {
return Ok{};
}
+// Result<> IRBuilder::makeThrowRef() {}
+
Result<> IRBuilder::makeTupleMake(uint32_t arity) {
TupleMake curr(wasm.allocator);
curr.operands.resize(arity);
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 94b434dfb..49cf1ccfc 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2794,7 +2794,7 @@ Expression* SExpressionWasmBuilder::makeTry(Element& s) {
if (!wasm.getTagOrNull(tag)) {
throw SParseException("bad tag name", s, inner);
}
- ret->catchTags.push_back(getTagName(*inner[1]));
+ ret->catchTags.push_back(tag);
ret->catchBodies.push_back(makeMaybeBlock(inner, 2, type));
}
@@ -2837,6 +2837,65 @@ Expression* SExpressionWasmBuilder::makeTry(Element& s) {
return ret;
}
+Expression* SExpressionWasmBuilder::makeTryTable(Element& s) {
+ auto ret = allocator.alloc<TryTable>();
+ Index i = 1;
+ Name sName;
+ if (s.size() > i && s[i]->dollared()) {
+ // the try_table is labeled
+ sName = s[i++]->str();
+ } else {
+ sName = "try_table";
+ }
+ auto label = nameMapper.pushLabelName(sName);
+ Type type = parseBlockType(s, i); // signature
+
+ while (i < s.size()) {
+ Element& inner = *s[i];
+
+ if (elementStartsWith(inner, "catch") ||
+ elementStartsWith(inner, "catch_ref")) {
+ bool isRef = elementStartsWith(inner, "catch_ref");
+ if (inner.size() < 3) {
+ throw SParseException("invalid catch/catch_ref block", s, inner);
+ }
+ Name tag = getTagName(*inner[1]);
+ if (!wasm.getTagOrNull(tag)) {
+ throw SParseException("bad tag name", s, inner);
+ }
+ ret->catchTags.push_back(tag);
+ ret->catchDests.push_back(getLabel(*inner[2]));
+ ret->catchRefs.push_back(isRef);
+ } else if (elementStartsWith(inner, "catch_all") ||
+ elementStartsWith(inner, "catch_all_ref")) {
+ bool isRef = elementStartsWith(inner, "catch_all_ref");
+ if (inner.size() < 2) {
+ throw SParseException(
+ "invalid catch_all/catch_all_ref block", s, inner);
+ }
+ ret->catchTags.push_back(Name());
+ ret->catchDests.push_back(getLabel(*inner[1]));
+ ret->catchRefs.push_back(isRef);
+ } else {
+ break;
+ }
+ i++;
+ }
+
+ ret->body = makeMaybeBlock(s, i, type);
+ ret->finalize(type, &wasm);
+ nameMapper.popLabelName(label);
+ // create a break target if we must
+ if (BranchUtils::BranchSeeker::has(ret, label)) {
+ auto* block = allocator.alloc<Block>();
+ block->name = label;
+ block->list.push_back(ret);
+ block->finalize(type);
+ return block;
+ }
+ return ret;
+}
+
Expression* SExpressionWasmBuilder::makeThrow(Element& s) {
auto ret = allocator.alloc<Throw>();
Index i = 1;
@@ -2859,6 +2918,13 @@ Expression* SExpressionWasmBuilder::makeRethrow(Element& s) {
return ret;
}
+Expression* SExpressionWasmBuilder::makeThrowRef(Element& s) {
+ auto ret = allocator.alloc<ThrowRef>();
+ ret->exnref = parseExpression(s[1]);
+ ret->finalize();
+ return ret;
+}
+
Expression* SExpressionWasmBuilder::makeTupleMake(Element& s) {
auto ret = allocator.alloc<TupleMake>();
size_t arity = std::stoll(s[1]->toString());
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 0f8facfd5..3b14b3c35 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -16,6 +16,7 @@
#include "wasm-stack.h"
#include "ir/find_all.h"
+#include "wasm-binary.h"
#include "wasm-debug.h"
namespace wasm {
@@ -1953,11 +1954,32 @@ void BinaryInstWriter::visitTry(Try* curr) {
emitResultType(curr->type);
}
+void BinaryInstWriter::visitTryTable(TryTable* curr) {
+ o << int8_t(BinaryConsts::TryTable);
+ emitResultType(curr->type);
+ o << U32LEB(curr->catchTags.size());
+ for (Index i = 0; i < curr->catchTags.size(); i++) {
+ if (curr->catchTags[i]) {
+ o << (curr->catchRefs[i] ? int8_t(BinaryConsts::CatchRef)
+ : int8_t(BinaryConsts::Catch));
+ o << U32LEB(parent.getTagIndex(curr->catchTags[i]));
+ } else {
+ o << (curr->catchRefs[i] ? int8_t(BinaryConsts::CatchAllRef)
+ : int8_t(BinaryConsts::CatchAll));
+ }
+ o << U32LEB(getBreakIndex(curr->catchDests[i]));
+ }
+ // the binary format requires this; we have a block if we need one
+ // catch_*** clauses should refer to block labels without entering the try
+ // scope. So we do this at the end.
+ breakStack.emplace_back(IMPOSSIBLE_CONTINUE);
+}
+
void BinaryInstWriter::emitCatch(Try* curr, Index i) {
if (func && !sourceMap) {
parent.writeExtraDebugLocation(curr, func, i);
}
- o << int8_t(BinaryConsts::Catch)
+ o << int8_t(BinaryConsts::Catch_P3)
<< U32LEB(parent.getTagIndex(curr->catchTags[i]));
}
@@ -1965,7 +1987,7 @@ void BinaryInstWriter::emitCatchAll(Try* curr) {
if (func && !sourceMap) {
parent.writeExtraDebugLocation(curr, func, curr->catchBodies.size());
}
- o << int8_t(BinaryConsts::CatchAll);
+ o << int8_t(BinaryConsts::CatchAll_P3);
}
void BinaryInstWriter::emitDelegate(Try* curr) {
@@ -1986,6 +2008,10 @@ void BinaryInstWriter::visitRethrow(Rethrow* curr) {
o << int8_t(BinaryConsts::Rethrow) << U32LEB(getBreakIndex(curr->target));
}
+void BinaryInstWriter::visitThrowRef(ThrowRef* curr) {
+ o << int8_t(BinaryConsts::ThrowRef);
+}
+
void BinaryInstWriter::visitNop(Nop* curr) { o << int8_t(BinaryConsts::Nop); }
void BinaryInstWriter::visitUnreachable(Unreachable* curr) {
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index cb54e1597..c1b4a86e7 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -354,7 +354,8 @@ public:
case Expression::ReturnId:
case Expression::UnreachableId:
case Expression::ThrowId:
- case Expression::RethrowId: {
+ case Expression::RethrowId:
+ case Expression::ThrowRefId: {
// These can all be unreachable without an unreachable child.
return;
}
@@ -445,8 +446,10 @@ public:
void noteDelegate(Name name, Expression* curr);
void noteRethrow(Name name, Expression* curr);
void visitTry(Try* curr);
+ void visitTryTable(TryTable* curr);
void visitThrow(Throw* curr);
void visitRethrow(Rethrow* curr);
+ void visitThrowRef(ThrowRef* curr);
void visitTupleMake(TupleMake* curr);
void visitTupleExtract(TupleExtract* curr);
void visitCallRef(CallRef* curr);
@@ -2442,6 +2445,10 @@ void FunctionValidator::visitTry(Try* curr) {
rethrowTargetNames.erase(curr->name);
}
+void FunctionValidator::visitTryTable(TryTable* curr) {
+ // TODO
+}
+
void FunctionValidator::visitThrow(Throw* curr) {
shouldBeTrue(
getModule()->features.hasExceptionHandling(),
@@ -2516,6 +2523,10 @@ void FunctionValidator::visitTupleMake(TupleMake* curr) {
"Type of tuple.make does not match types of its operands");
}
+void FunctionValidator::visitThrowRef(ThrowRef* curr) {
+ // TODO
+}
+
void FunctionValidator::visitTupleExtract(TupleExtract* curr) {
shouldBeTrue(getModule()->features.hasMultivalue(),
curr,
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 8ff2dc978..6589ca069 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -900,6 +900,47 @@ void Throw::finalize() { type = Type::unreachable; }
void Rethrow::finalize() { type = Type::unreachable; }
+void ThrowRef::finalize() { type = Type::unreachable; }
+
+bool TryTable::hasCatchAll() const {
+ return std::any_of(
+ catchTags.begin(), catchTags.end(), [](Name t) { return !t; });
+}
+
+static void populateTryTableSentTypes(TryTable* curr, Module* wasm) {
+ if (!wasm) {
+ return;
+ }
+ curr->sentTypes.clear();
+ Type exnref = Type(HeapType::exn, Nullable);
+ for (Index i = 0; i < curr->catchTags.size(); i++) {
+ auto tagName = curr->catchTags[i];
+ std::vector<Type> sentType;
+ if (tagName) {
+ for (auto t : wasm->getTag(tagName)->sig.params) {
+ sentType.push_back(t);
+ }
+ }
+ if (curr->catchRefs[i]) {
+ sentType.push_back(exnref);
+ }
+ curr->sentTypes.push_back(sentType.empty() ? Type::none : Type(sentType));
+ }
+}
+
+void TryTable::finalize(Module* wasm) {
+ type = body->type;
+ populateTryTableSentTypes(this, wasm);
+}
+
+void TryTable::finalize(Type type_, Module* wasm) {
+ type = type_;
+ if (type == Type::none && body->type == Type::unreachable) {
+ type = Type::unreachable;
+ }
+ populateTryTableSentTypes(this, wasm);
+}
+
void TupleMake::finalize() {
std::vector<Type> types;
types.reserve(operands.size());
diff --git a/src/wasm2js.h b/src/wasm2js.h
index 4aef8c378..c3f3b5fa6 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -2263,6 +2263,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
unimplemented(curr);
WASM_UNREACHABLE("unimp");
}
+ Ref visitTryTable(TryTable* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE("unimp");
+ }
Ref visitThrow(Throw* curr) {
unimplemented(curr);
WASM_UNREACHABLE("unimp");
@@ -2271,6 +2275,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
unimplemented(curr);
WASM_UNREACHABLE("unimp");
}
+ Ref visitThrowRef(ThrowRef* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE("unimp");
+ }
Ref visitPop(Pop* curr) {
unimplemented(curr);
WASM_UNREACHABLE("unimp");
diff --git a/test/binaryen.js/exception-handling.js.txt b/test/binaryen.js/exception-handling.js.txt
index 062f05202..d2a0c6dd6 100644
--- a/test/binaryen.js/exception-handling.js.txt
+++ b/test/binaryen.js/exception-handling.js.txt
@@ -34,7 +34,7 @@
)
)
-getExpressionInfo(throw) = {"id":52,"type":1,"tag":"e"}
-getExpressionInfo(rethrow) = {"id":53,"type":1,"target":"l0"}
+getExpressionInfo(throw) = {"id":53,"type":1,"tag":"e"}
+getExpressionInfo(rethrow) = {"id":54,"type":1,"target":"l0"}
getExpressionInfo(try_catch) = {"id":51,"type":1,"name":"l0","hasCatchAll":0,"delegateTarget":"","isDelegate":0}
getExpressionInfo(try_delegate) = {"id":51,"type":0,"name":"try_outer","hasCatchAll":1,"delegateTarget":"","isDelegate":0}
diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt
index c3513b6ba..168bc0073 100644
--- a/test/binaryen.js/kitchen-sink.js.txt
+++ b/test/binaryen.js/kitchen-sink.js.txt
@@ -83,39 +83,39 @@ TableSetId: 46
TableSizeId: 47
TableGrowId: 48
TryId: 51
-ThrowId: 52
-RethrowId: 53
-TupleMakeId: 54
-TupleExtractId: 55
-RefI31Id: 56
-I31GetId: 57
-CallRefId: 58
-RefTestId: 59
-RefCastId: 60
-BrOnId: 61
-StructNewId: 62
-StructGetId: 63
-StructSetId: 64
-ArrayNewId: 65
-ArrayNewFixedId: 68
-ArrayGetId: 69
-ArraySetId: 70
-ArrayLenId: 71
-ArrayCopy: 72
-RefAs: 76
-StringNew: 77
-StringConst: 78
-StringMeasure: 79
-StringEncode: 80
-StringConcat: 81
-StringEq: 82
-StringAs: 83
-StringWTF8Advance: 84
-StringWTF16Get: 85
-StringIterNext: 86
-StringIterMove: 87
-StringSliceWTF: 88
-StringSliceIter: 89
+ThrowId: 53
+RethrowId: 54
+TupleMakeId: 56
+TupleExtractId: 57
+RefI31Id: 58
+I31GetId: 59
+CallRefId: 60
+RefTestId: 61
+RefCastId: 62
+BrOnId: 63
+StructNewId: 64
+StructGetId: 65
+StructSetId: 66
+ArrayNewId: 67
+ArrayNewFixedId: 70
+ArrayGetId: 71
+ArraySetId: 72
+ArrayLenId: 73
+ArrayCopy: 74
+RefAs: 78
+StringNew: 79
+StringConst: 80
+StringMeasure: 81
+StringEncode: 82
+StringConcat: 83
+StringEq: 84
+StringAs: 85
+StringWTF8Advance: 86
+StringWTF16Get: 87
+StringIterNext: 88
+StringIterMove: 89
+StringSliceWTF: 90
+StringSliceIter: 91
getExpressionInfo={"id":15,"type":4,"op":6}
(f32.neg
(f32.const -33.61199951171875)
diff --git a/test/lit/basic/exception-handling.wast b/test/lit/basic/exception-handling.wast
index 945cca1af..a06e377e6 100644
--- a/test/lit/basic/exception-handling.wast
+++ b/test/lit/basic/exception-handling.wast
@@ -10,9 +10,71 @@
;; RUN: cat %t.bin.nodebug.wast | filecheck %s --check-prefix=CHECK-BIN-NODEBUG
(module
- ;; CHECK-TEXT: (type $0 (func (result exnref)))
+ ;; CHECK-TEXT: (type $0 (func))
- ;; CHECK-TEXT: (func $exnref-nullexnref-test (type $0) (result exnref)
+ ;; CHECK-TEXT: (type $1 (func (result exnref)))
+
+ ;; CHECK-TEXT: (type $2 (func (result i32)))
+
+ ;; CHECK-TEXT: (type $3 (func (result i32 i64)))
+
+ ;; CHECK-TEXT: (type $4 (func (result i32 i64 exnref)))
+
+ ;; CHECK-TEXT: (type $5 (func (result i32 exnref)))
+
+ ;; CHECK-TEXT: (type $6 (func (param i32)))
+
+ ;; CHECK-TEXT: (type $7 (func (param i64)))
+
+ ;; CHECK-TEXT: (type $8 (func (param i32 i64)))
+
+ ;; CHECK-TEXT: (type $9 (func (param eqref)))
+
+ ;; CHECK-TEXT: (tag $e-i32 (param i32))
+ ;; CHECK-BIN: (type $0 (func))
+
+ ;; CHECK-BIN: (type $1 (func (result exnref)))
+
+ ;; CHECK-BIN: (type $2 (func (result i32)))
+
+ ;; CHECK-BIN: (type $3 (func (result i32 i64)))
+
+ ;; CHECK-BIN: (type $4 (func (result i32 i64 exnref)))
+
+ ;; CHECK-BIN: (type $5 (func (result i32 exnref)))
+
+ ;; CHECK-BIN: (type $6 (func (param i32)))
+
+ ;; CHECK-BIN: (type $7 (func (param i64)))
+
+ ;; CHECK-BIN: (type $8 (func (param i32 i64)))
+
+ ;; CHECK-BIN: (type $9 (func (param eqref)))
+
+ ;; CHECK-BIN: (tag $e-i32 (param i32))
+ (tag $e-i32 (param i32))
+ ;; CHECK-TEXT: (tag $e-i64 (param i64))
+ ;; CHECK-BIN: (tag $e-i64 (param i64))
+ (tag $e-i64 (param i64))
+ ;; CHECK-TEXT: (tag $e-i32-i64 (param i32 i64))
+ ;; CHECK-BIN: (tag $e-i32-i64 (param i32 i64))
+ (tag $e-i32-i64 (param i32 i64))
+ ;; CHECK-TEXT: (tag $e-eqref (param eqref))
+ ;; CHECK-BIN: (tag $e-eqref (param eqref))
+ (tag $e-eqref (param (ref null eq)))
+ ;; CHECK-TEXT: (tag $e-empty)
+ ;; CHECK-BIN: (tag $e-empty)
+ (tag $e-empty)
+
+ ;; CHECK-TEXT: (func $foo (type $0)
+ ;; CHECK-TEXT-NEXT: (nop)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-BIN: (func $foo (type $0)
+ ;; CHECK-BIN-NEXT: (nop)
+ ;; CHECK-BIN-NEXT: )
+ (func $foo)
+
+ ;; CHECK-TEXT: (func $exnref-nullexnref-test (type $1) (result exnref)
;; CHECK-TEXT-NEXT: (local $exn exnref)
;; CHECK-TEXT-NEXT: (local $null-exn nullexnref)
;; CHECK-TEXT-NEXT: (if (result exnref)
@@ -25,9 +87,7 @@
;; CHECK-TEXT-NEXT: (local.get $exn)
;; CHECK-TEXT-NEXT: )
;; CHECK-TEXT-NEXT: )
- ;; CHECK-BIN: (type $0 (func (result exnref)))
-
- ;; CHECK-BIN: (func $exnref-nullexnref-test (type $0) (result exnref)
+ ;; CHECK-BIN: (func $exnref-nullexnref-test (type $1) (result exnref)
;; CHECK-BIN-NEXT: (local $exn exnref)
;; CHECK-BIN-NEXT: (local $null-exn nullexnref)
;; CHECK-BIN-NEXT: (if (result exnref)
@@ -51,10 +111,413 @@
(local.get $exn)
)
)
+
+ ;; CHECK-TEXT: (func $catchless-try-table (type $0)
+ ;; CHECK-TEXT-NEXT: (try_table
+ ;; CHECK-TEXT-NEXT: (nop)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (try_table
+ ;; CHECK-TEXT-NEXT: (throw $e-empty)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-BIN: (func $catchless-try-table (type $0)
+ ;; CHECK-BIN-NEXT: (try_table
+ ;; CHECK-BIN-NEXT: (nop)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (try_table
+ ;; CHECK-BIN-NEXT: (throw $e-empty)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ (func $catchless-try-table
+ (try_table)
+ (try_table
+ (throw $e-empty)
+ )
+ )
+
+ ;; CHECK-TEXT: (func $simple-try-table-and-throw (type $2) (result i32)
+ ;; CHECK-TEXT-NEXT: (block $l-catch (result i32)
+ ;; CHECK-TEXT-NEXT: (try_table (catch $e-i32 $l-catch)
+ ;; CHECK-TEXT-NEXT: (throw $e-i32
+ ;; CHECK-TEXT-NEXT: (i32.const 0)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-BIN: (func $simple-try-table-and-throw (type $2) (result i32)
+ ;; CHECK-BIN-NEXT: (block $label$1 (result i32)
+ ;; CHECK-BIN-NEXT: (try_table (catch $e-i32 $label$1)
+ ;; CHECK-BIN-NEXT: (throw $e-i32
+ ;; CHECK-BIN-NEXT: (i32.const 0)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ (func $simple-try-table-and-throw (result i32)
+ (block $l-catch (result i32)
+ (try_table (catch $e-i32 $l-catch)
+ (throw $e-i32 (i32.const 0))
+ )
+ )
+ )
+
+ ;; CHECK-TEXT: (func $try-table-and-throw-ref (type $0)
+ ;; CHECK-TEXT-NEXT: (throw_ref
+ ;; CHECK-TEXT-NEXT: (block $l-catch-all-ref (result exnref)
+ ;; CHECK-TEXT-NEXT: (try_table (catch_all_ref $l-catch-all-ref)
+ ;; CHECK-TEXT-NEXT: (throw $e-i64
+ ;; CHECK-TEXT-NEXT: (i64.const 0)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-BIN: (func $try-table-and-throw-ref (type $0)
+ ;; CHECK-BIN-NEXT: (throw_ref
+ ;; CHECK-BIN-NEXT: (block $label$1 (result exnref)
+ ;; CHECK-BIN-NEXT: (try_table (catch_all_ref $label$1)
+ ;; CHECK-BIN-NEXT: (throw $e-i64
+ ;; CHECK-BIN-NEXT: (i64.const 0)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ (func $try-table-and-throw-ref
+ (throw_ref
+ (block $l-catch-all-ref (result exnref)
+ (try_table (catch_all_ref $l-catch-all-ref)
+ (throw $e-i64 (i64.const 0))
+ )
+ )
+ )
+ )
+
+ ;; CHECK-TEXT: (func $try-table-multivalue-tag (type $0)
+ ;; CHECK-TEXT-NEXT: (block $outer
+ ;; CHECK-TEXT-NEXT: (tuple.drop 3
+ ;; CHECK-TEXT-NEXT: (block $l-catch-ref (type $4) (result i32 i64 exnref)
+ ;; CHECK-TEXT-NEXT: (tuple.drop 2
+ ;; CHECK-TEXT-NEXT: (block $l-catch (type $3) (result i32 i64)
+ ;; CHECK-TEXT-NEXT: (try_table (catch $e-i32-i64 $l-catch) (catch_ref $e-i32-i64 $l-catch-ref)
+ ;; CHECK-TEXT-NEXT: (throw $e-i32-i64
+ ;; CHECK-TEXT-NEXT: (i32.const 0)
+ ;; CHECK-TEXT-NEXT: (i64.const 0)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (br $outer)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-BIN: (func $try-table-multivalue-tag (type $0)
+ ;; CHECK-BIN-NEXT: (local $0 (i32 i64))
+ ;; CHECK-BIN-NEXT: (local $1 i32)
+ ;; CHECK-BIN-NEXT: (local $2 (i32 i64 exnref))
+ ;; CHECK-BIN-NEXT: (local $3 i64)
+ ;; CHECK-BIN-NEXT: (local $4 i32)
+ ;; CHECK-BIN-NEXT: (block $label$1
+ ;; CHECK-BIN-NEXT: (local.set $2
+ ;; CHECK-BIN-NEXT: (block $label$2 (type $4) (result i32 i64 exnref)
+ ;; CHECK-BIN-NEXT: (local.set $0
+ ;; CHECK-BIN-NEXT: (block $label$3 (type $3) (result i32 i64)
+ ;; CHECK-BIN-NEXT: (try_table (catch $e-i32-i64 $label$3) (catch_ref $e-i32-i64 $label$2)
+ ;; CHECK-BIN-NEXT: (throw $e-i32-i64
+ ;; CHECK-BIN-NEXT: (i32.const 0)
+ ;; CHECK-BIN-NEXT: (i64.const 0)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block (result i32)
+ ;; CHECK-BIN-NEXT: (local.set $1
+ ;; CHECK-BIN-NEXT: (tuple.extract 2 0
+ ;; CHECK-BIN-NEXT: (local.get $0)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (tuple.extract 2 1
+ ;; CHECK-BIN-NEXT: (local.get $0)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (local.get $1)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (br $label$1)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block (result i32)
+ ;; CHECK-BIN-NEXT: (local.set $4
+ ;; CHECK-BIN-NEXT: (tuple.extract 3 0
+ ;; CHECK-BIN-NEXT: (local.get $2)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block (result i64)
+ ;; CHECK-BIN-NEXT: (local.set $3
+ ;; CHECK-BIN-NEXT: (tuple.extract 3 1
+ ;; CHECK-BIN-NEXT: (local.get $2)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (tuple.extract 3 2
+ ;; CHECK-BIN-NEXT: (local.get $2)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (local.get $3)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (local.get $4)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ (func $try-table-multivalue-tag
+ (block $outer
+ (tuple.drop 3
+ (block $l-catch-ref (result i32 i64 exnref)
+ (tuple.drop 2
+ (block $l-catch (result i32 i64)
+ (try_table (catch $e-i32-i64 $l-catch)
+ (catch_ref $e-i32-i64 $l-catch-ref)
+ (throw $e-i32-i64 (i32.const 0) (i64.const 0))
+ )
+ )
+ )
+ (br $outer)
+ )
+ )
+ )
+ )
+
+ ;; CHECK-TEXT: (func $try-table-all-catch-clauses (type $0)
+ ;; CHECK-TEXT-NEXT: (block $outer
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (block $l-catch (result i32)
+ ;; CHECK-TEXT-NEXT: (tuple.drop 2
+ ;; CHECK-TEXT-NEXT: (block $l-catch-ref (type $5) (result i32 exnref)
+ ;; CHECK-TEXT-NEXT: (block $l-catch-all
+ ;; CHECK-TEXT-NEXT: (throw_ref
+ ;; CHECK-TEXT-NEXT: (block $l-catch-all-ref (result exnref)
+ ;; CHECK-TEXT-NEXT: (try_table (catch $e-i32 $l-catch) (catch_ref $e-i32 $l-catch-ref) (catch_all $l-catch-all) (catch_all_ref $l-catch-all-ref)
+ ;; CHECK-TEXT-NEXT: (call $foo)
+ ;; CHECK-TEXT-NEXT: (call $foo)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (br $outer)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (br $outer)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (br $outer)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-BIN: (func $try-table-all-catch-clauses (type $0)
+ ;; CHECK-BIN-NEXT: (local $0 (i32 exnref))
+ ;; CHECK-BIN-NEXT: (local $1 i32)
+ ;; CHECK-BIN-NEXT: (block $label$1
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block $label$2 (result i32)
+ ;; CHECK-BIN-NEXT: (local.set $0
+ ;; CHECK-BIN-NEXT: (block $label$3 (type $5) (result i32 exnref)
+ ;; CHECK-BIN-NEXT: (block $label$4
+ ;; CHECK-BIN-NEXT: (throw_ref
+ ;; CHECK-BIN-NEXT: (block $label$5 (result exnref)
+ ;; CHECK-BIN-NEXT: (try_table (catch $e-i32 $label$2) (catch_ref $e-i32 $label$3) (catch_all $label$4) (catch_all_ref $label$5)
+ ;; CHECK-BIN-NEXT: (call $foo)
+ ;; CHECK-BIN-NEXT: (call $foo)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (br $label$1)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (br $label$1)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block (result i32)
+ ;; CHECK-BIN-NEXT: (local.set $1
+ ;; CHECK-BIN-NEXT: (tuple.extract 2 0
+ ;; CHECK-BIN-NEXT: (local.get $0)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (tuple.extract 2 1
+ ;; CHECK-BIN-NEXT: (local.get $0)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (local.get $1)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (br $label$1)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ (func $try-table-all-catch-clauses
+ (block $outer
+ (drop
+ (block $l-catch (result i32)
+ (tuple.drop 2
+ (block $l-catch-ref (result i32 exnref)
+ (block $l-catch-all
+ (throw_ref
+ (block $l-catch-all-ref (result exnref)
+ (try_table (catch $e-i32 $l-catch)
+ (catch_ref $e-i32 $l-catch-ref)
+ (catch_all $l-catch-all)
+ (catch_all_ref $l-catch-all-ref)
+ (call $foo)
+ (call $foo)
+ )
+ (br $outer)
+ )
+ )
+ )
+ (br $outer)
+ )
+ )
+ (br $outer)
+ )
+ )
+ )
+ )
+
+ ;; CHECK-TEXT: (func $try-table-with-label-and-br (type $2) (result i32)
+ ;; CHECK-TEXT-NEXT: (block $l-catch (result i32)
+ ;; CHECK-TEXT-NEXT: (block $l (result i32)
+ ;; CHECK-TEXT-NEXT: (try_table (result i32) (catch $e-i32 $l-catch)
+ ;; CHECK-TEXT-NEXT: (br $l
+ ;; CHECK-TEXT-NEXT: (i32.const 0)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-BIN: (func $try-table-with-label-and-br (type $2) (result i32)
+ ;; CHECK-BIN-NEXT: (block $label$1 (result i32)
+ ;; CHECK-BIN-NEXT: (block $label$2 (result i32)
+ ;; CHECK-BIN-NEXT: (try_table (result i32) (catch $e-i32 $label$1)
+ ;; CHECK-BIN-NEXT: (br $label$2
+ ;; CHECK-BIN-NEXT: (i32.const 0)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ (func $try-table-with-label-and-br (result i32)
+ (block $l-catch (result i32)
+ (try_table $l (result i32) (catch $e-i32 $l-catch)
+ (br $l (i32.const 0))
+ )
+ )
+ )
+
+ ;; CHECK-TEXT: (func $nested-try-table (type $1) (result exnref)
+ ;; CHECK-TEXT-NEXT: (block $l-catch-outer (result exnref)
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (block $l-catch-inner (result i32)
+ ;; CHECK-TEXT-NEXT: (try_table (catch_all_ref $l-catch-outer)
+ ;; CHECK-TEXT-NEXT: (try_table (catch $e-i32 $l-catch-inner)
+ ;; CHECK-TEXT-NEXT: (if
+ ;; CHECK-TEXT-NEXT: (i32.const 0)
+ ;; CHECK-TEXT-NEXT: (throw $e-i32
+ ;; CHECK-TEXT-NEXT: (i32.const 3)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (throw $e-eqref
+ ;; CHECK-TEXT-NEXT: (ref.null none)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (ref.null noexn)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-BIN: (func $nested-try-table (type $1) (result exnref)
+ ;; CHECK-BIN-NEXT: (block $label$1 (result exnref)
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block $label$2 (result i32)
+ ;; CHECK-BIN-NEXT: (try_table (catch_all_ref $label$1)
+ ;; CHECK-BIN-NEXT: (try_table (catch $e-i32 $label$2)
+ ;; CHECK-BIN-NEXT: (if
+ ;; CHECK-BIN-NEXT: (i32.const 0)
+ ;; CHECK-BIN-NEXT: (throw $e-i32
+ ;; CHECK-BIN-NEXT: (i32.const 3)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (throw $e-eqref
+ ;; CHECK-BIN-NEXT: (ref.null none)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (ref.null noexn)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ (func $nested-try-table (result exnref)
+ (block $l-catch-outer (result exnref)
+ (drop
+ (block $l-catch-inner (result i32)
+ (try_table (catch_all_ref $l-catch-outer)
+ (try_table (catch $e-i32 $l-catch-inner)
+ (if
+ (i32.const 0)
+ (throw $e-i32 (i32.const 3))
+ (throw $e-eqref (ref.null eq))
+ )
+ )
+ )
+ )
+ )
+ (ref.null noexn)
+ )
+ )
)
-;; CHECK-BIN-NODEBUG: (type $0 (func (result exnref)))
+;; CHECK-BIN-NODEBUG: (type $0 (func))
+
+;; CHECK-BIN-NODEBUG: (type $1 (func (result exnref)))
+
+;; CHECK-BIN-NODEBUG: (type $2 (func (result i32)))
+
+;; CHECK-BIN-NODEBUG: (type $3 (func (result i32 i64)))
+
+;; CHECK-BIN-NODEBUG: (type $4 (func (result i32 i64 exnref)))
+
+;; CHECK-BIN-NODEBUG: (type $5 (func (result i32 exnref)))
+
+;; CHECK-BIN-NODEBUG: (type $6 (func (param i32)))
+
+;; CHECK-BIN-NODEBUG: (type $7 (func (param i64)))
+
+;; CHECK-BIN-NODEBUG: (type $8 (func (param i32 i64)))
+
+;; CHECK-BIN-NODEBUG: (type $9 (func (param eqref)))
+
+;; CHECK-BIN-NODEBUG: (tag $tag$0 (param i32))
+
+;; CHECK-BIN-NODEBUG: (tag $tag$1 (param i64))
-;; CHECK-BIN-NODEBUG: (func $0 (type $0) (result exnref)
+;; CHECK-BIN-NODEBUG: (tag $tag$2 (param i32 i64))
+
+;; CHECK-BIN-NODEBUG: (tag $tag$3 (param eqref))
+
+;; CHECK-BIN-NODEBUG: (tag $tag$4)
+
+;; CHECK-BIN-NODEBUG: (func $0 (type $0)
+;; CHECK-BIN-NODEBUG-NEXT: (nop)
+;; CHECK-BIN-NODEBUG-NEXT: )
+
+;; CHECK-BIN-NODEBUG: (func $1 (type $1) (result exnref)
;; CHECK-BIN-NODEBUG-NEXT: (local $0 exnref)
;; CHECK-BIN-NODEBUG-NEXT: (local $1 nullexnref)
;; CHECK-BIN-NODEBUG-NEXT: (if (result exnref)
@@ -67,3 +530,177 @@
;; CHECK-BIN-NODEBUG-NEXT: (local.get $0)
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: )
+
+;; CHECK-BIN-NODEBUG: (func $2 (type $0)
+;; CHECK-BIN-NODEBUG-NEXT: (try_table
+;; CHECK-BIN-NODEBUG-NEXT: (nop)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (try_table
+;; CHECK-BIN-NODEBUG-NEXT: (throw $tag$4)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+
+;; CHECK-BIN-NODEBUG: (func $3 (type $2) (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (catch $tag$0 $label$1)
+;; CHECK-BIN-NODEBUG-NEXT: (throw $tag$0
+;; CHECK-BIN-NODEBUG-NEXT: (i32.const 0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+
+;; CHECK-BIN-NODEBUG: (func $4 (type $0)
+;; CHECK-BIN-NODEBUG-NEXT: (throw_ref
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result exnref)
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (catch_all_ref $label$1)
+;; CHECK-BIN-NODEBUG-NEXT: (throw $tag$1
+;; CHECK-BIN-NODEBUG-NEXT: (i64.const 0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+
+;; CHECK-BIN-NODEBUG: (func $5 (type $0)
+;; CHECK-BIN-NODEBUG-NEXT: (local $0 (i32 i64))
+;; CHECK-BIN-NODEBUG-NEXT: (local $1 i32)
+;; CHECK-BIN-NODEBUG-NEXT: (local $2 (i32 i64 exnref))
+;; CHECK-BIN-NODEBUG-NEXT: (local $3 i64)
+;; CHECK-BIN-NODEBUG-NEXT: (local $4 i32)
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$1
+;; CHECK-BIN-NODEBUG-NEXT: (local.set $2
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$2 (type $4) (result i32 i64 exnref)
+;; CHECK-BIN-NODEBUG-NEXT: (local.set $0
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$3 (type $3) (result i32 i64)
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (catch $tag$2 $label$3) (catch_ref $tag$2 $label$2)
+;; CHECK-BIN-NODEBUG-NEXT: (throw $tag$2
+;; CHECK-BIN-NODEBUG-NEXT: (i32.const 0)
+;; CHECK-BIN-NODEBUG-NEXT: (i64.const 0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (local.set $1
+;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 0
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 1
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $1)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (br $label$1)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (local.set $4
+;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 3 0
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $2)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block (result i64)
+;; CHECK-BIN-NODEBUG-NEXT: (local.set $3
+;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 3 1
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $2)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 3 2
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $2)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $3)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $4)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+
+;; CHECK-BIN-NODEBUG: (func $6 (type $0)
+;; CHECK-BIN-NODEBUG-NEXT: (local $0 (i32 exnref))
+;; CHECK-BIN-NODEBUG-NEXT: (local $1 i32)
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$1
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$2 (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (local.set $0
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$3 (type $5) (result i32 exnref)
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$4
+;; CHECK-BIN-NODEBUG-NEXT: (throw_ref
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$5 (result exnref)
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (catch $tag$0 $label$2) (catch_ref $tag$0 $label$3) (catch_all $label$4) (catch_all_ref $label$5)
+;; CHECK-BIN-NODEBUG-NEXT: (call $0)
+;; CHECK-BIN-NODEBUG-NEXT: (call $0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (br $label$1)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (br $label$1)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (local.set $1
+;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 0
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 1
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $1)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (br $label$1)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+
+;; CHECK-BIN-NODEBUG: (func $7 (type $2) (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$2 (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (result i32) (catch $tag$0 $label$1)
+;; CHECK-BIN-NODEBUG-NEXT: (br $label$2
+;; CHECK-BIN-NODEBUG-NEXT: (i32.const 0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+
+;; CHECK-BIN-NODEBUG: (func $8 (type $1) (result exnref)
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result exnref)
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$2 (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (catch_all_ref $label$1)
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (catch $tag$0 $label$2)
+;; CHECK-BIN-NODEBUG-NEXT: (if
+;; CHECK-BIN-NODEBUG-NEXT: (i32.const 0)
+;; CHECK-BIN-NODEBUG-NEXT: (throw $tag$0
+;; CHECK-BIN-NODEBUG-NEXT: (i32.const 3)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (throw $tag$3
+;; CHECK-BIN-NODEBUG-NEXT: (ref.null none)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (ref.null noexn)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )