summaryrefslogtreecommitdiff
path: root/src/passes/LocalCSE.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/passes/LocalCSE.cpp')
-rw-r--r--src/passes/LocalCSE.cpp156
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