summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHeejin Ahn <aheejin@gmail.com>2021-01-15 18:48:00 +0900
committerGitHub <noreply@github.com>2021-01-15 18:48:00 +0900
commitbeccdf70258cd99ea25f10af13103e14dc243ffa (patch)
tree1081d7d350fbab7f901b917f2f082c8d351c3157 /src
parentf18c18e01d03d6d293fe3d701408855bbcea58bd (diff)
downloadbinaryen-beccdf70258cd99ea25f10af13103e14dc243ffa.tar.gz
binaryen-beccdf70258cd99ea25f10af13103e14dc243ffa.tar.bz2
binaryen-beccdf70258cd99ea25f10af13103e14dc243ffa.zip
Basic EH instrucion support for the new spec (#3487)
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. Also added a few more tests in existing EH test functions in test/passes. In the previous spec, `catch` was catching all exceptions so it was assumed that anything `try` body throws is caught by its `catch`, but now we can assume the same only if there is a `catch_all`. Newly added tests test cases when there is a `catch_all` and cases there are only `catch`es separately.
Diffstat (limited to 'src')
-rw-r--r--src/binaryen-c.cpp127
-rw-r--r--src/binaryen-c.h78
-rw-r--r--src/cfg/cfg-traversal.h4
-rw-r--r--src/ir/ExpressionAnalyzer.cpp1
-rw-r--r--src/ir/ExpressionManipulator.cpp1
-rw-r--r--src/ir/branch-utils.h2
-rw-r--r--src/ir/cost.h4
-rw-r--r--src/ir/effects.h33
-rw-r--r--src/ir/utils.h6
-rw-r--r--src/js/binaryen.js-post.js118
-rw-r--r--src/passes/CodeFolding.cpp8
-rw-r--r--src/passes/DeadCodeElimination.cpp7
-rw-r--r--src/passes/MergeBlocks.cpp2
-rw-r--r--src/passes/OptimizeInstructions.cpp4
-rw-r--r--src/passes/Print.cpp32
-rw-r--r--src/passes/SimplifyLocals.cpp4
-rw-r--r--src/passes/Vacuum.cpp4
-rw-r--r--src/wasm-binary.h1
-rw-r--r--src/wasm-builder.h19
-rw-r--r--src/wasm-delegations-fields.h21
-rw-r--r--src/wasm-interpreter.h8
-rw-r--r--src/wasm-stack.h29
-rw-r--r--src/wasm-traversal.h9
-rw-r--r--src/wasm.h10
-rw-r--r--src/wasm/wasm-binary.cpp60
-rw-r--r--src/wasm/wasm-s-parser.cpp51
-rw-r--r--src/wasm/wasm-stack.cpp32
-rw-r--r--src/wasm/wasm-validator.cpp34
-rw-r--r--src/wasm/wasm.cpp12
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;
}
}