summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm2wasm.h3
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/RelooperJumpThreading.cpp173
-rw-r--r--src/passes/pass.cpp1
-rw-r--r--src/passes/passes.h1
5 files changed, 179 insertions, 0 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 84db006c4..478a0dd7c 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -522,6 +522,8 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
optimizingBuilder = make_unique<OptimizingIncrementalModuleBuilder>(&wasm, numFunctions, [&](PassRunner& passRunner) {
// run autodrop first, before optimizations
passRunner.add<AutoDrop>();
+ // optimize relooper label variable usage at the wasm level, where it is easy
+ passRunner.add("relooper-jump-threading");
});
}
@@ -802,6 +804,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
add->right = parent->builder.makeConst(Literal((int32_t)parent->functionTableStarts[tableName]));
}
};
+
PassRunner passRunner(&wasm);
passRunner.add<FinalizeCalls>(this);
passRunner.add<ReFinalize>(); // FinalizeCalls changes call types, need to percolate
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index 1b4c65562..9e3cfbabb 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -11,6 +11,7 @@ SET(passes_SOURCES
PostEmscripten.cpp
Precompute.cpp
Print.cpp
+ RelooperJumpThreading.cpp
RemoveImports.cpp
RemoveMemory.cpp
RemoveUnusedBrs.cpp
diff --git a/src/passes/RelooperJumpThreading.cpp b/src/passes/RelooperJumpThreading.cpp
new file mode 100644
index 000000000..a0c33811a
--- /dev/null
+++ b/src/passes/RelooperJumpThreading.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2016 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.
+ */
+
+// Optimize relooper-generated label variable usage: add blocks and turn
+// a label-set/break/label-check into a break into the new block.
+// This assumes the very specific output the fastcomp relooper emits,
+// including the name of the 'label' variable.
+
+#include "wasm.h"
+#include "pass.h"
+#include "ast_utils.h"
+
+namespace wasm {
+
+static Name LABEL("label");
+
+// We need to use new label names, which we cannot create in parallel, so pre-create them
+
+const Index MAX_NAME_INDEX = 1000;
+
+std::vector<Name>* innerNames = nullptr;
+std::vector<Name>* outerNames = nullptr;
+
+struct NameEnsurer {
+ NameEnsurer() {
+ assert(!innerNames);
+ assert(!outerNames);
+ innerNames = new std::vector<Name>;
+ outerNames = new std::vector<Name>;
+ for (Index i = 0; i < MAX_NAME_INDEX; i++) {
+ innerNames->push_back(Name(std::string("jumpthreading$inner$") + std::to_string(i)));
+ outerNames->push_back(Name(std::string("jumpthreading$outer$") + std::to_string(i)));
+ }
+ }
+};
+
+struct RelooperJumpThreading : public WalkerPass<ExpressionStackWalker<RelooperJumpThreading, Visitor<RelooperJumpThreading>>> {
+ bool isFunctionParallel() override { return true; }
+
+ Pass* create() override { return new RelooperJumpThreading; }
+
+ void prepareToRun(PassRunner* runner, Module* module) override {
+ static NameEnsurer ensurer;
+ }
+
+ Index labelIndex;
+ Index newNameCounter = 0;
+
+ void visitBlock(Block* curr) {
+ // look for the if label == X pattern
+ auto& list = curr->list;
+ if (list.size() == 0) return;
+ for (Index i = 0; i < list.size() - 1; i++) {
+ Index origin = i;
+ for (Index j = i + 1; j < list.size(); j++) {
+ if (auto* iff = isLabelCheckingIf(list[j])) {
+ optimizeJumpsToLabelCheck(list[origin], iff);
+ ExpressionManipulator::nop(iff);
+ i++;
+ continue;
+ }
+ // if the next element is a block, it may be the holding block of label-checking ifs
+ if (auto* holder = list[j]->dynCast<Block>()) {
+ if (holder->list.size() > 0) {
+ if (If* iff = isLabelCheckingIf(holder->list[0])) {
+ // this is indeed a holder. we can process the ifs, and must also move
+ // the block to enclose the origin, so it is properly reachable
+ assert(holder->list.size() == 1); // must be size 1, a relooper multiple will have its own label, and is an if-else sequence and nothing more
+ optimizeJumpsToLabelCheck(list[origin], iff);
+ holder->list[0] = list[origin];
+ list[origin] = holder;
+ // reuse the if as a nop
+ list[j] = iff;
+ ExpressionManipulator::nop(iff);
+ i++;
+ continue;
+ }
+ }
+ }
+ break; // we didn't see something we like, so stop here
+ }
+ }
+ }
+
+ void doWalkFunction(Function* func) {
+ // if there isn't a label variable, nothing for us to do
+ if (func->localIndices.count(LABEL)) {
+ labelIndex = func->getLocalIndex(LABEL);
+ WalkerPass<ExpressionStackWalker<RelooperJumpThreading, Visitor<RelooperJumpThreading>>>::doWalkFunction(func);
+ }
+ }
+
+private:
+ If* isLabelCheckingIf(Expression* curr) {
+ auto* iff = curr->dynCast<If>();
+ if (!iff) return nullptr;
+ auto* condition = iff->condition->dynCast<Binary>();
+ if (!(condition && condition->op == EqInt32)) return nullptr;
+ auto* left = condition->left->dynCast<GetLocal>();
+ if (!(left && left->index == labelIndex)) return nullptr;
+ return iff;
+ }
+
+ // optimizes jumps to a label check
+ // * origin is where the jumps originate, and also where we should write our output
+ // * iff is the if
+ void optimizeJumpsToLabelCheck(Expression*& origin, If* iff) {
+ Index nameCounter = newNameCounter++;
+ if (nameCounter >= MAX_NAME_INDEX) {
+ std::cerr << "too many names in RelooperJumpThreading :(\n";
+ return;
+ }
+ Index num = iff->condition->cast<Binary>()->right->cast<Const>()->value.geti32();
+ // create a new block for this jump target
+ Builder builder(*getModule());
+ // origin is where all jumps to this target must come from - the element right before this if
+ // we break out of inner to reach the target. instead of flowing out of normally, we break out of the outer, so we skip the target.
+ auto innerName = innerNames->at(nameCounter);
+ auto outerName = outerNames->at(nameCounter);
+ auto* ifFalse = iff->ifFalse;
+ // all assignments of label to the target can be replaced with breaks to the target, via innerName
+ struct JumpUpdater : public PostWalker<JumpUpdater, Visitor<JumpUpdater>> {
+ Index labelIndex;
+ Index targetNum;
+ Name targetName;
+
+ void visitSetLocal(SetLocal* curr) {
+ if (curr->index == labelIndex) {
+ if (Index(curr->value->cast<Const>()->value.geti32()) == targetNum) {
+ replaceCurrent(Builder(*getModule()).makeBreak(targetName));
+ }
+ }
+ }
+ };
+ JumpUpdater updater;
+ updater.labelIndex = labelIndex;
+ updater.targetNum = num;
+ updater.targetName = innerName;
+ updater.setModule(getModule());
+ updater.walk(origin);
+ // restructure code
+ auto* inner = builder.blockifyWithName(origin, innerName, builder.makeBreak(outerName));
+ auto* outer = builder.makeSequence(inner, iff->ifTrue);
+ outer->name = outerName;
+ origin = outer;
+ // if another label value is checked here, handle that too
+ if (ifFalse) {
+ optimizeJumpsToLabelCheck(origin, ifFalse->cast<If>());
+ }
+ }
+};
+
+// declare pass
+
+Pass *createRelooperJumpThreadingPass() {
+ return new RelooperJumpThreading();
+}
+
+} // namespace wasm
+
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 2139471f7..49c614534 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -75,6 +75,7 @@ void PassRegistry::registerPasses() {
registerPass("print", "print in s-expression format", createPrinterPass);
registerPass("print-minified", "print in minified s-expression format", createMinifiedPrinterPass);
registerPass("print-full", "print in full s-expression format", createFullPrinterPass);
+ registerPass("relooper-jump-threading", "thread relooper jumps (fastcomp output only)", createRelooperJumpThreadingPass);
registerPass("remove-imports", "removes imports and replaces them with nops", createRemoveImportsPass);
registerPass("remove-memory", "removes memory segments", createRemoveMemoryPass);
registerPass("remove-unused-brs", "removes breaks from locations that are not needed", createRemoveUnusedBrsPass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 4bb76edad..d2a0990c6 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -36,6 +36,7 @@ Pass *createPostEmscriptenPass();
Pass *createPrinterPass();
Pass *createMinifiedPrinterPass();
Pass *createFullPrinterPass();
+Pass *createRelooperJumpThreadingPass();
Pass *createRemoveImportsPass();
Pass *createRemoveMemoryPass();
Pass *createRemoveUnusedBrsPass();