summaryrefslogtreecommitdiff
path: root/src/passes/RemoveUnusedBrs.cpp
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2016-09-12 12:24:52 -0700
committerAlon Zakai <alonzakai@gmail.com>2016-09-12 12:24:52 -0700
commit284865e47ed545beeff40629caa59f169885f560 (patch)
tree1d0f0d73eaa45325a7f445b43914754e1196514e /src/passes/RemoveUnusedBrs.cpp
parent4d0fea95aec72f932efa83a0601b98c177e59a85 (diff)
downloadbinaryen-284865e47ed545beeff40629caa59f169885f560.tar.gz
binaryen-284865e47ed545beeff40629caa59f169885f560.tar.bz2
binaryen-284865e47ed545beeff40629caa59f169885f560.zip
simple jump threading
Diffstat (limited to 'src/passes/RemoveUnusedBrs.cpp')
-rw-r--r--src/passes/RemoveUnusedBrs.cpp77
1 files changed, 74 insertions, 3 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp
index c4cb0c554..924cd7848 100644
--- a/src/passes/RemoveUnusedBrs.cpp
+++ b/src/passes/RemoveUnusedBrs.cpp
@@ -312,8 +312,81 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs, Visitor<R
typeUpdater.walkFunction(func);
}
+ // thread trivial jumps
+ struct JumpThreader : public ControlFlowWalker<JumpThreader, Visitor<JumpThreader>> {
+ // map of all value-less breaks going to a block (and not a loop)
+ std::map<Block*, std::vector<Break*>> breaksToBlock;
+
+ // number of definitions of each name - when a name is defined more than once, it is not trivially safe to do this
+ std::map<Name, Index> numDefs;
+
+ // the names to update, when we can (just one def)
+ std::map<Break*, Name> newNames;
+
+ void visitBreak(Break* curr) {
+ if (!curr->value) {
+ if (auto* target = findBreakTarget(curr->name)->dynCast<Block>()) {
+ breaksToBlock[target].push_back(curr);
+ }
+ }
+ }
+ // TODO: Switch?
+ void visitBlock(Block* curr) {
+ if (curr->name.is()) numDefs[curr->name]++;
+
+ auto& list = curr->list;
+ if (list.size() == 1 && curr->name.is()) {
+ // if this block has just one child, a sub-block, then jumps to the former are jumps to us, really
+ if (auto* child = list[0]->dynCast<Block>()) {
+ if (child->name.is() && child->name != curr->name) {
+ auto& breaks = breaksToBlock[child];
+ for (auto* br : breaks) {
+ newNames[br] = curr->name;
+ breaksToBlock[curr].push_back(br); // update the list - we may push it even more later
+ }
+ breaksToBlock.erase(child);
+ }
+ }
+ } else if (list.size() == 2) {
+ // if this block has two children, a child-block and a simple jump, then jumps to child-block can be replaced with jumps to the new target
+ auto* child = list[0]->dynCast<Block>();
+ auto* jump = list[1]->dynCast<Break>();
+ if (child && child->name.is() && jump && ExpressionAnalyzer::isSimple(jump)) {
+ auto& breaks = breaksToBlock[child];
+ for (auto* br : breaks) {
+ newNames[br] = jump->name;
+ }
+ // if the jump is to another block then we can update the list, and maybe push it even more later
+ if (auto* newTarget = findBreakTarget(jump->name)->dynCast<Block>()) {
+ for (auto* br : breaks) {
+ breaksToBlock[newTarget].push_back(br);
+ }
+ }
+ breaksToBlock.erase(child);
+ }
+ }
+ }
+ void visitLoop(Loop* curr) {
+ if (curr->name.is()) numDefs[curr->name]++;
+ }
+
+ void finish() {
+ for (auto& iter : newNames) {
+ auto* br = iter.first;
+ auto name = iter.second;
+ if (numDefs[name] == 1) {
+ br->name = name;
+ }
+ }
+ }
+ };
+ JumpThreader jumpThreader;
+ jumpThreader.setModule(getModule());
+ jumpThreader.walkFunction(func);
+ jumpThreader.finish();
+
// perform some final optimizations
- struct FinalOptimizer : public WalkerPass<PostWalker<FinalOptimizer, Visitor<FinalOptimizer>>> {
+ struct FinalOptimizer : public PostWalker<FinalOptimizer, Visitor<FinalOptimizer>> {
void visitBlock(Block* curr) {
// if a block has an if br else br, we can un-conditionalize the latter, allowing
// the if to become a br_if.
@@ -375,6 +448,4 @@ Pass *createRemoveUnusedBrsPass() {
return new RemoveUnusedBrs();
}
-// TODO: jump threading, when a jump->jump->target => jump->target
-
} // namespace wasm