summaryrefslogtreecommitdiff
path: root/src/passes/Print.cpp
diff options
context:
space:
mode:
authorHeejin Ahn <aheejin@gmail.com>2019-08-13 00:29:26 +0900
committerGitHub <noreply@github.com>2019-08-13 00:29:26 +0900
commite2f49d8227f2b71e4dede5cf4074bb9f65e3d77f (patch)
tree30b132b02824839d1d7718ed32c6b90cc0828151 /src/passes/Print.cpp
parent69ad1e8a8d2e1d395e30230433742f4f5668563b (diff)
downloadbinaryen-e2f49d8227f2b71e4dede5cf4074bb9f65e3d77f.tar.gz
binaryen-e2f49d8227f2b71e4dede5cf4074bb9f65e3d77f.tar.bz2
binaryen-e2f49d8227f2b71e4dede5cf4074bb9f65e3d77f.zip
Add basic exception handling support (#2282)
This adds basic support for exception handling instructions, according to the spec: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md This PR includes support for: - Binary reading/writing - Wast reading/writing - Stack IR - Validation - binaryen.js + C API - Few IR routines: branch-utils, type-updating, etc - Few passes: just enough to make `wasm-opt -O` pass - Tests This PR does not include support for many optimization passes, fuzzer, or interpreter. They will be follow-up PRs. Try-catch construct is modeled in Binaryen IR in a similar manner to that of if-else: each of try body and catch body will contain a block, which can be omitted if there is only a single instruction. This block will not be emitted in wast or binary, as in if-else. As in if-else, `class Try` contains two expressions each for try body and catch body, and `catch` is not modeled as an instruction. `exnref` value pushed by `catch` is get by `pop` instruction. `br_on_exn` is special: it returns different types of values when taken and not taken. We make `exnref`, the type `br_on_exn` pushes if not taken, as `br_on_exn`'s type.
Diffstat (limited to 'src/passes/Print.cpp')
-rw-r--r--src/passes/Print.cpp120
1 files changed, 93 insertions, 27 deletions
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();
}