summaryrefslogtreecommitdiff
path: root/src/cfg
diff options
context:
space:
mode:
authorHeejin Ahn <aheejin@gmail.com>2024-01-25 16:22:01 -0800
committerGitHub <noreply@github.com>2024-01-25 16:22:01 -0800
commitd23a63fce8635aa6e401d016a5d0bf23f6f030e8 (patch)
tree03923b15979409ed7dcc8d60ed97242a9ab28819 /src/cfg
parent5fb2137d7f1272b7b107f0413190ad94bb69a73b (diff)
downloadbinaryen-d23a63fce8635aa6e401d016a5d0bf23f6f030e8.tar.gz
binaryen-d23a63fce8635aa6e401d016a5d0bf23f6f030e8.tar.bz2
binaryen-d23a63fce8635aa6e401d016a5d0bf23f6f030e8.zip
[EH] Support CFGWalker for new EH spec (#6235)
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.
Diffstat (limited to 'src/cfg')
-rw-r--r--src/cfg/cfg-traversal.h100
1 files changed, 71 insertions, 29 deletions
diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h
index e72195cc3..9c68ae863 100644
--- a/src/cfg/cfg-traversal.h
+++ b/src/cfg/cfg-traversal.h
@@ -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
@@ -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
@@ -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--;
}
@@ -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:
@@ -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;
}
@@ -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: {}
}
}