summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/RemoveUnusedBrs.cpp51
-rw-r--r--test/lit/passes/remove-unused-brs-eh.wast57
-rw-r--r--test/lit/passes/remove-unused-brs-gc.wast24
-rw-r--r--test/lit/passes/vacuum-eh.wast1
4 files changed, 108 insertions, 25 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp
index 0c316e7e7..c166d3c07 100644
--- a/src/passes/RemoveUnusedBrs.cpp
+++ b/src/passes/RemoveUnusedBrs.cpp
@@ -27,6 +27,7 @@
#include "ir/utils.h"
#include "parsing.h"
#include "pass.h"
+#include "support/small_set.h"
#include "wasm-builder.h"
#include "wasm.h"
@@ -1020,31 +1021,35 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
}
} while (anotherCycle);
- // thread trivial jumps
- struct JumpThreader : public ControlFlowWalker<JumpThreader> {
- // map of all value-less breaks and switches going to a block (and not a
- // loop)
- std::map<Block*, std::vector<Expression*>> branchesToBlock;
+ // Thread trivial jumps.
+ struct JumpThreader
+ : public PostWalker<JumpThreader,
+ UnifiedExpressionVisitor<JumpThreader>> {
+ // Map of all labels (branch targets) to the branches going to them. (We
+ // only care about blocks here, and not loops, but for simplicitly we
+ // store all branch targets since blocks are 99% of that set anyhow. Any
+ // loops are ignored later.)
+ std::unordered_map<Name, std::vector<Expression*>> labelToBranches;
bool worked = false;
- void visitBreak(Break* curr) {
- if (!curr->value) {
- if (auto* target = findBreakTarget(curr->name)->dynCast<Block>()) {
- branchesToBlock[target].push_back(curr);
- }
- }
- }
- void visitSwitch(Switch* curr) {
- if (!curr->value) {
- auto names = BranchUtils::getUniqueTargets(curr);
- for (auto name : names) {
- if (auto* target = findBreakTarget(name)->dynCast<Block>()) {
- branchesToBlock[target].push_back(curr);
+ void visitExpression(Expression* curr) {
+ // Find the relevant targets: targets that (as mentioned above) have no
+ // value sent to them.
+ SmallSet<Name, 2> relevantTargets;
+ BranchUtils::operateOnScopeNameUsesAndSentTypes(
+ curr, [&](Name name, Type sent) {
+ if (sent == Type::none) {
+ relevantTargets.insert(name);
}
- }
+ });
+
+ // Note ourselves on all relevant targets.
+ for (auto target : relevantTargets) {
+ labelToBranches[target].push_back(curr);
}
}
+
void visitBlock(Block* curr) {
auto& list = curr->list;
if (list.size() == 1 && curr->name.is()) {
@@ -1073,7 +1078,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
}
void redirectBranches(Block* from, Name to) {
- auto& branches = branchesToBlock[from];
+ auto& branches = labelToBranches[from->name];
for (auto* branch : branches) {
if (BranchUtils::replacePossibleTarget(branch, from->name, to)) {
worked = true;
@@ -1081,10 +1086,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
}
// if the jump is to another block then we can update the list, and
// maybe push it even more later
- if (auto* newTarget = findBreakTarget(to)->dynCast<Block>()) {
- for (auto* branch : branches) {
- branchesToBlock[newTarget].push_back(branch);
- }
+ for (auto* branch : branches) {
+ labelToBranches[to].push_back(branch);
}
}
diff --git a/test/lit/passes/remove-unused-brs-eh.wast b/test/lit/passes/remove-unused-brs-eh.wast
index a4c6591ab..544ff81b0 100644
--- a/test/lit/passes/remove-unused-brs-eh.wast
+++ b/test/lit/passes/remove-unused-brs-eh.wast
@@ -244,6 +244,63 @@
(call $throw-caught-precise-later)
)
)
+
+ ;; CHECK: (func $threading (type $0)
+ ;; CHECK-NEXT: (block $outer
+ ;; CHECK-NEXT: (block $middle
+ ;; CHECK-NEXT: (block $inner
+ ;; CHECK-NEXT: (try_table (catch $e $outer) (catch $f $outer) (catch_all $outer)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $threading
+ (block $outer
+ (block $middle
+ (block $inner
+ ;; All the branch targets here will turn into "outer", see below.
+ (try_table (catch $e $outer) (catch $f $middle) (catch_all $inner)
+ )
+ )
+ ;; Jumping to inner is the same as middle, as there is nothing
+ ;; between them.
+ )
+ ;; Jumping to middle is the same as outer, as we jump there anyhow.
+ (br $outer)
+ )
+ )
+
+ ;; CHECK: (func $threading-2 (type $0)
+ ;; CHECK-NEXT: (block $outer
+ ;; CHECK-NEXT: (block $middle
+ ;; CHECK-NEXT: (block $inner
+ ;; CHECK-NEXT: (try_table (catch $e $outer) (catch $f $middle) (catch_all $outer)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $outer)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $threading-2
+ (block $outer
+ (block $middle
+ (block $inner
+ (try_table (catch $e $outer) (catch $f $middle) (catch_all $inner)
+ ;; Only inner will turn into outer.
+ )
+ )
+ ;; Skip over middle, so jumps to inner go to outer.
+ (br $outer)
+ )
+ ;; Stop execution between middle and outer. We should still optimize
+ ;; inner to outer.
+ (unreachable)
+ )
+ )
)
(module
diff --git a/test/lit/passes/remove-unused-brs-gc.wast b/test/lit/passes/remove-unused-brs-gc.wast
index 115002e89..42c2f4b1c 100644
--- a/test/lit/passes/remove-unused-brs-gc.wast
+++ b/test/lit/passes/remove-unused-brs-gc.wast
@@ -782,4 +782,28 @@
)
)
)
+
+ ;; CHECK: (func $threading (type $10) (param $x anyref)
+ ;; CHECK-NEXT: (block $outer
+ ;; CHECK-NEXT: (block $inner
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_null $outer
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $threading (param $x anyref)
+ (block $outer
+ (block $inner
+ ;; This jump can go to $outer.
+ (drop
+ (br_on_null $inner
+ (local.get $x)
+ )
+ )
+ )
+ )
+ )
)
diff --git a/test/lit/passes/vacuum-eh.wast b/test/lit/passes/vacuum-eh.wast
index d42fed56b..4df41f854 100644
--- a/test/lit/passes/vacuum-eh.wast
+++ b/test/lit/passes/vacuum-eh.wast
@@ -154,7 +154,6 @@
;; try_table could be optimized out. We do this for `try` but not for
;; `try_table` - we leave such optimizations to --remove-unused-brs (that
;; pass can see that the throw can be converted to a br).
-
(block $catch
(try_table (catch_all $catch)
(throw $e (i32.const 0))