Skip to content

Commit d23a63f

Browse files
authored
[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.
1 parent 5fb2137 commit d23a63f

File tree

3 files changed

+413
-29
lines changed

3 files changed

+413
-29
lines changed

scripts/fuzz_opt.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ def is_git_repo():
319319
# New EH implementation is in progress
320320
'exception-handling.wast',
321321
'translate-eh-old-to-new.wast',
322+
'rse-eh.wast',
322323
]
323324

324325

src/cfg/cfg-traversal.h

Lines changed: 71 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
9999
// that can reach catch blocks (each item is assumed to be able to reach any
100100
// of the catches, although that could be improved perhaps).
101101
std::vector<std::vector<BasicBlock*>> throwingInstsStack;
102-
// stack of 'Try' expressions corresponding to throwingInstsStack.
102+
// stack of 'Try'/'TryTable' expressions corresponding to throwingInstsStack.
103103
std::vector<Expression*> tryStack;
104104
// A stack for each try, where each entry is a list of blocks, one for each
105105
// catch, used during processing. We start by assigning the start blocks to
@@ -254,11 +254,11 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
254254
}
255255

256256
static void doEndThrowingInst(SubType* self, Expression** currp) {
257-
// If the innermost try does not have a catch_all clause, an exception
258-
// thrown can be caught by any of its outer catch block. And if that outer
259-
// try-catch also does not have a catch_all, this continues until we
260-
// encounter a try-catch_all. Create a link to all those possible catch
261-
// unwind destinations.
257+
// If the innermost try/try_table does not have a catch_all clause, an
258+
// exception thrown can be caught by any of its outer catch block. And if
259+
// that outer try/try_table also does not have a catch_all, this continues
260+
// until we encounter a try/try_table-catch_all. Create a link to all those
261+
// possible catch unwind destinations.
262262
// TODO This can be more precise for `throw`s if we compare tag types and
263263
// create links to outer catch BBs only when the exception is not caught.
264264
// TODO This can also be more precise if we analyze the structure of nested
@@ -281,36 +281,47 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
281281
// end
282282
assert(self->tryStack.size() == self->throwingInstsStack.size());
283283
for (int i = self->throwingInstsStack.size() - 1; i >= 0;) {
284-
auto* tryy = self->tryStack[i]->template cast<Try>();
285-
if (tryy->isDelegate()) {
286-
// If this delegates to the caller, there is no possibility that this
287-
// instruction can throw to outer catches.
288-
if (tryy->delegateTarget == DELEGATE_CALLER_TARGET) {
289-
break;
290-
}
291-
// If this delegates to an outer try, we skip catches between this try
292-
// and the target try.
293-
[[maybe_unused]] bool found = false;
294-
for (int j = i - 1; j >= 0; j--) {
295-
if (self->tryStack[j]->template cast<Try>()->name ==
296-
tryy->delegateTarget) {
297-
i = j;
298-
found = true;
284+
if (auto* tryy = self->tryStack[i]->template dynCast<Try>()) {
285+
if (tryy->isDelegate()) {
286+
// If this delegates to the caller, there is no possibility that this
287+
// instruction can throw to outer catches.
288+
if (tryy->delegateTarget == DELEGATE_CALLER_TARGET) {
299289
break;
300290
}
291+
// If this delegates to an outer try, we skip catches between this try
292+
// and the target try.
293+
[[maybe_unused]] bool found = false;
294+
for (int j = i - 1; j >= 0; j--) {
295+
if (self->tryStack[j]->template cast<Try>()->name ==
296+
tryy->delegateTarget) {
297+
i = j;
298+
found = true;
299+
break;
300+
}
301+
}
302+
assert(found);
303+
continue;
301304
}
302-
assert(found);
303-
continue;
304305
}
305306

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

310-
// If this try has catch_all, there is no possibility that this
311-
// instruction can throw to outer catches. Stop here.
312-
if (tryy->hasCatchAll()) {
313-
break;
312+
if (auto* tryy = self->tryStack[i]->template dynCast<Try>()) {
313+
// If this try has catch_all, there is no possibility that this
314+
// instruction can throw to outer catches. Stop here.
315+
if (tryy->hasCatchAll()) {
316+
break;
317+
}
318+
} else if (auto* tryTable =
319+
self->tryStack[i]->template dynCast<TryTable>()) {
320+
if (tryTable->hasCatchAll()) {
321+
break;
322+
}
323+
} else {
324+
WASM_UNREACHABLE("invalid throwingInstsStack item");
314325
}
315326
i--;
316327
}
@@ -411,6 +422,28 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
411422
self->startUnreachableBlock();
412423
}
413424

425+
static void doStartTryTable(SubType* self, Expression** currp) {
426+
auto* curr = (*currp)->cast<TryTable>();
427+
self->throwingInstsStack.emplace_back();
428+
self->tryStack.push_back(curr);
429+
}
430+
431+
static void doEndTryTable(SubType* self, Expression** currp) {
432+
auto* curr = (*currp)->cast<TryTable>();
433+
434+
auto catchTargets = BranchUtils::getUniqueTargets(curr);
435+
// Add catch destinations to the targets.
436+
for (auto target : catchTargets) {
437+
auto& preds = self->throwingInstsStack.back();
438+
for (auto* pred : preds) {
439+
self->branches[target].push_back(pred);
440+
}
441+
}
442+
443+
self->throwingInstsStack.pop_back();
444+
self->tryStack.pop_back();
445+
}
446+
414447
static bool isReturnCall(Expression* curr) {
415448
switch (curr->_id) {
416449
case Expression::Id::CallId:
@@ -478,8 +511,13 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
478511
self->pushTask(SubType::doStartTry, currp);
479512
return; // don't do anything else
480513
}
514+
case Expression::Id::TryTableId: {
515+
self->pushTask(SubType::doEndTryTable, currp);
516+
break;
517+
}
481518
case Expression::Id::ThrowId:
482-
case Expression::Id::RethrowId: {
519+
case Expression::Id::RethrowId:
520+
case Expression::Id::ThrowRefId: {
483521
self->pushTask(SubType::doEndThrow, currp);
484522
break;
485523
}
@@ -499,6 +537,10 @@ struct CFGWalker : public PostWalker<SubType, VisitorType> {
499537
self->pushTask(SubType::doStartLoop, currp);
500538
break;
501539
}
540+
case Expression::Id::TryTableId: {
541+
self->pushTask(SubType::doStartTryTable, currp);
542+
break;
543+
}
502544
default: {}
503545
}
504546
}

0 commit comments

Comments
 (0)