From bc7e50a55205fbed2e91cb63440349904c75cfad Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Fri, 1 Jan 2021 18:20:46 -0800 Subject: [PATCH 01/10] Basic EH instrucion support for the new spec This updates `try`-`catch`-`catch_all` and `rethrow` instructions to match the new spec. `delegate` is not included. Now `Try` contains not a single `catchBody` expression but a vector of catch bodies and events. This updates most existing routines, optimizations, and tests modulo the interpreter and the CFG traversal. Because the interpreter has not been updated yet, the EH spec test is temporarily disabled in check.py. Also, because the CFG traversal for EH is not yet updated, several EH tests in `rse_all-features.wast`, which uses CFG traversal, are temporarily commented out. --- check.py | 3 + src/binaryen-c.cpp | 129 +++++++++++-- src/binaryen-c.h | 78 ++++++-- src/cfg/cfg-traversal.h | 3 + src/ir/ExpressionAnalyzer.cpp | 5 + src/ir/ExpressionManipulator.cpp | 1 + src/ir/branch-utils.h | 2 + src/ir/cost.h | 2 +- src/ir/effects.h | 33 +++- src/ir/utils.h | 6 +- src/js/binaryen.js-post.js | 118 ++++++++++-- src/passes/CodeFolding.cpp | 8 +- src/passes/DeadCodeElimination.cpp | 7 +- src/passes/OptimizeInstructions.cpp | 4 +- src/passes/Print.cpp | 32 +++- src/passes/SimplifyLocals.cpp | 4 +- src/passes/Vacuum.cpp | 4 +- src/wasm-binary.h | 1 + src/wasm-builder.h | 19 +- src/wasm-delegations-fields.h | 9 +- src/wasm-interpreter.h | 4 + src/wasm-stack.h | 29 ++- src/wasm-traversal.h | 9 +- src/wasm.h | 10 +- src/wasm/wasm-binary.cpp | 60 ++++-- src/wasm/wasm-s-parser.cpp | 52 +++++- src/wasm/wasm-stack.cpp | 32 +++- src/wasm/wasm-validator.cpp | 37 ++-- src/wasm/wasm.cpp | 12 +- test/binaryen.js/exception-handling.js | 34 ++-- test/binaryen.js/exception-handling.js.txt | 20 +- test/binaryen.js/expressions.js | 71 ++++++-- test/binaryen.js/expressions.js.txt | 63 ++++++- test/binaryen.js/kitchen-sink.js | 14 +- test/binaryen.js/kitchen-sink.js.txt | 26 +-- test/binaryen.js/sideffects.js | 2 +- test/break-within-catch.wasm | Bin 32 -> 42 bytes test/break-within-catch.wasm.fromBinary | 6 +- test/example/c-api-kitchen-sink.c | 40 +--- test/example/c-api-kitchen-sink.txt | 16 +- test/exception-handling.wast | 92 ++++++++-- test/exception-handling.wast.from-wast | 113 ++++++++++-- test/exception-handling.wast.fromBinary | 137 ++++++++++++-- ...ption-handling.wast.fromBinary.noDebugInfo | 135 ++++++++++++-- test/passes/code-pushing_all-features.txt | 42 ++++- test/passes/code-pushing_all-features.wast | 35 +++- test/passes/dce_all-features.txt | 16 +- test/passes/dce_all-features.wast | 16 +- ...e-stack-ir_print-stack-ir_all-features.txt | 24 +-- ...-stack-ir_print-stack-ir_all-features.wast | 14 +- ...ures_disable-typed-function-references.txt | 28 +-- ...res_disable-typed-function-references.wast | 18 +- .../optimize-instructions_all-features.txt | 5 +- .../optimize-instructions_all-features.wast | 5 +- ...ve-unused-module-elements_all-features.txt | 17 +- ...e-unused-module-elements_all-features.wast | 15 +- ...unused-names_code-folding_all-features.txt | 37 ++-- ...nused-names_code-folding_all-features.wast | 30 ++- ...unused-names_merge-blocks_all-features.txt | 7 - ...nused-names_merge-blocks_all-features.wast | 10 - ...mes_optimize-instructions_all-features.txt | 59 +++--- ...es_optimize-instructions_all-features.wast | 48 +++-- test/passes/rse_all-features.txt | 133 -------------- test/passes/rse_all-features.wast | 171 +++++++++--------- test/passes/simplify-locals_all-features.txt | 50 ++--- test/passes/simplify-locals_all-features.wast | 46 ++--- test/passes/vacuum_all-features.txt | 34 +++- test/passes/vacuum_all-features.wast | 43 +++-- test/reference-types.wast | 41 +++-- test/reference-types.wast.from-wast | 47 +++-- test/reference-types.wast.fromBinary | 47 +++-- ...eference-types.wast.fromBinary.noDebugInfo | 47 +++-- 72 files changed, 1610 insertions(+), 957 deletions(-) diff --git a/check.py b/check.py index 237697d4302..fa9a060f843 100755 --- a/check.py +++ b/check.py @@ -189,6 +189,9 @@ def run_spec_tests(): # windows has some failures that need to be investigated if base == 'names.wast' and shared.skip_if_on_windows('spec: ' + base): continue + # FIXME Reenable this after updating interpreter for EH + if base == 'exception-handling.wast': + continue def run_spec_test(wast): cmd = shared.WASM_SHELL + [wast] diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 59ca3aebcca..65601949971 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -1200,10 +1200,20 @@ BinaryenExpressionRef BinaryenRefEq(BinaryenModuleRef module, BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module, BinaryenExpressionRef body, - BinaryenExpressionRef catchBody) { - return static_cast( - Builder(*(Module*)module) - .makeTry((Expression*)body, (Expression*)catchBody)); + const char** catchEvents, + BinaryenIndex numCatchEvents, + BinaryenExpressionRef* catchBodies, + BinaryenIndex numCatchBodies) { + auto* ret = ((Module*)module)->allocator.alloc(); + ret->body = (Expression*)body; + for (BinaryenIndex i = 0; i < numCatchEvents; i++) { + ret->catchEvents.push_back(catchEvents[i]); + } + for (BinaryenIndex i = 0; i < numCatchBodies; i++) { + ret->catchBodies.push_back((Expression*)catchBodies[i]); + } + ret->finalize(); + return ret; } BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module, @@ -1219,9 +1229,8 @@ BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module, } BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module, - BinaryenExpressionRef exnref) { - return static_cast( - Builder(*(Module*)module).makeRethrow((Expression*)exnref)); + BinaryenIndex depth) { + return static_cast(Builder(*(Module*)module).makeRethrow(depth)); } BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module, @@ -2755,17 +2764,101 @@ void BinaryenTrySetBody(BinaryenExpressionRef expr, assert(bodyExpr); static_cast(expression)->body = (Expression*)bodyExpr; } -BinaryenExpressionRef BinaryenTryGetCatchBody(BinaryenExpressionRef expr) { +BinaryenIndex BinaryenTryGetNumCatchEvents(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is()); + return static_cast(expression)->catchEvents.size(); +} +BinaryenIndex BinaryenTryGetNumCatchBodies(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is()); + return static_cast(expression)->catchBodies.size(); +} +const char* BinaryenTryGetCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index) { + auto* expression = (Expression*)expr; + assert(expression->is()); + assert(index < static_cast(expression)->catchEvents.size()); + return static_cast(expression)->catchEvents[index].c_str(); +} +void BinaryenTrySetCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index, + const char* catchEvent) { + auto* expression = (Expression*)expr; + assert(expression->is()); + assert(index < static_cast(expression)->catchEvents.size()); + assert(catchEvent); + static_cast(expression)->catchEvents[index] = catchEvent; +} +BinaryenIndex BinaryenTryAppendCatchEvent(BinaryenExpressionRef expr, + const char* catchEvent) { auto* expression = (Expression*)expr; assert(expression->is()); - return static_cast(expression)->catchBody; + assert(catchEvent); + auto& list = static_cast(expression)->catchEvents; + auto index = list.size(); + list.push_back(catchEvent); + return index; } -void BinaryenTrySetCatchBody(BinaryenExpressionRef expr, - BinaryenExpressionRef catchBodyExpr) { +void BinaryenTryInsertCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index, + const char* catchEvent) { auto* expression = (Expression*)expr; assert(expression->is()); - assert(catchBodyExpr); - static_cast(expression)->catchBody = (Expression*)catchBodyExpr; + assert(catchEvent); + static_cast(expression)->catchEvents.insertAt(index, catchEvent); +} +const char* BinaryenTryRemoveCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index) { + auto* expression = (Expression*)expr; + assert(expression->is()); + return static_cast(expression)->catchEvents.removeAt(index).c_str(); +} +BinaryenExpressionRef BinaryenTryGetCatchBodyAt(BinaryenExpressionRef expr, + BinaryenIndex index) { + auto* expression = (Expression*)expr; + assert(expression->is()); + assert(index < static_cast(expression)->catchBodies.size()); + return static_cast(expression)->catchBodies[index]; +} +void BinaryenTrySetCatchBodyAt(BinaryenExpressionRef expr, + BinaryenIndex index, + BinaryenExpressionRef catchExpr) { + auto* expression = (Expression*)expr; + assert(expression->is()); + assert(index < static_cast(expression)->catchBodies.size()); + assert(catchExpr); + static_cast(expression)->catchBodies[index] = (Expression*)catchExpr; +} +BinaryenIndex BinaryenTryAppendCatchBody(BinaryenExpressionRef expr, + BinaryenExpressionRef catchExpr) { + auto* expression = (Expression*)expr; + assert(expression->is()); + assert(catchExpr); + auto& list = static_cast(expression)->catchBodies; + auto index = list.size(); + list.push_back((Expression*)catchExpr); + return index; +} +void BinaryenTryInsertCatchBodyAt(BinaryenExpressionRef expr, + BinaryenIndex index, + BinaryenExpressionRef catchExpr) { + auto* expression = (Expression*)expr; + assert(expression->is()); + assert(catchExpr); + static_cast(expression) + ->catchBodies.insertAt(index, (Expression*)catchExpr); +} +BinaryenExpressionRef BinaryenTryRemoveCatchBodyAt(BinaryenExpressionRef expr, + BinaryenIndex index) { + auto* expression = (Expression*)expr; + assert(expression->is()); + return static_cast(expression)->catchBodies.removeAt(index); +} +int BinaryenTryHasCatchAll(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is()); + return static_cast(expression)->hasCatchAll(); } // Throw const char* BinaryenThrowGetEvent(BinaryenExpressionRef expr) { @@ -2825,17 +2918,15 @@ BinaryenExpressionRef BinaryenThrowRemoveOperandAt(BinaryenExpressionRef expr, return static_cast(expression)->operands.removeAt(index); } // Rethrow -BinaryenExpressionRef BinaryenRethrowGetExnref(BinaryenExpressionRef expr) { +BinaryenIndex BinaryenRethrowGetDepth(BinaryenExpressionRef expr) { auto* expression = (Expression*)expr; assert(expression->is()); - return static_cast(expression)->exnref; + return static_cast(expression)->depth; } -void BinaryenRethrowSetExnref(BinaryenExpressionRef expr, - BinaryenExpressionRef exnrefExpr) { +void BinaryenRethrowSetDepth(BinaryenExpressionRef expr, BinaryenIndex depth) { auto* expression = (Expression*)expr; assert(expression->is()); - assert(exnrefExpr); - static_cast(expression)->exnref = (Expression*)exnrefExpr; + static_cast(expression)->depth = depth; } // BrOnExn const char* BinaryenBrOnExnGetEvent(BinaryenExpressionRef expr) { diff --git a/src/binaryen-c.h b/src/binaryen-c.h index a32d9ff1c3e..8d991343d69 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -796,16 +796,20 @@ BINARYEN_API BinaryenExpressionRef BinaryenRefFunc(BinaryenModuleRef module, BINARYEN_API BinaryenExpressionRef BinaryenRefEq(BinaryenModuleRef module, BinaryenExpressionRef left, BinaryenExpressionRef right); -BINARYEN_API BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module, - BinaryenExpressionRef body, - BinaryenExpressionRef catchBody); +BINARYEN_API BinaryenExpressionRef +BinaryenTry(BinaryenModuleRef module, + BinaryenExpressionRef body, + const char** catchEvents, + BinaryenIndex numCatchEvents, + BinaryenExpressionRef* catchBodies, + BinaryenIndex numCatchBodies); BINARYEN_API BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module, const char* event, BinaryenExpressionRef* operands, BinaryenIndex numOperands); -BINARYEN_API BinaryenExpressionRef -BinaryenRethrow(BinaryenModuleRef module, BinaryenExpressionRef exnref); +BINARYEN_API BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module, + BinaryenIndex depth); BINARYEN_API BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module, const char* name, @@ -1714,12 +1718,57 @@ BinaryenTryGetBody(BinaryenExpressionRef expr); // Sets the body expression of a `try` expression. BINARYEN_API void BinaryenTrySetBody(BinaryenExpressionRef expr, BinaryenExpressionRef bodyExpr); -// Gets the catch body expression of a `try` expression. +// Gets the number of catch blocks (= the number of catch events) of a `try` +// expression. +BINARYEN_API BinaryenIndex +BinaryenTryGetNumCatchEvents(BinaryenExpressionRef expr); +// Gets the number of catch/catch_all blocks of a `try` expression. +BINARYEN_API BinaryenIndex +BinaryenTryGetNumCatchBodies(BinaryenExpressionRef expr); +// Gets the catch event at the specified index of a `try` expression. +BINARYEN_API const char* BinaryenTryGetCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index); +// Sets the catch event at the specified index of a `try` expression. +BINARYEN_API void BinaryenTrySetCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index, + const char* catchEvent); +// Appends a catch event to a `try` expression, returning its insertion index. +BINARYEN_API BinaryenIndex +BinaryenTryAppendCatchEvent(BinaryenExpressionRef expr, const char* catchEvent); +// Inserts a catch event at the specified index of a `try` expression, moving +// existing catch events including the one previously at that index one index +// up. +BINARYEN_API void BinaryenTryInsertCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index, + const char* catchEvent); +// Removes the catch event at the specified index of a `try` expression, moving +// all subsequent catch events one index down. Returns the event. +BINARYEN_API const char* +BinaryenTryRemoveCatchEventAt(BinaryenExpressionRef expr, BinaryenIndex index); +// Gets the catch body expression at the specified index of a `try` expression. +BINARYEN_API BinaryenExpressionRef +BinaryenTryGetCatchBodyAt(BinaryenExpressionRef expr, BinaryenIndex index); +// Sets the catch body expression at the specified index of a `try` expression. +BINARYEN_API void BinaryenTrySetCatchBodyAt(BinaryenExpressionRef expr, + BinaryenIndex index, + BinaryenExpressionRef catchExpr); +// Appends a catch expression to a `try` expression, returning its insertion +// index. +BINARYEN_API BinaryenIndex BinaryenTryAppendCatchBody( + BinaryenExpressionRef expr, BinaryenExpressionRef catchExpr); +// Inserts a catch expression at the specified index of a `try` expression, +// moving existing catch bodies including the one previously at that index one +// index up. +BINARYEN_API void BinaryenTryInsertCatchBodyAt(BinaryenExpressionRef expr, + BinaryenIndex index, + BinaryenExpressionRef catchExpr); +// Removes the catch expression at the specified index of a `try` expression, +// moving all subsequent catch bodies one index down. Returns the catch +// expression. BINARYEN_API BinaryenExpressionRef -BinaryenTryGetCatchBody(BinaryenExpressionRef expr); -// Sets the catch body expression of a `try` expression. -BINARYEN_API void BinaryenTrySetCatchBody(BinaryenExpressionRef expr, - BinaryenExpressionRef catchBodyExpr); +BinaryenTryRemoveCatchBodyAt(BinaryenExpressionRef expr, BinaryenIndex index); +// Gets whether an `try` expression has a catch_all clause. +BINARYEN_API int BinaryenTryHasCatchAll(BinaryenExpressionRef expr); // Throw @@ -1757,12 +1806,11 @@ BinaryenThrowRemoveOperandAt(BinaryenExpressionRef expr, BinaryenIndex index); // Rethrow -// Gets the exception reference expression of a `rethrow` expression. -BINARYEN_API BinaryenExpressionRef -BinaryenRethrowGetExnref(BinaryenExpressionRef expr); +// Gets the depth of a `rethrow` expression. +BINARYEN_API BinaryenIndex BinaryenRethrowGetDepth(BinaryenExpressionRef expr); // Sets the exception reference expression of a `rethrow` expression. -BINARYEN_API void BinaryenRethrowSetExnref(BinaryenExpressionRef expr, - BinaryenExpressionRef exnrefExpr); +BINARYEN_API void BinaryenRethrowSetDepth(BinaryenExpressionRef expr, + BinaryenIndex depth); // BrOnExn diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h index 647b795a472..9bfd755f477 100644 --- a/src/cfg/cfg-traversal.h +++ b/src/cfg/cfg-traversal.h @@ -304,11 +304,14 @@ struct CFGWalker : public ControlFlowWalker { break; } case Expression::Id::TryId: { + // FIXME Update the implementation to match the new spec + /* self->pushTask(SubType::doEndTry, currp); self->pushTask(SubType::scan, &curr->cast()->catchBody); self->pushTask(SubType::doStartCatch, currp); self->pushTask(SubType::scan, &curr->cast()->body); self->pushTask(SubType::doStartTry, currp); + */ return; // don't do anything else } case Expression::Id::ThrowId: diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp index 88dce6767dd..945af1e6b65 100644 --- a/src/ir/ExpressionAnalyzer.cpp +++ b/src/ir/ExpressionAnalyzer.cpp @@ -206,6 +206,7 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left, } #define DELEGATE_FIELD_INT_ARRAY(id, name) COMPARE_LIST(name) +#define DELEGATE_FIELD_NAME_VECTOR(id, name) COMPARE_LIST(name) #define DELEGATE_FIELD_SCOPE_NAME_DEF(id, name) \ if (castLeft->name.is() != castRight->name.is()) { \ @@ -306,6 +307,10 @@ size_t ExpressionAnalyzer::hash(Expression* curr) { #define DELEGATE_FIELD_SIGNATURE(id, name) HASH_FIELD(name) #define DELEGATE_FIELD_NAME(id, name) visitNonScopeName(cast->name) +#define DELEGATE_FIELD_NAME_VECTOR(id, name) \ + for (auto n : cast->name) { \ + visitNonScopeName(n); \ + } #define DELEGATE_FIELD_TYPE(id, name) visitType(cast->name); #define DELEGATE_FIELD_ADDRESS(id, name) visitAddress(cast->name); diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp index 18c3f2df9db..7ad8d0f505d 100644 --- a/src/ir/ExpressionManipulator.cpp +++ b/src/ir/ExpressionManipulator.cpp @@ -97,6 +97,7 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) { COPY_FIELD_LIST(name) #define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, name) COPY_VECTOR(name) +#define DELEGATE_FIELD_NAME_VECTOR(id, name) COPY_VECTOR(name) #define DELEGATE_FIELD_INT_ARRAY(id, name) COPY_ARRAY(name) diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h index d7a6edcaed9..2e7ef470d2a 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -58,6 +58,7 @@ template void operateOnScopeNameUses(Expression* expr, T func) { #define DELEGATE_FIELD_INT(id, name) #define DELEGATE_FIELD_LITERAL(id, name) #define DELEGATE_FIELD_NAME(id, name) +#define DELEGATE_FIELD_NAME_VECTOR(id, name) #define DELEGATE_FIELD_SCOPE_NAME_DEF(id, name) #define DELEGATE_FIELD_SIGNATURE(id, name) #define DELEGATE_FIELD_TYPE(id, name) @@ -104,6 +105,7 @@ template void operateOnScopeNameDefs(Expression* expr, T func) { #define DELEGATE_FIELD_INT(id, name) #define DELEGATE_FIELD_LITERAL(id, name) #define DELEGATE_FIELD_NAME(id, name) +#define DELEGATE_FIELD_NAME_VECTOR(id, name) #define DELEGATE_FIELD_SIGNATURE(id, name) #define DELEGATE_FIELD_TYPE(id, name) #define DELEGATE_FIELD_ADDRESS(id, name) diff --git a/src/ir/cost.h b/src/ir/cost.h index fbb4e83adab..a107cdc45c4 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -546,7 +546,7 @@ struct CostAnalyzer : public OverriddenVisitor { } Index visitTry(Try* curr) { // We assume no exception will be thrown in most cases - return visit(curr->body) + maybeVisit(curr->catchBody); + return visit(curr->body); } Index visitThrow(Throw* curr) { Index ret = 100; diff --git a/src/ir/effects.h b/src/ir/effects.h index 71957d1f82b..22fab593ddb 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -84,14 +84,16 @@ class EffectAnalyzer { // wrt atomics (e.g. memory.grow) bool isAtomic = false; bool throws = false; - // The nested depth of try. If an instruction that may throw is inside an - // inner try, we don't mark it as 'throws', because it will be caught by an - // inner catch. + // The nested depth of try-catch_all. If an instruction that may throw is + // inside an inner try-catch_all, we don't mark it as 'throws', because it + // will be caught by an inner catch_all. We only count 'try's with a + // 'catch_all' because instructions within a 'try' without a 'catch_all' can + // still throw outside of the try. size_t tryDepth = 0; // The nested depth of catch. This is necessary to track danglng pops. size_t catchDepth = 0; - // If this expression contains 'exnref.pop's that are not enclosed in 'catch' - // body. For example, (drop (exnref.pop)) should set this to true. + // If this expression contains 'pop's that are not enclosed in 'catch' body. + // For example, (drop (pop i32)) should set this to true. bool danglingPop = false; // Helper functions to check for various effect types @@ -258,7 +260,10 @@ class EffectAnalyzer { if (curr->is()) { self->pushTask(doVisitTry, currp); self->pushTask(doEndCatch, currp); - self->pushTask(scan, &curr->cast()->catchBody); + auto& catchBodies = curr->cast()->catchBodies; + for (int i = int(catchBodies.size()) - 1; i >= 0; i--) { + self->pushTask(scan, &catchBodies[i]); + } self->pushTask(doStartCatch, currp); self->pushTask(scan, &curr->cast()->body); self->pushTask(doStartTry, currp); @@ -269,12 +274,22 @@ class EffectAnalyzer { } static void doStartTry(InternalAnalyzer* self, Expression** currp) { - self->parent.tryDepth++; + Try* curr = (*currp)->cast(); + // We only count 'try's with a 'catch_all' because instructions within a + // 'try' without a 'catch_all' can still throw outside of the try. + if (curr->hasCatchAll()) { + self->parent.tryDepth++; + } } static void doStartCatch(InternalAnalyzer* self, Expression** currp) { - assert(self->parent.tryDepth > 0 && "try depth cannot be negative"); - self->parent.tryDepth--; + Try* curr = (*currp)->cast(); + // We only count 'try's with a 'catch_all' because instructions within a + // 'try' without a 'catch_all' can still throw outside of the try. + if (curr->hasCatchAll()) { + assert(self->parent.tryDepth > 0 && "try depth cannot be negative"); + self->parent.tryDepth--; + } self->parent.catchDepth++; } diff --git a/src/ir/utils.h b/src/ir/utils.h index 0136fd11c4d..424298bb3ec 100644 --- a/src/ir/utils.h +++ b/src/ir/utils.h @@ -222,8 +222,10 @@ struct AutoDrop : public WalkerPass> { if (maybeDrop(curr->body)) { acted = true; } - if (maybeDrop(curr->catchBody)) { - acted = true; + for (auto* catchBody : curr->catchBodies) { + if (maybeDrop(catchBody)) { + acted = true; + } } if (acted) { reFinalize(); diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 6cdf26e320b..c3dc2144c7f 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -2135,14 +2135,15 @@ function wrapModule(module, self = {}) { } }; - self['try'] = function(body, catchBody) { - return Module['_BinaryenTry'](module, body, catchBody); + self['try'] = function(body, catchEvents, catchBodies) { + return preserveStack(() => + Module['_BinaryenTry'](module, body, i32sToStack(catchEvents.map(strToStack)), catchEvents.length, i32sToStack(catchBodies), catchBodies.length)); }; self['throw'] = function(event_, operands) { return preserveStack(() => Module['_BinaryenThrow'](module, strToStack(event_), i32sToStack(operands), operands.length)); }; - self['rethrow'] = function(exnref) { - return Module['_BinaryenRethrow'](module, exnref); + self['rethrow'] = function(depth) { + return Module['_BinaryenRethrow'](module, depth); }; self['br_on_exn'] = function(label, event_, exnref) { return preserveStack(() => Module['_BinaryenBrOnExn'](module, strToStack(label), strToStack(event_), exnref)); @@ -2850,7 +2851,9 @@ Module['getExpressionInfo'] = function(expr) { 'id': id, 'type': type, 'body': Module['_BinaryenTryGetBody'](expr), - 'catchBody': Module['_BinaryenTryGetCatchBody'](expr) + 'catchEvents': getAllNested(expr, Module['_BinaryenTryGetNumCatchEvents'], Module['_BinaryenTryGetCatchEventAt']), + 'catchBodies': getAllNested(expr, Module['_BinaryenTryGetNumCatchBodies'], Module['_BinaryenTryGetCatchBodyAt']), + 'hasCatchAll': Module['_BinaryenTryHasCatchAll'](expr) }; case Module['ThrowId']: return { @@ -2863,7 +2866,7 @@ Module['getExpressionInfo'] = function(expr) { return { 'id': id, 'type': type, - 'exnref': Module['_BinaryenRethrowGetExnref'](expr) + 'depth': Module['_BinaryenRethrowGetDepth'](expr) }; case Module['BrOnExnId']: return { @@ -4189,12 +4192,97 @@ Module['Try'] = makeExpressionWrapper({ 'setBody'(expr, bodyExpr) { Module['_BinaryenTrySetBody'](expr, bodyExpr); }, - 'getCatchBody'(expr) { - return Module['_BinaryenTryGetCatchBody'](expr); + 'getNumCatchEvents'(expr) { + return Module['_BinaryenTryGetNumCatchEvents'](expr); + }, + 'getCatchEvents'(expr) { + const numCatchEvents = Module['_BinaryenTryGetNumCatchEvents'](expr); + const catchEvents = new Array(numCatchEvents); + let index = 0; + while (index < numCatchEvents) { + catchEvents[index] = UTF8ToString(Module['_BinaryenTryGetCatchEventAt'](expr, index++)); + } + return catchEvents; + }, + 'setCatchEvents'(expr, catchEvents) { + const numCatchEvents = catchEvents.length; + let prevNumCatchEvents = Module['_BinaryenTryGetNumCatchEvents'](expr); + let index = 0; + while (index < numCatchEvents) { + preserveStack(() => { + if (index < prevNumCatchEvents) { + Module['_BinaryenTrySetCatchEventAt'](expr, index, strToStack(catchEvents[index])); + } else { + Module['_BinaryenTryAppendCatchEvent'](expr, strToStack(catchEvents[index])); + } + }); + ++index; + } + while (prevNumCatchEvents > index) { + Module['_BinaryenTryRemoveCatchEventAt'](expr, --prevNumCatchEvents); + } + }, + 'getCatchEventAt'(expr, index) { + return UTF8ToString(Module['_BinaryenTryGetCatchEventAt'](expr, index)); + }, + 'setCatchEventAt'(expr, index, catchEvent) { + preserveStack(() => { Module['_BinaryenTrySetCatchEventAt'](expr, index, strToStack(catchEvent)) }); + }, + 'appendCatchEvent'(expr, catchEvent) { + preserveStack(() => Module['_BinaryenTryAppendCatchEvent'](expr, strToStack(catchEvent))); + }, + 'insertCatchEventAt'(expr, index, catchEvent) { + preserveStack(() => { Module['_BinaryenTryInsertCatchEventAt'](expr, index, strToStack(catchEvent)) }); + }, + 'removeCatchEventAt'(expr, index) { + return UTF8ToString(Module['_BinaryenTryRemoveCatchEventAt'](expr, index)); + }, + 'getNumCatchBodies'(expr) { + return Module['_BinaryenTryGetNumCatchBodies'](expr); + }, + 'getCatchBodies'(expr) { + const numCatchBodies = Module['_BinaryenTryGetNumCatchBodies'](expr); + const catchbodies = new Array(numCatchBodies); + let index = 0; + while (index < numCatchBodies) { + catchbodies[index] = Module['_BinaryenTryGetCatchBodyAt'](expr, index++); + } + return catchbodies; + }, + 'setCatchBodies'(expr, catchbodies) { + const numCatchBodies = catchbodies.length; + let prevNumCatchBodies = Module['_BinaryenTryGetNumCatchBodies'](expr); + let index = 0; + while (index < numCatchBodies) { + if (index < prevNumCatchBodies) { + Module['_BinaryenTrySetCatchBodyAt'](expr, index, catchbodies[index]); + } else { + Module['_BinaryenTryAppendCatchBody'](expr, catchbodies[index]); + } + ++index; + } + while (prevNumCatchBodies > index) { + Module['_BinaryenTryRemoveCatchBodyAt'](expr, --prevNumCatchBodies); + } + }, + 'getCatchBodyAt'(expr, index) { + return Module['_BinaryenTryGetCatchBodyAt'](expr, index); + }, + 'setCatchBodyAt'(expr, index, catchExpr) { + Module['_BinaryenTrySetCatchBodyAt'](expr, index, catchExpr); + }, + 'appendCatchBody'(expr, catchExpr) { + return Module['_BinaryenTryAppendCatchBody'](expr, catchExpr); + }, + 'insertCatchBodyAt'(expr, index, catchExpr) { + Module['_BinaryenTryInsertCatchBodyAt'](expr, index, catchExpr); + }, + 'removeCatchBodyAt'(expr, index) { + return Module['_BinaryenTryRemoveCatchBodyAt'](expr, index); + }, + 'hasCatchAll'(expr) { + return Boolean(Module['_BinaryenTryHasCatchAll'](expr)); }, - 'setCatchBody'(expr, catchBodyExpr) { - Module['_BinaryenTrySetCatchBody'](expr, catchBodyExpr); - } }); Module['Throw'] = makeExpressionWrapper({ @@ -4250,11 +4338,11 @@ Module['Throw'] = makeExpressionWrapper({ }); Module['Rethrow'] = makeExpressionWrapper({ - 'getExnref'(expr) { - return Module['_BinaryenRethrowGetExnref'](expr); + 'getDepth'(expr) { + return Module['_BinaryenRethrowGetDepth'](expr); }, - 'setExnref'(expr, exnrefExpr) { - Module['_BinaryenRethrowSetExnref'](expr, exnrefExpr); + 'setDepth'(expr, depthExpr) { + Module['_BinaryenRethrowSetDepth'](expr, depthExpr); } }); diff --git a/src/passes/CodeFolding.cpp b/src/passes/CodeFolding.cpp index ee54c50a6df..3a9e90e2a6c 100644 --- a/src/passes/CodeFolding.cpp +++ b/src/passes/CodeFolding.cpp @@ -306,10 +306,10 @@ struct CodeFolding : public WalkerPass> { } if (getModule()->features.hasExceptionHandling()) { EffectAnalyzer effects(getPassOptions(), getModule()->features, item); - // Currently pop instructions are only used for exnref.pop, which is a - // pseudo instruction following a catch. We cannot move expressions - // containing pops if they are not enclosed in a 'catch' body, because a - // pop instruction should follow right after 'catch'. + // Currently pop instructions are used only after 'catch', which is a + // pseudo instruction to simulate its behavior. We cannot move + // expressions containing pops if they are not enclosed in a 'catch' + // body, because a pop instruction should follow right after 'catch'. if (effects.danglingPop) { return false; } diff --git a/src/passes/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp index 3e84a7be1f1..c30da5428e4 100644 --- a/src/passes/DeadCodeElimination.cpp +++ b/src/passes/DeadCodeElimination.cpp @@ -161,9 +161,12 @@ struct DeadCodeElimination } else if (auto* tryy = curr->dynCast()) { // If both try body and catch body are unreachable, there is no need for a // concrete type, which may allow more reduction. + bool allCatchesUnreachable = true; + for (auto* catchBody : tryy->catchBodies) { + allCatchesUnreachable &= catchBody->type == Type::unreachable; + } if (tryy->type != Type::unreachable && - tryy->body->type == Type::unreachable && - tryy->catchBody->type == Type::unreachable) { + tryy->body->type == Type::unreachable && allCatchesUnreachable) { typeUpdater.changeType(tryy, Type::unreachable); } } else { diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 2b0a95fc72d..c06763643ce 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1099,7 +1099,9 @@ struct OptimizeInstructions } else if (auto* tryy = boolean->dynCast()) { if (tryy->type == Type::i32) { tryy->body = optimizeBoolean(tryy->body); - tryy->catchBody = optimizeBoolean(tryy->catchBody); + for (Index i = 0; i < tryy->catchBodies.size(); i++) { + tryy->catchBodies[i] = optimizeBoolean(tryy->catchBodies[i]); + } } } // TODO: recurse into br values? diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index e1d80a11a76..b6da2986171 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1710,7 +1710,10 @@ struct PrintExpressionContents printMedium(o, "throw "); printName(curr->event, o); } - void visitRethrow(Rethrow* curr) { printMedium(o, "rethrow"); } + void visitRethrow(Rethrow* curr) { + printMedium(o, "rethrow "); + o << curr->depth; + } void visitBrOnExn(BrOnExn* curr) { printMedium(o, "br_on_exn "); printName(curr->name, o); @@ -2363,12 +2366,23 @@ struct PrintSExpression : public OverriddenVisitor { maybePrintImplicitBlock(curr->body, true); decIndent(); o << "\n"; - doIndent(o, indent); - o << "(catch"; - incIndent(); - maybePrintImplicitBlock(curr->catchBody, true); - decIndent(); - o << "\n"; + for (size_t i = 0; i < curr->catchEvents.size(); i++) { + doIndent(o, indent); + o << "(catch "; + printName(curr->catchEvents[i], o); + incIndent(); + maybePrintImplicitBlock(curr->catchBodies[i], true); + decIndent(); + o << "\n"; + } + if (curr->hasCatchAll()) { + doIndent(o, indent); + o << "(catch_all"; + incIndent(); + maybePrintImplicitBlock(curr->catchBodies.back(), true); + decIndent(); + o << "\n"; + } decIndent(); if (full) { o << " ;; end try"; @@ -2386,9 +2400,7 @@ struct PrintSExpression : public OverriddenVisitor { void visitRethrow(Rethrow* curr) { o << '('; PrintExpressionContents(currFunction, o).visit(curr); - incIndent(); - printFullLine(curr->exnref); - decIndent(); + o << ')'; } void visitBrOnExn(BrOnExn* curr) { o << '('; diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp index 963575c5f82..9f164ae92fb 100644 --- a/src/passes/SimplifyLocals.cpp +++ b/src/passes/SimplifyLocals.cpp @@ -425,8 +425,8 @@ struct SimplifyLocals if (set->isTee()) { return false; } - // We cannot move expressions containing exnref.pops that are not enclosed - // in 'catch', because 'exnref.pop' should follow right after 'catch'. + // We cannot move expressions containing pops that are not enclosed in + // 'catch', because 'pop' should follow right after 'catch'. FeatureSet features = this->getModule()->features; if (features.hasExceptionHandling() && EffectAnalyzer(this->getPassOptions(), features, set->value) diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp index 789c53ad744..ca777431aa1 100644 --- a/src/passes/Vacuum.cpp +++ b/src/passes/Vacuum.cpp @@ -341,7 +341,9 @@ struct Vacuum : public WalkerPass> { if (!EffectAnalyzer(getPassOptions(), getModule()->features, curr->body) .throws) { replaceCurrent(curr->body); - typeUpdater.noteRecursiveRemoval(curr->catchBody); + for (auto* catchBody : curr->catchBodies) { + typeUpdater.noteRecursiveRemoval(catchBody); + } } } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 284f81aabc5..ea18fdf67b8 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -995,6 +995,7 @@ enum ASTNodes { Try = 0x06, Catch = 0x07, + CatchAll = 0x05, Throw = 0x08, Rethrow = 0x09, BrOnExn = 0x0a, diff --git a/src/wasm-builder.h b/src/wasm-builder.h index a2aa2d505fa..faf275f66d9 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -624,17 +624,24 @@ class Builder { ret->finalize(); return ret; } - Try* makeTry(Expression* body, Expression* catchBody) { + Try* makeTry(Expression* body, + const std::vector& catchEvents, + const std::vector& catchBodies) { auto* ret = wasm.allocator.alloc(); ret->body = body; - ret->catchBody = catchBody; + ret->catchEvents.set(catchEvents); + ret->catchBodies.set(catchBodies); ret->finalize(); return ret; } - Try* makeTry(Expression* body, Expression* catchBody, Type type) { + Try* makeTry(Expression* body, + const std::vector& catchEvents, + const std::vector& catchBodies, + Type type) { auto* ret = wasm.allocator.alloc(); ret->body = body; - ret->catchBody = catchBody; + ret->catchEvents.set(catchEvents); + ret->catchBodies.set(catchBodies); ret->finalize(type); return ret; } @@ -648,9 +655,9 @@ class Builder { ret->finalize(); return ret; } - Rethrow* makeRethrow(Expression* exnref) { + Rethrow* makeRethrow(Index depth) { auto* ret = wasm.allocator.alloc(); - ret->exnref = exnref; + ret->depth = depth; ret->finalize(); return ret; } diff --git a/src/wasm-delegations-fields.h b/src/wasm-delegations-fields.h index 37fb60ef287..880df984d32 100644 --- a/src/wasm-delegations-fields.h +++ b/src/wasm-delegations-fields.h @@ -57,6 +57,9 @@ // // DELEGATE_FIELD_NAME(id, name) - called for a Name. // +// DELEGATE_FIELD_NAME_VECTOR(id, name) - called for a variable-sized vector of +// Names. +// // DELEGATE_FIELD_SCOPE_NAME_DEF(id, name) - called for a scope name definition // (like a block's name). // @@ -490,7 +493,8 @@ switch (DELEGATE_ID) { } case Expression::Id::TryId: { DELEGATE_START(Try); - DELEGATE_FIELD_CHILD(Try, catchBody); + DELEGATE_FIELD_CHILD_VECTOR(Try, catchBodies); + DELEGATE_FIELD_NAME_VECTOR(Try, catchEvents); DELEGATE_FIELD_CHILD(Try, body); DELEGATE_END(Try); break; @@ -504,7 +508,7 @@ switch (DELEGATE_ID) { } case Expression::Id::RethrowId: { DELEGATE_START(Rethrow); - DELEGATE_FIELD_CHILD(Rethrow, exnref); + DELEGATE_FIELD_INT(Rethrow, depth); DELEGATE_END(Rethrow); break; } @@ -665,6 +669,7 @@ switch (DELEGATE_ID) { #undef DELEGATE_FIELD_INT_ARRAY #undef DELEGATE_FIELD_LITERAL #undef DELEGATE_FIELD_NAME +#undef DELEGATE_FIELD_NAME_VECTOR #undef DELEGATE_FIELD_SCOPE_NAME_DEF #undef DELEGATE_FIELD_SCOPE_NAME_USE #undef DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 2435428366a..a77d7227cf9 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2943,6 +2943,8 @@ template class ModuleInstanceBase { return {}; } Flow visitTry(Try* curr) { + // FIXME Update the implementation to match the new spec + /* NOTE_ENTER("Try"); try { return this->visit(curr->body); @@ -2950,6 +2952,8 @@ template class ModuleInstanceBase { instance.multiValues.push_back(e.exn); return this->visit(curr->catchBody); } + */ + return Flow(); } Flow visitPop(Pop* curr) { NOTE_ENTER("Pop"); diff --git a/src/wasm-stack.h b/src/wasm-stack.h index 1bbf686d847..bd44ed72e78 100644 --- a/src/wasm-stack.h +++ b/src/wasm-stack.h @@ -70,6 +70,7 @@ class StackInst { LoopEnd, // the ending of a loop TryBegin, // the beginning of a try Catch, // the catch within a try + CatchAll, // the catch_all within a try TryEnd // the ending of a try } op; @@ -106,7 +107,8 @@ class BinaryInstWriter : public OverriddenVisitor { void emitResultType(Type type); void emitIfElse(If* curr); - void emitCatch(Try* curr); + void emitCatch(Try* curr, Index i); + void emitCatchAll(Try* curr); // emit an end at the end of a block/loop/if/try void emitScopeEnd(Expression* curr); // emit an end at the end of a function @@ -161,7 +163,12 @@ class BinaryenIRWriter : public Visitor> { void emit(Expression* curr) { static_cast(this)->emit(curr); } void emitHeader() { static_cast(this)->emitHeader(); } void emitIfElse(If* curr) { static_cast(this)->emitIfElse(curr); } - void emitCatch(Try* curr) { static_cast(this)->emitCatch(curr); } + void emitCatch(Try* curr, Index i) { + static_cast(this)->emitCatch(curr, i); + } + void emitCatchAll(Try* curr) { + static_cast(this)->emitCatchAll(curr); + } void emitScopeEnd(Expression* curr) { static_cast(this)->emitScopeEnd(curr); } @@ -328,8 +335,14 @@ void BinaryenIRWriter::visitLoop(Loop* curr) { template void BinaryenIRWriter::visitTry(Try* curr) { emit(curr); visitPossibleBlockContents(curr->body); - emitCatch(curr); - visitPossibleBlockContents(curr->catchBody); + for (Index i = 0; i < curr->catchEvents.size(); i++) { + emitCatch(curr, i); + visitPossibleBlockContents(curr->catchBodies[i]); + } + if (curr->hasCatchAll()) { + emitCatchAll(curr); + visitPossibleBlockContents(curr->catchBodies.back()); + } emitScopeEnd(curr); if (curr->type == Type::unreachable) { emitUnreachable(); @@ -360,7 +373,8 @@ class BinaryenIRToBinaryWriter writer.mapLocalsAndEmitHeader(); } void emitIfElse(If* curr) { writer.emitIfElse(curr); } - void emitCatch(Try* curr) { writer.emitCatch(curr); } + void emitCatch(Try* curr, Index i) { writer.emitCatch(curr, i); } + void emitCatchAll(Try* curr) { writer.emitCatchAll(curr); } void emitScopeEnd(Expression* curr) { writer.emitScopeEnd(curr); } void emitFunctionEnd() { if (func->epilogLocation.size()) { @@ -394,9 +408,12 @@ class StackIRGenerator : public BinaryenIRWriter { void emitIfElse(If* curr) { stackIR.push_back(makeStackInst(StackInst::IfElse, curr)); } - void emitCatch(Try* curr) { + void emitCatch(Try* curr, Index i) { stackIR.push_back(makeStackInst(StackInst::Catch, curr)); } + void emitCatchAll(Try* curr) { + stackIR.push_back(makeStackInst(StackInst::CatchAll, curr)); + } void emitFunctionEnd() {} void emitUnreachable() { stackIR.push_back(makeStackInst(Builder(module).makeUnreachable())); diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index e3de27f62b1..28470c6142b 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -337,6 +337,7 @@ struct PostWalker : public Walker { #define DELEGATE_FIELD_INT_ARRAY(id, name) #define DELEGATE_FIELD_LITERAL(id, name) #define DELEGATE_FIELD_NAME(id, name) +#define DELEGATE_FIELD_NAME_VECTOR(id, name) #define DELEGATE_FIELD_SCOPE_NAME_DEF(id, name) #define DELEGATE_FIELD_SCOPE_NAME_USE(id, name) #define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, name) @@ -566,8 +567,11 @@ struct LinearExecutionWalker : public PostWalker { case Expression::Id::TryId: { self->pushTask(SubType::doVisitTry, currp); self->pushTask(SubType::doNoteNonLinear, currp); - self->pushTask(SubType::scan, &curr->cast()->catchBody); - self->pushTask(SubType::doNoteNonLinear, currp); + auto& list = curr->cast()->catchBodies; + for (int i = int(list.size()) - 1; i >= 0; i--) { + self->pushTask(SubType::scan, &list[i]); + self->pushTask(SubType::doNoteNonLinear, currp); + } self->pushTask(SubType::scan, &curr->cast()->body); break; } @@ -583,7 +587,6 @@ struct LinearExecutionWalker : public PostWalker { case Expression::Id::RethrowId: { self->pushTask(SubType::doVisitRethrow, currp); self->pushTask(SubType::doNoteNonLinear, currp); - self->pushTask(SubType::scan, &curr->cast()->exnref); break; } case Expression::Id::BrOnExnId: { diff --git a/src/wasm.h b/src/wasm.h index 29d2e9b5db0..00496f52110 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1253,11 +1253,16 @@ class RefEq : public SpecificExpression { class Try : public SpecificExpression { public: - Try(MixedArena& allocator) {} + Try(MixedArena& allocator) : catchEvents(allocator), catchBodies(allocator) {} Expression* body; - Expression* catchBody; + ArenaVector catchEvents; + ExpressionList catchBodies; + bool hasCatchAll() const { + assert(catchBodies.size() - catchEvents.size() <= 1); + return catchBodies.size() - catchEvents.size() == 1; + } void finalize(); void finalize(Type type_); }; @@ -1277,6 +1282,7 @@ class Rethrow : public SpecificExpression { Rethrow(MixedArena& allocator) {} Expression* exnref; + Index depth; void finalize(); }; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index beaed1b1185..9b4794997b8 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2128,7 +2128,7 @@ void WasmBinaryBuilder::processExpressions() { } auto peek = input[pos]; if (peek == BinaryConsts::End || peek == BinaryConsts::Else || - peek == BinaryConsts::Catch) { + peek == BinaryConsts::Catch || peek == BinaryConsts::CatchAll) { BYN_TRACE("== processExpressions finished with unreachable" << std::endl); lastSeparator = BinaryConsts::ASTNodes(peek); @@ -5494,7 +5494,8 @@ void WasmBinaryBuilder::visitTryOrTryInBlock(Expression*& out) { // blocks instead. curr->type = getType(); curr->body = getBlockOrSingleton(curr->type); - if (lastSeparator != BinaryConsts::Catch) { + if (lastSeparator != BinaryConsts::Catch && + lastSeparator != BinaryConsts::CatchAll) { throwError("No catch instruction within a try scope"); } @@ -5544,25 +5545,48 @@ void WasmBinaryBuilder::visitTryOrTryInBlock(Expression*& out) { // ) // ) // ) + + Builder builder(wasm); Name catchLabel = getNextLabel(); breakStack.push_back({catchLabel, curr->type}); - auto start = expressionStack.size(); - Builder builder(wasm); - pushExpression(builder.makePop(Type::exnref)); + auto readCatchBody = [&](Type eventType) { + auto start = expressionStack.size(); + if (eventType != Type::none) { + pushExpression(builder.makePop(eventType)); + } + processExpressions(); + size_t end = expressionStack.size(); + if (start > end) { + throwError("block cannot pop from outside"); + } + if (end - start == 1) { + curr->catchBodies.push_back(popExpression()); + } else { + auto* block = allocator.alloc(); + pushBlockElements(block, curr->type, start); + block->finalize(curr->type); + curr->catchBodies.push_back(block); + } + }; - processExpressions(); - size_t end = expressionStack.size(); - if (start > end) { - throwError("block cannot pop from outside"); - } - if (end - start == 1) { - curr->catchBody = popExpression(); - } else { - auto* block = allocator.alloc(); - pushBlockElements(block, curr->type, start); - block->finalize(curr->type); - curr->catchBody = block; + while (lastSeparator == BinaryConsts::Catch || + lastSeparator == BinaryConsts::CatchAll) { + if (lastSeparator == BinaryConsts::Catch) { + auto index = getU32LEB(); + if (index >= wasm.events.size()) { + throwError("bad event index"); + } + auto* event = wasm.events[index].get(); + curr->catchEvents.push_back(event->name); + readCatchBody(event->sig.params); + + } else { // catch_all + if (curr->hasCatchAll()) { + throwError("there should be at most one 'catch_all' clause per try"); + } + readCatchBody(Type::none); + } } curr->finalize(curr->type); @@ -5595,7 +5619,7 @@ void WasmBinaryBuilder::visitThrow(Throw* curr) { void WasmBinaryBuilder::visitRethrow(Rethrow* curr) { BYN_TRACE("zz node: Rethrow\n"); - curr->exnref = popNonVoidExpression(); + curr->depth = getU32LEB(); curr->finalize(); } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index d76a3c49a01..1452b22f9e2 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1947,13 +1947,19 @@ Expression* SExpressionWasmBuilder::makeRefEq(Element& s) { // try-catch-end is written in the folded wast format as // (try -// ... +// (do +// ... +// ) // (catch // ... // ) +// ... +// (catch_all +// ... +// ) // ) -// The parenthesis wrapping 'catch' is just a syntax and does not affect nested -// depths of instructions within. +// Zero or multiple catch blocks can exist. Zero or one catch_all block can +// exist, and if it does, it should be at the end. Expression* SExpressionWasmBuilder::makeTry(Element& s) { auto ret = allocator.alloc(); Index i = 1; @@ -1966,16 +1972,41 @@ Expression* SExpressionWasmBuilder::makeTry(Element& s) { } auto label = nameMapper.pushLabelName(sName); Type type = parseOptionalResultType(s, i); // signature + if (!elementStartsWith(*s[i], "do")) { throw ParseException( "try body should start with 'do'", s[i]->line, s[i]->col); } - ret->body = makeTryOrCatchBody(*s[i++], type, true); - if (!elementStartsWith(*s[i], "catch")) { - throw ParseException("catch clause does not exist", s[i]->line, s[i]->col); + ret->body = makeMaybeBlock(*s[i++], 1, type); + + while (i < s.size() && elementStartsWith(*s[i], "catch")) { + Element& inner = *s[i++]; + if (inner.size() < 3) { + throw ParseException("invalid catch block", inner.line, inner.col); + } + Name event = getEventName(*inner[1]); + if (!wasm.getEventOrNull(event)) { + throw ParseException("bad event name", inner[1]->line, inner[1]->col); + } + ret->catchEvents.push_back(getEventName(*inner[1])); + ret->catchBodies.push_back(makeMaybeBlock(inner, 2, type)); + } + + if (i < s.size() && elementStartsWith(*s[i], "catch_all")) { + ret->catchBodies.push_back(makeMaybeBlock(*s[i++], 1, type)); + } + + if (i != s.size()) { + throw ParseException( + "there should be at most one catch_all block at the end", s.line, s.col); + } + if (ret->catchBodies.empty()) { + throw ParseException("no catch bodies", s.line, s.col); } - ret->catchBody = makeTryOrCatchBody(*s[i++], type, false); + ret->finalize(type); + // TODO If 'delegate' targets a try block, should we make 'try' take a label + // rather than wrapping it with a block like this? nameMapper.popLabelName(label); // create a break target if we must if (BranchUtils::BranchSeeker::has(ret, label)) { @@ -1993,10 +2024,11 @@ SExpressionWasmBuilder::makeTryOrCatchBody(Element& s, Type type, bool isTry) { if (isTry && !elementStartsWith(s, "do")) { throw ParseException("invalid try do clause", s.line, s.col); } - if (!isTry && !elementStartsWith(s, "catch")) { + if (!isTry && + !(elementStartsWith(s, "catch") || elementStartsWith(s, "catch_all"))) { throw ParseException("invalid catch clause", s.line, s.col); } - if (s.size() == 1) { // (do) or (catch) without instructions + if (s.size() == 1) { // (do) / (catch) / (catch_all) without instructions return makeNop(); } auto ret = allocator.alloc(); @@ -2027,7 +2059,7 @@ Expression* SExpressionWasmBuilder::makeThrow(Element& s) { Expression* SExpressionWasmBuilder::makeRethrow(Element& s) { auto ret = allocator.alloc(); - ret->exnref = parseExpression(*s[1]); + ret->depth = atoi(s[1]->str().c_str()); ret->finalize(); return ret; } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 049573fc5e7..b87d3d9a6f9 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1859,14 +1859,26 @@ void BinaryInstWriter::visitTry(Try* curr) { emitResultType(curr->type); } -void BinaryInstWriter::emitCatch(Try* curr) { +void BinaryInstWriter::emitCatch(Try* curr, Index i) { assert(!breakStack.empty()); breakStack.pop_back(); breakStack.emplace_back(IMPOSSIBLE_CONTINUE); if (func && !sourceMap) { parent.writeExtraDebugLocation(curr, func, BinaryLocations::Catch); } - o << int8_t(BinaryConsts::Catch); + o << int8_t(BinaryConsts::Catch) + << U32LEB(parent.getEventIndex(curr->catchEvents[i])); +} + +void BinaryInstWriter::emitCatchAll(Try* curr) { + assert(!breakStack.empty()); + breakStack.pop_back(); + breakStack.emplace_back(IMPOSSIBLE_CONTINUE); + // TODO Is this right? + if (func && !sourceMap) { + parent.writeExtraDebugLocation(curr, func, BinaryLocations::Catch); + } + o << int8_t(BinaryConsts::CatchAll); } void BinaryInstWriter::visitThrow(Throw* curr) { @@ -1874,7 +1886,7 @@ void BinaryInstWriter::visitThrow(Throw* curr) { } void BinaryInstWriter::visitRethrow(Rethrow* curr) { - o << int8_t(BinaryConsts::Rethrow); + o << int8_t(BinaryConsts::Rethrow) << U32LEB(curr->depth); } void BinaryInstWriter::visitBrOnExn(BrOnExn* curr) { @@ -2200,23 +2212,27 @@ StackInst* StackIRGenerator::makeStackInst(StackInst::Op op, void StackIRToBinaryWriter::write() { writer.mapLocalsAndEmitHeader(); + // Stack to track indices of catches within a try + SmallVector catchIndexStack; for (auto* inst : *func->stackIR) { if (!inst) { continue; // a nullptr is just something we can skip } switch (inst->op) { + case StackInst::TryBegin: + catchIndexStack.push_back(0); case StackInst::Basic: case StackInst::BlockBegin: case StackInst::IfBegin: - case StackInst::LoopBegin: - case StackInst::TryBegin: { + case StackInst::LoopBegin: { writer.visit(inst->origin); break; } + case StackInst::TryEnd: + catchIndexStack.pop_back(); case StackInst::BlockEnd: case StackInst::IfEnd: - case StackInst::LoopEnd: - case StackInst::TryEnd: { + case StackInst::LoopEnd: { writer.emitScopeEnd(inst->origin); break; } @@ -2225,7 +2241,7 @@ void StackIRToBinaryWriter::write() { break; } case StackInst::Catch: { - writer.emitCatch(inst->origin->cast()); + writer.emitCatch(inst->origin->cast(), catchIndexStack.back()++); break; } default: diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index b0fc4d9c8b8..352e07dc210 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2026,20 +2026,35 @@ void FunctionValidator::visitTry(Try* curr) { curr->type, curr->body, "try's type does not match try body's type"); - shouldBeSubTypeOrFirstIsUnreachable( - curr->catchBody->type, - curr->type, - curr->catchBody, - "try's type does not match catch's body type"); + for (auto catchBody : curr->catchBodies) { + shouldBeSubTypeOrFirstIsUnreachable( + catchBody->type, + curr->type, + catchBody, + "try's type does not match catch's body type"); + } } else { shouldBeEqual(curr->body->type, Type(Type::unreachable), curr, "unreachable try-catch must have unreachable try body"); - shouldBeEqual(curr->catchBody->type, - Type(Type::unreachable), + for (auto catchBody : curr->catchBodies) { + shouldBeEqual(catchBody->type, + Type(Type::unreachable), + curr, + "unreachable try-catch must have unreachable catch body"); + } + } + if (curr->hasCatchAll()) { + shouldBeEqual(curr->catchBodies.size(), + curr->catchEvents.size() + 1, + curr, + "the number of catch blocks and events do not match"); + } else { + shouldBeEqual(curr->catchBodies.size(), + curr->catchEvents.size(), curr, - "unreachable try-catch must have unreachable catch body"); + "the number of catch blocks and events do not match"); } } @@ -2084,11 +2099,7 @@ void FunctionValidator::visitRethrow(Rethrow* curr) { Type(Type::unreachable), curr, "rethrow's type must be unreachable"); - shouldBeSubTypeOrFirstIsUnreachable( - curr->exnref->type, - Type::exnref, - curr->exnref, - "rethrow's argument must be exnref type or its subtype"); + // TODO Validate depth field } void FunctionValidator::visitBrOnExn(BrOnExn* curr) { diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index fb523e25497..55b03c228c6 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -949,13 +949,19 @@ void RefEq::finalize() { } void Try::finalize() { - type = Type::getLeastUpperBound(body->type, catchBody->type); + type = body->type; + for (auto catchBody : catchBodies) { + type = Type::getLeastUpperBound(type, catchBody->type); + } } void Try::finalize(Type type_) { type = type_; - if (type == Type::none && body->type == Type::unreachable && - catchBody->type == Type::unreachable) { + bool allUnreachable = body->type == Type::unreachable; + for (auto catchBody : catchBodies) { + allUnreachable &= catchBody->type == Type::unreachable; + } + if (type == Type::none && allUnreachable) { type = Type::unreachable; } } diff --git a/test/binaryen.js/exception-handling.js b/test/binaryen.js/exception-handling.js index 3f5f6de7449..764e145d44b 100644 --- a/test/binaryen.js/exception-handling.js +++ b/test/binaryen.js/exception-handling.js @@ -1,7 +1,8 @@ function cleanInfo(info) { var ret = {}; for (var x in info) { - if (x == 'id' || x == 'type' || x == 'name' || x == 'event') { + if (x == 'id' || x == 'type' || x == 'name' || x == 'event' || + x == 'depth' || x == 'hasCatchAll') { ret[x] = info[x]; } } @@ -23,36 +24,31 @@ var event_ = module.addEvent("e", 0, binaryen.i32, binaryen.none); // (throw $e (i32.const 0)) // ) // (catch -// ;; We don't support multi-value yet. Use locals instead. -// (local.set 0 (exnref.pop)) -// (drop -// (block $l (result i32) -// (rethrow -// (br_on_exn $l $e (local.get 0)) -// ) -// ) -// ) +// (drop (pop i32)) +// (rethrow 0) // ) // ) var throw_ = module.throw("e", [module.i32.const(0)]); -var br_on_exn = module.br_on_exn("l", "e", module.local.get(0, binaryen.exnref)); -var rethrow = module.rethrow(br_on_exn); +var rethrow = module.rethrow(0); var try_ = module.try( throw_, - module.block(null, [ - module.local.set(0, module.exnref.pop()), - module.drop( - module.block("l", [rethrow], binaryen.i32) + ["e"], + [ + module.block(null, + [ + module.drop(module.i32.pop()), + rethrow + ], + binaryen.none ) ] - ) ); -var func = module.addFunction("test", binaryen.none, binaryen.none, [binaryen.exnref], try_); + +var func = module.addFunction("test", binaryen.none, binaryen.none, [], try_); console.log(module.emitText()); assert(module.validate()); console.log("getExpressionInfo(throw) = " + stringify(throw_)); -console.log("getExpressionInfo(br_on_exn) = " + stringify(br_on_exn)); console.log("getExpressionInfo(rethrow) = " + stringify(rethrow)); console.log("getExpressionInfo(try) = " + stringify(try_)); diff --git a/test/binaryen.js/exception-handling.js.txt b/test/binaryen.js/exception-handling.js.txt index 19861dd21cd..e7c72e6a76f 100644 --- a/test/binaryen.js/exception-handling.js.txt +++ b/test/binaryen.js/exception-handling.js.txt @@ -3,32 +3,22 @@ (type $i32_=>_none (func (param i32))) (event $e (attr 0) (param i32)) (func $test - (local $0 exnref) (try (do (throw $e (i32.const 0) ) ) - (catch - (local.set $0 - (pop exnref) - ) + (catch $e (drop - (block $l (result i32) - (rethrow - (br_on_exn $l $e - (local.get $0) - ) - ) - ) + (pop i32) ) + (rethrow 0) ) ) ) ) getExpressionInfo(throw) = {"id":47,"type":1,"event":"e"} -getExpressionInfo(br_on_exn) = {"id":49,"type":9,"name":"l","event":"e"} -getExpressionInfo(rethrow) = {"id":48,"type":1} -getExpressionInfo(try) = {"id":46,"type":0} +getExpressionInfo(rethrow) = {"id":48,"type":1,"depth":0} +getExpressionInfo(try) = {"id":46,"type":1,"hasCatchAll":0} diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js index 61fc0a3ef6d..598afd6fa75 100644 --- a/test/binaryen.js/expressions.js +++ b/test/binaryen.js/expressions.js @@ -1435,31 +1435,67 @@ console.log("# RefEq"); console.log("# Try"); (function testTry() { const module = new binaryen.Module(); + module.addEvent("event1", 0, binaryen.none, binaryen.none); + module.addEvent("event2", 0, binaryen.none, binaryen.none); + module.addEvent("event3", 0, binaryen.none, binaryen.none); var body = module.i32.const(1); - var catchBody = module.i32.const(2); - const theTry = binaryen.Try(module.try(body, catchBody)); + var catchBodies = [ + module.i32.const(2), + module.i32.const(3) + ]; + const theTry = binaryen.Try(module.try(body, ["event1"], catchBodies)); assert(theTry instanceof binaryen.Try); assert(theTry instanceof binaryen.Expression); assert(theTry.body === body); - assert(theTry.catchBody === catchBody); + assertDeepEqual(theTry.catchBodies, catchBodies); assert(theTry.type === binaryen.i32); + assert(theTry.getNumCatchEvents() == 1); + assert(theTry.getNumCatchBodies() == 2); + assert(theTry.hasCatchAll() == 1); + console.log(theTry.toText()); - theTry.body = body = module.i32.const(3); + theTry.body = body = module.i32.const(4); assert(theTry.body === body); - theTry.catchBody = catchBody = module.i32.const(4); - assert(theTry.catchBody === catchBody); + theTry.catchBodies = catchBodies = [ + module.i32.const(5) // set + //remove + ] + assertDeepEqual(theTry.catchBodies, catchBodies); + console.log(theTry.toText()); + + theTry.insertCatchEventAt(1, "event2"); + theTry.insertCatchBodyAt(0, module.i32.const(6)); + assert(theTry.getNumCatchEvents() == 2); + assert(theTry.getNumCatchBodies() == 2); + assert(theTry.hasCatchAll() == 0); + console.log(theTry.toText()); + + assert(theTry.removeCatchEventAt(1) == "event2"); + theTry.removeCatchBodyAt(1); + assert(theTry.getNumCatchEvents() == 1); + assert(theTry.getNumCatchBodies() == 1); + console.log(theTry.toText()); + + theTry.appendCatchEvent("event3"); + theTry.appendCatchBody(module.drop(module.i32.const(7))); + assert(theTry.getCatchEventAt(0) == "event1"); + assert(theTry.getCatchEventAt(1) == "event3"); + theTry.setCatchEvents(["event2", "event3"]); + assertDeepEqual(theTry.getCatchEvents(), ["event2", "event3"]); + theTry.setCatchBodies([module.i32.const(8), module.i32.const(9)]); + assert(theTry.getCatchEventAt(0) == "event2"); + assert(theTry.getCatchEventAt(1) == "event3"); + theTry.setCatchEventAt(1, "event1"); + theTry.setCatchBodyAt(1, module.i32.const(10)); + assert(theTry.getCatchEventAt(1) == "event1"); + console.log(theTry.toText()); + theTry.type = binaryen.f64; theTry.finalize(); assert(theTry.type === binaryen.i32); console.log(theTry.toText()); - assert( - theTry.toText() - == - "(try (result i32)\n (do\n (i32.const 3)\n )\n (catch\n (i32.const 4)\n )\n)\n" - ); - module.dispose(); })(); @@ -1512,15 +1548,14 @@ console.log("# Rethrow"); (function testRethrow() { const module = new binaryen.Module(); - var exnref = module.local.get(1, binaryen.exnref); - const theRethrow = binaryen.Rethrow(module.rethrow(exnref)); + const theRethrow = binaryen.Rethrow(module.rethrow(0)); assert(theRethrow instanceof binaryen.Rethrow); assert(theRethrow instanceof binaryen.Expression); - assert(theRethrow.exnref === exnref); + assert(theRethrow.depth === 0); assert(theRethrow.type === binaryen.unreachable); - theRethrow.exnref = exnref = module.local.get(2, binaryen.exnref); - assert(theRethrow.exnref === exnref); + theRethrow.depth = 1 + assert(theRethrow.depth === 1); theRethrow.type = binaryen.f64; theRethrow.finalize(); assert(theRethrow.type === binaryen.unreachable); @@ -1529,7 +1564,7 @@ console.log("# Rethrow"); assert( theRethrow.toText() == - "(rethrow\n (local.get $2)\n)\n" + "(rethrow 1)\n" ); module.dispose(); diff --git a/test/binaryen.js/expressions.js.txt b/test/binaryen.js/expressions.js.txt index ba946b296ab..d6c090d1878 100644 --- a/test/binaryen.js/expressions.js.txt +++ b/test/binaryen.js/expressions.js.txt @@ -219,11 +219,68 @@ # Try (try (result i32) (do + (i32.const 1) + ) + (catch $event1 + (i32.const 2) + ) + (catch_all (i32.const 3) ) - (catch +) + +(try (result i32) + (do (i32.const 4) ) + (catch $event1 + (i32.const 5) + ) +) + +(try (result i32) + (do + (i32.const 4) + ) + (catch $event1 + (i32.const 6) + ) + (catch $event2 + (i32.const 5) + ) +) + +(try (result i32) + (do + (i32.const 4) + ) + (catch $event1 + (i32.const 6) + ) +) + +(try (result i32) + (do + (i32.const 4) + ) + (catch $event2 + (i32.const 8) + ) + (catch $event1 + (i32.const 10) + ) +) + +(try (result i32) + (do + (i32.const 4) + ) + (catch $event2 + (i32.const 8) + ) + (catch $event1 + (i32.const 10) + ) ) # Throw @@ -233,9 +290,7 @@ ) # Rethrow -(rethrow - (local.get $2) -) +(rethrow 1) # BrOnExn (br_on_exn $bar $event2 diff --git a/test/binaryen.js/kitchen-sink.js b/test/binaryen.js/kitchen-sink.js index 146335130dc..8e7e81f2629 100644 --- a/test/binaryen.js/kitchen-sink.js +++ b/test/binaryen.js/kitchen-sink.js @@ -547,18 +547,8 @@ function test_core() { // Exception handling module.try( module.throw("a-event", [module.i32.const(0)]), - module.block(null, [ - module.local.set(5, module.exnref.pop()), - module.drop( - module.block("try-block", [ - module.rethrow( - module.br_on_exn("try-block", "a-event", - module.local.get(5, binaryen.exnref)), - ) - ], binaryen.i32) - ) - ] - ) + ["a-event"], + [module.drop(module.i32.pop())] ), // Atomics diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 907d490fe95..ef15d8a0041 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -1865,18 +1865,9 @@ getExpressionInfo(tuple[3])={"id":14,"type":5,"value":3.7} (i32.const 0) ) ) - (catch - (local.set $5 - (pop exnref) - ) + (catch $a-event (drop - (block $try-block (result i32) - (rethrow - (br_on_exn $try-block $a-event - (local.get $5) - ) - ) - ) + (pop i32) ) ) ) @@ -3737,18 +3728,9 @@ getExpressionInfo(tuple[3])={"id":14,"type":5,"value":3.7} (i32.const 0) ) ) - (catch - (local.set $5 - (pop exnref) - ) + (catch $a-event (drop - (block $try-block (result i32) - (rethrow - (br_on_exn $try-block $a-event - (local.get $5) - ) - ) - ) + (pop i32) ) ) ) diff --git a/test/binaryen.js/sideffects.js b/test/binaryen.js/sideffects.js index bfb5404c2ac..cb0e8ac7f8d 100644 --- a/test/binaryen.js/sideffects.js +++ b/test/binaryen.js/sideffects.js @@ -108,7 +108,7 @@ assert( assert( binaryen.getSideEffects( - module.drop(module.exnref.pop()), + module.drop(module.i32.pop()), module.getFeatures() ) == diff --git a/test/break-within-catch.wasm b/test/break-within-catch.wasm index 90b08f9a9c690561b8c1ed00bbe0daebad195b31..39a0cafb26a6a2917afb55066b4cd7680de34e6d 100644 GIT binary patch literal 42 xcmZQbEY4+QU|?Y6U`k+MNMNjIU}j=u;ALiHVC3Ru_none (func)) + (type $i32_=>_none (func (param i32))) + (event $event$0 (attr 0) (param i32)) (func $0 (block $label$2 (try (do (nop) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (br $label$2) ) diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index 79cbabca377..58b1e00aabc 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -325,40 +325,18 @@ void test_core() { // (do // (throw $a-event (i32.const 0)) // ) - // (catch - // ;; We don't support multi-value yet. Use locals instead. - // (local.set 0 (exnref.pop)) - // (drop - // (block $try-block (result i32) - // (rethrow - // (br_on_exn $try-block $a-event (local.get 5)) - // ) - // ) - // ) + // (catch $a-event + // (drop (i32 pop)) // ) + // (catch_all) // ) BinaryenExpressionRef tryBody = BinaryenThrow( module, "a-event", (BinaryenExpressionRef[]){makeInt32(module, 0)}, 1); - BinaryenExpressionRef catchBody = BinaryenBlock( - module, - NULL, - (BinaryenExpressionRef[]){ - BinaryenLocalSet(module, 5, BinaryenPop(module, BinaryenTypeExnref())), - BinaryenDrop( - module, - BinaryenBlock(module, - "try-block", - (BinaryenExpressionRef[]){BinaryenRethrow( - module, - BinaryenBrOnExn( - module, - "try-block", - "a-event", - BinaryenLocalGet(module, 5, BinaryenTypeExnref())))}, - 1, - BinaryenTypeInt32()))}, - 2, - BinaryenTypeNone()); + BinaryenExpressionRef catchBody = + BinaryenDrop(module, BinaryenPop(module, BinaryenTypeInt32())); + BinaryenExpressionRef catchAllBody = BinaryenNop(module); + BinaryenExpressionRef catchBodies[] = {catchBody, catchAllBody}; + const char* catchEvents[] = {"a-event"}; BinaryenType i32 = BinaryenTypeInt32(); BinaryenType i64 = BinaryenTypeInt64(); @@ -747,7 +725,7 @@ void test_core() { BinaryenRefNull(module, BinaryenTypeEqref()), BinaryenRefNull(module, BinaryenTypeEqref())), // Exception handling - BinaryenTry(module, tryBody, catchBody), + BinaryenTry(module, tryBody, catchEvents, 1, catchBodies, 2), // Atomics BinaryenAtomicStore( module, diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 083fa1ce551..7fa387daafa 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -1771,20 +1771,14 @@ BinaryenFeatureAll: 8191 (i32.const 0) ) ) - (catch - (local.set $5 - (pop exnref) - ) + (catch $a-event (drop - (block $try-block (result i32) - (rethrow - (br_on_exn $try-block $a-event - (local.get $5) - ) - ) - ) + (pop i32) ) ) + (catch_all + (nop) + ) ) (i32.atomic.store (i32.const 0) diff --git a/test/exception-handling.wast b/test/exception-handling.wast index 862b82d88ee..72eb0be4a18 100644 --- a/test/exception-handling.wast +++ b/test/exception-handling.wast @@ -1,6 +1,7 @@ (module - (event $e0 (attr 0) (param i32)) - (event $e1 (attr 0) (param externref)) + (event $e-i32 (attr 0) (param i32)) + (event $e-i64 (attr 0) (param i64)) + (event $e-i32-i64 (attr 0) (param i32 i64)) (func $exnref_test (param $0 exnref) (result exnref) (local.get $0) @@ -9,20 +10,27 @@ (func $foo) (func $bar) - (func $eh_test (local $exn exnref) + (func $eh_test (local $x (i32 i64)) + ;; Simple try-catch (try (do - (throw $e0 (i32.const 0)) + (throw $e-i32 (i32.const 0)) ) - (catch - ;; Multi-value is not available yet, so block can't take a value from - ;; stack. So this uses locals for now. - (local.set $exn (pop exnref)) + (catch $e-i32 + (drop (pop i32)) + ) + ) + + ;; try-catch with multivalue event + (try + (do + (throw $e-i32-i64 (i32.const 0) (i64.const 0)) + ) + (catch $e-i32-i64 + (local.set $x (pop i32 i64)) (drop - (block $l0 (result i32) - (rethrow - (br_on_exn $l0 $e0 (local.get $exn)) - ) + (tuple.extract 0 + (local.get $x) ) ) ) @@ -33,7 +41,8 @@ (do (br $l1) ) - (catch + (catch $e-i32 + (drop (pop i32)) (br $l1) ) ) @@ -41,8 +50,8 @@ ;; Empty try body (try (do) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) ) ) @@ -52,11 +61,60 @@ (call $foo) (call $bar) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) + (call $foo) + (call $bar) + ) + ) + + ;; Multiple catch clauses + (try + (do + (throw $e-i32 (i32.const 0)) + ) + (catch $e-i32 + (drop (pop i32)) + ) + (catch $e-i64 + (drop (pop i64)) + ) + ) + + ;; Single catch-all clause + (try + (do + (throw $e-i32 (i32.const 0)) + ) + (catch_all) + ) + + ;; catch and catch-all clauses together + (try + (do + (throw $e-i32 (i32.const 0)) + ) + (catch $e-i32 + (drop (pop i32)) + ) + (catch $e-i64 + (drop (pop i64)) + ) + (catch_all (call $foo) (call $bar) ) ) + + ;; rethrow + (try + (do + (throw $e-i32 (i32.const 0)) + ) + (catch $e-i32 + (drop (pop i32)) + (rethrow 0) + ) + ) ) ) diff --git a/test/exception-handling.wast.from-wast b/test/exception-handling.wast.from-wast index 9fd06c0b1aa..64d78d54e7d 100644 --- a/test/exception-handling.wast.from-wast +++ b/test/exception-handling.wast.from-wast @@ -1,10 +1,12 @@ (module (type $none_=>_none (func)) (type $i32_=>_none (func (param i32))) - (type $externref_=>_none (func (param externref))) + (type $i64_=>_none (func (param i64))) + (type $i32_i64_=>_none (func (param i32 i64))) (type $exnref_=>_exnref (func (param exnref) (result exnref))) - (event $e0 (attr 0) (param i32)) - (event $e1 (attr 0) (param externref)) + (event $e-i32 (attr 0) (param i32)) + (event $e-i64 (attr 0) (param i64)) + (event $e-i32-i64 (attr 0) (param i32 i64)) (func $exnref_test (param $0 exnref) (result exnref) (local.get $0) ) @@ -15,24 +17,33 @@ (nop) ) (func $eh_test - (local $exn exnref) + (local $x (i32 i64)) (try (do - (throw $e0 + (throw $e-i32 (i32.const 0) ) ) - (catch - (local.set $exn - (pop exnref) + (catch $e-i32 + (drop + (pop i32) + ) + ) + ) + (try + (do + (throw $e-i32-i64 + (i32.const 0) + (i64.const 0) + ) + ) + (catch $e-i32-i64 + (local.set $x + (pop i32 i64) ) (drop - (block $l0 (result i32) - (rethrow - (br_on_exn $l0 $e0 - (local.get $exn) - ) - ) + (tuple.extract 0 + (local.get $x) ) ) ) @@ -42,7 +53,10 @@ (do (br $l1) ) - (catch + (catch $e-i32 + (drop + (pop i32) + ) (br $l1) ) ) @@ -51,9 +65,9 @@ (do (nop) ) - (catch + (catch $e-i32 (drop - (pop exnref) + (pop i32) ) ) ) @@ -62,13 +76,74 @@ (call $foo) (call $bar) ) - (catch + (catch $e-i32 + (drop + (pop i32) + ) + (call $foo) + (call $bar) + ) + ) + (try + (do + (throw $e-i32 + (i32.const 0) + ) + ) + (catch $e-i32 + (drop + (pop i32) + ) + ) + (catch $e-i64 + (drop + (pop i64) + ) + ) + ) + (try + (do + (throw $e-i32 + (i32.const 0) + ) + ) + (catch_all + (nop) + ) + ) + (try + (do + (throw $e-i32 + (i32.const 0) + ) + ) + (catch $e-i32 (drop - (pop exnref) + (pop i32) ) + ) + (catch $e-i64 + (drop + (pop i64) + ) + ) + (catch_all (call $foo) (call $bar) ) ) + (try + (do + (throw $e-i32 + (i32.const 0) + ) + ) + (catch $e-i32 + (drop + (pop i32) + ) + (rethrow 0) + ) + ) ) ) diff --git a/test/exception-handling.wast.fromBinary b/test/exception-handling.wast.fromBinary index 4e189559319..292827970fe 100644 --- a/test/exception-handling.wast.fromBinary +++ b/test/exception-handling.wast.fromBinary @@ -1,10 +1,12 @@ (module (type $none_=>_none (func)) (type $i32_=>_none (func (param i32))) - (type $externref_=>_none (func (param externref))) + (type $i64_=>_none (func (param i64))) + (type $i32_i64_=>_none (func (param i32 i64))) (type $exnref_=>_exnref (func (param exnref) (result exnref))) (event $event$0 (attr 0) (param i32)) - (event $event$1 (attr 0) (param externref)) + (event $event$1 (attr 0) (param i64)) + (event $event$2 (attr 0) (param i32 i64)) (func $exnref_test (param $0 exnref) (result exnref) (local.get $0) ) @@ -15,38 +17,72 @@ (nop) ) (func $eh_test - (local $exn exnref) + (local $x i32) + (local $1 i64) + (local $2 (i32 i64)) + (local $3 i32) + (local $4 i32) (try (do (throw $event$0 (i32.const 0) ) ) - (catch - (local.set $exn - (pop exnref) - ) + (catch $event$0 (drop - (block $label$3 (result i32) - (rethrow - (br_on_exn $label$3 $event$0 - (local.get $exn) + (pop i32) + ) + ) + ) + (try + (do + (throw $event$2 + (i32.const 0) + (i64.const 0) + ) + ) + (catch $event$2 + (local.set $2 + (pop i32 i64) + ) + (local.set $x + (block (result i32) + (local.set $3 + (tuple.extract 0 + (local.get $2) + ) + ) + (local.set $1 + (tuple.extract 1 + (local.get $2) ) ) + (local.get $3) + ) + ) + (drop + (block (result i32) + (local.set $4 + (local.get $x) + ) + (drop + (local.get $1) + ) + (local.get $4) ) ) ) ) - (block $label$4 + (block $label$5 (try (do - (br $label$4) + (br $label$5) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) - (br $label$4) + (br $label$5) ) ) ) @@ -54,9 +90,9 @@ (do (nop) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) ) ) @@ -65,14 +101,75 @@ (call $foo) (call $bar) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (call $foo) (call $bar) ) ) + (try + (do + (throw $event$0 + (i32.const 0) + ) + ) + (catch $event$0 + (drop + (pop i32) + ) + ) + (catch $event$1 + (drop + (pop i64) + ) + ) + ) + (try + (do + (throw $event$0 + (i32.const 0) + ) + ) + (catch_all + (nop) + ) + ) + (try + (do + (throw $event$0 + (i32.const 0) + ) + ) + (catch $event$0 + (drop + (pop i32) + ) + ) + (catch $event$1 + (drop + (pop i64) + ) + ) + (catch_all + (call $foo) + (call $bar) + ) + ) + (try + (do + (throw $event$0 + (i32.const 0) + ) + ) + (catch $event$0 + (drop + (pop i32) + ) + (rethrow 0) + ) + ) ) ) diff --git a/test/exception-handling.wast.fromBinary.noDebugInfo b/test/exception-handling.wast.fromBinary.noDebugInfo index 0b5a7d89667..1f1ebdb6faf 100644 --- a/test/exception-handling.wast.fromBinary.noDebugInfo +++ b/test/exception-handling.wast.fromBinary.noDebugInfo @@ -1,10 +1,12 @@ (module (type $none_=>_none (func)) (type $i32_=>_none (func (param i32))) - (type $externref_=>_none (func (param externref))) + (type $i64_=>_none (func (param i64))) + (type $i32_i64_=>_none (func (param i32 i64))) (type $exnref_=>_exnref (func (param exnref) (result exnref))) (event $event$0 (attr 0) (param i32)) - (event $event$1 (attr 0) (param externref)) + (event $event$1 (attr 0) (param i64)) + (event $event$2 (attr 0) (param i32 i64)) (func $0 (param $0 exnref) (result exnref) (local.get $0) ) @@ -15,38 +17,72 @@ (nop) ) (func $3 - (local $0 exnref) + (local $0 i32) + (local $1 i64) + (local $2 (i32 i64)) + (local $3 i32) + (local $4 i32) (try (do (throw $event$0 (i32.const 0) ) ) - (catch + (catch $event$0 + (drop + (pop i32) + ) + ) + ) + (try + (do + (throw $event$2 + (i32.const 0) + (i64.const 0) + ) + ) + (catch $event$2 + (local.set $2 + (pop i32 i64) + ) (local.set $0 - (pop exnref) + (block (result i32) + (local.set $3 + (tuple.extract 0 + (local.get $2) + ) + ) + (local.set $1 + (tuple.extract 1 + (local.get $2) + ) + ) + (local.get $3) + ) ) (drop - (block $label$3 (result i32) - (rethrow - (br_on_exn $label$3 $event$0 - (local.get $0) - ) + (block (result i32) + (local.set $4 + (local.get $0) + ) + (drop + (local.get $1) ) + (local.get $4) ) ) ) ) - (block $label$4 + (block $label$5 (try (do - (br $label$4) + (br $label$5) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) - (br $label$4) + (br $label$5) ) ) ) @@ -54,9 +90,9 @@ (do (nop) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) ) ) @@ -65,14 +101,75 @@ (call $1) (call $2) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (call $1) (call $2) ) ) + (try + (do + (throw $event$0 + (i32.const 0) + ) + ) + (catch $event$0 + (drop + (pop i32) + ) + ) + (catch $event$1 + (drop + (pop i64) + ) + ) + ) + (try + (do + (throw $event$0 + (i32.const 0) + ) + ) + (catch_all + (nop) + ) + ) + (try + (do + (throw $event$0 + (i32.const 0) + ) + ) + (catch $event$0 + (drop + (pop i32) + ) + ) + (catch $event$1 + (drop + (pop i64) + ) + ) + (catch_all + (call $1) + (call $2) + ) + ) + (try + (do + (throw $event$0 + (i32.const 0) + ) + ) + (catch $event$0 + (drop + (pop i32) + ) + (rethrow 0) + ) + ) ) ) diff --git a/test/passes/code-pushing_all-features.txt b/test/passes/code-pushing_all-features.txt index 30eb7e45816..1f975a2dde9 100644 --- a/test/passes/code-pushing_all-features.txt +++ b/test/passes/code-pushing_all-features.txt @@ -40,7 +40,7 @@ ) ) ) - (func $can-push-past-throw-within-try + (func $can-push-past-try (local $x i32) (block $out (try @@ -49,9 +49,9 @@ (i32.const 0) ) ) - (catch + (catch_all (drop - (pop exnref) + (pop i32) ) ) ) @@ -69,6 +69,36 @@ ) ) ) + (func $foo + (nop) + ) + (func $cant-push-past-try + (local $x i32) + (block $out + (local.set $x + (i32.const 1) + ) + (try + (do + (call $foo) + ) + (catch $e + (drop + (pop i32) + ) + ) + ) + (drop + (i32.const 1) + ) + (br_if $out + (i32.const 2) + ) + (drop + (local.get $x) + ) + ) + ) (func $cant-push-past-rethrow-within-catch (local $x i32) (block $out @@ -81,10 +111,8 @@ (i32.const 0) ) ) - (catch - (rethrow - (pop exnref) - ) + (catch_all + (rethrow 0) ) ) (drop diff --git a/test/passes/code-pushing_all-features.wast b/test/passes/code-pushing_all-features.wast index b67b98d7e81..0d18afcc5b7 100644 --- a/test/passes/code-pushing_all-features.wast +++ b/test/passes/code-pushing_all-features.wast @@ -25,18 +25,39 @@ ) ) - (func $can-push-past-throw-within-try + (func $can-push-past-try (local $x i32) (block $out ;; This local.set can be pushed down, because the 'throw' below is going - ;; to be caught by the inner catch + ;; to be caught by the inner catch_all (local.set $x (i32.const 1)) (try (do (throw $e (i32.const 0)) ) - (catch - (drop (pop exnref)) + (catch_all + (drop (pop i32)) + ) + ) + (drop (i32.const 1)) + (br_if $out (i32.const 2)) + (drop (local.get $x)) + ) + ) + + (func $foo) + (func $cant-push-past-try + (local $x i32) + (block $out + ;; This local.set cannot be pushed down, because the exception thrown by + ;; 'call $foo' below may not be caught by 'catch $e' + (local.set $x (i32.const 1)) + (try + (do + (call $foo) + ) + (catch $e + (drop (pop i32)) ) ) (drop (i32.const 1)) @@ -49,14 +70,14 @@ (local $x i32) (block $out ;; This local.set cannot be pushed down, because there is 'rethrow' within - ;; the inner catch + ;; the inner catch_all (local.set $x (i32.const 1)) (try (do (throw $e (i32.const 0)) ) - (catch - (rethrow (pop exnref)) + (catch_all + (rethrow 0) ) ) (drop (i32.const 1)) diff --git a/test/passes/dce_all-features.txt b/test/passes/dce_all-features.txt index 4088d5a3465..447077b6ccd 100644 --- a/test/passes/dce_all-features.txt +++ b/test/passes/dce_all-features.txt @@ -551,10 +551,8 @@ (do (unreachable) ) - (catch - (drop - (pop exnref) - ) + (catch_all + (nop) ) ) (call $foo) @@ -564,7 +562,7 @@ (do (nop) ) - (catch + (catch_all (unreachable) ) ) @@ -575,7 +573,7 @@ (do (unreachable) ) - (catch + (catch_all (unreachable) ) ) @@ -591,9 +589,7 @@ (func $rethrow (block $label$0 (block $label$1 - (rethrow - (ref.null exn) - ) + (rethrow 0) ) ) ) @@ -624,7 +620,7 @@ (do (unreachable) ) - (catch + (catch_all (unreachable) ) ) diff --git a/test/passes/dce_all-features.wast b/test/passes/dce_all-features.wast index 9ebcccb60b0..fac6d762342 100644 --- a/test/passes/dce_all-features.wast +++ b/test/passes/dce_all-features.wast @@ -746,11 +746,7 @@ (do (unreachable) ) - (catch - (drop - (pop exnref) - ) - ) + (catch_all) ) (call $foo) ;; shouldn't be dce'd ) @@ -758,7 +754,7 @@ (func $catch_unreachable (try (do) - (catch + (catch_all (unreachable) ) ) @@ -770,7 +766,7 @@ (do (unreachable) ) - (catch + (catch_all (unreachable) ) ) @@ -799,9 +795,7 @@ (if (i32.clz (block $label$1 (result i32) - (rethrow - (ref.null exn) - ) + (rethrow 0) ) ) (nop) @@ -835,7 +829,7 @@ (do (unreachable) ) - (catch + (catch_all (unreachable) ) ) diff --git a/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.txt b/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.txt index 28348883ad7..fb9e2907bc3 100644 --- a/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.txt +++ b/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.txt @@ -3,20 +3,15 @@ (type $i32_=>_none (func (param i32))) (event $e0 (attr 0) (param i32)) (func $eh - (local $exn exnref) try i32.const 0 throw $e0 catch - local.set $exn - block $l0 (result i32) - local.get $exn - br_on_exn $l0 $e0 - rethrow - end drop + rethrow 0 end + unreachable ) ) (module @@ -24,26 +19,17 @@ (type $i32_=>_none (func (param i32))) (event $e0 (attr 0) (param i32)) (func $eh (; has Stack IR ;) - (local $exn exnref) (try (do (throw $e0 (i32.const 0) ) ) - (catch - (local.set $exn - (pop exnref) - ) + (catch $e0 (drop - (block $l0 (result i32) - (rethrow - (br_on_exn $l0 $e0 - (local.get $exn) - ) - ) - ) + (pop i32) ) + (rethrow 0) ) ) ) diff --git a/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.wast b/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.wast index e2bbfff2d49..7dbb4aa7276 100644 --- a/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.wast +++ b/test/passes/generate-stack-ir_optimize-stack-ir_print-stack-ir_all-features.wast @@ -1,20 +1,14 @@ (module (event $e0 (attr 0) (param i32)) - (func $eh (local $exn exnref) + (func $eh (try (do (throw $e0 (i32.const 0)) ) - (catch - (local.set $exn (pop exnref)) - (drop - (block $l0 (result i32) - (rethrow - (br_on_exn $l0 $e0 (local.get $exn)) - ) - ) - ) + (catch $e0 + (drop (pop i32)) + (rethrow 0) ) ) ) diff --git a/test/passes/instrument-locals_all-features_disable-typed-function-references.txt b/test/passes/instrument-locals_all-features_disable-typed-function-references.txt index d027f54a0b3..5fc177d9dfa 100644 --- a/test/passes/instrument-locals_all-features_disable-typed-function-references.txt +++ b/test/passes/instrument-locals_all-features_disable-typed-function-references.txt @@ -11,6 +11,7 @@ (type $i32_i32_eqref_=>_eqref (func (param i32 i32 eqref) (result eqref))) (type $i32_i32_i31ref_=>_i31ref (func (param i32 i32 i31ref) (result i31ref))) (type $none_=>_none (func)) + (type $i32_=>_none (func (param i32))) (import "env" "get_i32" (func $get_i32 (param i32 i32 i32) (result i32))) (import "env" "get_i64" (func $get_i64 (param i32 i32 i64) (result i64))) (import "env" "get_f32" (func $get_f32 (param i32 i32 f32) (result f32))) @@ -33,6 +34,7 @@ (import "env" "set_i31ref" (func $set_i31ref (param i32 i32 i31ref) (result i31ref))) (import "env" "get_v128" (func $get_v128 (param i32 i32 v128) (result v128))) (import "env" "set_v128" (func $set_v128 (param i32 i32 v128) (result v128))) + (event $e (attr 0) (param i32)) (func $test (local $x i32) (local $y i64) @@ -242,29 +244,9 @@ (do (nop) ) - (catch - (local.set $F - (pop funcref) - ) - ) - ) - (try - (do - (nop) - ) - (catch - (local.set $X - (pop externref) - ) - ) - ) - (try - (do - (nop) - ) - (catch - (local.set $E - (pop exnref) + (catch $e + (local.set $x + (pop i32) ) ) ) diff --git a/test/passes/instrument-locals_all-features_disable-typed-function-references.wast b/test/passes/instrument-locals_all-features_disable-typed-function-references.wast index c709630b5b6..53b2349facb 100644 --- a/test/passes/instrument-locals_all-features_disable-typed-function-references.wast +++ b/test/passes/instrument-locals_all-features_disable-typed-function-references.wast @@ -1,4 +1,6 @@ (module + (event $e (attr 0) (param i32)) + (func $test (local $x i32) (local $y i64) @@ -44,20 +46,8 @@ ;; Pop instructions should not be instrumented (try (do) - (catch - (local.set $F (pop funcref)) - ) - ) - (try - (do) - (catch - (local.set $X (pop externref)) - ) - ) - (try - (do) - (catch - (local.set $E (pop exnref)) + (catch $e + (local.set $x (pop i32)) ) ) diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index 5a2ede97a42..df60ebfaa85 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -5687,10 +5687,7 @@ (do (i32.const 123) ) - (catch - (drop - (pop exnref) - ) + (catch_all (i32.const 456) ) ) diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index d90dd7aebc6..191189f7076 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -6160,10 +6160,7 @@ ) ) ) - (catch - (drop - (pop exnref) - ) + (catch_all (i32.eqz (i32.eqz (i32.const 456) diff --git a/test/passes/remove-unused-module-elements_all-features.txt b/test/passes/remove-unused-module-elements_all-features.txt index 81382e4f46c..0873cfee40d 100644 --- a/test/passes/remove-unused-module-elements_all-features.txt +++ b/test/passes/remove-unused-module-elements_all-features.txt @@ -279,34 +279,23 @@ ) ) (module - (type $i32_=>_none (func (param i32))) (type $none_=>_none (func)) + (type $i32_=>_none (func (param i32))) (type $i64_=>_none (func (param i64))) (event $e-export (attr 0) (param i64)) (event $e-throw (attr 0) (param i32)) - (event $e-bronexn (attr 0) (param i32)) (export "e-export" (event $e-export)) (start $start) (func $start - (local $exn exnref) (try (do (throw $e-throw (i32.const 0) ) ) - (catch - (local.set $exn - (pop exnref) - ) + (catch $e-catch (drop - (block $l0 (result i32) - (rethrow - (br_on_exn $l0 $e-bronexn - (local.get $exn) - ) - ) - ) + (pop i32) ) ) ) diff --git a/test/passes/remove-unused-module-elements_all-features.wast b/test/passes/remove-unused-module-elements_all-features.wast index bf9d5c7ff78..c70dc1495a7 100644 --- a/test/passes/remove-unused-module-elements_all-features.wast +++ b/test/passes/remove-unused-module-elements_all-features.wast @@ -266,24 +266,17 @@ (event $e-remove (attr 0) (type $0)) ;; can be removed (event $e-export (attr 0) (param i64)) ;; cannot be removed (exported) (event $e-throw (attr 0) (type $0)) ;; cannot be removed (used in throw) - (event $e-bronexn (attr 0) (type $0)) ;; cannot be removed (used in br_on_exn) + (event $e-catch (attr 0) (type $0)) ;; cannot be removed (used in catch) (export "e-export" (event $e-export)) (import "env" "e" (event $e-import (attr 0) (param i32))) (start $start) - (func $start (local $exn exnref) (; 0 ;) + (func $start (try (do (throw $e-throw (i32.const 0)) ) - (catch - (local.set $exn (pop exnref)) - (drop - (block $l0 (result i32) - (rethrow - (br_on_exn $l0 $e-bronexn (local.get $exn)) - ) - ) - ) + (catch $e-catch + (drop (pop i32)) ) ) ) diff --git a/test/passes/remove-unused-names_code-folding_all-features.txt b/test/passes/remove-unused-names_code-folding_all-features.txt index 9736c3c0ae9..2f78a83e740 100644 --- a/test/passes/remove-unused-names_code-folding_all-features.txt +++ b/test/passes/remove-unused-names_code-folding_all-features.txt @@ -1,9 +1,10 @@ (module (type $none_=>_none (func)) (type $none_=>_i32 (func (result i32))) + (type $i32_=>_none (func (param i32))) (type $i32_=>_i32 (func (param i32) (result i32))) (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) - (type $none_=>_exnref (func (result exnref))) + (event $e-i32 (attr 0) (param i32)) (event $e (attr 0) (param)) (func $ifs (if @@ -1710,8 +1711,7 @@ (i32.const 2) ) ) - (func $exnref_pop-test - (local $exn exnref) + (func $pop-test (block $folding-inner0 (try (do @@ -1719,17 +1719,17 @@ (do (nop) ) - (catch - (local.set $exn - (pop exnref) + (catch $e-i32 + (drop + (pop i32) ) (br $folding-inner0) ) ) ) - (catch - (local.set $exn - (pop exnref) + (catch $e-i32 + (drop + (pop i32) ) (br $folding-inner0) ) @@ -1791,7 +1791,7 @@ (func $foo (nop) ) - (func $try-call-optimize-terminating-tails (result exnref) + (func $try-call-optimize-terminating-tails (result i32) (try (do (call $foo) @@ -1799,26 +1799,22 @@ (call $foo) (call $foo) (return - (ref.null exn) + (i32.const 0) ) ) - (catch - (drop - (pop exnref) - ) + (catch_all (call $foo) (call $foo) (call $foo) (call $foo) (return - (ref.null exn) + (i32.const 0) ) ) ) - (ref.null exn) + (i32.const 0) ) (func $try-call-optimize-expression-tails - (local $exn exnref) (block $x (try (do @@ -1827,10 +1823,7 @@ (call $foo) (br $x) ) - (catch - (local.set $exn - (pop exnref) - ) + (catch_all (call $foo) (call $foo) (call $foo) diff --git a/test/passes/remove-unused-names_code-folding_all-features.wast b/test/passes/remove-unused-names_code-folding_all-features.wast index 823ed45ebd9..387b8600b3e 100644 --- a/test/passes/remove-unused-names_code-folding_all-features.wast +++ b/test/passes/remove-unused-names_code-folding_all-features.wast @@ -1192,15 +1192,15 @@ ) ) - (func $exnref_pop-test (local $exn exnref) + (event $e-i32 (attr 0) (param i32)) + (func $pop-test (try (do (try (do) - (catch - ;; Expressions containing (pop exnref) should NOT be taken out and - ;; folded. - (local.set $exn (pop exnref)) + (catch $e-i32 + ;; Expressions containing a pop should NOT be taken out and folded. + (drop (pop i32)) (drop (i32.const 111)) (drop (i32.const 222)) (drop (i32.const 333)) @@ -1208,8 +1208,8 @@ ) ) ) - (catch - (local.set $exn (pop exnref)) + (catch $e-i32 + (drop (pop i32)) (drop (i32.const 111)) (drop (i32.const 222)) (drop (i32.const 333)) @@ -1246,7 +1246,7 @@ ) (func $foo) - (func $try-call-optimize-terminating-tails (result exnref) + (func $try-call-optimize-terminating-tails (result i32) (try (do ;; Expressions that can throw should NOT be taken out of 'try' scope. @@ -1254,21 +1254,20 @@ (call $foo) (call $foo) (call $foo) - (return (ref.null exn)) + (return (i32.const 0)) ) - (catch - (drop (pop exnref)) + (catch_all (call $foo) (call $foo) (call $foo) (call $foo) - (return (ref.null exn)) + (return (i32.const 0)) ) ) - (ref.null exn) + (i32.const 0) ) - (func $try-call-optimize-expression-tails (local $exn exnref) + (func $try-call-optimize-expression-tails (block $x (try (do @@ -1278,8 +1277,7 @@ (call $foo) (br $x) ) - (catch - (local.set $exn (pop exnref)) + (catch_all (call $foo) (call $foo) (call $foo) diff --git a/test/passes/remove-unused-names_merge-blocks_all-features.txt b/test/passes/remove-unused-names_merge-blocks_all-features.txt index 90a56d32d69..46d45d3fef1 100644 --- a/test/passes/remove-unused-names_merge-blocks_all-features.txt +++ b/test/passes/remove-unused-names_merge-blocks_all-features.txt @@ -1711,13 +1711,6 @@ (i32.const 3) ) ) - (func $rethrow - (local $0 exnref) - (call $foo) - (rethrow - (local.get $0) - ) - ) (func $br_on_exn (result i32) (local $0 exnref) (block $label$0 (result i32) diff --git a/test/passes/remove-unused-names_merge-blocks_all-features.wast b/test/passes/remove-unused-names_merge-blocks_all-features.wast index 9a6840650bf..6869db6ad00 100644 --- a/test/passes/remove-unused-names_merge-blocks_all-features.wast +++ b/test/passes/remove-unused-names_merge-blocks_all-features.wast @@ -1571,16 +1571,6 @@ ) ) - ;; 'call $foo' within 'block' of `rethrow' can be hoisted - (func $rethrow (local $0 exnref) - (rethrow - (block (result exnref) - (call $foo) - (local.get $0) - ) - ) - ) - ;; 'call $foo' within 'block' of `br_on_exn' can be hoisted (func $br_on_exn (result i32) (local $0 exnref) (block $label$0 (result i32) diff --git a/test/passes/remove-unused-names_optimize-instructions_all-features.txt b/test/passes/remove-unused-names_optimize-instructions_all-features.txt index 8b7fc343b12..cf1e4413250 100644 --- a/test/passes/remove-unused-names_optimize-instructions_all-features.txt +++ b/test/passes/remove-unused-names_optimize-instructions_all-features.txt @@ -10,15 +10,13 @@ (local $x1 i32) (local $x2 i32) (local $x3 i32) + (local $x4 i32) (local.set $x0 (try (result i32) (do (i32.const 1) ) - (catch - (drop - (pop exnref) - ) + (catch_all (i32.const 3) ) ) @@ -32,10 +30,7 @@ (call $dummy) (i32.const 1) ) - (catch - (drop - (pop exnref) - ) + (catch_all (i32.const 3) ) ) @@ -55,36 +50,59 @@ (i32.const 0) ) ) - (catch + (catch $e (drop - (pop exnref) + (pop i32) ) ) ) (i32.const 1) ) - (catch + (catch $e (drop - (pop exnref) + (pop i32) ) (i32.const 3) ) ) ) (drop - (local.get $x2) + (i32.and + (local.get $x2) + (i32.const 7) + ) ) (local.set $x3 (try (result i32) (do (try (do + (throw $e + (i32.const 0) + ) + ) + (catch_all (nop) ) - (catch - (drop - (pop exnref) - ) + ) + (i32.const 1) + ) + (catch_all + (i32.const 3) + ) + ) + ) + (drop + (local.get $x3) + ) + (local.set $x4 + (try (result i32) + (do + (try + (do + (nop) + ) + (catch_all (throw $e (i32.const 0) ) @@ -92,17 +110,14 @@ ) (i32.const 1) ) - (catch - (drop - (pop exnref) - ) + (catch_all (i32.const 3) ) ) ) (drop (i32.and - (local.get $x3) + (local.get $x4) (i32.const 7) ) ) diff --git a/test/passes/remove-unused-names_optimize-instructions_all-features.wast b/test/passes/remove-unused-names_optimize-instructions_all-features.wast index 7c75c2bcf90..e08c42169d6 100644 --- a/test/passes/remove-unused-names_optimize-instructions_all-features.wast +++ b/test/passes/remove-unused-names_optimize-instructions_all-features.wast @@ -7,6 +7,7 @@ (local $x1 i32) (local $x2 i32) (local $x3 i32) + (local $x4 i32) ;; try - try body does not throw, can (local.set $x0 @@ -14,8 +15,7 @@ (do (i32.const 1) ) - (catch - (drop (pop exnref)) + (catch_all (i32.const 3) ) ) @@ -29,15 +29,15 @@ (call $dummy) (i32.const 1) ) - (catch - (drop (pop exnref)) + (catch_all (i32.const 3) ) ) ) (drop (i32.and (local.get $x1) (i32.const 7))) - ;; nested try - inner try may throw but will be caught by inner catch, can + ;; nested try - inner try may throw and may not be caught by inner catch, + ;; can't (local.set $x2 (try (result i32) (do @@ -45,39 +45,57 @@ (do (throw $e (i32.const 0)) ) - (catch - (drop (pop exnref)) + (catch $e + (drop (pop i32)) ) ) (i32.const 1) ) - (catch - (drop (pop exnref)) + (catch $e + (drop (pop i32)) (i32.const 3) ) ) ) (drop (i32.and (local.get $x2) (i32.const 7))) - ;; nested try - inner catch may throw, can't + ;; nested try - inner try may throw but will be caught by inner catch_all, + ;; can (local.set $x3 (try (result i32) (do (try - (do) - (catch - (drop (pop exnref)) + (do (throw $e (i32.const 0)) ) + (catch_all) ) (i32.const 1) ) - (catch - (drop (pop exnref)) + (catch_all (i32.const 3) ) ) ) (drop (i32.and (local.get $x3) (i32.const 7))) + + ;; nested try - inner catch_all may throw, can't + (local.set $x4 + (try (result i32) + (do + (try + (do) + (catch_all + (throw $e (i32.const 0)) + ) + ) + (i32.const 1) + ) + (catch_all + (i32.const 3) + ) + ) + ) + (drop (i32.and (local.get $x4) (i32.const 7))) ) ) diff --git a/test/passes/rse_all-features.txt b/test/passes/rse_all-features.txt index 95773bb237b..8ad883d9f15 100644 --- a/test/passes/rse_all-features.txt +++ b/test/passes/rse_all-features.txt @@ -3,7 +3,6 @@ (type $i32_=>_none (func (param i32))) (type $i32_i32_=>_none (func (param i32 i32))) (type $i32_f64_=>_none (func (param i32 f64))) - (event $e (attr 0) (param i32)) (func $basic (param $x i32) (param $y f64) (local $a f32) (local $b i64) @@ -475,136 +474,4 @@ ) ) ) - (func $try1 - (local $x i32) - (try - (do - (nop) - ) - (catch - (drop - (pop exnref) - ) - (local.set $x - (i32.const 1) - ) - ) - ) - (local.set $x - (i32.const 1) - ) - ) - (func $try2 - (local $x i32) - (try - (do - (throw $e - (i32.const 0) - ) - (local.set $x - (i32.const 1) - ) - ) - (catch - (drop - (pop exnref) - ) - ) - ) - (local.set $x - (i32.const 1) - ) - ) - (func $try3 - (local $x i32) - (try - (do - (throw $e - (i32.const 0) - ) - ) - (catch - (drop - (pop exnref) - ) - (local.set $x - (i32.const 1) - ) - ) - ) - (drop - (i32.const 1) - ) - ) - (func $foo - (nop) - ) - (func $try4 - (local $x i32) - (try - (do - (call $foo) - (local.set $x - (i32.const 1) - ) - ) - (catch - (drop - (pop exnref) - ) - ) - ) - (local.set $x - (i32.const 1) - ) - ) - (func $try5 - (local $x i32) - (try - (do - (local.set $x - (i32.const 1) - ) - (call $foo) - ) - (catch - (drop - (pop exnref) - ) - ) - ) - (drop - (i32.const 1) - ) - ) - (func $nested-try - (local $x i32) - (try - (do - (try - (do - (throw $e - (i32.const 0) - ) - ) - (catch - (rethrow - (pop exnref) - ) - ) - ) - ) - (catch - (drop - (pop exnref) - ) - (local.set $x - (i32.const 1) - ) - ) - ) - (drop - (i32.const 1) - ) - ) ) diff --git a/test/passes/rse_all-features.wast b/test/passes/rse_all-features.wast index d77dae379e0..94470ef537a 100644 --- a/test/passes/rse_all-features.wast +++ b/test/passes/rse_all-features.wast @@ -287,89 +287,90 @@ ) ) - (event $e (attr 0) (param i32)) - (func $try1 - (local $x i32) - (try - (do) - (catch - (drop (pop exnref)) - (local.set $x (i32.const 1)) - ) - ) - (local.set $x (i32.const 1)) ;; should NOT be dropped - ) - (func $try2 - (local $x i32) - (try - (do - (throw $e (i32.const 0)) - (local.set $x (i32.const 1)) - ) - (catch - (drop (pop exnref)) - ) - ) - (local.set $x (i32.const 1)) ;; should NOT be dropped - ) - (func $try3 - (local $x i32) - (try - (do - (throw $e (i32.const 0)) - ) - (catch - (drop (pop exnref)) - (local.set $x (i32.const 1)) - ) - ) - (local.set $x (i32.const 1)) ;; should be dropped - ) - (func $foo) - (func $try4 - (local $x i32) - (try - (do - (call $foo) - (local.set $x (i32.const 1)) - ) - (catch - (drop (pop exnref)) - ) - ) - (local.set $x (i32.const 1)) ;; should NOT be dropped - ) - (func $try5 - (local $x i32) - (try - (do - (local.set $x (i32.const 1)) - (call $foo) - ) - (catch - (drop (pop exnref)) - ) - ) - (local.set $x (i32.const 1)) ;; should be dropped - ) - (func $nested-try - (local $x i32) - (try - (do - (try - (do - (throw $e (i32.const 0)) - ) - (catch - (rethrow (pop exnref)) - ) - ) - ) - (catch - (drop (pop exnref)) - (local.set $x (i32.const 1)) - ) - ) - (local.set $x (i32.const 1)) ;; should be dropped - ) +;; FIXME Reenable these tests after fixing CFG traversal for EH +;; (event $e (attr 0) (param i32)) +;; (func $try1 +;; (local $x i32) +;; (try +;; (do) +;; (catch +;; (drop (pop exnref)) +;; (local.set $x (i32.const 1)) +;; ) +;; ) +;; (local.set $x (i32.const 1)) ;; should NOT be dropped +;; ) +;; (func $try2 +;; (local $x i32) +;; (try +;; (do +;; (throw $e (i32.const 0)) +;; (local.set $x (i32.const 1)) +;; ) +;; (catch +;; (drop (pop exnref)) +;; ) +;; ) +;; (local.set $x (i32.const 1)) ;; should NOT be dropped +;; ) +;; (func $try3 +;; (local $x i32) +;; (try +;; (do +;; (throw $e (i32.const 0)) +;; ) +;; (catch +;; (drop (pop exnref)) +;; (local.set $x (i32.const 1)) +;; ) +;; ) +;; (local.set $x (i32.const 1)) ;; should be dropped +;; ) +;; (func $foo) +;; (func $try4 +;; (local $x i32) +;; (try +;; (do +;; (call $foo) +;; (local.set $x (i32.const 1)) +;; ) +;; (catch +;; (drop (pop exnref)) +;; ) +;; ) +;; (local.set $x (i32.const 1)) ;; should NOT be dropped +;; ) +;; (func $try5 +;; (local $x i32) +;; (try +;; (do +;; (local.set $x (i32.const 1)) +;; (call $foo) +;; ) +;; (catch +;; (drop (pop exnref)) +;; ) +;; ) +;; (local.set $x (i32.const 1)) ;; should be dropped +;; ) +;; (func $nested-try +;; (local $x i32) +;; (try +;; (do +;; (try +;; (do +;; (throw $e (i32.const 0)) +;; ) +;; (catch +;; (rethrow (pop exnref)) +;; ) +;; ) +;; ) +;; (catch +;; (drop (pop exnref)) +;; (local.set $x (i32.const 1)) +;; ) +;; ) +;; (local.set $x (i32.const 1)) ;; should be dropped +;; ) ) diff --git a/test/passes/simplify-locals_all-features.txt b/test/passes/simplify-locals_all-features.txt index 8587e115cb6..2604ad88db5 100644 --- a/test/passes/simplify-locals_all-features.txt +++ b/test/passes/simplify-locals_all-features.txt @@ -1896,12 +1896,14 @@ ) (module (type $none_=>_none (func)) + (type $i32_=>_none (func (param i32))) (type $exnref_=>_none (func (param exnref))) - (type $i32_exnref_=>_none (func (param i32 exnref))) + (type $i32_i32_=>_none (func (param i32 i32))) (type $none_=>_i32 (func (result i32))) (type $none_=>_exnref (func (result exnref))) (event $event$0 (attr 0) (param)) (event $event$1 (attr 0) (param exnref)) + (event $e-i32 (attr 0) (param i32)) (func $unoptimizable-br_on_exn-block (result exnref) (local $0 exnref) (block $label$0 @@ -1923,36 +1925,18 @@ ) ) ) - (func $rethrow-trap - (local $0 i32) - (drop - (block $label$1 (result i32) - (try - (do - (rethrow - (ref.null exn) - ) - ) - (catch - (nop) - ) - ) - (i32.const 0) - ) - ) - ) - (func $foo (param $0 i32) (param $1 exnref) + (func $foo (param $0 i32) (param $1 i32) (nop) ) (func $pop-cannot-be-sinked - (local $0 exnref) + (local $0 i32) (try (do (nop) ) - (catch + (catch $e-i32 (local.set $0 - (pop exnref) + (pop i32) ) (call $foo (i32.const 3) @@ -1962,21 +1946,21 @@ ) ) (func $pop-within-catch-can-be-sinked - (local $0 exnref) + (local $0 i32) (try (do (nop) ) - (catch + (catch_all (nop) (call $foo (i32.const 3) - (try (result exnref) + (try (result i32) (do - (ref.null exn) + (i32.const 0) ) - (catch - (pop exnref) + (catch $e-i32 + (pop i32) ) ) ) @@ -1997,9 +1981,9 @@ (local.get $0) ) ) - (catch + (catch $e-i32 (drop - (pop exnref) + (pop i32) ) ) ) @@ -2013,9 +1997,9 @@ (i32.const 3) ) ) - (catch + (catch $e-i32 (drop - (pop exnref) + (pop i32) ) ) ) diff --git a/test/passes/simplify-locals_all-features.wast b/test/passes/simplify-locals_all-features.wast index 61150edc1b5..971af79fa17 100644 --- a/test/passes/simplify-locals_all-features.wast +++ b/test/passes/simplify-locals_all-features.wast @@ -1698,29 +1698,15 @@ ) ) - (func $rethrow-trap (local $0 i32) - ;; This dead local.set cannot be replaced with a nop because rethrow can - ;; trap. - (local.set $0 - (block $label$1 (result i32) - (try - (do (rethrow (ref.null exn))) - (catch) - ) - (i32.const 0) - ) - ) - ) - - (func $foo (param i32 exnref)) - (func $pop-cannot-be-sinked (local $0 exnref) + (event $e-i32 (attr 0) (param i32)) + (func $foo (param i32 i32)) + (func $pop-cannot-be-sinked (local $0 i32) (try (do) - (catch - ;; This (local.set $0) of (pop exnref) cannot be sinked to - ;; (local.get $0) below, because pop exnref should follow right after - ;; 'catch'. - (local.set $0 (pop exnref)) + (catch $e-i32 + ;; This (local.set $0) of (pop i32) cannot be sinked to (local.get $0) + ;; below, because the pop should follow right after 'catch'. + (local.set $0 (pop i32)) (call $foo (i32.const 3) (local.get $0) @@ -1729,17 +1715,17 @@ ) ) - (func $pop-within-catch-can-be-sinked (local $0 exnref) + (func $pop-within-catch-can-be-sinked (local $0 i32) (try (do) - (catch + (catch_all ;; This whole 'try' body can be sinked to eliminate local.set / ;; local.get. Even though it contains a pop, it is enclosed within ;; try-catch, so it is OK. (local.set $0 - (try (result exnref) - (do (ref.null exn)) - (catch (pop exnref)) + (try (result i32) + (do (i32.const 0)) + (catch $e-i32 (pop i32)) ) ) (call $foo @@ -1761,8 +1747,8 @@ (do (drop (local.get $0)) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) ) ) ) @@ -1776,8 +1762,8 @@ (do (drop (local.get $0)) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) ) ) ) diff --git a/test/passes/vacuum_all-features.txt b/test/passes/vacuum_all-features.txt index 0b9d70ca41c..894c16a5d69 100644 --- a/test/passes/vacuum_all-features.txt +++ b/test/passes/vacuum_all-features.txt @@ -441,7 +441,7 @@ (func $try-test (nop) ) - (func $inner-try-test + (func $inner-try-catch_all-test (local $0 i32) (try (do @@ -449,16 +449,40 @@ (i32.const 0) ) ) - (catch - (drop - (pop exnref) - ) + (catch_all (local.set $0 (i32.const 1) ) ) ) ) + (func $inner-try-catch-test + (local $0 i32) + (try + (do + (try + (do + (throw $e + (i32.const 0) + ) + ) + (catch $e + (drop + (pop i32) + ) + (local.set $0 + (i32.const 1) + ) + ) + ) + ) + (catch $e + (drop + (pop i32) + ) + ) + ) + ) (func $br-in-catch (unreachable) ) diff --git a/test/passes/vacuum_all-features.wast b/test/passes/vacuum_all-features.wast index 9eb13b55fa9..b132f845a8d 100644 --- a/test/passes/vacuum_all-features.wast +++ b/test/passes/vacuum_all-features.wast @@ -804,29 +804,50 @@ (do (drop (i32.const 0)) ) - (catch - (drop (pop exnref)) + (catch $e + (drop (pop i32)) ) ) ) - ;; The exception thrown in the inner try is caught by the inner catch, so the - ;; outer try body does not throw and the outer try-catch can be removed - (func $inner-try-test (local $0 i32) + ;; The exception thrown in the inner try is caught by the inner catch_all, so + ;; the outer try body does not throw and the outer try-catch can be removed + (func $inner-try-catch_all-test (local $0 i32) (try (do (try (do (throw $e (i32.const 0)) ) - (catch - (drop (pop exnref)) + (catch_all (local.set $0 (i32.const 1)) ) ) ) - (catch - (drop (pop exnref)) + (catch $e + (drop (pop i32)) + ) + ) + ) + + ;; The exception thrown in the inner try may not be caught by the inner catch + ;; (here it will be caught because they use the same event, but in general we + ;; cannot know), so the outer try-catch cannot be removed + (func $inner-try-catch-test (local $0 i32) + (try + (do + (try + (do + (throw $e (i32.const 0)) + ) + (catch $e + (drop (pop i32)) + (local.set $0 (i32.const 1)) + ) + ) + ) + (catch $e + (drop (pop i32)) ) ) ) @@ -840,8 +861,8 @@ (do (unreachable) ) - (catch - (drop (pop exnref)) + (catch $e + (drop (pop i32)) (br $label$1) ) ) diff --git a/test/reference-types.wast b/test/reference-types.wast index ddc3550c537..bd19aa5c5bc 100644 --- a/test/reference-types.wast +++ b/test/reference-types.wast @@ -38,6 +38,8 @@ (global $global_anyref4 (mut anyref) (ref.func $foo)) (global $global_anyref5 (mut anyref) (ref.null exn)) + (event $e-i32 (attr 0) (param i32)) + (func $test (local $local_externref externref) (local $local_funcref funcref) @@ -462,8 +464,8 @@ (do (local.get $local_externref) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) (ref.null extern) ) ) @@ -473,8 +475,8 @@ (do (ref.func $foo) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) (ref.null func) ) ) @@ -484,8 +486,9 @@ (do (ref.null exn) ) - (catch - (pop exnref) + (catch $e-i32 + (drop (pop i32)) + (ref.null exn) ) ) ) @@ -496,8 +499,8 @@ (do (local.get $local_externref) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) (ref.func $foo) ) ) @@ -507,8 +510,9 @@ (do (local.get $local_externref) ) - (catch - (pop exnref) + (catch $e-i32 + (drop (pop i32)) + (local.get $local_exnref) ) ) ) @@ -517,8 +521,8 @@ (do (ref.func $foo) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) (local.get $local_externref) ) ) @@ -528,8 +532,9 @@ (do (ref.func $foo) ) - (catch - (pop exnref) + (catch $e-i32 + (drop (pop i32)) + (local.get $local_exnref) ) ) ) @@ -538,8 +543,8 @@ (do (ref.null exn) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) (local.get $local_externref) ) ) @@ -549,8 +554,8 @@ (do (ref.null exn) ) - (catch - (drop (pop exnref)) + (catch $e-i32 + (drop (pop i32)) (ref.func $foo) ) ) diff --git a/test/reference-types.wast.from-wast b/test/reference-types.wast.from-wast index 6a4bfede63a..9deee6a1538 100644 --- a/test/reference-types.wast.from-wast +++ b/test/reference-types.wast.from-wast @@ -8,6 +8,7 @@ (type $none_=>_externref (func (result externref))) (type $none_=>_exnref (func (result exnref))) (type $none_=>_none (func)) + (type $i32_=>_none (func (param i32))) (type $externref_=>_funcref (func (param externref) (result funcref))) (import "env" "import_global" (global $import_global externref)) (import "env" "import_func" (func $import_func (param externref) (result funcref))) @@ -22,6 +23,7 @@ (global $global_anyref3 (mut anyref) (ref.null func)) (global $global_anyref4 (mut anyref) (ref.func $foo)) (global $global_anyref5 (mut anyref) (ref.null exn)) + (event $e-i32 (attr 0) (param i32)) (export "export_func" (func $import_func)) (export "export_global" (global $import_global)) (func $take_externref (param $0 externref) @@ -700,9 +702,9 @@ (do (local.get $local_externref) ) - (catch + (catch $e-i32 (drop - (pop exnref) + (pop i32) ) (ref.null extern) ) @@ -713,9 +715,9 @@ (do (ref.func $foo) ) - (catch + (catch $e-i32 (drop - (pop exnref) + (pop i32) ) (ref.null func) ) @@ -726,8 +728,11 @@ (do (ref.null exn) ) - (catch - (pop exnref) + (catch $e-i32 + (drop + (pop i32) + ) + (ref.null exn) ) ) ) @@ -736,9 +741,9 @@ (do (local.get $local_externref) ) - (catch + (catch $e-i32 (drop - (pop exnref) + (pop i32) ) (ref.func $foo) ) @@ -749,8 +754,11 @@ (do (local.get $local_externref) ) - (catch - (pop exnref) + (catch $e-i32 + (drop + (pop i32) + ) + (local.get $local_exnref) ) ) ) @@ -759,9 +767,9 @@ (do (ref.func $foo) ) - (catch + (catch $e-i32 (drop - (pop exnref) + (pop i32) ) (local.get $local_externref) ) @@ -772,8 +780,11 @@ (do (ref.func $foo) ) - (catch - (pop exnref) + (catch $e-i32 + (drop + (pop i32) + ) + (local.get $local_exnref) ) ) ) @@ -782,9 +793,9 @@ (do (ref.null exn) ) - (catch + (catch $e-i32 (drop - (pop exnref) + (pop i32) ) (local.get $local_externref) ) @@ -795,9 +806,9 @@ (do (ref.null exn) ) - (catch + (catch $e-i32 (drop - (pop exnref) + (pop i32) ) (ref.func $foo) ) diff --git a/test/reference-types.wast.fromBinary b/test/reference-types.wast.fromBinary index a540048f9a4..50d403cafe7 100644 --- a/test/reference-types.wast.fromBinary +++ b/test/reference-types.wast.fromBinary @@ -8,6 +8,7 @@ (type $none_=>_externref (func (result externref))) (type $none_=>_exnref (func (result exnref))) (type $none_=>_none (func)) + (type $i32_=>_none (func (param i32))) (type $externref_=>_funcref (func (param externref) (result funcref))) (import "env" "import_global" (global $import_global externref)) (import "env" "import_func" (func $import_func (param externref) (result funcref))) @@ -22,6 +23,7 @@ (global $global_anyref3 (mut anyref) (ref.null func)) (global $global_anyref4 (mut anyref) (ref.func $foo)) (global $global_anyref5 (mut anyref) (ref.null exn)) + (event $event$0 (attr 0) (param i32)) (export "export_func" (func $import_func)) (export "export_global" (global $import_global)) (func $take_externref (param $0 externref) @@ -700,9 +702,9 @@ (do (local.get $local_funcref) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (ref.null extern) ) @@ -713,9 +715,9 @@ (do (ref.func $foo) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (ref.null func) ) @@ -726,8 +728,11 @@ (do (ref.null exn) ) - (catch - (pop exnref) + (catch $event$0 + (drop + (pop i32) + ) + (ref.null exn) ) ) ) @@ -736,9 +741,9 @@ (do (local.get $local_funcref) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (ref.func $foo) ) @@ -749,8 +754,11 @@ (do (local.get $local_funcref) ) - (catch - (pop exnref) + (catch $event$0 + (drop + (pop i32) + ) + (local.get $local_exnref) ) ) ) @@ -759,9 +767,9 @@ (do (ref.func $foo) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (local.get $local_funcref) ) @@ -772,8 +780,11 @@ (do (ref.func $foo) ) - (catch - (pop exnref) + (catch $event$0 + (drop + (pop i32) + ) + (local.get $local_exnref) ) ) ) @@ -782,9 +793,9 @@ (do (ref.null exn) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (local.get $local_funcref) ) @@ -795,9 +806,9 @@ (do (ref.null exn) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (ref.func $foo) ) diff --git a/test/reference-types.wast.fromBinary.noDebugInfo b/test/reference-types.wast.fromBinary.noDebugInfo index 9baf18af552..d60129e01d1 100644 --- a/test/reference-types.wast.fromBinary.noDebugInfo +++ b/test/reference-types.wast.fromBinary.noDebugInfo @@ -8,6 +8,7 @@ (type $none_=>_externref (func (result externref))) (type $none_=>_exnref (func (result exnref))) (type $none_=>_none (func)) + (type $i32_=>_none (func (param i32))) (type $externref_=>_funcref (func (param externref) (result funcref))) (import "env" "import_global" (global $gimport$0 externref)) (import "env" "import_func" (func $fimport$0 (param externref) (result funcref))) @@ -22,6 +23,7 @@ (global $global$6 (mut anyref) (ref.null func)) (global $global$7 (mut anyref) (ref.func $4)) (global $global$8 (mut anyref) (ref.null exn)) + (event $event$0 (attr 0) (param i32)) (export "export_func" (func $fimport$0)) (export "export_global" (global $gimport$0)) (func $0 (param $0 externref) @@ -700,9 +702,9 @@ (do (local.get $1) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (ref.null extern) ) @@ -713,9 +715,9 @@ (do (ref.func $4) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (ref.null func) ) @@ -726,8 +728,11 @@ (do (ref.null exn) ) - (catch - (pop exnref) + (catch $event$0 + (drop + (pop i32) + ) + (ref.null exn) ) ) ) @@ -736,9 +741,9 @@ (do (local.get $1) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (ref.func $4) ) @@ -749,8 +754,11 @@ (do (local.get $1) ) - (catch - (pop exnref) + (catch $event$0 + (drop + (pop i32) + ) + (local.get $2) ) ) ) @@ -759,9 +767,9 @@ (do (ref.func $4) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (local.get $1) ) @@ -772,8 +780,11 @@ (do (ref.func $4) ) - (catch - (pop exnref) + (catch $event$0 + (drop + (pop i32) + ) + (local.get $2) ) ) ) @@ -782,9 +793,9 @@ (do (ref.null exn) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (local.get $1) ) @@ -795,9 +806,9 @@ (do (ref.null exn) ) - (catch + (catch $event$0 (drop - (pop exnref) + (pop i32) ) (ref.func $4) ) From 08d6b9931fbaf1349decb4ae2be3819ce85f2da9 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Jan 2021 07:19:52 -0800 Subject: [PATCH 02/10] Add fallthrough comments --- src/wasm.h | 1 - src/wasm/wasm-stack.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/wasm.h b/src/wasm.h index 00496f52110..a194892c2c1 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1260,7 +1260,6 @@ class Try : public SpecificExpression { ExpressionList catchBodies; bool hasCatchAll() const { - assert(catchBodies.size() - catchEvents.size() <= 1); return catchBodies.size() - catchEvents.size() == 1; } void finalize(); diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index b87d3d9a6f9..137bf6ea70e 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2221,7 +2221,7 @@ void StackIRToBinaryWriter::write() { switch (inst->op) { case StackInst::TryBegin: catchIndexStack.push_back(0); - case StackInst::Basic: + case StackInst::Basic: // fallthrough case StackInst::BlockBegin: case StackInst::IfBegin: case StackInst::LoopBegin: { @@ -2230,7 +2230,7 @@ void StackIRToBinaryWriter::write() { } case StackInst::TryEnd: catchIndexStack.pop_back(); - case StackInst::BlockEnd: + case StackInst::BlockEnd: // fallthrough case StackInst::IfEnd: case StackInst::LoopEnd: { writer.emitScopeEnd(inst->origin); From 514090e9f2d37ff00a375f27e8b940b097fb812d Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Jan 2021 07:24:37 -0800 Subject: [PATCH 03/10] Comment fix --- src/wasm/wasm-s-parser.cpp | 7 +++---- src/wasm/wasm-stack.cpp | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 1452b22f9e2..dbd042a7f82 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1958,8 +1958,9 @@ Expression* SExpressionWasmBuilder::makeRefEq(Element& s) { // ... // ) // ) -// Zero or multiple catch blocks can exist. Zero or one catch_all block can -// exist, and if it does, it should be at the end. +// Any number of catch blocks can exist, including none. Zero or one catch_all +// block can exist, and if it does, it should be at the end. There should be at +// least one catch or catch_all body per try. Expression* SExpressionWasmBuilder::makeTry(Element& s) { auto ret = allocator.alloc(); Index i = 1; @@ -2005,8 +2006,6 @@ Expression* SExpressionWasmBuilder::makeTry(Element& s) { } ret->finalize(type); - // TODO If 'delegate' targets a try block, should we make 'try' take a label - // rather than wrapping it with a block like this? nameMapper.popLabelName(label); // create a break target if we must if (BranchUtils::BranchSeeker::has(ret, label)) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 137bf6ea70e..11a163f35e9 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1874,7 +1874,6 @@ void BinaryInstWriter::emitCatchAll(Try* curr) { assert(!breakStack.empty()); breakStack.pop_back(); breakStack.emplace_back(IMPOSSIBLE_CONTINUE); - // TODO Is this right? if (func && !sourceMap) { parent.writeExtraDebugLocation(curr, func, BinaryLocations::Catch); } From b66635dd577c445c0e31bcc2fa7c12af04279775 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Jan 2021 07:50:45 -0800 Subject: [PATCH 04/10] more comment --- src/wasm/wasm-validator.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 352e07dc210..e9a457f9558 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2099,7 +2099,9 @@ void FunctionValidator::visitRethrow(Rethrow* curr) { Type(Type::unreachable), curr, "rethrow's type must be unreachable"); - // TODO Validate depth field + // TODO Validate depth field if it becomes necessary in future. This requires + // a separate stack for 'catch', and the current LLVM toolchain only generates + // depth 0 for C++ support. } void FunctionValidator::visitBrOnExn(BrOnExn* curr) { From 34ff4f6ccd5a4da1d7371ee7bb24895d76f3966a Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 13 Jan 2021 07:53:14 -0800 Subject: [PATCH 05/10] fallthrough comment another try --- src/wasm/wasm-stack.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 11a163f35e9..ee1278860b0 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2220,7 +2220,8 @@ void StackIRToBinaryWriter::write() { switch (inst->op) { case StackInst::TryBegin: catchIndexStack.push_back(0); - case StackInst::Basic: // fallthrough + // fallthrough + case StackInst::Basic: case StackInst::BlockBegin: case StackInst::IfBegin: case StackInst::LoopBegin: { @@ -2229,7 +2230,8 @@ void StackIRToBinaryWriter::write() { } case StackInst::TryEnd: catchIndexStack.pop_back(); - case StackInst::BlockEnd: // fallthrough + // fallthrough + case StackInst::BlockEnd: case StackInst::IfEnd: case StackInst::LoopEnd: { writer.emitScopeEnd(inst->origin); From 8ed17c5d7144e1cf74cfc1c62abb3522e8be6f6e Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 14 Jan 2021 17:14:42 +0900 Subject: [PATCH 06/10] Apply suggestions from code review Co-authored-by: Thomas Lively <7121787+tlively@users.noreply.github.com> --- src/passes/CodeFolding.cpp | 3 +-- test/passes/simplify-locals_all-features.wast | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/passes/CodeFolding.cpp b/src/passes/CodeFolding.cpp index 3a9e90e2a6c..2892707587c 100644 --- a/src/passes/CodeFolding.cpp +++ b/src/passes/CodeFolding.cpp @@ -306,8 +306,7 @@ struct CodeFolding : public WalkerPass> { } if (getModule()->features.hasExceptionHandling()) { EffectAnalyzer effects(getPassOptions(), getModule()->features, item); - // Currently pop instructions are used only after 'catch', which is a - // pseudo instruction to simulate its behavior. We cannot move + // Pop instructions are pseudoinstructions used only after 'catch' to simulate its behavior. We cannot move // expressions containing pops if they are not enclosed in a 'catch' // body, because a pop instruction should follow right after 'catch'. if (effects.danglingPop) { diff --git a/test/passes/simplify-locals_all-features.wast b/test/passes/simplify-locals_all-features.wast index 971af79fa17..13ff1367994 100644 --- a/test/passes/simplify-locals_all-features.wast +++ b/test/passes/simplify-locals_all-features.wast @@ -1704,7 +1704,7 @@ (try (do) (catch $e-i32 - ;; This (local.set $0) of (pop i32) cannot be sinked to (local.get $0) + ;; This (local.set $0) of (pop i32) cannot be sunk to (local.get $0) ;; below, because the pop should follow right after 'catch'. (local.set $0 (pop i32)) (call $foo From ca15394ceaddb2ee76e39c57b35272ab6e22c05b Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 14 Jan 2021 00:16:20 -0800 Subject: [PATCH 07/10] Address comments --- src/binaryen-c.cpp | 16 ++++++++-------- src/cfg/cfg-traversal.h | 3 ++- src/ir/ExpressionAnalyzer.cpp | 4 ---- src/ir/cost.h | 2 +- src/passes/CodeFolding.cpp | 7 ++++--- src/passes/MergeBlocks.cpp | 2 -- src/wasm-delegations-fields.h | 14 +++++++++++++- src/wasm-interpreter.h | 6 +++++- src/wasm.h | 1 - src/wasm/wasm-s-parser.cpp | 2 +- src/wasm/wasm-stack.cpp | 5 ++--- src/wasm/wasm-validator.cpp | 14 +++----------- test/binaryen.js/exception-handling.js | 1 + test/passes/vacuum_all-features.txt | 3 ++- test/passes/vacuum_all-features.wast | 8 ++++---- 15 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 65601949971..f08b5f777d4 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -1200,20 +1200,20 @@ BinaryenExpressionRef BinaryenRefEq(BinaryenModuleRef module, BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module, BinaryenExpressionRef body, - const char** catchEvents, + const char** catchEvents_, BinaryenIndex numCatchEvents, - BinaryenExpressionRef* catchBodies, + BinaryenExpressionRef* catchBodies_, BinaryenIndex numCatchBodies) { - auto* ret = ((Module*)module)->allocator.alloc(); - ret->body = (Expression*)body; + std::vector catchEvents; + std::vector catchBodies; for (BinaryenIndex i = 0; i < numCatchEvents; i++) { - ret->catchEvents.push_back(catchEvents[i]); + catchEvents.push_back(catchEvents_[i]); } for (BinaryenIndex i = 0; i < numCatchBodies; i++) { - ret->catchBodies.push_back((Expression*)catchBodies[i]); + catchBodies.push_back((Expression*)catchBodies_[i]); } - ret->finalize(); - return ret; + return static_cast( + Builder(*(Module*)module).makeTry(body, catchEvents, catchBodies)); } BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module, diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h index 9bfd755f477..479c09e4b09 100644 --- a/src/cfg/cfg-traversal.h +++ b/src/cfg/cfg-traversal.h @@ -305,14 +305,15 @@ struct CFGWalker : public ControlFlowWalker { } case Expression::Id::TryId: { // FIXME Update the implementation to match the new spec + WASM_UNREACHABLE("unimp"); /* self->pushTask(SubType::doEndTry, currp); self->pushTask(SubType::scan, &curr->cast()->catchBody); self->pushTask(SubType::doStartCatch, currp); self->pushTask(SubType::scan, &curr->cast()->body); self->pushTask(SubType::doStartTry, currp); - */ return; // don't do anything else + */ } case Expression::Id::ThrowId: case Expression::Id::RethrowId: { diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp index 945af1e6b65..04ef52026f3 100644 --- a/src/ir/ExpressionAnalyzer.cpp +++ b/src/ir/ExpressionAnalyzer.cpp @@ -307,10 +307,6 @@ size_t ExpressionAnalyzer::hash(Expression* curr) { #define DELEGATE_FIELD_SIGNATURE(id, name) HASH_FIELD(name) #define DELEGATE_FIELD_NAME(id, name) visitNonScopeName(cast->name) -#define DELEGATE_FIELD_NAME_VECTOR(id, name) \ - for (auto n : cast->name) { \ - visitNonScopeName(n); \ - } #define DELEGATE_FIELD_TYPE(id, name) visitType(cast->name); #define DELEGATE_FIELD_ADDRESS(id, name) visitAddress(cast->name); diff --git a/src/ir/cost.h b/src/ir/cost.h index a107cdc45c4..4424f2e00a5 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -555,7 +555,7 @@ struct CostAnalyzer : public OverriddenVisitor { } return ret; } - Index visitRethrow(Rethrow* curr) { return 100 + visit(curr->exnref); } + Index visitRethrow(Rethrow* curr) { return 100; } Index visitBrOnExn(BrOnExn* curr) { return 1 + visit(curr->exnref) + curr->sent.size(); } diff --git a/src/passes/CodeFolding.cpp b/src/passes/CodeFolding.cpp index 2892707587c..a0fc11c83bd 100644 --- a/src/passes/CodeFolding.cpp +++ b/src/passes/CodeFolding.cpp @@ -306,9 +306,10 @@ struct CodeFolding : public WalkerPass> { } if (getModule()->features.hasExceptionHandling()) { EffectAnalyzer effects(getPassOptions(), getModule()->features, item); - // Pop instructions are pseudoinstructions used only after 'catch' to simulate its behavior. We cannot move - // expressions containing pops if they are not enclosed in a 'catch' - // body, because a pop instruction should follow right after 'catch'. + // Pop instructions are pseudoinstructions used only after 'catch' to + // simulate its behavior. We cannot move expressions containing pops if + // they are not enclosed in a 'catch' body, because a pop instruction + // should follow right after 'catch'. if (effects.danglingPop) { return false; } diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index 33dbec77c83..0ce776524b6 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -597,8 +597,6 @@ struct MergeBlocks : public WalkerPass> { } } - void visitRethrow(Rethrow* curr) { optimize(curr, curr->exnref); } - void visitBrOnExn(BrOnExn* curr) { optimize(curr, curr->exnref); } }; diff --git a/src/wasm-delegations-fields.h b/src/wasm-delegations-fields.h index 880df984d32..b5d80b86a48 100644 --- a/src/wasm-delegations-fields.h +++ b/src/wasm-delegations-fields.h @@ -58,7 +58,8 @@ // DELEGATE_FIELD_NAME(id, name) - called for a Name. // // DELEGATE_FIELD_NAME_VECTOR(id, name) - called for a variable-sized vector of -// Names. +// names (like try's catch event names). If this is not defined, and +// DELEGATE_GET_FIELD is, then DELEGATE_FIELD_CHILD is called on them. // // DELEGATE_FIELD_SCOPE_NAME_DEF(id, name) - called for a scope name definition // (like a block's name). @@ -127,6 +128,17 @@ #error please define DELEGATE_FIELD_NAME(id, name) #endif +#ifndef DELEGATE_FIELD_NAME_VECTOR +#ifdef DELEGATE_GET_FIELD +#define DELEGATE_FIELD_NAME_VECTOR(id, name) \ + for (Index i = 0; i < (DELEGATE_GET_FIELD(id, name)).size(); i++) { \ + DELEGATE_FIELD_NAME(id, name[i]); \ + } +#else +#error please define DELEGATE_FIELD_NAME_VECTOR(id, name) +#endif +#endif + #ifndef DELEGATE_FIELD_SCOPE_NAME_DEF #error please define DELEGATE_FIELD_SCOPE_NAME_DEF(id, name) #endif diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index a77d7227cf9..2636b20f25f 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1347,7 +1347,10 @@ class ExpressionRunner : public OverriddenVisitor { WASM_UNREACHABLE("throw"); } Flow visitRethrow(Rethrow* curr) { + // FIXME Update the implementation to match the new spec NOTE_ENTER("Rethrow"); + WASM_UNREACHABLE("unimp"); + /* Flow flow = visit(curr->exnref); if (flow.breaking()) { return flow; @@ -1358,6 +1361,7 @@ class ExpressionRunner : public OverriddenVisitor { } throwException(value); WASM_UNREACHABLE("rethrow"); + */ } Flow visitBrOnExn(BrOnExn* curr) { NOTE_ENTER("BrOnExn"); @@ -2944,6 +2948,7 @@ template class ModuleInstanceBase { } Flow visitTry(Try* curr) { // FIXME Update the implementation to match the new spec + WASM_UNREACHABLE("unimp"); /* NOTE_ENTER("Try"); try { @@ -2953,7 +2958,6 @@ template class ModuleInstanceBase { return this->visit(curr->catchBody); } */ - return Flow(); } Flow visitPop(Pop* curr) { NOTE_ENTER("Pop"); diff --git a/src/wasm.h b/src/wasm.h index a194892c2c1..eeccd7be033 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1280,7 +1280,6 @@ class Rethrow : public SpecificExpression { public: Rethrow(MixedArena& allocator) {} - Expression* exnref; Index depth; void finalize(); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index dbd042a7f82..29715261eeb 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2024,7 +2024,7 @@ SExpressionWasmBuilder::makeTryOrCatchBody(Element& s, Type type, bool isTry) { throw ParseException("invalid try do clause", s.line, s.col); } if (!isTry && - !(elementStartsWith(s, "catch") || elementStartsWith(s, "catch_all"))) { + !elementStartsWith(s, "catch") && !elementStartsWith(s, "catch_all")) { throw ParseException("invalid catch clause", s.line, s.col); } if (s.size() == 1) { // (do) / (catch) / (catch_all) without instructions diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index ee1278860b0..c96ceabb6c3 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1863,6 +1863,7 @@ void BinaryInstWriter::emitCatch(Try* curr, Index i) { assert(!breakStack.empty()); breakStack.pop_back(); breakStack.emplace_back(IMPOSSIBLE_CONTINUE); + // TODO Fix handling of BinaryLocations for the new EH spec if (func && !sourceMap) { parent.writeExtraDebugLocation(curr, func, BinaryLocations::Catch); } @@ -1874,9 +1875,7 @@ void BinaryInstWriter::emitCatchAll(Try* curr) { assert(!breakStack.empty()); breakStack.pop_back(); breakStack.emplace_back(IMPOSSIBLE_CONTINUE); - if (func && !sourceMap) { - parent.writeExtraDebugLocation(curr, func, BinaryLocations::Catch); - } + // TODO Fix handling of BinaryLocations for the new EH spec o << int8_t(BinaryConsts::CatchAll); } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index e9a457f9558..80bd3ad3e1a 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2045,17 +2045,9 @@ void FunctionValidator::visitTry(Try* curr) { "unreachable try-catch must have unreachable catch body"); } } - if (curr->hasCatchAll()) { - shouldBeEqual(curr->catchBodies.size(), - curr->catchEvents.size() + 1, - curr, - "the number of catch blocks and events do not match"); - } else { - shouldBeEqual(curr->catchBodies.size(), - curr->catchEvents.size(), - curr, - "the number of catch blocks and events do not match"); - } + shouldBeTrue(curr->catchBodies.size() - curr->catchEvents.size() <= 1, + curr, + "the number of catch blocks and events do not match"); } void FunctionValidator::visitThrow(Throw* curr) { diff --git a/test/binaryen.js/exception-handling.js b/test/binaryen.js/exception-handling.js index 764e145d44b..e6cadcf8844 100644 --- a/test/binaryen.js/exception-handling.js +++ b/test/binaryen.js/exception-handling.js @@ -1,6 +1,7 @@ function cleanInfo(info) { var ret = {}; for (var x in info) { + // Filter out address pointers and only print meaningful info if (x == 'id' || x == 'type' || x == 'name' || x == 'event' || x == 'depth' || x == 'hasCatchAll') { ret[x] = info[x]; diff --git a/test/passes/vacuum_all-features.txt b/test/passes/vacuum_all-features.txt index 894c16a5d69..82fb7e50657 100644 --- a/test/passes/vacuum_all-features.txt +++ b/test/passes/vacuum_all-features.txt @@ -438,6 +438,7 @@ (type $none_=>_none (func)) (type $i32_=>_none (func (param i32))) (event $e (attr 0) (param i32)) + (event $e2 (attr 0) (param i32)) (func $try-test (nop) ) @@ -462,7 +463,7 @@ (do (try (do - (throw $e + (throw $e2 (i32.const 0) ) ) diff --git a/test/passes/vacuum_all-features.wast b/test/passes/vacuum_all-features.wast index b132f845a8d..94f952f5d81 100644 --- a/test/passes/vacuum_all-features.wast +++ b/test/passes/vacuum_all-features.wast @@ -798,6 +798,7 @@ (module (event $e (attr 0) (param i32)) + (event $e2 (attr 0) (param i32)) ;; When try body does not throw, try-body can be replaced with the try body (func $try-test (try @@ -830,15 +831,14 @@ ) ) - ;; The exception thrown in the inner try may not be caught by the inner catch - ;; (here it will be caught because they use the same event, but in general we - ;; cannot know), so the outer try-catch cannot be removed + ;; The exception thrown in the inner try will not be caught by the inner + ;; catch, so the outer try-catch cannot be removed (func $inner-try-catch-test (local $0 i32) (try (do (try (do - (throw $e (i32.const 0)) + (throw $e2 (i32.const 0)) ) (catch $e (drop (pop i32)) From 4a97ef8d30524fbeb1637fb26b1e8b0e50f17bfd Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 14 Jan 2021 02:37:45 -0800 Subject: [PATCH 08/10] clang-format --- src/wasm/wasm-s-parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 29715261eeb..84576fde763 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2023,8 +2023,8 @@ SExpressionWasmBuilder::makeTryOrCatchBody(Element& s, Type type, bool isTry) { if (isTry && !elementStartsWith(s, "do")) { throw ParseException("invalid try do clause", s.line, s.col); } - if (!isTry && - !elementStartsWith(s, "catch") && !elementStartsWith(s, "catch_all")) { + if (!isTry && !elementStartsWith(s, "catch") && + !elementStartsWith(s, "catch_all")) { throw ParseException("invalid catch clause", s.line, s.col); } if (s.size() == 1) { // (do) / (catch) / (catch_all) without instructions From 93d695d71ee9203fd6e3bb4eaf629c0b5858a854 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 14 Jan 2021 07:49:01 -0800 Subject: [PATCH 09/10] One more line in binaryenjs test --- test/binaryen.js/expressions.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js index 598afd6fa75..757d88b1bc1 100644 --- a/test/binaryen.js/expressions.js +++ b/test/binaryen.js/expressions.js @@ -1457,11 +1457,13 @@ console.log("# Try"); theTry.body = body = module.i32.const(4); assert(theTry.body === body); - theTry.catchBodies = catchBodies = [ + catchBodies = [ module.i32.const(5) // set //remove - ] + ]; + theTry.setCatchBodies(catchBodies); assertDeepEqual(theTry.catchBodies, catchBodies); + assertDeepEqual(theTry.getCatchBodies(), catchBodies); console.log(theTry.toText()); theTry.insertCatchEventAt(1, "event2"); From 8d628649fca24371e8728aa8f180dc248e682dbf Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Fri, 15 Jan 2021 01:27:34 -0800 Subject: [PATCH 10/10] Disallow non-zero depth for rethrow --- 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 80bd3ad3e1a..064a151c361 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2091,9 +2091,10 @@ void FunctionValidator::visitRethrow(Rethrow* curr) { Type(Type::unreachable), curr, "rethrow's type must be unreachable"); - // TODO Validate depth field if it becomes necessary in future. This requires - // a separate stack for 'catch', and the current LLVM toolchain only generates - // depth 0 for C++ support. + // TODO Allow non-zero depths and Validate the depth field. The current LLVM + // toolchain only generates depth 0 for C++ support. + shouldBeEqual( + curr->depth, (Index)0, curr, "rethrow only support depth 0 at the moment"); } void FunctionValidator::visitBrOnExn(BrOnExn* curr) {