Skip to content

Commit

Permalink
[EH] Support CFGWalker for new EH spec (#6235)
Browse files Browse the repository at this point in the history
This adds support `CFGWalker` for the new EH instructions (`try_table`
and `throw_ref`). `CFGWalker` is used by many different passes, but in
the same vein as #3494, this adds tests for `RedundantSetElimination`
pass. `rse-eh.wast` file is created from translated and simplified
version of `rse-eh-old.wast`, but many tests were removed because we
don't have special `catch` block or `delegate` anymore.
  • Loading branch information
aheejin authored Jan 26, 2024
1 parent 5fb2137 commit d23a63f
Show file tree
Hide file tree
Showing 3 changed files with 413 additions and 29 deletions.
1 change: 1 addition & 0 deletions scripts/fuzz_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ def is_git_repo():
# New EH implementation is in progress
'exception-handling.wast',
'translate-eh-old-to-new.wast',
'rse-eh.wast',
]


Expand Down
100 changes: 71 additions & 29 deletions src/cfg/cfg-traversal.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
// that can reach catch blocks (each item is assumed to be able to reach any
// of the catches, although that could be improved perhaps).
std::vector<std::vector<BasicBlock*>> throwingInstsStack;
// stack of 'Try' expressions corresponding to throwingInstsStack.
// stack of 'Try'/'TryTable' expressions corresponding to throwingInstsStack.
std::vector<Expression*> tryStack;
// A stack for each try, where each entry is a list of blocks, one for each
// catch, used during processing. We start by assigning the start blocks to
Expand Down Expand Up @@ -254,11 +254,11 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
}

static void doEndThrowingInst(SubType* self, Expression** currp) {
// If the innermost try does not have a catch_all clause, an exception
// thrown can be caught by any of its outer catch block. And if that outer
// try-catch also does not have a catch_all, this continues until we
// encounter a try-catch_all. Create a link to all those possible catch
// unwind destinations.
// If the innermost try/try_table does not have a catch_all clause, an
// exception thrown can be caught by any of its outer catch block. And if
// that outer try/try_table also does not have a catch_all, this continues
// until we encounter a try/try_table-catch_all. Create a link to all those
// possible catch unwind destinations.
// TODO This can be more precise for `throw`s if we compare tag types and
// create links to outer catch BBs only when the exception is not caught.
// TODO This can also be more precise if we analyze the structure of nested
Expand All @@ -281,36 +281,47 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
// end
assert(self->tryStack.size() == self->throwingInstsStack.size());
for (int i = self->throwingInstsStack.size() - 1; i >= 0;) {
auto* tryy = self->tryStack[i]->template cast<Try>();
if (tryy->isDelegate()) {
// If this delegates to the caller, there is no possibility that this
// instruction can throw to outer catches.
if (tryy->delegateTarget == DELEGATE_CALLER_TARGET) {
break;
}
// If this delegates to an outer try, we skip catches between this try
// and the target try.
[[maybe_unused]] bool found = false;
for (int j = i - 1; j >= 0; j--) {
if (self->tryStack[j]->template cast<Try>()->name ==
tryy->delegateTarget) {
i = j;
found = true;
if (auto* tryy = self->tryStack[i]->template dynCast<Try>()) {
if (tryy->isDelegate()) {
// If this delegates to the caller, there is no possibility that this
// instruction can throw to outer catches.
if (tryy->delegateTarget == DELEGATE_CALLER_TARGET) {
break;
}
// If this delegates to an outer try, we skip catches between this try
// and the target try.
[[maybe_unused]] bool found = false;
for (int j = i - 1; j >= 0; j--) {
if (self->tryStack[j]->template cast<Try>()->name ==
tryy->delegateTarget) {
i = j;
found = true;
break;
}
}
assert(found);
continue;
}
assert(found);
continue;
}

// Exception thrown. Note outselves so that we will create a link to each
// catch within the try when we get there.
// catch within the try / each destination block within the try_table when
// we get there.
self->throwingInstsStack[i].push_back(self->currBasicBlock);

// If this try has catch_all, there is no possibility that this
// instruction can throw to outer catches. Stop here.
if (tryy->hasCatchAll()) {
break;
if (auto* tryy = self->tryStack[i]->template dynCast<Try>()) {
// If this try has catch_all, there is no possibility that this
// instruction can throw to outer catches. Stop here.
if (tryy->hasCatchAll()) {
break;
}
} else if (auto* tryTable =
self->tryStack[i]->template dynCast<TryTable>()) {
if (tryTable->hasCatchAll()) {
break;
}
} else {
WASM_UNREACHABLE("invalid throwingInstsStack item");
}
i--;
}
Expand Down Expand Up @@ -411,6 +422,28 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
self->startUnreachableBlock();
}

static void doStartTryTable(SubType* self, Expression** currp) {
auto* curr = (*currp)->cast<TryTable>();
self->throwingInstsStack.emplace_back();
self->tryStack.push_back(curr);
}

static void doEndTryTable(SubType* self, Expression** currp) {
auto* curr = (*currp)->cast<TryTable>();

auto catchTargets = BranchUtils::getUniqueTargets(curr);
// Add catch destinations to the targets.
for (auto target : catchTargets) {
auto& preds = self->throwingInstsStack.back();
for (auto* pred : preds) {
self->branches[target].push_back(pred);
}
}

self->throwingInstsStack.pop_back();
self->tryStack.pop_back();
}

static bool isReturnCall(Expression* curr) {
switch (curr->_id) {
case Expression::Id::CallId:
Expand Down Expand Up @@ -478,8 +511,13 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
self->pushTask(SubType::doStartTry, currp);
return; // don't do anything else
}
case Expression::Id::TryTableId: {
self->pushTask(SubType::doEndTryTable, currp);
break;
}
case Expression::Id::ThrowId:
case Expression::Id::RethrowId: {
case Expression::Id::RethrowId:
case Expression::Id::ThrowRefId: {
self->pushTask(SubType::doEndThrow, currp);
break;
}
Expand All @@ -499,6 +537,10 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
self->pushTask(SubType::doStartLoop, currp);
break;
}
case Expression::Id::TryTableId: {
self->pushTask(SubType::doStartTryTable, currp);
break;
}
default: {}
}
}
Expand Down
Loading

0 comments on commit d23a63f

Please sign in to comment.