diff options
Diffstat (limited to 'src/passes/LocalCSE.cpp')
-rw-r--r-- | src/passes/LocalCSE.cpp | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/passes/LocalCSE.cpp b/src/passes/LocalCSE.cpp new file mode 100644 index 000000000..6d48a1107 --- /dev/null +++ b/src/passes/LocalCSE.cpp @@ -0,0 +1,156 @@ +/* + * Copyright 2017 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. + */ + +// +// Local CSE +// +// In each linear area of execution, +// * track each relevant (big enough) expression +// * if already seen, write to a local if not already, and reuse +// * invalidate the list as we see effects +// +// TODO: global, inter-block gvn etc. +// + +#include <wasm.h> +#include <wasm-builder.h> +#include <wasm-traversal.h> +#include <pass.h> +#include <ast_utils.h> +#include <ast/hashed.h> + +namespace wasm { + +const Index UNUSED = -1; + +struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> { + bool isFunctionParallel() override { return true; } + + Pass* create() override { return new LocalCSE(); } + + // information for an expression we can reuse + struct UsableInfo { + Expression** item; + Index index; // if not UNUSED, then the local we are assigned to, use that to reuse us + EffectAnalyzer effects; + + UsableInfo(Expression** item, PassOptions& passOptions) : item(item), index(UNUSED), effects(passOptions, *item) {} + }; + + // a list of usables in a linear execution trace + typedef HashedExpressionMap<UsableInfo> Usables; + + // locals in current linear execution trace, which we try to sink + Usables usables; + + static void doNoteNonLinear(LocalCSE* self, Expression** currp) { + self->usables.clear(); + } + + void checkInvalidations(EffectAnalyzer& effects) { + // TODO: this is O(bad) + std::vector<HashedExpression> invalidated; + for (auto& sinkable : usables) { + if (effects.invalidates(sinkable.second.effects)) { + invalidated.push_back(sinkable.first); + } + } + for (auto index : invalidated) { + usables.erase(index); + } + } + + std::vector<Expression*> expressionStack; + + static void visitPre(LocalCSE* self, Expression** currp) { + // pre operations + Expression* curr = *currp; + + EffectAnalyzer effects(self->getPassOptions()); + if (effects.checkPre(curr)) { + self->checkInvalidations(effects); + } + + self->expressionStack.push_back(curr); + } + + static void visitPost(LocalCSE* self, Expression** currp) { + auto* curr = *currp; + + // main operations + if (self->isRelevant(curr)) { + self->handle(currp, curr); + } + + // post operations + + EffectAnalyzer effects(self->getPassOptions()); + if (effects.checkPost(curr)) { + self->checkInvalidations(effects); + } + + self->expressionStack.pop_back(); + } + + // override scan to add a pre and a post check task to all nodes + static void scan(LocalCSE* self, Expression** currp) { + self->pushTask(visitPost, currp); + + WalkerPass<LinearExecutionWalker<LocalCSE>>::scan(self, currp); + + self->pushTask(visitPre, currp); + } + + bool isRelevant(Expression* curr) { + if (curr->is<GetLocal>()) { + return false; // trivial, this is what we optimize to! + } + if (!isConcreteWasmType(curr->type)) { + return false; // don't bother with unreachable etc. + } + if (EffectAnalyzer(getPassOptions(), curr).hasSideEffects()) { + return false; // we can't combine things with side effects + } + // check what we care about TODO: use optimize/shrink levels? + return Measurer::measure(curr) > 1; + } + + void handle(Expression** currp, Expression* curr) { + HashedExpression hashed(curr); + auto iter = usables.find(hashed); + if (iter != usables.end()) { + // already exists in the table, this is good to reuse + auto& info = iter->second; + if (info.index == UNUSED) { + // we need to assign to a local. create a new one + auto index = info.index = Builder::addVar(getFunction(), curr->type); + (*info.item) = Builder(*getModule()).makeTeeLocal(index, *info.item); + } + replaceCurrent( + Builder(*getModule()).makeGetLocal(info.index, curr->type) + ); + } else { + // not in table, add this, maybe we can help others later + usables.emplace(std::make_pair(hashed, UsableInfo(currp, getPassOptions()))); + } + } +}; + +Pass *createLocalCSEPass() { + return new LocalCSE(); +} + +} // namespace wasm |