summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-10-14 14:38:12 -0700
committerGitHub <noreply@github.com>2024-10-14 14:38:12 -0700
commite201819761bd8ae21bd03b2656a15544f9e44c32 (patch)
treebbaed23fa02d5b0191c9a3d3cf42b4ab2341552d /src
parent31b4558f3decc49c5d780083d995d7c094132b77 (diff)
downloadbinaryen-e201819761bd8ae21bd03b2656a15544f9e44c32.tar.gz
binaryen-e201819761bd8ae21bd03b2656a15544f9e44c32.tar.bz2
binaryen-e201819761bd8ae21bd03b2656a15544f9e44c32.zip
[Wasm EH] Optimize away _ref from try_table catches when unused (#6996)
If we have (drop (block $b (result exnref) (try_table (catch_all_ref $b) then we don't really need to send the ref: it is dropped, so we can just replace catch_all_ref with catch_all and then remove the drop and the block value. MergeBlocks already had logic to remove block values, so it is the natural place to add this.
Diffstat (limited to 'src')
-rw-r--r--src/passes/MergeBlocks.cpp56
1 files changed, 52 insertions, 4 deletions
diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp
index 147ba9f45..e0c5e575a 100644
--- a/src/passes/MergeBlocks.cpp
+++ b/src/passes/MergeBlocks.cpp
@@ -83,9 +83,11 @@
namespace wasm {
-// Looks for reasons we can't remove the values from breaks to an origin
-// For example, if there is a switch targeting us, we can't do it - we can't
-// remove the value from other targets
+// Looks for reasons we can't remove the values from breaks to an origin. This
+// is run when we know the value sent to that block is dropped, so the value is
+// not needed, but some corner cases stop us (for example, if there is a switch
+// targeting us, we can't do it - we can't remove the value from the switch's
+// other targets).
struct ProblemFinder
: public ControlFlowWalker<ProblemFinder,
UnifiedExpressionVisitor<ProblemFinder>> {
@@ -123,6 +125,39 @@ struct ProblemFinder
return;
}
+ if (auto* tryy = curr->dynCast<TryTable>()) {
+ auto num = tryy->catchTags.size();
+ for (Index i = 0; i < num; i++) {
+ if (tryy->catchDests[i] == origin) {
+ // This try_table branches to the origin we care about. We know the
+ // value being sent to the block is dropped, so we'd like to stop
+ // anything from being sent to it. We can stop a ref from being sent,
+ // so if that is enough to remove all the values, then we can
+ // optimize here. In other words, if this is a catch_all_ref (which
+ // can only send a ref) or this is a catch_ref of a specific tag that
+ // has no contents (so if we remove the ref, nothing remains), then we
+ // can optimize, but if this is is a catch of a tag *with* contents
+ // then those contents stop us.
+ //
+ // TODO: We could also support cases where the target block has
+ // multiple values, and remove just ref at the end. That might
+ // make more sense in TupleOptimization though as it would need
+ // to track uses of parts of a tuple.
+ if (!tryy->catchTags[i] ||
+ getModule()->getTag(tryy->catchTags[i])->sig.params.size() == 0) {
+ // There must be a ref here, otherwise there is no value being sent
+ // at all, and we should not be running ProblemFinder at all.
+ assert(tryy->catchRefs[i]);
+ } else {
+ // Anything else is a problem.
+ foundProblem = true;
+ return;
+ }
+ }
+ }
+ return;
+ }
+
// Any other branch type - switch, br_on, etc. - is not handled yet.
BranchUtils::operateOnScopeNameUses(curr, [&](Name& name) {
if (name == origin) {
@@ -174,6 +209,18 @@ struct BreakValueDropper : public ControlFlowWalker<BreakValueDropper> {
replaceCurrent(curr->value);
}
}
+
+ void visitTryTable(TryTable* curr) {
+ auto num = curr->catchTags.size();
+ for (Index i = 0; i < num; i++) {
+ if (curr->catchDests[i] == origin) {
+ // Remove the existing ref being sent.
+ assert(curr->catchRefs[i]);
+ curr->catchRefs[i] = false;
+ curr->sentTypes[i] = Type::none;
+ }
+ }
+ }
};
// Checks for code after an unreachable element.
@@ -223,7 +270,8 @@ static bool optimizeDroppedBlock(Drop* drop,
drop->finalize();
block->list.back() = drop;
}
- block->finalize();
+ // Remove the old type, which was from the value we just removed.
+ block->finalize(Type::none);
return true;
}