summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/binaryen-c.cpp172
-rw-r--r--src/binaryen-c.h31
-rw-r--r--src/gen-s-parser.inc71
-rw-r--r--src/ir/ExpressionAnalyzer.cpp7
-rw-r--r--src/ir/ExpressionManipulator.cpp18
-rw-r--r--src/ir/ReFinalize.cpp9
-rw-r--r--src/ir/branch-utils.h43
-rw-r--r--src/ir/effects.h5
-rw-r--r--src/ir/type-updating.h10
-rw-r--r--src/ir/utils.h8
-rw-r--r--src/js/binaryen.js-post.js48
-rw-r--r--src/passes/DeadCodeElimination.cpp16
-rw-r--r--src/passes/Precompute.cpp4
-rw-r--r--src/passes/Print.cpp120
-rw-r--r--src/passes/RemoveUnusedNames.cpp6
-rw-r--r--src/wasm-binary.h42
-rw-r--r--src/wasm-builder.h48
-rw-r--r--src/wasm-interpreter.h4
-rw-r--r--src/wasm-s-parser.h5
-rw-r--r--src/wasm-stack.h54
-rw-r--r--src/wasm-traversal.h102
-rw-r--r--src/wasm.h50
-rw-r--r--src/wasm/wasm-binary.cpp98
-rw-r--r--src/wasm/wasm-s-parser.cpp96
-rw-r--r--src/wasm/wasm-stack.cpp45
-rw-r--r--src/wasm/wasm-validator.cpp105
-rw-r--r--src/wasm/wasm.cpp56
-rw-r--r--src/wasm2js.h16
28 files changed, 1209 insertions, 80 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 81eff0c8a..5d7566e4a 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -354,6 +354,14 @@ BinaryenExpressionId BinaryenMemoryCopyId(void) {
BinaryenExpressionId BinaryenMemoryFillId(void) {
return Expression::Id::MemoryFillId;
}
+BinaryenExpressionId BinaryenTryId(void) { return Expression::Id::TryId; }
+BinaryenExpressionId BinaryenThrowId(void) { return Expression::Id::ThrowId; }
+BinaryenExpressionId BinaryenRethrowId(void) {
+ return Expression::Id::RethrowId;
+}
+BinaryenExpressionId BinaryenBrOnExnId(void) {
+ return Expression::Id::BrOnExnId;
+}
BinaryenExpressionId BinaryenPushId(void) { return Expression::Id::PushId; }
BinaryenExpressionId BinaryenPopId(void) { return Expression::Id::PopId; }
@@ -1591,6 +1599,73 @@ BinaryenExpressionRef BinaryenPop(BinaryenModuleRef module, BinaryenType type) {
return static_cast<Expression*>(ret);
}
+BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
+ BinaryenExpressionRef body,
+ BinaryenExpressionRef catchBody) {
+ auto* ret = Builder(*(Module*)module)
+ .makeTry((Expression*)body, (Expression*)catchBody);
+ if (tracing) {
+ traceExpression(ret, "BinaryenTry", body, catchBody);
+ }
+ return static_cast<Expression*>(ret);
+}
+
+BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module,
+ const char* event,
+ BinaryenExpressionRef* operands,
+ BinaryenIndex numOperands) {
+ std::vector<Expression*> args;
+ for (BinaryenIndex i = 0; i < numOperands; i++) {
+ args.push_back((Expression*)operands[i]);
+ }
+ auto* ret = Builder(*(Module*)module).makeThrow(event, args);
+
+ if (tracing) {
+ std::cout << " {\n";
+ std::cout << " BinaryenExpressionRef operands[] = { ";
+ for (BinaryenIndex i = 0; i < numOperands; i++) {
+ if (i > 0) {
+ std::cout << ", ";
+ }
+ std::cout << "expressions[" << expressions[operands[i]] << "]";
+ }
+ if (numOperands == 0) {
+ // ensure the array is not empty, otherwise a compiler error on VS
+ std::cout << "0";
+ }
+ std::cout << " };\n ";
+ traceExpression(
+ ret, "BinaryenThrow", StringLit(event), "operands", numOperands);
+ std::cout << " }\n";
+ }
+ return static_cast<Expression*>(ret);
+}
+
+BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module,
+ BinaryenExpressionRef exnref) {
+ auto* ret = Builder(*(Module*)module).makeRethrow((Expression*)exnref);
+ if (tracing) {
+ traceExpression(ret, "BinaryenRethrow", exnref);
+ }
+ return static_cast<Expression*>(ret);
+}
+
+BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module,
+ const char* name,
+ const char* eventName,
+ BinaryenExpressionRef exnref) {
+ Module* wasm = (Module*)module;
+ Event* event = wasm->getEventOrNull(eventName);
+ assert(event && "br_on_exn's event must exist");
+ auto* ret = Builder(*wasm).makeBrOnExn(name, event, (Expression*)exnref);
+
+ if (tracing) {
+ traceExpression(
+ ret, "BinaryenBrOnExn", StringLit(name), StringLit(eventName), exnref);
+ }
+ return static_cast<Expression*>(ret);
+}
+
// Expression utility
BinaryenExpressionId BinaryenExpressionGetId(BinaryenExpressionRef expr) {
@@ -2721,6 +2796,7 @@ BinaryenExpressionRef BinaryenMemoryFillGetSize(BinaryenExpressionRef expr) {
assert(expression->is<MemoryFill>());
return static_cast<MemoryFill*>(expression)->size;
}
+// Push
BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr) {
if (tracing) {
std::cout << " BinaryenPushGetValue(expressions[" << expressions[expr]
@@ -2731,6 +2807,102 @@ BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr) {
assert(expression->is<Push>());
return static_cast<Push*>(expression)->value;
}
+// Try
+BinaryenExpressionRef BinaryenTryGetBody(BinaryenExpressionRef expr) {
+ if (tracing) {
+ std::cout << " BinaryenTryGetBody(expressions[" << expressions[expr]
+ << "]);\n";
+ }
+
+ auto* expression = (Expression*)expr;
+ assert(expression->is<Try>());
+ return static_cast<Try*>(expression)->body;
+}
+BinaryenExpressionRef BinaryenTryGetCatchBody(BinaryenExpressionRef expr) {
+ if (tracing) {
+ std::cout << " BinaryenTryGetCatchBody(expressions[" << expressions[expr]
+ << "]);\n";
+ }
+
+ auto* expression = (Expression*)expr;
+ assert(expression->is<Try>());
+ return static_cast<Try*>(expression)->catchBody;
+}
+// Throw
+const char* BinaryenThrowGetEvent(BinaryenExpressionRef expr) {
+ if (tracing) {
+ std::cout << " BinaryenThrowGetEvent(expressions[" << expressions[expr]
+ << "]);\n";
+ }
+
+ auto* expression = (Expression*)expr;
+ assert(expression->is<Throw>());
+ return static_cast<Throw*>(expression)->event.c_str();
+}
+BinaryenExpressionRef BinaryenThrowGetOperand(BinaryenExpressionRef expr,
+ BinaryenIndex index) {
+ if (tracing) {
+ std::cout << " BinaryenThrowGetOperand(expressions[" << expressions[expr]
+ << "], " << index << ");\n";
+ }
+
+ auto* expression = (Expression*)expr;
+ assert(expression->is<Throw>());
+ assert(index < static_cast<Throw*>(expression)->operands.size());
+ return static_cast<Throw*>(expression)->operands[index];
+}
+BinaryenIndex BinaryenThrowGetNumOperands(BinaryenExpressionRef expr) {
+ if (tracing) {
+ std::cout << " BinaryenThrowGetNumOperands(expressions["
+ << expressions[expr] << "]);\n";
+ }
+
+ auto* expression = (Expression*)expr;
+ assert(expression->is<Throw>());
+ return static_cast<Throw*>(expression)->operands.size();
+}
+// Rethrow
+BinaryenExpressionRef BinaryenRethrowGetExnref(BinaryenExpressionRef expr) {
+ if (tracing) {
+ std::cout << " BinaryenRethrowGetExnref(expressions[" << expressions[expr]
+ << "]);\n";
+ }
+
+ auto* expression = (Expression*)expr;
+ assert(expression->is<Rethrow>());
+ return static_cast<Rethrow*>(expression)->exnref;
+}
+// BrOnExn
+const char* BinaryenBrOnExnGetEvent(BinaryenExpressionRef expr) {
+ if (tracing) {
+ std::cout << " BinaryenBrOnExnGetEvent(expressions[" << expressions[expr]
+ << "]);\n";
+ }
+
+ auto* expression = (Expression*)expr;
+ assert(expression->is<BrOnExn>());
+ return static_cast<BrOnExn*>(expression)->event.c_str();
+}
+const char* BinaryenBrOnExnGetName(BinaryenExpressionRef expr) {
+ if (tracing) {
+ std::cout << " BinaryenBrOnExnGetName(expressions[" << expressions[expr]
+ << "]);\n";
+ }
+
+ auto* expression = (Expression*)expr;
+ assert(expression->is<BrOnExn>());
+ return static_cast<BrOnExn*>(expression)->name.c_str();
+}
+BinaryenExpressionRef BinaryenBrOnExnGetExnref(BinaryenExpressionRef expr) {
+ if (tracing) {
+ std::cout << " BinaryenBrOnExnGetExnref(expressions[" << expressions[expr]
+ << "]);\n";
+ }
+
+ auto* expression = (Expression*)expr;
+ assert(expression->is<BrOnExn>());
+ return static_cast<BrOnExn*>(expression)->exnref;
+}
// Functions
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index dc5d50fef..37e8c3204 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -127,6 +127,10 @@ BinaryenExpressionId BinaryenMemoryInitId(void);
BinaryenExpressionId BinaryenDataDropId(void);
BinaryenExpressionId BinaryenMemoryCopyId(void);
BinaryenExpressionId BinaryenMemoryFillId(void);
+BinaryenExpressionId BinaryenTryId(void);
+BinaryenExpressionId BinaryenThrowId(void);
+BinaryenExpressionId BinaryenRethrowId(void);
+BinaryenExpressionId BinaryenBrOnExnId(void);
BinaryenExpressionId BinaryenPushId(void);
BinaryenExpressionId BinaryenPopId(void);
@@ -701,6 +705,19 @@ BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module,
BinaryenExpressionRef dest,
BinaryenExpressionRef value,
BinaryenExpressionRef size);
+BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
+ BinaryenExpressionRef body,
+ BinaryenExpressionRef catchBody);
+BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module,
+ const char* event,
+ BinaryenExpressionRef* operands,
+ BinaryenIndex numOperands);
+BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module,
+ BinaryenExpressionRef exnref);
+BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module,
+ const char* name,
+ const char* eventName,
+ BinaryenExpressionRef exnref);
BinaryenExpressionRef BinaryenPush(BinaryenModuleRef module,
BinaryenExpressionRef value);
BinaryenExpressionRef BinaryenPop(BinaryenModuleRef module, BinaryenType type);
@@ -855,6 +872,20 @@ BinaryenExpressionRef BinaryenMemoryFillGetDest(BinaryenExpressionRef expr);
BinaryenExpressionRef BinaryenMemoryFillGetValue(BinaryenExpressionRef expr);
BinaryenExpressionRef BinaryenMemoryFillGetSize(BinaryenExpressionRef expr);
+BinaryenExpressionRef BinaryenTryGetBody(BinaryenExpressionRef expr);
+BinaryenExpressionRef BinaryenTryGetCatchBody(BinaryenExpressionRef expr);
+
+const char* BinaryenThrowGetEvent(BinaryenExpressionRef expr);
+BinaryenExpressionRef BinaryenThrowGetOperand(BinaryenExpressionRef expr,
+ BinaryenIndex index);
+BinaryenIndex BinaryenThrowGetNumOperands(BinaryenExpressionRef expr);
+
+BinaryenExpressionRef BinaryenRethrowGetExnref(BinaryenExpressionRef expr);
+
+const char* BinaryenBrOnExnGetEvent(BinaryenExpressionRef expr);
+const char* BinaryenBrOnExnGetName(BinaryenExpressionRef expr);
+BinaryenExpressionRef BinaryenBrOnExnGetExnref(BinaryenExpressionRef expr);
+
BinaryenExpressionRef BinaryenPushGetValue(BinaryenExpressionRef expr);
// Functions
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index 3fcb18000..15a73cdf2 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -25,6 +25,9 @@ switch (op[0]) {
case 'i':
if (strcmp(op, "br_if") == 0) { return makeBreak(s); }
goto parse_error;
+ case 'o':
+ if (strcmp(op, "br_on_exn") == 0) { return makeBrOnExn(s); }
+ goto parse_error;
case 't':
if (strcmp(op, "br_table") == 0) { return makeBreakTable(s); }
goto parse_error;
@@ -38,12 +41,20 @@ switch (op[0]) {
}
}
case 'c': {
- switch (op[4]) {
- case '\0':
- if (strcmp(op, "call") == 0) { return makeCall(s, /*isReturn=*/false); }
- goto parse_error;
- case '_':
- if (strcmp(op, "call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/false); }
+ switch (op[2]) {
+ case 'l': {
+ switch (op[4]) {
+ case '\0':
+ if (strcmp(op, "call") == 0) { return makeCall(s, /*isReturn=*/false); }
+ goto parse_error;
+ case '_':
+ if (strcmp(op, "call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/false); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
+ case 't':
+ if (strcmp(op, "catch") == 0) { return makeCatch(s); }
goto parse_error;
default: goto parse_error;
}
@@ -2237,18 +2248,26 @@ switch (op[0]) {
if (strcmp(op, "push") == 0) { return makePush(s); }
goto parse_error;
case 'r': {
- switch (op[6]) {
- case '\0':
- if (strcmp(op, "return") == 0) { return makeReturn(s); }
+ switch (op[3]) {
+ case 'h':
+ if (strcmp(op, "rethrow") == 0) { return makeRethrow(s); }
goto parse_error;
- case '_': {
- switch (op[11]) {
+ case 'u': {
+ switch (op[6]) {
case '\0':
- if (strcmp(op, "return_call") == 0) { return makeCall(s, /*isReturn=*/true); }
- goto parse_error;
- case '_':
- if (strcmp(op, "return_call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/true); }
+ if (strcmp(op, "return") == 0) { return makeReturn(s); }
goto parse_error;
+ case '_': {
+ switch (op[11]) {
+ case '\0':
+ if (strcmp(op, "return_call") == 0) { return makeCall(s, /*isReturn=*/true); }
+ goto parse_error;
+ case '_':
+ if (strcmp(op, "return_call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/true); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
default: goto parse_error;
}
}
@@ -2258,9 +2277,25 @@ switch (op[0]) {
case 's':
if (strcmp(op, "select") == 0) { return makeSelect(s); }
goto parse_error;
- case 't':
- if (strcmp(op, "then") == 0) { return makeThenOrElse(s); }
- goto parse_error;
+ case 't': {
+ switch (op[1]) {
+ case 'h': {
+ switch (op[2]) {
+ case 'e':
+ if (strcmp(op, "then") == 0) { return makeThenOrElse(s); }
+ goto parse_error;
+ case 'r':
+ if (strcmp(op, "throw") == 0) { return makeThrow(s); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
+ case 'r':
+ if (strcmp(op, "try") == 0) { return makeTry(s); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
case 'u':
if (strcmp(op, "unreachable") == 0) { return makeUnreachable(); }
goto parse_error;
diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp
index 99c2798b0..f8f96d5c8 100644
--- a/src/ir/ExpressionAnalyzer.cpp
+++ b/src/ir/ExpressionAnalyzer.cpp
@@ -211,6 +211,13 @@ template<typename T> void visitImmediates(Expression* curr, T& visitor) {
visitor.visitInt(curr->op);
visitor.visitNonScopeName(curr->nameOperand);
}
+ void visitTry(Try* curr) {}
+ void visitThrow(Throw* curr) { visitor.visitNonScopeName(curr->event); }
+ void visitRethrow(Rethrow* curr) {}
+ void visitBrOnExn(BrOnExn* curr) {
+ visitor.visitScopeName(curr->name);
+ visitor.visitNonScopeName(curr->event);
+ }
void visitNop(Nop* curr) {}
void visitUnreachable(Unreachable* curr) {}
void visitPush(Push* curr) {}
diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp
index 817dce561..e650f40a7 100644
--- a/src/ir/ExpressionManipulator.cpp
+++ b/src/ir/ExpressionManipulator.cpp
@@ -219,6 +219,24 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) {
builder.makeHost(curr->op, curr->nameOperand, std::move(operands));
return ret;
}
+ Expression* visitTry(Try* curr) {
+ return builder.makeTry(
+ copy(curr->body), copy(curr->catchBody), curr->type);
+ }
+ Expression* visitThrow(Throw* curr) {
+ std::vector<Expression*> operands;
+ for (Index i = 0; i < curr->operands.size(); i++) {
+ operands.push_back(copy(curr->operands[i]));
+ }
+ return builder.makeThrow(curr->event, std::move(operands));
+ }
+ Expression* visitRethrow(Rethrow* curr) {
+ return builder.makeRethrow(copy(curr->exnref));
+ }
+ Expression* visitBrOnExn(BrOnExn* curr) {
+ return builder.makeBrOnExn(
+ curr->name, curr->event, copy(curr->exnref), curr->eventParams);
+ }
Expression* visitNop(Nop* curr) { return builder.makeNop(); }
Expression* visitUnreachable(Unreachable* curr) {
return builder.makeUnreachable();
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
index 9e3b59c70..05abb76e1 100644
--- a/src/ir/ReFinalize.cpp
+++ b/src/ir/ReFinalize.cpp
@@ -62,6 +62,8 @@ void ReFinalize::visitBlock(Block* curr) {
auto type = iter->second;
if (type == none) {
// we need to fix this up. set the values to unreachables
+ // note that we don't need to handle br_on_exn here, because its value
+ // type is never none
for (auto* br : FindAll<Break>(curr).list) {
handleBranchForVisitBlock(br, curr->name, getModule());
}
@@ -155,6 +157,13 @@ void ReFinalize::visitSelect(Select* curr) { curr->finalize(); }
void ReFinalize::visitDrop(Drop* curr) { curr->finalize(); }
void ReFinalize::visitReturn(Return* curr) { curr->finalize(); }
void ReFinalize::visitHost(Host* curr) { curr->finalize(); }
+void ReFinalize::visitTry(Try* curr) { curr->finalize(); }
+void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); }
+void ReFinalize::visitRethrow(Rethrow* curr) { curr->finalize(); }
+void ReFinalize::visitBrOnExn(BrOnExn* curr) {
+ curr->finalize();
+ updateBreakValueType(curr->name, curr->getSingleSentType());
+}
void ReFinalize::visitNop(Nop* curr) { curr->finalize(); }
void ReFinalize::visitUnreachable(Unreachable* curr) { curr->finalize(); }
void ReFinalize::visitPush(Push* curr) { curr->finalize(); }
diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h
index ce7d7b0f6..976dd72ec 100644
--- a/src/ir/branch-utils.h
+++ b/src/ir/branch-utils.h
@@ -37,20 +37,22 @@ inline bool isBranchReachable(Switch* sw) {
sw->condition->type != unreachable;
}
+inline bool isBranchReachable(BrOnExn* br) {
+ return br->exnref->type != unreachable;
+}
+
inline bool isBranchReachable(Expression* expr) {
if (auto* br = expr->dynCast<Break>()) {
return isBranchReachable(br);
} else if (auto* sw = expr->dynCast<Switch>()) {
return isBranchReachable(sw);
+ } else if (auto* br = expr->dynCast<BrOnExn>()) {
+ return isBranchReachable(br);
}
WASM_UNREACHABLE();
}
-inline std::set<Name> getUniqueTargets(Break* br) {
- std::set<Name> ret;
- ret.insert(br->name);
- return ret;
-}
+inline std::set<Name> getUniqueTargets(Break* br) { return {br->name}; }
inline std::set<Name> getUniqueTargets(Switch* sw) {
std::set<Name> ret;
@@ -61,6 +63,8 @@ inline std::set<Name> getUniqueTargets(Switch* sw) {
return ret;
}
+inline std::set<Name> getUniqueTargets(BrOnExn* br) { return {br->name}; }
+
// If we branch to 'from', change that to 'to' instead.
inline bool replacePossibleTarget(Expression* branch, Name from, Name to) {
bool worked = false;
@@ -80,6 +84,11 @@ inline bool replacePossibleTarget(Expression* branch, Name from, Name to) {
sw->default_ = to;
worked = true;
}
+ } else if (auto* br = branch->dynCast<BrOnExn>()) {
+ if (br->name == from) {
+ br->name = to;
+ worked = true;
+ }
} else {
WASM_UNREACHABLE();
}
@@ -99,6 +108,7 @@ inline std::set<Name> getExitingBranches(Expression* ast) {
}
targets.insert(curr->default_);
}
+ void visitBrOnExn(BrOnExn* curr) { targets.insert(curr->name); }
void visitBlock(Block* curr) {
if (curr->name.is()) {
targets.erase(curr->name);
@@ -153,15 +163,15 @@ struct BranchSeeker : public PostWalker<BranchSeeker> {
BranchSeeker(Name target) : target(target) {}
- void noteFound(Expression* value) {
+ void noteFound(Expression* value) { noteFound(value ? value->type : none); }
+
+ void noteFound(Type type) {
found++;
if (found == 1) {
valueType = unreachable;
}
- if (!value) {
- valueType = none;
- } else if (value->type != unreachable) {
- valueType = value->type;
+ if (type != unreachable) {
+ valueType = type;
}
}
@@ -202,6 +212,19 @@ struct BranchSeeker : public PostWalker<BranchSeeker> {
}
}
+ void visitBrOnExn(BrOnExn* curr) {
+ if (!named) {
+ // ignore an unreachable br_on_exn
+ if (curr->exnref->type == unreachable) {
+ return;
+ }
+ }
+ // check the br_on_exn
+ if (curr->name == target) {
+ noteFound(curr->getSingleSentType());
+ }
+ }
+
static bool hasReachable(Expression* tree, Name target) {
if (!target.is()) {
return false;
diff --git a/src/ir/effects.h b/src/ir/effects.h
index dac5b878a..e3997f5d2 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -374,6 +374,11 @@ struct EffectAnalyzer
// Atomics are also sequentially consistent with memory.grow.
isAtomic = true;
}
+ void visitTry(Try* curr) {}
+ // We safely model throws as branches
+ void visitThrow(Throw* curr) { branches = true; }
+ void visitRethrow(Rethrow* curr) { branches = true; }
+ void visitBrOnExn(BrOnExn* curr) { breakNames.insert(curr->name); }
void visitNop(Nop* curr) {}
void visitUnreachable(Unreachable* curr) { branches = true; }
void visitPush(Push* curr) { calls = true; }
diff --git a/src/ir/type-updating.h b/src/ir/type-updating.h
index ef9fe78e9..09b1b4bdc 100644
--- a/src/ir/type-updating.h
+++ b/src/ir/type-updating.h
@@ -65,6 +65,8 @@ struct TypeUpdater
blockInfos[target];
}
blockInfos[sw->default_];
+ } else if (auto* br = curr->dynCast<BrOnExn>()) {
+ blockInfos[br->name];
}
// add a break to the info, for break and switch
discoverBreaks(curr, +1);
@@ -151,6 +153,8 @@ struct TypeUpdater
noteBreakChange(br->name, change, br->value);
} else if (auto* sw = curr->dynCast<Switch>()) {
applySwitchChanges(sw, change);
+ } else if (auto* br = curr->dynCast<BrOnExn>()) {
+ noteBreakChange(br->name, change, br->getSingleSentType());
}
}
@@ -168,6 +172,10 @@ struct TypeUpdater
// note the addition of a node
void noteBreakChange(Name name, int change, Expression* value) {
+ noteBreakChange(name, change, value ? value->type : none);
+ }
+
+ void noteBreakChange(Name name, int change, Type type) {
auto iter = blockInfos.find(name);
if (iter == blockInfos.end()) {
return; // we can ignore breaks to loops
@@ -186,7 +194,7 @@ struct TypeUpdater
if (block->type != unreachable) {
return; // was already reachable, had a fallthrough
}
- changeTypeTo(block, value ? value->type : none);
+ changeTypeTo(block, type);
}
}
}
diff --git a/src/ir/utils.h b/src/ir/utils.h
index ae6368e3b..5c6a09290 100644
--- a/src/ir/utils.h
+++ b/src/ir/utils.h
@@ -144,6 +144,10 @@ struct ReFinalize
void visitDrop(Drop* curr);
void visitReturn(Return* curr);
void visitHost(Host* curr);
+ void visitTry(Try* curr);
+ void visitThrow(Throw* curr);
+ void visitRethrow(Rethrow* curr);
+ void visitBrOnExn(BrOnExn* curr);
void visitNop(Nop* curr);
void visitUnreachable(Unreachable* curr);
void visitPush(Push* curr);
@@ -203,6 +207,10 @@ struct ReFinalizeNode : public OverriddenVisitor<ReFinalizeNode> {
void visitDrop(Drop* curr) { curr->finalize(); }
void visitReturn(Return* curr) { curr->finalize(); }
void visitHost(Host* curr) { curr->finalize(); }
+ void visitTry(Try* curr) { curr->finalize(); }
+ void visitThrow(Throw* curr) { curr->finalize(); }
+ void visitRethrow(Rethrow* curr) { curr->finalize(); }
+ void visitBrOnExn(BrOnExn* curr) { curr->finalize(); }
void visitNop(Nop* curr) { curr->finalize(); }
void visitUnreachable(Unreachable* curr) { curr->finalize(); }
void visitPush(Push* curr) { curr->finalize(); }
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js
index e08d3cf4d..a09ad76ab 100644
--- a/src/js/binaryen.js-post.js
+++ b/src/js/binaryen.js-post.js
@@ -78,6 +78,10 @@ Module['MemoryInitId'] = Module['_BinaryenMemoryInitId']();
Module['DataDropId'] = Module['_BinaryenDataDropId']();
Module['MemoryCopyId'] = Module['_BinaryenMemoryCopyId']();
Module['MemoryFillId'] = Module['_BinaryenMemoryFillId']();
+Module['TryId'] = Module['_BinaryenTryId']();
+Module['ThrowId'] = Module['_BinaryenThrowId']();
+Module['RethrowId'] = Module['_BinaryenRethrowId']();
+Module['BrOnExnId'] = Module['_BinaryenBrOnExnId']();
Module['PushId'] = Module['_BinaryenPushId']();
Module['PopId'] = Module['_BinaryenPopId']();
@@ -1771,6 +1775,22 @@ function wrapModule(module, self) {
self['notify'] = function(ptr, notifyCount) {
return Module['_BinaryenAtomicNotify'](module, ptr, notifyCount);
};
+ self['try'] = function(body, catchBody) {
+ return Module['_BinaryenTry'](module, body, catchBody);
+ };
+ self['throw'] = function(event_, operands) {
+ return preserveStack(function() {
+ return Module['_BinaryenThrow'](module, strToStack(event_), i32sToStack(operands), operands.length);
+ });
+ };
+ self['rethrow'] = function(exnref) {
+ return Module['_BinaryenRethrow'](module, exnref);
+ };
+ self['br_on_exn'] = function(label, event_, exnref) {
+ return preserveStack(function() {
+ return Module['_BinaryenBrOnExn'](module, strToStack(label), strToStack(event_), exnref);
+ });
+ };
self['push'] = function(value) {
return Module['_BinaryenPush'](module, value);
};
@@ -2376,6 +2396,34 @@ Module['getExpressionInfo'] = function(expr) {
'value': Module['_BinaryenMemoryFillGetValue'](expr),
'size': Module['_BinaryenMemoryFillGetSize'](expr)
};
+ case Module['TryId']:
+ return {
+ 'id': id,
+ 'type': type,
+ 'body': Module['_BinaryenTryGetBody'](expr),
+ 'catchBody': Module['_BinaryenTryGetCatchBody'](expr)
+ };
+ case Module['ThrowId']:
+ return {
+ 'id': id,
+ 'type': type,
+ 'event': UTF8ToString(Module['_BinaryenThrowGetEvent'](expr)),
+ 'operands': getAllNested(expr, Module['_BinaryenThrowGetNumOperands'], Module['_BinaryenThrowGetOperand'])
+ };
+ case Module['RethrowId']:
+ return {
+ 'id': id,
+ 'type': type,
+ 'exnref': Module['_BinaryenRethrowGetExnref'](expr)
+ };
+ case Module['BrOnExnId']:
+ return {
+ 'id': id,
+ 'type': type,
+ 'name': UTF8ToString(Module['_BinaryenBrOnExnGetName'](expr)),
+ 'event': UTF8ToString(Module['_BinaryenBrOnExnGetEvent'](expr)),
+ 'exnref': Module['_BinaryenBrOnExnGetExnref'](expr)
+ };
case Module['PushId']:
return {
'id': id,
diff --git a/src/passes/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp
index 61d16afc0..f913c5a93 100644
--- a/src/passes/DeadCodeElimination.cpp
+++ b/src/passes/DeadCodeElimination.cpp
@@ -144,6 +144,14 @@ struct DeadCodeElimination
reachable = false;
}
+ void visitBrOnExn(BrOnExn* curr) {
+ if (isDead(curr->exnref)) {
+ replaceCurrent(curr->exnref);
+ return;
+ }
+ addBreak(curr->name);
+ }
+
void visitReturn(Return* curr) {
if (isDead(curr->value)) {
replaceCurrent(curr->value);
@@ -312,6 +320,14 @@ struct DeadCodeElimination
DELEGATE(Push);
case Expression::Id::PopId:
DELEGATE(Pop);
+ case Expression::Id::TryId:
+ DELEGATE(Try);
+ case Expression::Id::ThrowId:
+ DELEGATE(Throw);
+ case Expression::Id::RethrowId:
+ DELEGATE(Rethrow);
+ case Expression::Id::BrOnExnId:
+ DELEGATE(BrOnExn);
case Expression::Id::InvalidId:
WASM_UNREACHABLE();
case Expression::Id::NumExpressionIds:
diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp
index 55ff9e264..c4d90de04 100644
--- a/src/passes/Precompute.cpp
+++ b/src/passes/Precompute.cpp
@@ -130,6 +130,10 @@ public:
Flow visitMemoryCopy(MemoryCopy* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); }
Flow visitMemoryFill(MemoryFill* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); }
Flow visitHost(Host* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); }
+ Flow visitTry(Try* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); }
+ Flow visitThrow(Throw* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); }
+ Flow visitRethrow(Rethrow* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); }
+ Flow visitBrOnExn(BrOnExn* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); }
Flow visitPush(Push* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); }
Flow visitPop(Pop* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); }
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 812a69692..0bf61e784 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -1162,6 +1162,23 @@ struct PrintExpressionContents
break;
}
}
+ void visitTry(Try* curr) {
+ printMedium(o, "try");
+ if (isConcreteType(curr->type)) {
+ o << " (result " << printType(curr->type) << ')';
+ }
+ }
+ void visitThrow(Throw* curr) {
+ printMedium(o, "throw ");
+ printName(curr->event, o);
+ }
+ void visitRethrow(Rethrow* curr) { printMedium(o, "rethrow"); }
+ void visitBrOnExn(BrOnExn* curr) {
+ printMedium(o, "br_on_exn ");
+ printName(curr->name, o);
+ o << " ";
+ printName(curr->event, o);
+ }
void visitNop(Nop* curr) { printMinor(o, "nop"); }
void visitUnreachable(Unreachable* curr) { printMinor(o, "unreachable"); }
void visitPush(Push* curr) { prepareColor(o) << "push"; }
@@ -1260,6 +1277,20 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
o << maybeNewLine;
}
+ // loop, if, and try can contain implicit blocks. But they are not needed to
+ // be printed in some cases.
+ void maybePrintImplicitBlock(Expression* curr, bool allowMultipleInsts) {
+ auto block = curr->dynCast<Block>();
+ if (!full && block && block->name.isNull() &&
+ (allowMultipleInsts || block->list.size() == 1)) {
+ for (auto expression : block->list) {
+ printFullLine(expression);
+ }
+ } else {
+ printFullLine(curr);
+ }
+ }
+
void visitBlock(Block* curr) {
// special-case Block, because Block nesting (in their first element) can be
// incredibly deep
@@ -1319,22 +1350,9 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
PrintExpressionContents(currFunction, o).visit(curr);
incIndent();
printFullLine(curr->condition);
- // ifTrue and False have implict blocks, avoid printing them if possible
- if (!full && curr->ifTrue->is<Block>() &&
- curr->ifTrue->dynCast<Block>()->name.isNull() &&
- curr->ifTrue->dynCast<Block>()->list.size() == 1) {
- printFullLine(curr->ifTrue->dynCast<Block>()->list.back());
- } else {
- printFullLine(curr->ifTrue);
- }
+ maybePrintImplicitBlock(curr->ifTrue, false);
if (curr->ifFalse) {
- if (!full && curr->ifFalse->is<Block>() &&
- curr->ifFalse->dynCast<Block>()->name.isNull() &&
- curr->ifFalse->dynCast<Block>()->list.size() == 1) {
- printFullLine(curr->ifFalse->dynCast<Block>()->list.back());
- } else {
- printFullLine(curr->ifFalse);
- }
+ maybePrintImplicitBlock(curr->ifFalse, false);
}
decIndent();
if (full) {
@@ -1345,16 +1363,7 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
o << '(';
PrintExpressionContents(currFunction, o).visit(curr);
incIndent();
- auto block = curr->body->dynCast<Block>();
- if (!full && block && block->name.isNull()) {
- // wasm spec has loops containing children directly, while our ast
- // has a single child for simplicity. print out the optimal form.
- for (auto expression : block->list) {
- printFullLine(expression);
- }
- } else {
- printFullLine(curr->body);
- }
+ maybePrintImplicitBlock(curr->body, true);
decIndent();
if (full) {
o << " ;; end loop";
@@ -1631,6 +1640,54 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
}
}
}
+ // try-catch-end is written in the folded wast format as
+ // (try
+ // ...
+ // (catch
+ // ...
+ // )
+ // )
+ // The parenthesis wrapping 'catch' is just a syntax and does not affect
+ // nested depths of instructions within.
+ void visitTry(Try* curr) {
+ o << '(';
+ PrintExpressionContents(currFunction, o).visit(curr);
+ incIndent();
+ maybePrintImplicitBlock(curr->body, true);
+ doIndent(o, indent);
+ o << "(catch";
+ incIndent();
+ maybePrintImplicitBlock(curr->catchBody, true);
+ decIndent();
+ o << "\n";
+ decIndent();
+ if (full) {
+ o << " ;; end try";
+ }
+ }
+ void visitThrow(Throw* curr) {
+ o << '(';
+ PrintExpressionContents(currFunction, o).visit(curr);
+ incIndent();
+ for (auto operand : curr->operands) {
+ printFullLine(operand);
+ }
+ decIndent();
+ }
+ void visitRethrow(Rethrow* curr) {
+ o << '(';
+ PrintExpressionContents(currFunction, o).visit(curr);
+ incIndent();
+ printFullLine(curr->exnref);
+ decIndent();
+ }
+ void visitBrOnExn(BrOnExn* curr) {
+ o << '(';
+ PrintExpressionContents(currFunction, o).visit(curr);
+ incIndent();
+ printFullLine(curr->exnref);
+ decIndent();
+ }
void visitNop(Nop* curr) {
o << '(';
PrintExpressionContents(currFunction, o).visit(curr);
@@ -2198,7 +2255,8 @@ WasmPrinter::printStackIR(StackIR* ir, std::ostream& o, Function* func) {
}
case StackInst::BlockBegin:
case StackInst::IfBegin:
- case StackInst::LoopBegin: {
+ case StackInst::LoopBegin:
+ case StackInst::TryBegin: {
doIndent();
PrintExpressionContents(func, o).visit(inst->origin);
indent++;
@@ -2206,7 +2264,8 @@ WasmPrinter::printStackIR(StackIR* ir, std::ostream& o, Function* func) {
}
case StackInst::BlockEnd:
case StackInst::IfEnd:
- case StackInst::LoopEnd: {
+ case StackInst::LoopEnd:
+ case StackInst::TryEnd: {
indent--;
doIndent();
o << "end";
@@ -2219,6 +2278,13 @@ WasmPrinter::printStackIR(StackIR* ir, std::ostream& o, Function* func) {
indent++;
break;
}
+ case StackInst::Catch: {
+ indent--;
+ doIndent();
+ o << "catch";
+ indent++;
+ break;
+ }
default:
WASM_UNREACHABLE();
}
diff --git a/src/passes/RemoveUnusedNames.cpp b/src/passes/RemoveUnusedNames.cpp
index 3db5a2173..3b04522ad 100644
--- a/src/passes/RemoveUnusedNames.cpp
+++ b/src/passes/RemoveUnusedNames.cpp
@@ -42,6 +42,8 @@ struct RemoveUnusedNames : public WalkerPass<PostWalker<RemoveUnusedNames>> {
branchesSeen[curr->default_].insert(curr);
}
+ void visitBrOnExn(BrOnExn* curr) { branchesSeen[curr->name].insert(curr); }
+
void handleBreakTarget(Name& name) {
if (name.is()) {
if (branchesSeen.find(name) == branchesSeen.end()) {
@@ -73,6 +75,10 @@ struct RemoveUnusedNames : public WalkerPass<PostWalker<RemoveUnusedNames>> {
if (sw->default_ == curr->name) {
sw->default_ = child->name;
}
+ } else if (BrOnExn* br = branch->dynCast<BrOnExn>()) {
+ if (br->name == curr->name) {
+ br->name = child->name;
+ }
} else {
WASM_UNREACHABLE();
}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index f58a10cf7..1efa24b48 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -613,12 +613,14 @@ enum ASTNodes {
I64ExtendS16 = 0xc3,
I64ExtendS32 = 0xc4,
+ // prefixes
+
MiscPrefix = 0xfc,
SIMDPrefix = 0xfd,
- AtomicPrefix = 0xfe
-};
+ AtomicPrefix = 0xfe,
+
+ // atomic opcodes
-enum AtomicOpcodes {
AtomicNotify = 0x00,
I32AtomicWait = 0x01,
I64AtomicWait = 0x02,
@@ -691,10 +693,10 @@ enum AtomicOpcodes {
I64AtomicCmpxchg8U = 0x4c,
I64AtomicCmpxchg16U = 0x4d,
I64AtomicCmpxchg32U = 0x4e,
- AtomicCmpxchgOps_End = 0x4e
-};
+ AtomicCmpxchgOps_End = 0x4e,
+
+ // truncsat opcodes
-enum TruncSatOpcodes {
I32STruncSatF32 = 0x00,
I32UTruncSatF32 = 0x01,
I32STruncSatF64 = 0x02,
@@ -703,9 +705,9 @@ enum TruncSatOpcodes {
I64UTruncSatF32 = 0x05,
I64STruncSatF64 = 0x06,
I64UTruncSatF64 = 0x07,
-};
-enum SIMDOpcodes {
+ // SIMD opcodes
+
V128Load = 0x00,
V128Store = 0x01,
V128Const = 0x02,
@@ -845,14 +847,22 @@ enum SIMDOpcodes {
F32x4ConvertSI32x4 = 0xaf,
F32x4ConvertUI32x4 = 0xb0,
F64x2ConvertSI64x2 = 0xb1,
- F64x2ConvertUI64x2 = 0xb2
-};
+ F64x2ConvertUI64x2 = 0xb2,
+
+ // bulk memory opcodes
-enum BulkMemoryOpcodes {
MemoryInit = 0x08,
DataDrop = 0x09,
MemoryCopy = 0x0a,
- MemoryFill = 0x0b
+ MemoryFill = 0x0b,
+
+ // exception handling opcodes
+
+ Try = 0x06,
+ Catch = 0x07,
+ Throw = 0x08,
+ Rethrow = 0x09,
+ BrOnExn = 0x0a
};
enum MemoryAccess {
@@ -1184,6 +1194,8 @@ public:
void readNextDebugLocation();
void readSourceMapHeader();
+ void handleBrOnExnNotTaken(Expression* curr);
+
// AST reading
int depth = 0; // only for debugging
@@ -1192,7 +1204,7 @@ public:
void visitBlock(Block* curr);
// Gets a block of expressions. If it's just one, return that singleton.
- Expression* getBlockOrSingleton(Type type);
+ Expression* getBlockOrSingleton(Type type, unsigned numPops = 0);
void visitIf(If* curr);
void visitLoop(Loop* curr);
@@ -1238,6 +1250,10 @@ public:
void visitNop(Nop* curr);
void visitUnreachable(Unreachable* curr);
void visitDrop(Drop* curr);
+ void visitTry(Try* curr);
+ void visitThrow(Throw* curr);
+ void visitRethrow(Rethrow* curr);
+ void visitBrOnExn(BrOnExn* curr);
void throwError(std::string text);
};
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 923effb76..a0a43bc07 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -453,7 +453,6 @@ public:
return ret;
}
Const* makeConst(Literal value) {
- assert(isConcreteType(value.type));
auto* ret = allocator.alloc<Const>();
ret->value = value;
ret->type = value.type;
@@ -497,6 +496,53 @@ public:
ret->finalize();
return ret;
}
+ Try* makeTry(Expression* body, Expression* catchBody) {
+ auto* ret = allocator.alloc<Try>();
+ ret->body = body;
+ ret->catchBody = catchBody;
+ ret->finalize();
+ return ret;
+ }
+ Try* makeTry(Expression* body, Expression* catchBody, Type type) {
+ auto* ret = allocator.alloc<Try>();
+ ret->body = body;
+ ret->catchBody = catchBody;
+ ret->finalize(type);
+ return ret;
+ }
+ Throw* makeThrow(Event* event, const std::vector<Expression*>& args) {
+ return makeThrow(event->name, args);
+ }
+ Throw* makeThrow(Name event, const std::vector<Expression*>& args) {
+ auto* ret = allocator.alloc<Throw>();
+ ret->event = event;
+ ret->operands.set(args);
+ ret->finalize();
+ return ret;
+ }
+ Rethrow* makeRethrow(Expression* exnref) {
+ auto* ret = allocator.alloc<Rethrow>();
+ ret->exnref = exnref;
+ ret->finalize();
+ return ret;
+ }
+ BrOnExn* makeBrOnExn(Name name, Event* event, Expression* exnref) {
+ return makeBrOnExn(name, event->name, exnref, event->params);
+ }
+ BrOnExn* makeBrOnExn(Name name,
+ Name event,
+ Expression* exnref,
+ std::vector<Type>& eventParams) {
+ auto* ret = allocator.alloc<BrOnExn>();
+ ret->name = name;
+ ret->event = event;
+ ret->exnref = exnref;
+ // Copy params info into BrOnExn, because it is necessary when BrOnExn is
+ // refinalized without the module.
+ ret->eventParams = eventParams;
+ ret->finalize();
+ return ret;
+ }
Unreachable* makeUnreachable() { return allocator.alloc<Unreachable>(); }
Push* makePush(Expression* value) {
auto* ret = allocator.alloc<Push>();
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index f683f9b38..53345f0ab 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1048,6 +1048,10 @@ public:
Flow visitAtomicNotify(AtomicNotify*) { WASM_UNREACHABLE(); }
Flow visitPush(Push*) { WASM_UNREACHABLE(); }
Flow visitPop(Pop*) { WASM_UNREACHABLE(); }
+ Flow visitTry(Try*) { WASM_UNREACHABLE(); }
+ Flow visitThrow(Throw*) { WASM_UNREACHABLE(); }
+ Flow visitRethrow(Rethrow*) { WASM_UNREACHABLE(); }
+ Flow visitBrOnExn(BrOnExn*) { WASM_UNREACHABLE(); }
virtual void trap(const char* why) { WASM_UNREACHABLE(); }
};
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index c07cc49a9..d6ec89319 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -221,6 +221,11 @@ private:
Expression* makeBreak(Element& s);
Expression* makeBreakTable(Element& s);
Expression* makeReturn(Element& s);
+ Expression* makeTry(Element& s);
+ Expression* makeCatch(Element& s);
+ Expression* makeThrow(Element& s);
+ Expression* makeRethrow(Element& s);
+ Expression* makeBrOnExn(Element& s);
// Helper functions
Type parseOptionalResultType(Element& s, Index& i);
diff --git a/src/wasm-stack.h b/src/wasm-stack.h
index 64718b7b2..562a00684 100644
--- a/src/wasm-stack.h
+++ b/src/wasm-stack.h
@@ -67,6 +67,9 @@ public:
IfEnd, // the ending of a if
LoopBegin, // the beginning of a loop
LoopEnd, // the ending of a loop
+ TryBegin, // the beginning of a try
+ Catch, // the catch within a try
+ TryEnd // the ending of a try
} op;
Expression* origin; // the expression this originates from
@@ -115,6 +118,10 @@ public:
void visitSelect(Select* curr);
void visitReturn(Return* curr);
void visitHost(Host* curr);
+ void visitTry(Try* curr);
+ void visitThrow(Throw* curr);
+ void visitRethrow(Rethrow* curr);
+ void visitBrOnExn(BrOnExn* curr);
void visitNop(Nop* curr);
void visitUnreachable(Unreachable* curr);
void visitDrop(Drop* curr);
@@ -122,7 +129,8 @@ public:
void visitPop(Pop* curr);
void emitIfElse();
- void emitScopeEnd(); // emit an end at the end of a block/loop/if
+ void emitCatch();
+ void emitScopeEnd(); // emit an end at the end of a block/loop/if/try
void emitFunctionEnd(); // emit an end at the end of a function
void emitUnreachable();
void mapLocalsAndEmitHeader();
@@ -185,6 +193,10 @@ public:
void visitSelect(Select* curr);
void visitReturn(Return* curr);
void visitHost(Host* curr);
+ void visitTry(Try* curr);
+ void visitThrow(Throw* curr);
+ void visitRethrow(Rethrow* curr);
+ void visitBrOnExn(BrOnExn* curr);
void visitNop(Nop* curr);
void visitUnreachable(Unreachable* curr);
void visitDrop(Drop* curr);
@@ -198,6 +210,7 @@ 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 emitScopeEnd(Expression* curr) {
static_cast<SubType*>(this)->emitScopeEnd(curr);
}
@@ -305,7 +318,6 @@ template<typename SubType> void BinaryenIRWriter<SubType>::visitIf(If* curr) {
return;
}
emit(curr);
- // TODO: emit block contents directly, if possible
visitPossibleBlockContents(curr->ifTrue);
if (curr->ifFalse) {
@@ -657,6 +669,40 @@ void BinaryenIRWriter<SubType>::visitHost(Host* curr) {
emit(curr);
}
+template<typename SubType> void BinaryenIRWriter<SubType>::visitTry(Try* curr) {
+ emit(curr);
+ visitPossibleBlockContents(curr->body);
+ emitCatch(curr);
+ visitPossibleBlockContents(curr->catchBody);
+ emitScopeEnd(curr);
+ if (curr->type == unreachable) {
+ emitUnreachable();
+ }
+}
+
+template<typename SubType>
+void BinaryenIRWriter<SubType>::visitThrow(Throw* curr) {
+ for (auto* operand : curr->operands) {
+ visit(operand);
+ }
+ emit(curr);
+}
+
+template<typename SubType>
+void BinaryenIRWriter<SubType>::visitRethrow(Rethrow* curr) {
+ visit(curr->exnref);
+ emit(curr);
+}
+
+template<typename SubType>
+void BinaryenIRWriter<SubType>::visitBrOnExn(BrOnExn* curr) {
+ visit(curr->exnref);
+ emit(curr);
+ if (curr->type == unreachable) {
+ emitUnreachable();
+ }
+}
+
template<typename SubType> void BinaryenIRWriter<SubType>::visitNop(Nop* curr) {
emit(curr);
}
@@ -707,6 +753,7 @@ public:
writer.mapLocalsAndEmitHeader();
}
void emitIfElse(If* curr) { writer.emitIfElse(); }
+ void emitCatch(Try* curr) { writer.emitCatch(); }
void emitScopeEnd(Expression* curr) { writer.emitScopeEnd(); }
void emitFunctionEnd() {
if (func->epilogLocation.size()) {
@@ -740,6 +787,9 @@ public:
void emitIfElse(If* curr) {
stackIR.push_back(makeStackInst(StackInst::IfElse, curr));
}
+ void emitCatch(Try* curr) {
+ stackIR.push_back(makeStackInst(StackInst::Catch, curr));
+ }
void emitFunctionEnd() {}
void emitUnreachable() {
stackIR.push_back(makeStackInst(Builder(allocator).makeUnreachable()));
diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h
index 04ab5f04a..5e9925b63 100644
--- a/src/wasm-traversal.h
+++ b/src/wasm-traversal.h
@@ -70,6 +70,10 @@ template<typename SubType, typename ReturnType = void> struct Visitor {
ReturnType visitDrop(Drop* curr) { return ReturnType(); }
ReturnType visitReturn(Return* curr) { return ReturnType(); }
ReturnType visitHost(Host* curr) { return ReturnType(); }
+ ReturnType visitTry(Try* curr) { return ReturnType(); }
+ ReturnType visitThrow(Throw* curr) { return ReturnType(); }
+ ReturnType visitRethrow(Rethrow* curr) { return ReturnType(); }
+ ReturnType visitBrOnExn(BrOnExn* curr) { return ReturnType(); }
ReturnType visitNop(Nop* curr) { return ReturnType(); }
ReturnType visitUnreachable(Unreachable* curr) { return ReturnType(); }
ReturnType visitPush(Push* curr) { return ReturnType(); }
@@ -158,6 +162,14 @@ template<typename SubType, typename ReturnType = void> struct Visitor {
DELEGATE(Return);
case Expression::Id::HostId:
DELEGATE(Host);
+ case Expression::Id::TryId:
+ DELEGATE(Try);
+ case Expression::Id::ThrowId:
+ DELEGATE(Throw);
+ case Expression::Id::RethrowId:
+ DELEGATE(Rethrow);
+ case Expression::Id::BrOnExnId:
+ DELEGATE(BrOnExn);
case Expression::Id::NopId:
DELEGATE(Nop);
case Expression::Id::UnreachableId:
@@ -222,6 +234,10 @@ struct OverriddenVisitor {
UNIMPLEMENTED(Drop);
UNIMPLEMENTED(Return);
UNIMPLEMENTED(Host);
+ UNIMPLEMENTED(Try);
+ UNIMPLEMENTED(Throw);
+ UNIMPLEMENTED(Rethrow);
+ UNIMPLEMENTED(BrOnExn);
UNIMPLEMENTED(Nop);
UNIMPLEMENTED(Unreachable);
UNIMPLEMENTED(Push);
@@ -311,6 +327,14 @@ struct OverriddenVisitor {
DELEGATE(Return);
case Expression::Id::HostId:
DELEGATE(Host);
+ case Expression::Id::TryId:
+ DELEGATE(Try);
+ case Expression::Id::ThrowId:
+ DELEGATE(Throw);
+ case Expression::Id::RethrowId:
+ DELEGATE(Rethrow);
+ case Expression::Id::BrOnExnId:
+ DELEGATE(BrOnExn);
case Expression::Id::NopId:
DELEGATE(Nop);
case Expression::Id::UnreachableId:
@@ -436,6 +460,18 @@ struct UnifiedExpressionVisitor : public Visitor<SubType, ReturnType> {
ReturnType visitHost(Host* curr) {
return static_cast<SubType*>(this)->visitExpression(curr);
}
+ ReturnType visitTry(Try* curr) {
+ return static_cast<SubType*>(this)->visitExpression(curr);
+ }
+ ReturnType visitThrow(Throw* curr) {
+ return static_cast<SubType*>(this)->visitExpression(curr);
+ }
+ ReturnType visitRethrow(Rethrow* curr) {
+ return static_cast<SubType*>(this)->visitExpression(curr);
+ }
+ ReturnType visitBrOnExn(BrOnExn* curr) {
+ return static_cast<SubType*>(this)->visitExpression(curr);
+ }
ReturnType visitNop(Nop* curr) {
return static_cast<SubType*>(this)->visitExpression(curr);
}
@@ -723,6 +759,18 @@ struct Walker : public VisitorType {
static void doVisitHost(SubType* self, Expression** currp) {
self->visitHost((*currp)->cast<Host>());
}
+ static void doVisitTry(SubType* self, Expression** currp) {
+ self->visitTry((*currp)->cast<Try>());
+ }
+ static void doVisitThrow(SubType* self, Expression** currp) {
+ self->visitThrow((*currp)->cast<Throw>());
+ }
+ static void doVisitRethrow(SubType* self, Expression** currp) {
+ self->visitRethrow((*currp)->cast<Rethrow>());
+ }
+ static void doVisitBrOnExn(SubType* self, Expression** currp) {
+ self->visitBrOnExn((*currp)->cast<BrOnExn>());
+ }
static void doVisitNop(SubType* self, Expression** currp) {
self->visitNop((*currp)->cast<Nop>());
}
@@ -755,7 +803,6 @@ template<typename SubType, typename VisitorType = Visitor<SubType>>
struct PostWalker : public Walker<SubType, VisitorType> {
static void scan(SubType* self, Expression** currp) {
-
Expression* curr = *currp;
switch (curr->_id) {
case Expression::Id::InvalidId:
@@ -961,6 +1008,30 @@ struct PostWalker : public Walker<SubType, VisitorType> {
}
break;
}
+ case Expression::Id::TryId: {
+ self->pushTask(SubType::doVisitTry, currp);
+ self->pushTask(SubType::scan, &curr->cast<Try>()->catchBody);
+ self->pushTask(SubType::scan, &curr->cast<Try>()->body);
+ break;
+ }
+ case Expression::Id::ThrowId: {
+ self->pushTask(SubType::doVisitThrow, currp);
+ auto& list = curr->cast<Throw>()->operands;
+ for (int i = int(list.size()) - 1; i >= 0; i--) {
+ self->pushTask(SubType::scan, &list[i]);
+ }
+ break;
+ }
+ case Expression::Id::RethrowId: {
+ self->pushTask(SubType::doVisitRethrow, currp);
+ self->pushTask(SubType::scan, &curr->cast<Rethrow>()->exnref);
+ break;
+ }
+ case Expression::Id::BrOnExnId: {
+ self->pushTask(SubType::doVisitBrOnExn, currp);
+ self->pushTask(SubType::scan, &curr->cast<BrOnExn>()->exnref);
+ break;
+ }
case Expression::Id::NopId: {
self->pushTask(SubType::doVisitNop, currp);
break;
@@ -1196,6 +1267,35 @@ struct LinearExecutionWalker : public PostWalker<SubType, VisitorType> {
self->maybePushTask(SubType::scan, &curr->cast<Return>()->value);
break;
}
+ 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);
+ self->pushTask(SubType::scan, &curr->cast<Try>()->body);
+ break;
+ }
+ case Expression::Id::ThrowId: {
+ self->pushTask(SubType::doVisitThrow, currp);
+ self->pushTask(SubType::doNoteNonLinear, currp);
+ auto& list = curr->cast<Throw>()->operands;
+ for (int i = int(list.size()) - 1; i >= 0; i--) {
+ self->pushTask(SubType::scan, &list[i]);
+ }
+ break;
+ }
+ 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: {
+ self->pushTask(SubType::doVisitBrOnExn, currp);
+ self->pushTask(SubType::doNoteNonLinear, currp);
+ self->pushTask(SubType::scan, &curr->cast<BrOnExn>()->exnref);
+ break;
+ }
case Expression::Id::UnreachableId: {
self->pushTask(SubType::doVisitUnreachable, currp);
self->pushTask(SubType::doNoteNonLinear, currp);
diff --git a/src/wasm.h b/src/wasm.h
index 3ac89b91d..d4441259e 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -479,6 +479,10 @@ public:
MemoryFillId,
PushId,
PopId,
+ TryId,
+ ThrowId,
+ RethrowId,
+ BrOnExnId,
NumExpressionIds
};
Id _id;
@@ -1003,6 +1007,52 @@ public:
Pop(MixedArena& allocator) {}
};
+class Try : public SpecificExpression<Expression::TryId> {
+public:
+ Try(MixedArena& allocator) {}
+
+ Expression* body;
+ Expression* catchBody;
+
+ void finalize();
+ void finalize(Type type_);
+};
+
+class Throw : public SpecificExpression<Expression::ThrowId> {
+public:
+ Throw(MixedArena& allocator) : operands(allocator) {}
+
+ Name event;
+ ExpressionList operands;
+
+ void finalize();
+};
+
+class Rethrow : public SpecificExpression<Expression::RethrowId> {
+public:
+ Rethrow(MixedArena& allocator) {}
+
+ Expression* exnref;
+
+ void finalize();
+};
+
+class BrOnExn : public SpecificExpression<Expression::BrOnExnId> {
+public:
+ BrOnExn() { type = unreachable; }
+ BrOnExn(MixedArena& allocator) : BrOnExn() {}
+
+ Name name;
+ Name event;
+ Expression* exnref;
+ // This is duplicate info of param types stored in Event, but this is required
+ // for us to know the type of the value sent to the target block.
+ std::vector<Type> eventParams;
+
+ void finalize();
+ Type getSingleSentType();
+};
+
// Globals
struct Importable {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 039cef5e5..7047b674e 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -1754,7 +1754,8 @@ void WasmBinaryBuilder::processExpressions() {
throwError("unexpected end of input");
}
auto peek = input[pos];
- if (peek == BinaryConsts::End || peek == BinaryConsts::Else) {
+ if (peek == BinaryConsts::End || peek == BinaryConsts::Else ||
+ peek == BinaryConsts::Catch) {
if (debug) {
std::cerr << "== processExpressions finished with unreachable"
<< std::endl;
@@ -2260,8 +2261,21 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
break;
case BinaryConsts::End:
case BinaryConsts::Else:
+ case BinaryConsts::Catch:
curr = nullptr;
break;
+ case BinaryConsts::Try:
+ visitTry((curr = allocator.alloc<Try>())->cast<Try>());
+ break;
+ case BinaryConsts::Throw:
+ visitThrow((curr = allocator.alloc<Throw>())->cast<Throw>());
+ break;
+ case BinaryConsts::Rethrow:
+ visitRethrow((curr = allocator.alloc<Rethrow>())->cast<Rethrow>());
+ break;
+ case BinaryConsts::BrOnExn:
+ visitBrOnExn((curr = allocator.alloc<BrOnExn>())->cast<BrOnExn>());
+ break;
case BinaryConsts::AtomicPrefix: {
code = static_cast<uint8_t>(getU32LEB());
if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) {
@@ -2420,6 +2434,7 @@ void WasmBinaryBuilder::visitBlock(Block* curr) {
if (debug) {
std::cerr << "zz node: Block" << std::endl;
}
+
// special-case Block and de-recurse nested blocks in their first position, as
// that is a common pattern that can be very highly nested.
std::vector<Block*> stack;
@@ -2467,10 +2482,22 @@ void WasmBinaryBuilder::visitBlock(Block* curr) {
}
}
-Expression* WasmBinaryBuilder::getBlockOrSingleton(Type type) {
+// Gets a block of expressions. If it's just one, return that singleton.
+// numPops is the number of pop instructions we add before starting to parse the
+// block. Can be used when we need to assume certain number of values are on top
+// of the stack in the beginning.
+Expression* WasmBinaryBuilder::getBlockOrSingleton(Type type,
+ unsigned numPops) {
Name label = getNextLabel();
breakStack.push_back({label, type != none && type != unreachable});
auto start = expressionStack.size();
+
+ Builder builder(wasm);
+ for (unsigned i = 0; i < numPops; i++) {
+ auto* pop = builder.makePop(exnref);
+ expressionStack.push_back(pop);
+ }
+
processExpressions();
size_t end = expressionStack.size();
if (end < start) {
@@ -4347,6 +4374,73 @@ void WasmBinaryBuilder::visitDrop(Drop* curr) {
curr->finalize();
}
+void WasmBinaryBuilder::visitTry(Try* curr) {
+ if (debug) {
+ std::cerr << "zz node: Try" << std::endl;
+ }
+ // For simplicity of implementation, like if scopes, we create a hidden block
+ // within each try-body and catch-body, and let branches target those inner
+ // blocks instead.
+ curr->type = getType();
+ curr->body = getBlockOrSingleton(curr->type);
+ if (lastSeparator != BinaryConsts::Catch) {
+ throwError("No catch instruction within a try scope");
+ }
+ curr->catchBody = getBlockOrSingleton(curr->type, 1);
+ curr->finalize(curr->type);
+ if (lastSeparator != BinaryConsts::End) {
+ throwError("try should end with end");
+ }
+}
+
+void WasmBinaryBuilder::visitThrow(Throw* curr) {
+ if (debug) {
+ std::cerr << "zz node: Throw" << std::endl;
+ }
+ auto index = getU32LEB();
+ if (index >= wasm.events.size()) {
+ throwError("bad event index");
+ }
+ auto* event = wasm.events[index].get();
+ curr->event = event->name;
+ size_t num = event->params.size();
+ curr->operands.resize(num);
+ for (size_t i = 0; i < num; i++) {
+ curr->operands[num - i - 1] = popNonVoidExpression();
+ }
+ curr->finalize();
+}
+
+void WasmBinaryBuilder::visitRethrow(Rethrow* curr) {
+ if (debug) {
+ std::cerr << "zz node: Rethrow" << std::endl;
+ }
+ curr->exnref = popNonVoidExpression();
+ curr->finalize();
+}
+
+void WasmBinaryBuilder::visitBrOnExn(BrOnExn* curr) {
+ if (debug) {
+ std::cerr << "zz node: BrOnExn" << std::endl;
+ }
+ BreakTarget target = getBreakTarget(getU32LEB());
+ curr->name = target.name;
+ auto index = getU32LEB();
+ if (index >= wasm.events.size()) {
+ throwError("bad event index");
+ }
+ curr->event = wasm.events[index]->name;
+ curr->exnref = popNonVoidExpression();
+
+ Event* event = wasm.getEventOrNull(curr->event);
+ assert(event && "br_on_exn's event must exist");
+
+ // Copy params info into BrOnExn, because it is necessary when BrOnExn is
+ // refinalized without the module.
+ curr->eventParams = event->params;
+ curr->finalize();
+}
+
void WasmBinaryBuilder::throwError(std::string text) {
throw ParseException(text, 0, pos);
}
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 485b3251f..e1413d5fd 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -1726,6 +1726,102 @@ Expression* SExpressionWasmBuilder::makeReturn(Element& s) {
return ret;
}
+// try-catch-end is written in the folded wast format as
+// (try
+// ...
+// (catch
+// ...
+// )
+// )
+// The parenthesis wrapping 'catch' is just a syntax and does not affect nested
+// depths of instructions within.
+Expression* SExpressionWasmBuilder::makeTry(Element& s) {
+ auto ret = allocator.alloc<Try>();
+ Index i = 1;
+ Name sName;
+ if (s[i]->dollared()) {
+ // the try is labeled
+ sName = s[i++]->str();
+ } else {
+ sName = "try";
+ }
+ auto label = nameMapper.pushLabelName(sName);
+ Type type = parseOptionalResultType(s, i); // signature
+ if (elementStartsWith(*s[i], "catch")) { // empty try body
+ ret->body = makeNop();
+ } else {
+ ret->body = parseExpression(*s[i++]);
+ }
+ if (!elementStartsWith(*s[i], "catch")) {
+ throw ParseException("catch clause does not exist");
+ }
+ ret->catchBody = makeCatch(*s[i++]);
+ ret->finalize(type);
+ nameMapper.popLabelName(label);
+ // create a break target if we must
+ if (BranchUtils::BranchSeeker::hasNamed(ret, label)) {
+ auto* block = allocator.alloc<Block>();
+ block->name = label;
+ block->list.push_back(ret);
+ block->finalize(ret->type);
+ return block;
+ }
+ return ret;
+}
+
+Expression* SExpressionWasmBuilder::makeCatch(Element& s) {
+ if (!elementStartsWith(s, "catch")) {
+ throw ParseException("invalid catch clause", s.line, s.col);
+ }
+ auto ret = allocator.alloc<Block>();
+ for (size_t i = 1; i < s.size(); i++) {
+ ret->list.push_back(parseExpression(s[i]));
+ }
+ ret->finalize();
+ return ret;
+}
+
+Expression* SExpressionWasmBuilder::makeThrow(Element& s) {
+ auto ret = allocator.alloc<Throw>();
+ Index i = 1;
+
+ ret->event = getEventName(*s[i++]);
+ if (!wasm.getEventOrNull(ret->event)) {
+ throw ParseException("bad event name", s[1]->line, s[1]->col);
+ }
+ for (; i < s.size(); i++) {
+ ret->operands.push_back(parseExpression(s[i]));
+ }
+ ret->finalize();
+ return ret;
+}
+
+Expression* SExpressionWasmBuilder::makeRethrow(Element& s) {
+ auto ret = allocator.alloc<Rethrow>();
+ ret->exnref = parseExpression(*s[1]);
+ ret->finalize();
+ return ret;
+}
+
+Expression* SExpressionWasmBuilder::makeBrOnExn(Element& s) {
+ auto ret = allocator.alloc<BrOnExn>();
+ size_t i = 1;
+ ret->name = getLabel(*s[i++]);
+ ret->event = getEventName(*s[i++]);
+ if (!wasm.getEventOrNull(ret->event)) {
+ throw ParseException("bad event name", s[1]->line, s[1]->col);
+ }
+ ret->exnref = parseExpression(s[i]);
+
+ Event* event = wasm.getEventOrNull(ret->event);
+ assert(event && "br_on_exn's event must exist");
+ // Copy params info into BrOnExn, because it is necessary when BrOnExn is
+ // refinalized without the module.
+ ret->eventParams = event->params;
+ ret->finalize();
+ return ret;
+}
+
// converts an s-expression string representing binary data into an output
// sequence of raw bytes this appends to data, which may already contain
// content.
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index b8ec4fa35..d3aba3b8c 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -1396,6 +1396,32 @@ void BinaryInstWriter::visitHost(Host* curr) {
o << U32LEB(0); // Reserved flags field
}
+void BinaryInstWriter::visitTry(Try* curr) {
+ breakStack.emplace_back(IMPOSSIBLE_CONTINUE);
+ o << int8_t(BinaryConsts::Try);
+ o << binaryType(curr->type != unreachable ? curr->type : none);
+}
+
+void BinaryInstWriter::emitCatch() {
+ assert(!breakStack.empty());
+ breakStack.pop_back();
+ breakStack.emplace_back(IMPOSSIBLE_CONTINUE);
+ o << int8_t(BinaryConsts::Catch);
+}
+
+void BinaryInstWriter::visitThrow(Throw* curr) {
+ o << int8_t(BinaryConsts::Throw) << U32LEB(parent.getEventIndex(curr->event));
+}
+
+void BinaryInstWriter::visitRethrow(Rethrow* curr) {
+ o << int8_t(BinaryConsts::Rethrow);
+}
+
+void BinaryInstWriter::visitBrOnExn(BrOnExn* curr) {
+ o << int8_t(BinaryConsts::BrOnExn) << U32LEB(getBreakIndex(curr->name))
+ << U32LEB(parent.getEventIndex(curr->event));
+}
+
void BinaryInstWriter::visitNop(Nop* curr) { o << int8_t(BinaryConsts::Nop); }
void BinaryInstWriter::visitUnreachable(Unreachable* curr) {
@@ -1466,12 +1492,18 @@ void BinaryInstWriter::mapLocalsAndEmitHeader() {
mappedLocals[i] = index + currLocalsByType[v128] - 1;
continue;
}
+ index += numLocalsByType[v128];
+ if (type == exnref) {
+ mappedLocals[i] = index + currLocalsByType[exnref] - 1;
+ continue;
+ }
WASM_UNREACHABLE();
}
// Emit them.
o << U32LEB((numLocalsByType[i32] ? 1 : 0) + (numLocalsByType[i64] ? 1 : 0) +
(numLocalsByType[f32] ? 1 : 0) + (numLocalsByType[f64] ? 1 : 0) +
- (numLocalsByType[v128] ? 1 : 0));
+ (numLocalsByType[v128] ? 1 : 0) +
+ (numLocalsByType[exnref] ? 1 : 0));
if (numLocalsByType[i32]) {
o << U32LEB(numLocalsByType[i32]) << binaryType(i32);
}
@@ -1487,6 +1519,9 @@ void BinaryInstWriter::mapLocalsAndEmitHeader() {
if (numLocalsByType[v128]) {
o << U32LEB(numLocalsByType[v128]) << binaryType(v128);
}
+ if (numLocalsByType[exnref]) {
+ o << U32LEB(numLocalsByType[exnref]) << binaryType(exnref);
+ }
}
void BinaryInstWriter::emitMemoryAccess(size_t alignment,
@@ -1513,6 +1548,8 @@ void StackIRGenerator::emit(Expression* curr) {
stackInst = makeStackInst(StackInst::IfBegin, curr);
} else if (curr->is<Loop>()) {
stackInst = makeStackInst(StackInst::LoopBegin, curr);
+ } else if (curr->is<Try>()) {
+ stackInst = makeStackInst(StackInst::TryBegin, curr);
} else {
stackInst = makeStackInst(curr);
}
@@ -1527,6 +1564,8 @@ void StackIRGenerator::emitScopeEnd(Expression* curr) {
stackInst = makeStackInst(StackInst::IfEnd, curr);
} else if (curr->is<Loop>()) {
stackInst = makeStackInst(StackInst::LoopEnd, curr);
+ } else if (curr->is<Try>()) {
+ stackInst = makeStackInst(StackInst::TryEnd, curr);
} else {
WASM_UNREACHABLE();
}
@@ -1581,6 +1620,10 @@ void StackIRToBinaryWriter::write() {
writer.emitIfElse();
break;
}
+ case StackInst::Catch: {
+ writer.emitCatch();
+ break;
+ }
default:
WASM_UNREACHABLE();
}
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 73731d3e5..8eb3341de 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -254,6 +254,7 @@ public:
}
void noteBreak(Name name, Expression* value, Expression* curr);
+ void noteBreak(Name name, Type valueType, Expression* curr);
void visitBreak(Break* curr);
void visitSwitch(Switch* curr);
void visitCall(Call* curr);
@@ -284,6 +285,10 @@ public:
void visitDrop(Drop* curr);
void visitReturn(Return* curr);
void visitHost(Host* curr);
+ void visitTry(Try* curr);
+ void visitThrow(Throw* curr);
+ void visitRethrow(Rethrow* curr);
+ void visitBrOnExn(BrOnExn* curr);
void visitFunction(Function* curr);
// helpers
@@ -519,11 +524,15 @@ void FunctionValidator::visitIf(If* curr) {
void FunctionValidator::noteBreak(Name name,
Expression* value,
Expression* curr) {
- Type valueType = none;
- Index arity = 0;
if (value) {
- valueType = value->type;
- shouldBeUnequal(valueType, none, curr, "breaks must have a valid value");
+ shouldBeUnequal(value->type, none, curr, "breaks must have a valid value");
+ }
+ noteBreak(name, value ? value->type : none, curr);
+}
+
+void FunctionValidator::noteBreak(Name name, Type valueType, Expression* curr) {
+ Index arity = 0;
+ if (valueType != none) {
arity = 1;
}
auto iter = breakInfos.find(name);
@@ -1581,6 +1590,94 @@ void FunctionValidator::visitHost(Host* curr) {
}
}
+void FunctionValidator::visitTry(Try* curr) {
+ if (curr->type != unreachable) {
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->body->type,
+ curr->type,
+ curr->body,
+ "try's type does not match try body's type");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->catchBody->type,
+ curr->type,
+ curr->catchBody,
+ "try's type does not match catch's body type");
+ }
+ if (isConcreteType(curr->body->type)) {
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->catchBody->type,
+ curr->body->type,
+ curr->catchBody,
+ "try's body type must match catch's body type");
+ }
+ if (isConcreteType(curr->catchBody->type)) {
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->body->type,
+ curr->catchBody->type,
+ curr->body,
+ "try's body type must match catch's body type");
+ }
+}
+
+void FunctionValidator::visitThrow(Throw* curr) {
+ if (!info.validateGlobally) {
+ return;
+ }
+ shouldBeEqual(
+ curr->type, unreachable, curr, "throw's type must be unreachable");
+ auto* event = getModule()->getEventOrNull(curr->event);
+ if (!shouldBeTrue(!!event, curr, "throw's event must exist")) {
+ return;
+ }
+ if (!shouldBeTrue(curr->operands.size() == event->params.size(),
+ curr,
+ "event's param numbers must match")) {
+ return;
+ }
+ for (size_t i = 0; i < curr->operands.size(); i++) {
+ if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type,
+ event->params[i],
+ curr->operands[i],
+ "event param types must match") &&
+ !info.quiet) {
+ getStream() << "(on argument " << i << ")\n";
+ }
+ }
+}
+
+void FunctionValidator::visitRethrow(Rethrow* curr) {
+ shouldBeEqual(
+ curr->type, unreachable, curr, "rethrow's type must be unreachable");
+ shouldBeEqual(curr->exnref->type,
+ exnref,
+ curr->exnref,
+ "rethrow's argument must be exnref type");
+}
+
+void FunctionValidator::visitBrOnExn(BrOnExn* curr) {
+ Event* event = getModule()->getEventOrNull(curr->event);
+ shouldBeTrue(event != nullptr, curr, "br_on_exn's event must exist");
+ shouldBeTrue(event->params == curr->eventParams,
+ curr,
+ "br_on_exn's event params and event's params are different");
+ noteBreak(curr->name, curr->getSingleSentType(), curr);
+ shouldBeTrue(curr->exnref->type == unreachable ||
+ curr->exnref->type == exnref,
+ curr,
+ "br_on_exn's argument must be unreachable or exnref type");
+ if (curr->exnref->type == unreachable) {
+ shouldBeTrue(curr->type == unreachable,
+ curr,
+ "If exnref argument's type is unreachable, br_on_exn should "
+ "be unreachable too");
+ } else {
+ shouldBeTrue(curr->type == exnref,
+ curr,
+ "br_on_exn's type should be exnref unless its exnref argument "
+ "is unreachable");
+ }
+}
+
void FunctionValidator::visitFunction(Function* curr) {
FeatureSet typeFeatures = getFeatures(curr->result);
for (auto type : curr->params) {
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 04abbcd9f..97b60bfb5 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -168,6 +168,14 @@ const char* getExpressionName(Expression* curr) {
return "push";
case Expression::Id::PopId:
return "pop";
+ case Expression::TryId:
+ return "try";
+ case Expression::ThrowId:
+ return "throw";
+ case Expression::RethrowId:
+ return "rethrow";
+ case Expression::BrOnExnId:
+ return "br_on_exn";
case Expression::Id::NumExpressionIds:
WASM_UNREACHABLE();
}
@@ -204,6 +212,12 @@ struct TypeSeeker : public PostWalker<TypeSeeker> {
}
}
+ void visitBrOnExn(BrOnExn* curr) {
+ if (curr->name == targetName) {
+ types.push_back(curr->getSingleSentType());
+ }
+ }
+
void visitBlock(Block* curr) {
if (curr == target) {
if (curr->list.size() > 0) {
@@ -836,6 +850,48 @@ void Host::finalize() {
}
}
+void Try::finalize() {
+ if (body->type == catchBody->type) {
+ type = body->type;
+ } else if (isConcreteType(body->type) && catchBody->type == unreachable) {
+ type = body->type;
+ } else if (isConcreteType(catchBody->type) && body->type == unreachable) {
+ type = catchBody->type;
+ } else {
+ type = none;
+ }
+}
+
+void Try::finalize(Type type_) {
+ type = type_;
+ if (type == none && body->type == unreachable &&
+ catchBody->type == unreachable) {
+ type = unreachable;
+ }
+}
+
+void Throw::finalize() { type = unreachable; }
+
+void Rethrow::finalize() { type = unreachable; }
+
+void BrOnExn::finalize() {
+ if (exnref->type == unreachable) {
+ type = unreachable;
+ } else {
+ type = Type::exnref;
+ }
+}
+
+// br_on_exn's type is exnref, which it pushes onto the stack when it is not
+// taken, but the type of the value it pushes onto the stack when it is taken
+// should be the event type. So this is the type we 'send' to the block end when
+// it is taken. Currently we don't support multi value return from a block, we
+// pick the type of the first param from the event.
+// TODO Remove this function and generalize event type after multi-value support
+Type BrOnExn::getSingleSentType() {
+ return eventParams.empty() ? none : eventParams.front();
+}
+
void Push::finalize() {
if (value->type == unreachable) {
type = unreachable;
diff --git a/src/wasm2js.h b/src/wasm2js.h
index e032ac26d..25c51f490 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -1872,6 +1872,22 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
unimplemented(curr);
WASM_UNREACHABLE();
}
+ Ref visitTry(Try* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE();
+ }
+ Ref visitThrow(Throw* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE();
+ }
+ Ref visitRethrow(Rethrow* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE();
+ }
+ Ref visitBrOnExn(BrOnExn* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE();
+ }
Ref visitPush(Push* curr) {
unimplemented(curr);
WASM_UNREACHABLE();