From cdb8eb97421d05362a5eeb8dfba1bd01df3754cd Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Mon, 2 Oct 2023 13:58:11 -0700 Subject: [PATCH 01/13] [EH] Add instructions for new proposal 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. --- scripts/gen-s-parser.py | 4 +- src/gen-s-parser.inc | 47 +- src/ir/LocalStructuralDominance.cpp | 8 + src/ir/ReFinalize.cpp | 2 + src/ir/branch-utils.h | 11 + src/ir/cost.h | 5 + src/ir/effects.h | 10 + src/ir/possible-contents.cpp | 8 + src/ir/properties.h | 6 +- src/ir/subtype-exprs.h | 2 + src/parser/parsers.h | 5 + src/passes/Print.cpp | 35 ++ src/passes/TypeGeneralizing.cpp | 2 + src/wasm-binary.h | 12 +- src/wasm-builder.h | 25 + src/wasm-delegations-fields.def | 14 + src/wasm-delegations.def | 2 + src/wasm-interpreter.h | 2 + src/wasm-ir-builder.h | 2 + src/wasm-s-parser.h | 3 +- src/wasm-stack.h | 11 + src/wasm.h | 42 ++ src/wasm/parsing.cpp | 5 +- src/wasm/wasm-binary.cpp | 69 ++- src/wasm/wasm-ir-builder.cpp | 4 + src/wasm/wasm-s-parser.cpp | 68 ++- src/wasm/wasm-stack.cpp | 30 +- src/wasm/wasm-validator.cpp | 12 +- src/wasm/wasm.cpp | 41 ++ src/wasm2js.h | 8 + test/lit/basic/exception-handling.wast | 654 ++++++++++++++++++++++++- 31 files changed, 1112 insertions(+), 37 deletions(-) diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index 0dfe220b5f5..f716302d042 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -554,8 +554,10 @@ # # 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 bc0a03dd304..2c284d56180 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/LocalStructuralDominance.cpp b/src/ir/LocalStructuralDominance.cpp index 183ca7b3fe8..37cd7f4bae6 100644 --- a/src/ir/LocalStructuralDominance.cpp +++ b/src/ir/LocalStructuralDominance.cpp @@ -207,6 +207,14 @@ LocalStructuralDominance::LocalStructuralDominance(Function* func, currp = &curr->cast()->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()->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 3d74b1422f1..65f5c472463 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 3527f1b3693..5a8ca7c3c9a 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -78,6 +78,13 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) { func(name, sw->value ? sw->value->type : Type::none); } else if (auto* br = expr->dynCast()) { func(name, br->getSentType()); + } else if (auto* tt = expr->dynCast()) { + 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() || expr->is()); // delegate or rethrow } @@ -97,6 +104,10 @@ void operateOnScopeNameUsesAndSentValues(Expression* expr, T func) { func(name, sw->value); } else if (auto* br = expr->dynCast()) { func(name, br->ref); + } else if (auto* tt = expr->dynCast()) { + // 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() || expr->is()); // delegate or rethrow } diff --git a/src/ir/cost.h b/src/ir/cost.h index d3a48353581..0e87317a624 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -584,6 +584,10 @@ struct CostAnalyzer : public OverriddenVisitor { // 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 { 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 0e45ec70c4b..1156cd82350 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -706,6 +706,11 @@ class EffectAnalyzer { 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 @@ class EffectAnalyzer { 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 e7e0cd4fd47..e9037fa87e3 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,10 @@ struct InfoCollector } } void visitRethrow(Rethrow* curr) {} + void visitThrowRef(ThrowRef* curr) { + // TODO: optimize when possible + addRoot(curr); + } void visitTupleMake(TupleMake* curr) { if (isRelevant(curr->type)) { diff --git a/src/ir/properties.h b/src/ir/properties.h index e8a17b10659..ace5a0b710f 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() || curr->is() || curr->is() || - curr->is(); + curr->is() || curr->is(); } // Check if an expression is a control flow construct with a name, which implies @@ -478,8 +478,8 @@ inline bool isResultFallthrough(Expression* curr) { // unreachable, for example, but then there is no meaningful answer to give // anyhow. return curr->is() || curr->is() || curr->is() || - curr->is() || curr->is() || curr->is() || curr->is(); } inline bool canEmitSelectWithArms(Expression* ifTrue, Expression* ifFalse) { diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h index dc9ea1432f0..86805f88a7b 100644 --- a/src/ir/subtype-exprs.h +++ b/src/ir/subtype-exprs.h @@ -212,6 +212,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor { 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 { } } 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 4909ad05751..d79ea47a19d 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -117,6 +117,7 @@ template Result<> makeTableFill(Ctx&, Index); template Result<> makeTableCopy(Ctx&, Index); template Result<> makeThrow(Ctx&, Index); template Result<> makeRethrow(Ctx&, Index); +template Result<> makeThrowRef(Ctx&, Index); template Result<> makeTupleMake(Ctx&, Index); template Result<> makeTupleExtract(Ctx&, Index); template Result<> makeTupleDrop(Ctx&, Index); @@ -1511,6 +1512,10 @@ template Result<> makeRethrow(Ctx& ctx, Index pos) { return ctx.makeRethrow(pos, *label); } +template Result<> makeThrowRef(Ctx& ctx, Index pos) { + return ctx.in.err("unimplemented instruction"); +} + template Result<> makeTupleMake(Ctx& ctx, Index pos) { return ctx.in.err("unimplemented instruction"); } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 3feebd1213e..0f482191395 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -299,6 +299,7 @@ struct PrintSExpression : public UnifiedExpressionVisitor { 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,26 @@ 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 << ' '; + printName(curr->catchDests[i], 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 +2003,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 +2750,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 6b9df651078..d167b89a9f4 100644 --- a/src/passes/TypeGeneralizing.cpp +++ b/src/passes/TypeGeneralizing.cpp @@ -499,8 +499,10 @@ struct TransferFn : OverriddenVisitor { } 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 9e68ca33039..84720865ea1 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 @@ class WasmBinaryReader { 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 2f379b4e107..fdb489c33ac 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -846,6 +846,31 @@ class Builder { Try* makeTry(Name name, Expression* body, Name delegateTarget, Type type) { return makeTry(name, body, {}, {}, delegateTarget, type, true); } + TryTable* makeTryTable(Expression* body, + const std::vector& catchTags, + const std::vector& catchDests, + const std::vector& catchRefs) { + auto* ret = wasm.allocator.alloc(); + 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& catchTags, + const std::vector& catchDests, + const std::vector& catchRefs, + Type type) { + auto* ret = wasm.allocator.alloc(); + 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& args) { return makeThrow(tag->name, args); } diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index d7567b38dcd..693e6212c29 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -596,6 +596,14 @@ switch (DELEGATE_ID) { DELEGATE_END(Try); break; } + case Expression::Id::TryTableId: { + DELEGATE_START(TryTable); + 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 +617,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); diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def index 903b19bf798..e486c490fdb 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 3599def9097..eb0a1274abc 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1394,6 +1394,7 @@ class ExpressionRunner : public OverriddenVisitor { 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 @@ class ExpressionRunner : public OverriddenVisitor { 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 8b01977be3a..2a4961534c3 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -155,8 +155,10 @@ class IRBuilder : public UnifiedExpressionVisitor> { [[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(); // [[nodiscard]] Result<> makeTupleExtract(); [[nodiscard]] Result<> makeRefI31(); diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 446f2a292ef..7921378de4d 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -284,9 +284,10 @@ class SExpressionWasmBuilder { 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 1f66212ad81..a195f1a84bd 100644 --- a/src/wasm-stack.h +++ b/src/wasm-stack.h @@ -170,6 +170,7 @@ class BinaryenIRWriter : public Visitor> { 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 void BinaryenIRWriter::visitTry(Try* curr) { } } +template +void BinaryenIRWriter::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 { diff --git a/src/wasm.h b/src/wasm.h index ce334773a2c..ecb42ba9309 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,7 +703,9 @@ class Expression { TableFillId, TableCopyId, TryId, + TryTableId, ThrowId, + ThrowRefId, RethrowId, TupleMakeId, TupleExtractId, @@ -1452,6 +1456,7 @@ class TableCopy : public SpecificExpression { void finalize(); }; +// 'try' from the old (Phase 3) EH proposal class Try : public SpecificExpression { public: Try(MixedArena& allocator) : catchTags(allocator), catchBodies(allocator) {} @@ -1471,6 +1476,32 @@ class Try : public SpecificExpression { void finalize(Type type_); }; +// 'try_table' from the new EH proposal +class TryTable : public SpecificExpression { +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 catchTags; + // catches' destination blocks + ArenaVector catchDests; + // true for catch_ref and catch_all_ref + ArenaVector catchRefs; + + bool hasCatchAll() const; + + 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 sentTypes; +}; + class Throw : public SpecificExpression { public: Throw(MixedArena& allocator) : operands(allocator) {} @@ -1481,6 +1512,7 @@ class Throw : public SpecificExpression { void finalize(); }; +// 'rethrow' from the old (Phase 3) EH proposal class Rethrow : public SpecificExpression { public: Rethrow(MixedArena& allocator) {} @@ -1490,6 +1522,16 @@ class Rethrow : public SpecificExpression { void finalize(); }; +// 'throw_ref' from the new EH proposal +class ThrowRef : public SpecificExpression { +public: + ThrowRef(MixedArena& allocator) {} + + Expression* exnref; + + void finalize(); +}; + class TupleMake : public SpecificExpression { public: TupleMake(MixedArena& allocator) : operands(allocator) {} diff --git a/src/wasm/parsing.cpp b/src/wasm/parsing.cpp index c6134922a70..1606a2dd1ff 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 be1c27d039a..bf14efeb182 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())->cast()); + break; case BinaryConsts::Throw: visitThrow((curr = allocator.alloc())->cast()); break; case BinaryConsts::Rethrow: visitRethrow((curr = allocator.alloc())->cast()); break; + case BinaryConsts::ThrowRef: + visitThrowRef((curr = allocator.alloc())->cast()); + break; case BinaryConsts::MemorySize: { auto size = allocator.alloc(); curr = size; @@ -6917,9 +6923,9 @@ void WasmBinaryReader::visitTryOrTryInBlock(Expression*& out) { // here, then do that later. std::vector 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 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 d3f48b1c022..fd02ee2ed99 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -1286,6 +1286,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; @@ -1302,6 +1304,8 @@ Result<> IRBuilder::makeRethrow(Index label) { return Ok{}; } +// Result<> IRBuilder::makeThrowRef() {} + // Result<> IRBuilder::makeTupleMake() {} // Result<> IRBuilder::makeTupleExtract() {} diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 94b434dfba9..49cf1ccfc71 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(); + 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->name = label; + block->list.push_back(ret); + block->finalize(type); + return block; + } + return ret; +} + Expression* SExpressionWasmBuilder::makeThrow(Element& s) { auto ret = allocator.alloc(); Index i = 1; @@ -2859,6 +2918,13 @@ Expression* SExpressionWasmBuilder::makeRethrow(Element& s) { return ret; } +Expression* SExpressionWasmBuilder::makeThrowRef(Element& s) { + auto ret = allocator.alloc(); + ret->exnref = parseExpression(s[1]); + ret->finalize(); + return ret; +} + Expression* SExpressionWasmBuilder::makeTupleMake(Element& s) { auto ret = allocator.alloc(); size_t arity = std::stoll(s[1]->toString()); diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 0f8facfd510..442de4d6692 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) { + // the binary format requires this; we have a block if we need one + 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])); + } + // 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 cb54e1597d4..22908475d9f 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -354,7 +354,8 @@ struct FunctionValidator : public WalkerPass> { 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 @@ struct FunctionValidator : public WalkerPass> { 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,11 @@ void FunctionValidator::visitTry(Try* curr) { rethrowTargetNames.erase(curr->name); } +void FunctionValidator::visitTryTable(TryTable* curr) { + // check all array's length is the same including sentTypes + // +} + void FunctionValidator::visitThrow(Throw* curr) { shouldBeTrue( getModule()->features.hasExceptionHandling(), @@ -2516,6 +2524,8 @@ void FunctionValidator::visitTupleMake(TupleMake* curr) { "Type of tuple.make does not match types of its operands"); } +void FunctionValidator::visitThrowRef(ThrowRef* curr) {} + void FunctionValidator::visitTupleExtract(TupleExtract* curr) { shouldBeTrue(getModule()->features.hasMultivalue(), curr, diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 8ff2dc978aa..bc4e375b37e 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( + catchRefs.begin(), catchRefs.end(), [](bool v) { return v; }); +} + +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 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 types; types.reserve(operands.size()); diff --git a/src/wasm2js.h b/src/wasm2js.h index 4aef8c3789a..c3f3b5fa60d 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/lit/basic/exception-handling.wast b/test/lit/basic/exception-handling.wast index 945cca1af83..8b9310547fe 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) @@ -40,7 +100,8 @@ ;; CHECK-BIN-NEXT: (local.get $exn) ;; CHECK-BIN-NEXT: ) ;; CHECK-BIN-NEXT: ) - (func $exnref-nullexnref-test (result exnref) (local $exn exnref) (local $null-exn nullexnref) + (func $exnref-nullexnref-test (result exnref) + (local $exn exnref) (local $null-exn nullexnref) (if (result exnref) (i32.const 1) (if (result nullexnref) @@ -51,10 +112,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 $outer (result i32) + ;; CHECK-TEXT-NEXT: (block $l (result i32) + ;; CHECK-TEXT-NEXT: (try_table (result i32) (catch_all $outer) + ;; 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_all $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 $outer (result i32) + (try_table $l (result i32) (catch_all $outer) + (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 +531,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_all $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: ) From 424c7998ac05dfbfdba2cf14ee283c12c57aa970 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Dec 2023 17:03:03 -0800 Subject: [PATCH 02/13] clang-format --- src/wasm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wasm.h b/src/wasm.h index ecb42ba9309..9f2272a22ae 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1494,8 +1494,8 @@ class TryTable : public SpecificExpression { bool hasCatchAll() const; - void finalize(Module* wasm=nullptr); - void finalize(Type type_, Module* wasm=nullptr); + 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 From 168b984401b90fbb51247651540875a68ea7e025 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Dec 2023 17:05:19 -0800 Subject: [PATCH 03/13] simplify --- src/passes/Print.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 0f482191395..71181566d5e 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1987,11 +1987,10 @@ struct PrintExpressionContents printMedium(o, curr->catchRefs[i] ? "catch_ref " : "catch "); printName(curr->catchTags[i], o); o << ' '; - printName(curr->catchDests[i], o); } else { printMedium(o, curr->catchRefs[i] ? "catch_all_ref " : "catch_all "); - printName(curr->catchDests[i], o); } + printName(curr->catchDests[i], o); o << ')'; } } From 8bb08e569be6406475cc7457781afef4c602f31c Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Dec 2023 17:12:33 -0800 Subject: [PATCH 04/13] validator todo --- src/wasm/wasm-validator.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 22908475d9f..c1b4a86e78d 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2446,8 +2446,7 @@ void FunctionValidator::visitTry(Try* curr) { } void FunctionValidator::visitTryTable(TryTable* curr) { - // check all array's length is the same including sentTypes - // + // TODO } void FunctionValidator::visitThrow(Throw* curr) { @@ -2524,7 +2523,9 @@ void FunctionValidator::visitTupleMake(TupleMake* curr) { "Type of tuple.make does not match types of its operands"); } -void FunctionValidator::visitThrowRef(ThrowRef* curr) {} +void FunctionValidator::visitThrowRef(ThrowRef* curr) { + // TODO +} void FunctionValidator::visitTupleExtract(TupleExtract* curr) { shouldBeTrue(getModule()->features.hasMultivalue(), From 54b4e1cdd79e72c3c18ab35f33e19081728ef190 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Dec 2023 22:50:04 -0800 Subject: [PATCH 05/13] Fix stale type error --- test/lit/basic/exception-handling.wast | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/lit/basic/exception-handling.wast b/test/lit/basic/exception-handling.wast index 8b9310547fe..5c6dcfe7b49 100644 --- a/test/lit/basic/exception-handling.wast +++ b/test/lit/basic/exception-handling.wast @@ -392,9 +392,9 @@ ) ;; CHECK-TEXT: (func $try-table-with-label-and-br (type $2) (result i32) - ;; CHECK-TEXT-NEXT: (block $outer (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_all $outer) + ;; 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: ) @@ -405,7 +405,7 @@ ;; 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_all $label$1) + ;; 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: ) @@ -414,8 +414,8 @@ ;; CHECK-BIN-NEXT: ) ;; CHECK-BIN-NEXT: ) (func $try-table-with-label-and-br (result i32) - (block $outer (result i32) - (try_table $l (result i32) (catch_all $outer) + (block $l-catch (result i32) + (try_table $l (result i32) (catch $e-i32 $l-catch) (br $l (i32.const 0)) ) ) @@ -674,7 +674,7 @@ ;; 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_all $label$1) +;; 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: ) From 51e4efdcec6049658d9eea8f457414e361449df0 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Dec 2023 22:51:40 -0800 Subject: [PATCH 06/13] Add comments --- src/wasm.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/wasm.h b/src/wasm.h index 5ffbb966476..277b110950c 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1494,6 +1494,9 @@ class TryTable : public SpecificExpression { 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); From 6eaa08ccad1d2c2f5e7f32d93078a2667dd0f2d0 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Dec 2023 23:20:07 -0800 Subject: [PATCH 07/13] binaryen.js test fix --- src/wasm.h | 2 +- test/binaryen.js/exception-handling.js.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wasm.h b/src/wasm.h index 277b110950c..576a0d0f19d 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -705,8 +705,8 @@ class Expression { TryId, TryTableId, ThrowId, - ThrowRefId, RethrowId, + ThrowRefId, TupleMakeId, TupleExtractId, RefI31Id, diff --git a/test/binaryen.js/exception-handling.js.txt b/test/binaryen.js/exception-handling.js.txt index 062f05202d6..d2a0c6dd62f 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} From b3888f8c8027b6ac51d18b93586b4b8d9fefe51a Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Dec 2023 23:37:52 -0800 Subject: [PATCH 08/13] Fix binaryen.js instruction ID shift --- test/binaryen.js/kitchen-sink.js.txt | 66 ++++++++++++++-------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index c3513b6ba7d..168bc00733f 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) From 19a3a12f6845a4d2efb16c34b93629d9ca364df3 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Dec 2023 22:52:26 -0800 Subject: [PATCH 09/13] Add catchRefs and sentTypes to wasm-delegations-fields.def --- src/wasm-delegations-fields.def | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index 693e6212c29..3f8072445c0 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 @@ -598,6 +628,8 @@ switch (DELEGATE_ID) { } 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); @@ -923,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 @@ -932,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 From a1568de9580c97855f2981c3df73e560783f669e Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 14 Dec 2023 10:45:48 -0800 Subject: [PATCH 10/13] Fix --- src/ir/ExpressionAnalyzer.cpp | 2 ++ src/ir/ExpressionManipulator.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp index 62ad3ed3e5c..a75abd6ce9f 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 9232dddf269..4cac87a01b1 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" From 89a5e46ffbb79bcd3f34501f51620932c350dbdf Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 14 Dec 2023 18:25:24 -0800 Subject: [PATCH 11/13] Address comments --- src/ir/branch-utils.h | 8 +++----- src/ir/possible-contents.cpp | 5 +---- src/wasm/wasm-stack.cpp | 2 +- src/wasm/wasm.cpp | 2 +- test/lit/basic/exception-handling.wast | 3 +-- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h index 5b49d60a22d..53bebafc619 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -77,10 +77,7 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) { func(name, br->getSentType()); } else if (auto* tt = expr->dynCast()) { for (Index i = 0; i < tt->catchTags.size(); i++) { - auto dest = tt->catchDests[i]; - if (dest == name) { - func(name, tt->sentTypes[i]); - } + func(name, tt->sentTypes[i]); } } else { assert(expr->is() || expr->is()); // delegate or rethrow @@ -89,7 +86,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 void operateOnScopeNameUsesAndSentValues(Expression* expr, T func) { operateOnScopeNameUses(expr, [&](Name& name) { diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index e9037fa87e3..67b33553d55 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -1169,10 +1169,7 @@ struct InfoCollector } } void visitRethrow(Rethrow* curr) {} - void visitThrowRef(ThrowRef* curr) { - // TODO: optimize when possible - addRoot(curr); - } + void visitThrowRef(ThrowRef* curr) {} void visitTupleMake(TupleMake* curr) { if (isRelevant(curr->type)) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 442de4d6692..3b14b3c35ba 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1955,7 +1955,6 @@ void BinaryInstWriter::visitTry(Try* curr) { } void BinaryInstWriter::visitTryTable(TryTable* curr) { - // the binary format requires this; we have a block if we need one o << int8_t(BinaryConsts::TryTable); emitResultType(curr->type); o << U32LEB(curr->catchTags.size()); @@ -1970,6 +1969,7 @@ void BinaryInstWriter::visitTryTable(TryTable* curr) { } 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); diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index bc4e375b37e..20f248ec9c4 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -904,7 +904,7 @@ void ThrowRef::finalize() { type = Type::unreachable; } bool TryTable::hasCatchAll() const { return std::any_of( - catchRefs.begin(), catchRefs.end(), [](bool v) { return v; }); + catchTags.begin(), catchTags.end(), [](bool t) { return !t; }); } static void populateTryTableSentTypes(TryTable* curr, Module* wasm) { diff --git a/test/lit/basic/exception-handling.wast b/test/lit/basic/exception-handling.wast index 5c6dcfe7b49..a06e377e666 100644 --- a/test/lit/basic/exception-handling.wast +++ b/test/lit/basic/exception-handling.wast @@ -100,8 +100,7 @@ ;; CHECK-BIN-NEXT: (local.get $exn) ;; CHECK-BIN-NEXT: ) ;; CHECK-BIN-NEXT: ) - (func $exnref-nullexnref-test (result exnref) - (local $exn exnref) (local $null-exn nullexnref) + (func $exnref-nullexnref-test (result exnref) (local $exn exnref) (local $null-exn nullexnref) (if (result exnref) (i32.const 1) (if (result nullexnref) From 21407d78a7b4d90c8951487980f992a959601f30 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 14 Dec 2023 18:34:03 -0800 Subject: [PATCH 12/13] Fix --- src/wasm/wasm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 20f248ec9c4..6589ca06984 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -904,7 +904,7 @@ void ThrowRef::finalize() { type = Type::unreachable; } bool TryTable::hasCatchAll() const { return std::any_of( - catchTags.begin(), catchTags.end(), [](bool t) { return !t; }); + catchTags.begin(), catchTags.end(), [](Name t) { return !t; }); } static void populateTryTableSentTypes(TryTable* curr, Module* wasm) { From 5420500d13bb1677001e6e58b7294e24c8c15601 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Fri, 15 Dec 2023 17:57:40 -0800 Subject: [PATCH 13/13] Revert branch-utils --- src/ir/branch-utils.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h index 53bebafc619..3c02757015a 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -77,7 +77,10 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) { func(name, br->getSentType()); } else if (auto* tt = expr->dynCast()) { for (Index i = 0; i < tt->catchTags.size(); i++) { - func(name, tt->sentTypes[i]); + auto dest = tt->catchDests[i]; + if (dest == name) { + func(name, tt->sentTypes[i]); + } } } else { assert(expr->is() || expr->is()); // delegate or rethrow