| /* | |
| * Copyright 2015 WebAssembly Community Group participants | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // | |
| // Removes names from locations that are never branched to, and | |
| // merge names when possible (by merging their blocks) | |
| // | |
| #include <ir/branch-utils.h> | |
| #include <pass.h> | |
| #include <shared-constants.h> | |
| #include <wasm.h> | |
| namespace wasm { | |
| struct RemoveUnusedNames | |
| : public WalkerPass<PostWalker<RemoveUnusedNames, | |
| UnifiedExpressionVisitor<RemoveUnusedNames>>> { | |
| bool isFunctionParallel() override { return true; } | |
| // This pass only removes names, which can only help validation (as blocks | |
| // without names are ignored, see the README section on non-nullable locals). | |
| bool requiresNonNullableLocalFixups() override { return false; } | |
| std::unique_ptr<Pass> create() override { | |
| return std::make_unique<RemoveUnusedNames>(); | |
| } | |
| // We maintain a list of branches that we saw in children, then when we reach | |
| // a parent block, we know if it was branched to | |
| std::map<Name, std::set<Expression*>> branchesSeen; | |
| void visitExpression(Expression* curr) { | |
| BranchUtils::operateOnScopeNameUses(curr, [&](Name& name) { | |
| if (name.is()) { | |
| branchesSeen[name].insert(curr); | |
| } | |
| }); | |
| } | |
| void handleBreakTarget(Name& name) { | |
| if (name.is()) { | |
| if (branchesSeen.find(name) == branchesSeen.end()) { | |
| name = Name(); | |
| } else { | |
| branchesSeen.erase(name); | |
| } | |
| } | |
| } | |
| void visitBlock(Block* curr) { | |
| if (curr->name.is() && curr->list.size() == 1) { | |
| auto* child = curr->list[0]->dynCast<Block>(); | |
| if (child && child->name.is() && child->type == curr->type) { | |
| // we have just one child, this block, so breaking out of it goes to the | |
| // same place as breaking out of us, we just need one name (and block) | |
| auto& branches = branchesSeen[curr->name]; | |
| for (auto* branch : branches) { | |
| BranchUtils::replacePossibleTarget(branch, curr->name, child->name); | |
| } | |
| child->finalize(child->type); | |
| replaceCurrent(child); | |
| } | |
| } | |
| handleBreakTarget(curr->name); | |
| } | |
| void visitLoop(Loop* curr) { | |
| handleBreakTarget(curr->name); | |
| if (!curr->name.is() && curr->body->type == curr->type) { | |
| replaceCurrent(curr->body); | |
| } | |
| } | |
| void visitTry(Try* curr) { | |
| handleBreakTarget(curr->name); | |
| // Try has not just a break target but also an optional delegate with a | |
| // target name, so call the generic visitor as well to handle that. | |
| visitExpression(curr); | |
| } | |
| void visitFunction(Function* curr) { | |
| // When we reach the function body we can erase delegations to the caller. | |
| branchesSeen.erase(DELEGATE_CALLER_TARGET); | |
| assert(branchesSeen.empty()); | |
| } | |
| }; | |
| Pass* createRemoveUnusedNamesPass() { return new RemoveUnusedNames(); } | |
| } // namespace wasm |