/* * 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 #include #include #include #include #include namespace wasm { const Index UNUSED = -1; struct LocalCSE : public WalkerPass> { 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 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 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 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); super::scan(self, currp); self->pushTask(visitPre, currp); } bool isRelevant(Expression* curr) { if (curr->is()) { return false; // trivial, this is what we optimize to! } if (!isConcreteType(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