diff options
author | Heejin Ahn <aheejin@gmail.com> | 2023-12-19 11:10:27 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-19 11:10:27 -0800 |
commit | 4c53361b205ad30acc05136388b789296a4180f7 (patch) | |
tree | a5802be899e365b733efacba4364027b0af9c2e0 | |
parent | cad983c975a05bc262437a6d7ed3a61020ef4e8d (diff) | |
download | binaryen-4c53361b205ad30acc05136388b789296a4180f7.tar.gz binaryen-4c53361b205ad30acc05136388b789296a4180f7.tar.bz2 binaryen-4c53361b205ad30acc05136388b789296a4180f7.zip |
[EH] Add instructions for new proposal (#6181)
This adds basic support for the new instructions in the new EH proposal
passed at the Oct CG hybrid CG meeting:
https://github.com/WebAssembly/meetings/blob/main/main/2023/CG-10.md
https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md
This mainly adds two instructions: `try_table` and `throw_ref`. This is
the bare minimum required to read and write text and binary format, and
does not include analyses or optimizations. (It includes some analysis
required for validation of existing instructions.) Validation for
the new instructions is not yet included.
`try_table` faces the same problem with the `resume` instruction in
#6083 that without the module-level tag info, we are unable to know the
'sent types' of `try_table`. This solves it with a similar approach
taken in #6083: this adds `Module*` parameter to `finalize` methods,
which defaults to `nullptr` when not given. The `Module*` parameter is
given when called from the binary and text parser, and we cache those
tag types in `sentTypes` array within `TryTable` class. In later
optimization passes, as long as they don't touch tags, it is fine to
call `finalize` without the `Module*`. Refer to
https://github.com/WebAssembly/binaryen/pull/6083#issuecomment-1854634679
and #6096 for related discussions when `resume` was added.
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: ) |