From e2f49d8227f2b71e4dede5cf4074bb9f65e3d77f Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Tue, 13 Aug 2019 00:29:26 +0900 Subject: 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. --- src/ir/branch-utils.h | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'src/ir/branch-utils.h') 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()) { return isBranchReachable(br); } else if (auto* sw = expr->dynCast()) { return isBranchReachable(sw); + } else if (auto* br = expr->dynCast()) { + return isBranchReachable(br); } WASM_UNREACHABLE(); } -inline std::set getUniqueTargets(Break* br) { - std::set ret; - ret.insert(br->name); - return ret; -} +inline std::set getUniqueTargets(Break* br) { return {br->name}; } inline std::set getUniqueTargets(Switch* sw) { std::set ret; @@ -61,6 +63,8 @@ inline std::set getUniqueTargets(Switch* sw) { return ret; } +inline std::set 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()) { + if (br->name == from) { + br->name = to; + worked = true; + } } else { WASM_UNREACHABLE(); } @@ -99,6 +108,7 @@ inline std::set 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(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 { } } + 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; -- cgit v1.2.3