diff options
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: ) |