summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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;
}
}