diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 95 |
1 files changed, 79 insertions, 16 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 7fe169bdb..0c316e7e7 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -18,16 +18,17 @@ // Removes branches for which we go to where they go anyhow // -#include <ir/branch-utils.h> -#include <ir/cost.h> -#include <ir/effects.h> -#include <ir/gc-type-utils.h> -#include <ir/literal-utils.h> -#include <ir/utils.h> -#include <parsing.h> -#include <pass.h> -#include <wasm-builder.h> -#include <wasm.h> +#include "ir/branch-utils.h" +#include "ir/cost.h" +#include "ir/drop.h" +#include "ir/effects.h" +#include "ir/gc-type-utils.h" +#include "ir/literal-utils.h" +#include "ir/utils.h" +#include "parsing.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { @@ -260,7 +261,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } else if (curr->is<Nop>()) { // ignore (could be result of a previous cycle) self->stopValueFlow(); - } else if (curr->is<Loop>()) { + } else if (curr->is<Loop>()) { // TODO: eh // do nothing - it's ok for values to flow out } else if (auto* sw = curr->dynCast<Switch>()) { self->stopFlow(); @@ -462,13 +463,69 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // later down, see visitLocalSet. } + // A stack of try_tables that are parents of the current expression. + std::vector<TryTable*> tryTables; + + static void popTryTable(RemoveUnusedBrs* self, Expression** currp) { + assert(!self->tryTables.empty() && self->tryTables.back() == *currp); + self->tryTables.pop_back(); + } + + void visitThrow(Throw* curr) { + // If a throw will definitely be caught, and it is not a catch with a + // reference, then it is just a branch (i.e. the code is using exceptions as + // control flow). Turn it into a branch here so that the rest of the pass + // can optimize it with all other branches. + // + // To do so, look at the closest try and see if it will catch us, and + // proceed outwards if not. + auto thrownTag = curr->tag; + for (int i = tryTables.size() - 1; i >= 0; i--) { + auto* tryy = tryTables[i]; + for (Index j = 0; j < tryy->catchTags.size(); j++) { + auto tag = tryy->catchTags[j]; + // The tag must match, or be a catch_all. + if (tag == thrownTag || tag.isNull()) { + // This must not be a catch with exnref. + if (!tryy->catchRefs[j]) { + // Success! Create a break to replace the throw. + auto dest = tryy->catchDests[j]; + auto& wasm = *getModule(); + Builder builder(wasm); + if (!tag.isNull()) { + // We are catching a specific tag, so values might be sent. + Expression* value = nullptr; + if (curr->operands.size() == 1) { + value = curr->operands[0]; + } else if (curr->operands.size() > 1) { + value = builder.makeTupleMake(curr->operands); + } + auto* br = builder.makeBreak(dest, value); + replaceCurrent(br); + return; + } + + // catch_all: no values are sent. Drop the throw's children (while + // ignoring parent effects: the parent is a throw, but we have + // proven we can remove that effect). + auto* br = builder.makeBreak(dest); + auto* rep = getDroppedChildrenAndAppend( + curr, wasm, getPassOptions(), br, DropMode::IgnoreParentEffects); + replaceCurrent(rep); + } + + // Return even if we did not optimize: we found our tag was caught. + return; + } + } + } + } + // override scan to add a pre and a post check task to all nodes static void scan(RemoveUnusedBrs* self, Expression** currp) { self->pushTask(visitAny, currp); - auto* iff = (*currp)->dynCast<If>(); - - if (iff) { + if (auto* iff = (*currp)->dynCast<If>()) { if (iff->condition->type == Type::unreachable) { // avoid trying to optimize this, we never reach it anyhow return; @@ -484,9 +541,15 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { self->pushTask(scan, &iff->ifTrue); self->pushTask(clear, currp); // clear all flow after the condition self->pushTask(scan, &iff->condition); - } else { - Super::scan(self, currp); + return; + } + if (auto* tryy = (*currp)->dynCast<TryTable>()) { + // Push the try we are reaching, and add a task to pop it, after all the + // tasks that Super::scan will push for its children. + self->tryTables.push_back(tryy); + self->pushTask(popTryTable, currp); } + Super::scan(self, currp); } // optimizes a loop. returns true if we made changes |