diff options
author | Heejin Ahn <aheejin@gmail.com> | 2019-08-13 00:29:26 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-13 00:29:26 +0900 |
commit | e2f49d8227f2b71e4dede5cf4074bb9f65e3d77f (patch) | |
tree | 30b132b02824839d1d7718ed32c6b90cc0828151 /src/wasm/wasm-validator.cpp | |
parent | 69ad1e8a8d2e1d395e30230433742f4f5668563b (diff) | |
download | binaryen-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/wasm/wasm-validator.cpp')
-rw-r--r-- | src/wasm/wasm-validator.cpp | 105 |
1 files changed, 101 insertions, 4 deletions
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) { |