summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-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
5 files changed, 136 insertions, 53 deletions
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;
}
}