diff options
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 51 | ||||
-rw-r--r-- | test/lit/passes/remove-unused-brs-eh.wast | 57 | ||||
-rw-r--r-- | test/lit/passes/remove-unused-brs-gc.wast | 24 | ||||
-rw-r--r-- | test/lit/passes/vacuum-eh.wast | 1 |
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)) |