diff options
Diffstat (limited to 'src')
29 files changed, 565 insertions, 156 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 59ca3aebc..f08b5f777 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) { + const char** catchEvents_, + BinaryenIndex numCatchEvents, + BinaryenExpressionRef* catchBodies_, + BinaryenIndex numCatchBodies) { + std::vector<Name> catchEvents; + std::vector<Expression*> catchBodies; + for (BinaryenIndex i = 0; i < numCatchEvents; i++) { + catchEvents.push_back(catchEvents_[i]); + } + for (BinaryenIndex i = 0; i < numCatchBodies; i++) { + catchBodies.push_back((Expression*)catchBodies_[i]); + } return static_cast<Expression*>( - Builder(*(Module*)module) - .makeTry((Expression*)body, (Expression*)catchBody)); + Builder(*(Module*)module).makeTry(body, catchEvents, catchBodies)); } BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module, @@ -1219,9 +1229,8 @@ BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module, } BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module, - BinaryenExpressionRef exnref) { - return static_cast<Expression*>( - Builder(*(Module*)module).makeRethrow((Expression*)exnref)); + BinaryenIndex depth) { + return static_cast<Expression*>(Builder(*(Module*)module).makeRethrow(depth)); } BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module, @@ -2755,17 +2764,101 @@ void BinaryenTrySetBody(BinaryenExpressionRef expr, assert(bodyExpr); static_cast<Try*>(expression)->body = (Expression*)bodyExpr; } -BinaryenExpressionRef BinaryenTryGetCatchBody(BinaryenExpressionRef expr) { +BinaryenIndex BinaryenTryGetNumCatchEvents(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<Try>()); + return static_cast<Try*>(expression)->catchEvents.size(); +} +BinaryenIndex BinaryenTryGetNumCatchBodies(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<Try>()); + return static_cast<Try*>(expression)->catchBodies.size(); +} +const char* BinaryenTryGetCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index) { + auto* expression = (Expression*)expr; + assert(expression->is<Try>()); + assert(index < static_cast<Try*>(expression)->catchEvents.size()); + return static_cast<Try*>(expression)->catchEvents[index].c_str(); +} +void BinaryenTrySetCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index, + const char* catchEvent) { auto* expression = (Expression*)expr; assert(expression->is<Try>()); - return static_cast<Try*>(expression)->catchBody; + assert(index < static_cast<Try*>(expression)->catchEvents.size()); + assert(catchEvent); + static_cast<Try*>(expression)->catchEvents[index] = catchEvent; } -void BinaryenTrySetCatchBody(BinaryenExpressionRef expr, - BinaryenExpressionRef catchBodyExpr) { +BinaryenIndex BinaryenTryAppendCatchEvent(BinaryenExpressionRef expr, + const char* catchEvent) { auto* expression = (Expression*)expr; assert(expression->is<Try>()); - assert(catchBodyExpr); - static_cast<Try*>(expression)->catchBody = (Expression*)catchBodyExpr; + assert(catchEvent); + auto& list = static_cast<Try*>(expression)->catchEvents; + auto index = list.size(); + list.push_back(catchEvent); + return index; +} +void BinaryenTryInsertCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index, + const char* catchEvent) { + auto* expression = (Expression*)expr; + assert(expression->is<Try>()); + assert(catchEvent); + static_cast<Try*>(expression)->catchEvents.insertAt(index, catchEvent); +} +const char* BinaryenTryRemoveCatchEventAt(BinaryenExpressionRef expr, + BinaryenIndex index) { + auto* expression = (Expression*)expr; + assert(expression->is<Try>()); + return static_cast<Try*>(expression)->catchEvents.removeAt(index).c_str(); +} +BinaryenExpressionRef BinaryenTryGetCatchBodyAt(BinaryenExpressionRef expr, + BinaryenIndex index) { + auto* expression = (Expression*)expr; + assert(expression->is<Try>()); + assert(index < static_cast<Try*>(expression)->catchBodies.size()); + return static_cast<Try*>(expression)->catchBodies[index]; +} +void BinaryenTrySetCatchBodyAt(BinaryenExpressionRef expr, + BinaryenIndex index, + BinaryenExpressionRef catchExpr) { + auto* expression = (Expression*)expr; + assert(expression->is<Try>()); + assert(index < static_cast<Try*>(expression)->catchBodies.size()); + assert(catchExpr); + static_cast<Try*>(expression)->catchBodies[index] = (Expression*)catchExpr; +} +BinaryenIndex BinaryenTryAppendCatchBody(BinaryenExpressionRef expr, + BinaryenExpressionRef catchExpr) { + auto* expression = (Expression*)expr; + assert(expression->is<Try>()); + assert(catchExpr); + auto& list = static_cast<Try*>(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<Try>()); + assert(catchExpr); + static_cast<Try*>(expression) + ->catchBodies.insertAt(index, (Expression*)catchExpr); +} +BinaryenExpressionRef BinaryenTryRemoveCatchBodyAt(BinaryenExpressionRef expr, + BinaryenIndex index) { + auto* expression = (Expression*)expr; + assert(expression->is<Try>()); + return static_cast<Try*>(expression)->catchBodies.removeAt(index); +} +int BinaryenTryHasCatchAll(BinaryenExpressionRef expr) { + auto* expression = (Expression*)expr; + assert(expression->is<Try>()); + return static_cast<Try*>(expression)->hasCatchAll(); } // Throw const char* BinaryenThrowGetEvent(BinaryenExpressionRef expr) { @@ -2825,17 +2918,15 @@ BinaryenExpressionRef BinaryenThrowRemoveOperandAt(BinaryenExpressionRef expr, return static_cast<Throw*>(expression)->operands.removeAt(index); } // Rethrow -BinaryenExpressionRef BinaryenRethrowGetExnref(BinaryenExpressionRef expr) { +BinaryenIndex BinaryenRethrowGetDepth(BinaryenExpressionRef expr) { auto* expression = (Expression*)expr; assert(expression->is<Rethrow>()); - return static_cast<Rethrow*>(expression)->exnref; + return static_cast<Rethrow*>(expression)->depth; } -void BinaryenRethrowSetExnref(BinaryenExpressionRef expr, - BinaryenExpressionRef exnrefExpr) { +void BinaryenRethrowSetDepth(BinaryenExpressionRef expr, BinaryenIndex depth) { auto* expression = (Expression*)expr; assert(expression->is<Rethrow>()); - assert(exnrefExpr); - static_cast<Rethrow*>(expression)->exnref = (Expression*)exnrefExpr; + static_cast<Rethrow*>(expression)->depth = depth; } // BrOnExn const char* BinaryenBrOnExnGetEvent(BinaryenExpressionRef expr) { diff --git a/src/binaryen-c.h b/src/binaryen-c.h index a32d9ff1c..8d991343d 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 647b795a4..479c09e4b 100644 --- a/src/cfg/cfg-traversal.h +++ b/src/cfg/cfg-traversal.h @@ -304,12 +304,16 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { break; } 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<Try>()->catchBody); self->pushTask(SubType::doStartCatch, currp); self->pushTask(SubType::scan, &curr->cast<Try>()->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 88dce6767..04ef52026 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()) { \ diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp index 18c3f2df9..7ad8d0f50 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 d7a6edcae..2e7ef470d 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -58,6 +58,7 @@ template<typename T> 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<typename T> 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 fbb4e83ad..4424f2e00 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -546,7 +546,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> { } 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; @@ -555,7 +555,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> { } 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/ir/effects.h b/src/ir/effects.h index 71957d1f8..22fab593d 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -84,14 +84,16 @@ public: // 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 @@ private: if (curr->is<Try>()) { self->pushTask(doVisitTry, currp); self->pushTask(doEndCatch, currp); - self->pushTask(scan, &curr->cast<Try>()->catchBody); + auto& catchBodies = curr->cast<Try>()->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<Try>()->body); self->pushTask(doStartTry, currp); @@ -269,12 +274,22 @@ private: } static void doStartTry(InternalAnalyzer* self, Expression** currp) { - self->parent.tryDepth++; + Try* curr = (*currp)->cast<Try>(); + // 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<Try>(); + // 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 0136fd11c..424298bb3 100644 --- a/src/ir/utils.h +++ b/src/ir/utils.h @@ -222,8 +222,10 @@ struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> { 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 6cdf26e32..c3dc2144c 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 ee54c50a6..a0fc11c83 100644 --- a/src/passes/CodeFolding.cpp +++ b/src/passes/CodeFolding.cpp @@ -306,10 +306,10 @@ private: } 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'. + // 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/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp index 3e84a7be1..c30da5428 100644 --- a/src/passes/DeadCodeElimination.cpp +++ b/src/passes/DeadCodeElimination.cpp @@ -161,9 +161,12 @@ struct DeadCodeElimination } else if (auto* tryy = curr->dynCast<Try>()) { // 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/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index 33dbec77c..0ce776524 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -597,8 +597,6 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { } } - void visitRethrow(Rethrow* curr) { optimize(curr, curr->exnref); } - void visitBrOnExn(BrOnExn* curr) { optimize(curr, curr->exnref); } }; diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 2b0a95fc7..c06763643 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1099,7 +1099,9 @@ private: } else if (auto* tryy = boolean->dynCast<Try>()) { 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 e1d80a11a..b6da29861 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<PrintSExpression> { 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<PrintSExpression> { 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 963575c5f..9f164ae92 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 789c53ad7..ca777431a 100644 --- a/src/passes/Vacuum.cpp +++ b/src/passes/Vacuum.cpp @@ -341,7 +341,9 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { 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 284f81aab..ea18fdf67 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 a2aa2d505..faf275f66 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -624,17 +624,24 @@ public: ret->finalize(); return ret; } - Try* makeTry(Expression* body, Expression* catchBody) { + Try* makeTry(Expression* body, + const std::vector<Name>& catchEvents, + const std::vector<Expression*>& catchBodies) { auto* ret = wasm.allocator.alloc<Try>(); 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<Name>& catchEvents, + const std::vector<Expression*>& catchBodies, + Type type) { auto* ret = wasm.allocator.alloc<Try>(); ret->body = body; - ret->catchBody = catchBody; + ret->catchEvents.set(catchEvents); + ret->catchBodies.set(catchBodies); ret->finalize(type); return ret; } @@ -648,9 +655,9 @@ public: ret->finalize(); return ret; } - Rethrow* makeRethrow(Expression* exnref) { + Rethrow* makeRethrow(Index depth) { auto* ret = wasm.allocator.alloc<Rethrow>(); - 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 37fb60ef2..b5d80b86a 100644 --- a/src/wasm-delegations-fields.h +++ b/src/wasm-delegations-fields.h @@ -57,6 +57,10 @@ // // DELEGATE_FIELD_NAME(id, name) - called for a Name. // +// DELEGATE_FIELD_NAME_VECTOR(id, name) - called for a variable-sized vector of +// 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). // @@ -124,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 @@ -490,7 +505,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 +520,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 +681,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 243542836..2636b20f2 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1347,7 +1347,10 @@ public: 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 @@ public: } throwException(value); WASM_UNREACHABLE("rethrow"); + */ } Flow visitBrOnExn(BrOnExn* curr) { NOTE_ENTER("BrOnExn"); @@ -2943,6 +2947,9 @@ private: return {}; } Flow visitTry(Try* curr) { + // FIXME Update the implementation to match the new spec + WASM_UNREACHABLE("unimp"); + /* NOTE_ENTER("Try"); try { return this->visit(curr->body); @@ -2950,6 +2957,7 @@ private: instance.multiValues.push_back(e.exn); return this->visit(curr->catchBody); } + */ } Flow visitPop(Pop* curr) { NOTE_ENTER("Pop"); diff --git a/src/wasm-stack.h b/src/wasm-stack.h index 1bbf686d8..bd44ed72e 100644 --- a/src/wasm-stack.h +++ b/src/wasm-stack.h @@ -70,6 +70,7 @@ public: 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 @@ public: 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 @@ private: void emit(Expression* curr) { static_cast<SubType*>(this)->emit(curr); } void emitHeader() { static_cast<SubType*>(this)->emitHeader(); } void emitIfElse(If* curr) { static_cast<SubType*>(this)->emitIfElse(curr); } - void emitCatch(Try* curr) { static_cast<SubType*>(this)->emitCatch(curr); } + void emitCatch(Try* curr, Index i) { + static_cast<SubType*>(this)->emitCatch(curr, i); + } + void emitCatchAll(Try* curr) { + static_cast<SubType*>(this)->emitCatchAll(curr); + } void emitScopeEnd(Expression* curr) { static_cast<SubType*>(this)->emitScopeEnd(curr); } @@ -328,8 +335,14 @@ void BinaryenIRWriter<SubType>::visitLoop(Loop* curr) { template<typename SubType> void BinaryenIRWriter<SubType>::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 @@ public: 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 @@ public: 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 e3de27f62..28470c614 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -337,6 +337,7 @@ struct PostWalker : public Walker<SubType, VisitorType> { #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<SubType, VisitorType> { case Expression::Id::TryId: { self->pushTask(SubType::doVisitTry, currp); self->pushTask(SubType::doNoteNonLinear, currp); - self->pushTask(SubType::scan, &curr->cast<Try>()->catchBody); - self->pushTask(SubType::doNoteNonLinear, currp); + auto& list = curr->cast<Try>()->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<Try>()->body); break; } @@ -583,7 +587,6 @@ struct LinearExecutionWalker : public PostWalker<SubType, VisitorType> { case Expression::Id::RethrowId: { self->pushTask(SubType::doVisitRethrow, currp); self->pushTask(SubType::doNoteNonLinear, currp); - self->pushTask(SubType::scan, &curr->cast<Rethrow>()->exnref); break; } case Expression::Id::BrOnExnId: { diff --git a/src/wasm.h b/src/wasm.h index 29d2e9b5d..eeccd7be0 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1253,11 +1253,15 @@ public: class Try : public SpecificExpression<Expression::TryId> { public: - Try(MixedArena& allocator) {} + Try(MixedArena& allocator) : catchEvents(allocator), catchBodies(allocator) {} Expression* body; - Expression* catchBody; + ArenaVector<Name> catchEvents; + ExpressionList catchBodies; + bool hasCatchAll() const { + return catchBodies.size() - catchEvents.size() == 1; + } void finalize(); void finalize(Type type_); }; @@ -1276,7 +1280,7 @@ class Rethrow : public SpecificExpression<Expression::RethrowId> { public: Rethrow(MixedArena& allocator) {} - Expression* exnref; + Index depth; void finalize(); }; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index beaed1b11..9b4794997 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<Block>(); + 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<Block>(); - 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 d76a3c49a..84576fde7 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1947,13 +1947,20 @@ 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. +// 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<Try>(); Index i = 1; @@ -1966,15 +1973,38 @@ 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); nameMapper.popLabelName(label); // create a break target if we must @@ -1993,10 +2023,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<Block>(); @@ -2027,7 +2058,7 @@ Expression* SExpressionWasmBuilder::makeThrow(Element& s) { Expression* SExpressionWasmBuilder::makeRethrow(Element& s) { auto ret = allocator.alloc<Rethrow>(); - 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 049573fc5..c96ceabb6 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1859,14 +1859,24 @@ 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); + // TODO Fix handling of BinaryLocations for the new EH spec 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 Fix handling of BinaryLocations for the new EH spec + o << int8_t(BinaryConsts::CatchAll); } void BinaryInstWriter::visitThrow(Throw* curr) { @@ -1874,7 +1884,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 +2210,29 @@ StackInst* StackIRGenerator::makeStackInst(StackInst::Op op, void StackIRToBinaryWriter::write() { writer.mapLocalsAndEmitHeader(); + // Stack to track indices of catches within a try + SmallVector<Index, 4> 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); + // fallthrough 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(); + // fallthrough 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<Try>()); + writer.emitCatch(inst->origin->cast<Try>(), catchIndexStack.back()++); break; } default: diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index b0fc4d9c8..064a151c3 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2026,21 +2026,28 @@ 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), - curr, - "unreachable try-catch must have unreachable catch body"); + for (auto catchBody : curr->catchBodies) { + shouldBeEqual(catchBody->type, + Type(Type::unreachable), + curr, + "unreachable try-catch must have unreachable catch body"); + } } + shouldBeTrue(curr->catchBodies.size() - curr->catchEvents.size() <= 1, + curr, + "the number of catch blocks and events do not match"); } void FunctionValidator::visitThrow(Throw* curr) { @@ -2084,11 +2091,10 @@ 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 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) { diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index fb523e254..55b03c228 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; } } |