summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/CMakeLists.txt2
-rw-r--r--src/passes/Flatten.cpp337
-rw-r--r--src/passes/FlattenControlFlow.cpp476
-rw-r--r--src/passes/ReReloop.cpp2
-rw-r--r--src/passes/pass.cpp2
-rw-r--r--src/passes/passes.h98
-rw-r--r--src/wasm-traversal.h6
-rw-r--r--src/wasm2asm.h2
8 files changed, 396 insertions, 529 deletions
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index 3cb00b796..168af2761 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -7,7 +7,7 @@ SET(passes_SOURCES
DeadCodeElimination.cpp
DuplicateFunctionElimination.cpp
ExtractFunction.cpp
- FlattenControlFlow.cpp
+ Flatten.cpp
Inlining.cpp
LegalizeJSInterface.cpp
LocalCSE.cpp
diff --git a/src/passes/Flatten.cpp b/src/passes/Flatten.cpp
new file mode 100644
index 000000000..b16d9df3f
--- /dev/null
+++ b/src/passes/Flatten.cpp
@@ -0,0 +1,337 @@
+/*
+ * 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.
+ */
+
+//
+// Flattens code, removing nesting.e.g. an if return value would be
+// converted to a local
+//
+// (i32.add
+// (if (..condition..)
+// (..if true..)
+// (..if false..)
+// )
+// (i32.const 1)
+// )
+// =>
+// (if (..condition..)
+// (set_local $temp
+// (..if true..)
+// )
+// (set_local $temp
+// (..if false..)
+// )
+// )
+// (i32.add
+// (get_local $temp)
+// (i32.const 1)
+// )
+//
+// Formally, this pass flattens in the precise sense of
+// making the AST have these properties:
+//
+// 1. The operands of an instruction must be a get_local or a const.
+// anything else is written to a local earlier.
+// 2. Disallow block, loop, and if return values, i.e., do not use
+// control flow to pass around values.
+//
+
+#include <wasm.h>
+#include <pass.h>
+#include <wasm-builder.h>
+#include <ast_utils.h>
+#include <ast/effects.h>
+
+namespace wasm {
+
+// We use the following algorithm: we maintain a list of "preludes", code
+// that runs right before an expression. When we visit an expression we
+// must handle it and its preludes. If the expression has side effects,
+// we reduce it to a get_local and add a prelude for that. We then handle
+// the preludes, by moving them to the parent or handling them directly.
+// we can move them to the parent if the parent is not a control flow
+// structure. Otherwise, if the parent is a control flow structure, it
+// will incorporate the preludes of its children accordingly.
+// As a result, when we reach a node, we know its children have no
+// side effects (they have been moved to a prelude), or we are a
+// control flow structure (which allows children with side effects,
+// e.g. a return as a block element).
+// Once exception is that we allow an (unreachable) node, which is used
+// when we move something unreachable to another place, and need a
+// placeholder. We will never reach that (unreachable) anyhow
+struct Flatten : public WalkerPass<ExpressionStackWalker<Flatten, UnifiedExpressionVisitor<Flatten>>> {
+ bool isFunctionParallel() override { return true; }
+
+ Pass* create() override { return new Flatten; }
+
+ // For each expression, a bunch of expressions that should execute right before it
+ std::unordered_map<Expression*, std::vector<Expression*>> preludes;
+
+ // Break values are sent through a temp local
+ std::unordered_map<Name, Index> breakTemps;
+
+ void visitExpression(Expression* curr) {
+ std::vector<Expression*> ourPreludes;
+ Builder builder(*getModule());
+
+ if (isControlFlowStructure(curr)) {
+ // handle control flow explicitly. our children do not have control flow,
+ // but they do have preludes which we need to set up in the right place
+ assert(preludes.find(curr) == preludes.end()); // no one should have given us preludes, they are on the children
+ if (auto* block = curr->dynCast<Block>()) {
+ // make a new list, where each item's preludes are added before it
+ ExpressionList newList(getModule()->allocator);
+ for (auto* item : block->list) {
+ auto iter = preludes.find(item);
+ if (iter != preludes.end()) {
+ auto& itemPreludes = iter->second;
+ for (auto* prelude : itemPreludes) {
+ newList.push_back(prelude);
+ }
+ itemPreludes.clear();
+ }
+ newList.push_back(item);
+ }
+ block->list.swap(newList);
+ // remove a block return value
+ auto type = block->type;
+ if (isConcreteWasmType(type)) {
+ // if there is a temp index for breaking to the block, use that
+ Index temp;
+ auto iter = breakTemps.find(block->name);
+ if (iter != breakTemps.end()) {
+ temp = iter->second;
+ } else {
+ temp = builder.addVar(getFunction(), type);
+ }
+ auto*& last = block->list.back();
+ if (isConcreteWasmType(last->type)) {
+ last = builder.makeSetLocal(temp, last);
+ }
+ block->finalize(none);
+ // and we leave just a get of the value
+ auto* rep = builder.makeGetLocal(temp, type);
+ replaceCurrent(rep);
+ // the whole block is now a prelude
+ ourPreludes.push_back(block);
+ }
+ // the block now has no return value, and may have become unreachable
+ block->finalize(none);
+ } else if (auto* iff = curr->dynCast<If>()) {
+ // condition preludes go before the entire if
+ auto* rep = getPreludesWithExpression(iff->condition, iff);
+ // arm preludes go in the arms. we must also remove an if value
+ auto* originalIfTrue = iff->ifTrue;
+ auto* originalIfFalse = iff->ifFalse;
+ auto type = iff->type;
+ Expression* prelude = nullptr;
+ if (isConcreteWasmType(type)) {
+ Index temp = builder.addVar(getFunction(), type);
+ if (isConcreteWasmType(iff->ifTrue->type)) {
+ iff->ifTrue = builder.makeSetLocal(temp, iff->ifTrue);
+ }
+ if (iff->ifFalse && isConcreteWasmType(iff->ifFalse->type)) {
+ iff->ifFalse = builder.makeSetLocal(temp, iff->ifFalse);
+ }
+ // the whole if (+any preludes from the condition) is now a prelude
+ prelude = rep;
+ // and we leave just a get of the value
+ rep = builder.makeGetLocal(temp, type);
+ }
+ iff->ifTrue = getPreludesWithExpression(originalIfTrue, iff->ifTrue);
+ if (iff->ifFalse) iff->ifFalse = getPreludesWithExpression(originalIfFalse, iff->ifFalse);
+ iff->finalize();
+ if (prelude) {
+ ReFinalizeNode().visit(prelude);
+ ourPreludes.push_back(prelude);
+ }
+ replaceCurrent(rep);
+ } else if (auto* loop = curr->dynCast<Loop>()) {
+ // remove a loop value
+ Expression* rep = loop;
+ auto* originalBody = loop->body;
+ auto type = loop->type;
+ if (isConcreteWasmType(type)) {
+ Index temp = builder.addVar(getFunction(), type);
+ loop->body = builder.makeSetLocal(temp, loop->body);
+ // and we leave just a get of the value
+ rep = builder.makeGetLocal(temp, type);
+ // the whole if is now a prelude
+ ourPreludes.push_back(loop);
+ loop->type = none;
+ }
+ loop->body = getPreludesWithExpression(originalBody, loop->body);
+ loop->finalize();
+ replaceCurrent(rep);
+ } else {
+ WASM_UNREACHABLE();
+ }
+ } else {
+ // for anything else, there may be existing preludes
+ auto iter = preludes.find(curr);
+ if (iter != preludes.end()) {
+ ourPreludes.swap(iter->second);
+ }
+ // special handling
+ if (auto* br = curr->dynCast<Break>()) {
+ if (br->value) {
+ auto type = br->value->type;
+ if (isConcreteWasmType(type)) {
+ // we are sending a value. use a local instead
+ Index temp = getTempForBreakTarget(br->name, type);
+ ourPreludes.push_back(builder.makeSetLocal(temp, br->value));
+ if (br->condition) {
+ // the value must also flow out
+ ourPreludes.push_back(br);
+ if (isConcreteWasmType(br->type)) {
+ replaceCurrent(builder.makeGetLocal(temp, type));
+ } else {
+ assert(br->type == unreachable);
+ replaceCurrent(builder.makeUnreachable());
+ }
+ }
+ br->value = nullptr;
+ br->finalize();
+ } else {
+ assert(type == unreachable);
+ // we don't need the br at all
+ replaceCurrent(br->value);
+ }
+ }
+ } else if (auto* sw = curr->dynCast<Switch>()) {
+ if (sw->value) {
+ auto type = sw->value->type;
+ if (isConcreteWasmType(type)) {
+ // we are sending a value. use a local instead
+ Index temp = builder.addVar(getFunction(), type);
+ ourPreludes.push_back(builder.makeSetLocal(temp, sw->value));
+ // we don't know which break target will be hit - assign to them all
+ std::set<Name> names;
+ for (auto target : sw->targets) {
+ names.insert(target);
+ }
+ names.insert(sw->default_);
+ for (auto name : names) {
+ ourPreludes.push_back(builder.makeSetLocal(
+ getTempForBreakTarget(name, type),
+ builder.makeGetLocal(temp, type)
+ ));
+ }
+ sw->value = nullptr;
+ sw->finalize();
+ } else {
+ assert(type == unreachable);
+ // we don't need the br at all
+ replaceCurrent(sw->value);
+ }
+ }
+ }
+ }
+ // continue for general handling of everything, control flow or otherwise
+ curr = getCurrent(); // we may have replaced it
+ // we have changed children
+ ReFinalizeNode().visit(curr);
+ // handle side effects and control flow, things we need to be
+ // in the prelude. note that we must handle anything here, not just
+ // side effects, as a sibling after us may have side effect for us,
+ // and thus we need to move in anticipation of that (e.g., we are
+ // a get, and a later sibling is a tee - if just the tee moves,
+ // that is bade) TODO optimize
+ if (isControlFlowStructure(curr) || EffectAnalyzer(getPassOptions(), curr).hasAnything()) {
+ // we need to move the side effects to the prelude
+ if (curr->type == unreachable) {
+ ourPreludes.push_back(curr);
+ replaceCurrent(builder.makeUnreachable());
+ } else if (curr->type == none) {
+ if (!curr->is<Nop>()) {
+ ourPreludes.push_back(curr);
+ replaceCurrent(builder.makeNop());
+ }
+ } else {
+ // use a local
+ auto type = curr->type;
+ Index temp = builder.addVar(getFunction(), type);
+ ourPreludes.push_back(builder.makeSetLocal(temp, curr));
+ replaceCurrent(builder.makeGetLocal(temp, type));
+ }
+ }
+ // next, finish up: migrate our preludes if we can
+ if (!ourPreludes.empty()) {
+ auto* parent = getParent();
+ if (parent && !isControlFlowStructure(parent)) {
+ auto& parentPreludes = preludes[parent];
+ for (auto* prelude : ourPreludes) {
+ parentPreludes.push_back(prelude);
+ }
+ } else {
+ // keep our preludes, parent will handle them
+ preludes[getCurrent()].swap(ourPreludes);
+ }
+ }
+ }
+
+ void visitFunction(Function* curr) {
+ auto* originalBody = curr->body;
+ // if the body is a block with a result, turn that into a return
+ if (isConcreteWasmType(curr->body->type)) {
+ curr->body = Builder(*getModule()).makeReturn(curr->body);
+ }
+ // the body may have preludes
+ curr->body = getPreludesWithExpression(originalBody, curr->body);
+ }
+
+private:
+ bool isControlFlowStructure(Expression* curr) {
+ return curr->is<Block>() || curr->is<If>() || curr->is<Loop>();
+ }
+
+ // gets an expression, either by itself, or in a block with its
+ // preludes (which we use up) before it
+ Expression* getPreludesWithExpression(Expression* curr) {
+ return getPreludesWithExpression(curr, curr);
+ }
+
+ // gets an expression, either by itself, or in a block with some
+ // preludes (which we use up) for another expression before it
+ Expression* getPreludesWithExpression(Expression* preluder, Expression* after) {
+ auto iter = preludes.find(preluder);
+ if (iter == preludes.end()) return after;
+ // we have preludes
+ auto& thePreludes = iter->second;
+ auto* ret = Builder(*getModule()).makeBlock(thePreludes);
+ thePreludes.clear();
+ ret->list.push_back(after);
+ ret->finalize();
+ return ret;
+ }
+
+ // get the temp local to be used for breaks to that target. allocates
+ // one if there isn't one yet
+ Index getTempForBreakTarget(Name name, WasmType type) {
+ auto iter = breakTemps.find(name);
+ if (iter != breakTemps.end()) {
+ return iter->second;
+ } else {
+ return breakTemps[name] = Builder(*getModule()).addVar(getFunction(), type);
+ }
+ }
+};
+
+Pass *createFlattenPass() {
+ return new Flatten();
+}
+
+} // namespace wasm
+
diff --git a/src/passes/FlattenControlFlow.cpp b/src/passes/FlattenControlFlow.cpp
deleted file mode 100644
index dce8e6345..000000000
--- a/src/passes/FlattenControlFlow.cpp
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- * 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.
- */
-
-//
-// Flattens control flow, e.g.
-//
-// (i32.add
-// (if (..condition..)
-// (..if true..)
-// (..if false..)
-// )
-// (i32.const 1)
-// )
-// =>
-// (if (..condition..)
-// (set_local $temp
-// (..if true..)
-// )
-// (set_local $temp
-// (..if false..)
-// )
-// )
-// (i32.add
-// (get_local $temp)
-// (i32.const 1)
-// )
-//
-// Formally, this pass flattens control flow in the precise sense of
-// making the AST have these properties:
-//
-// 1. Control flow structures (block, loop, if) and control flow
-// operations (br, br_if, br_table, return, unreachable) may
-// only be block children, a loop body, or an if-true or if-false.
-// (I.e., they cannot be nested inside an i32.add, a drop, a
-// call, an if-condition, etc.)
-// 2. Disallow block, loop, and if return values, i.e., do not use
-// control flow to pass around values.
-//
-// Note that we do still allow normal arbitrary nesting of expressions
-// *without* control flow (i.e., this is not a reduction to 3-address
-// code form). We also allow nesting of control flow, but just nested
-// in other control flow, like an if in the true arm of an if, and
-// so forth. What we achieve here is that when you see an expression,
-// you know it has no control flow inside it, it will be fully
-// executed.
-//
-
-#include <wasm.h>
-#include <pass.h>
-#include <wasm-builder.h>
-#include <ast_utils.h>
-
-namespace wasm {
-
-// Looks for control flow changes and structures, excluding blocks (as we
-// want to put all control flow on them)
-struct ControlFlowChecker : public Visitor<ControlFlowChecker> {
- static bool is(Expression* node) {
- ControlFlowChecker finder;
- finder.visit(node);
- return finder.hasControlFlow;
- }
-
- bool hasControlFlow = false;
-
- void visitBreak(Break *curr) { hasControlFlow = true; }
- void visitSwitch(Switch *curr) { hasControlFlow = true; }
- void visitBlock(Block *curr) { hasControlFlow = true; }
- void visitLoop(Loop* curr) { hasControlFlow = true; }
- void visitIf(If* curr) { hasControlFlow = true; }
- void visitReturn(Return *curr) { hasControlFlow = true; }
- void visitUnreachable(Unreachable *curr) { hasControlFlow = true; }
-};
-
-struct FlattenControlFlow : public WalkerPass<PostWalker<FlattenControlFlow>> {
- bool isFunctionParallel() override { return true; }
-
- Pass* create() override { return new FlattenControlFlow; }
-
- std::unique_ptr<Builder> builder;
- // we get rid of block/if/loop values. this map tells us for
- // each break target what local index to use.
- // if this is a flowing value, there might not be a name assigned
- // (block ending, block with no name; or if value), so we use
- // the expr (and there will be exactly one set and get of it,
- // so we don't need a name)
- std::map<Name, Index> breakNameIndexes;
- std::map<Expression*, Index> breakExprIndexes;
-
- void doWalkFunction(Function* func) {
- builder = make_unique<Builder>(*getModule());
- walk(func->body);
- if (func->result != none) {
- // if the body had a fallthrough, receive it and return it
- auto iter = breakExprIndexes.find(func->body);
- if (iter != breakExprIndexes.end()) {
- func->body = builder->makeSequence(
- func->body,
- builder->makeReturn(
- builder->makeGetLocal(iter->second, func->result)
- )
- );
- }
- }
- }
-
- // returns the index to assign values to for a break target. allocates
- // the local if this is the first time we see it.
- // expr is used if this is a flowing value.
- Index getBreakTargetIndex(Name name, WasmType type, Expression* expr = nullptr, Index index = -1) {
- assert(isConcreteWasmType(type)); // we shouldn't get here if the value ins't actually set
- if (name.is()) {
- auto iter = breakNameIndexes.find(name);
- if (iter == breakNameIndexes.end()) {
- if (index == Index(-1)) {
- index = builder->addVar(getFunction(), type);
- }
- breakNameIndexes[name] = index;
- if (expr) {
- breakExprIndexes[expr] = index;
- }
- return index;
- }
- if (expr) {
- breakExprIndexes[expr] = iter->second;
- }
- return iter->second;
- } else {
- assert(expr);
- auto iter = breakExprIndexes.find(expr);
- if (iter == breakExprIndexes.end()) {
- if (index == Index(-1)) {
- index = builder->addVar(getFunction(), type);
- }
- return breakExprIndexes[expr] = index;
- }
- return iter->second;
- }
- }
-
- // When we reach a fallthrough value, it has already been flattened, and its value
- // assigned to the proper local. Or, it may not have needed to be flattened,
- // and we can just assign to a local. This method simply returns the fallthrough
- // replacement code.
- Expression* getFallthroughReplacement(Expression* child, Index myIndex) {
- auto iter = breakExprIndexes.find(child);
- if (iter != breakExprIndexes.end()) {
- // it was flattened and saved to a local
- return builder->makeSequence(
- child, // which no longer flows a value, now it sets the child index
- builder->makeSetLocal(
- myIndex,
- builder->makeGetLocal(iter->second, getFunction()->getLocalType(iter->second))
- )
- );
- }
- // a simple expression
- if (child->type == unreachable) {
- // no need to even set the local
- return child;
- } else {
- assert(!ControlFlowChecker::is(child));
- return builder->makeSetLocal(
- myIndex,
- child
- );
- }
- }
-
- // flattening fallthroughs makes them have type none. this gets their true type
- WasmType getFallthroughType(Expression* child) {
- auto iter = breakExprIndexes.find(child);
- if (iter != breakExprIndexes.end()) {
- // it was flattened and saved to a local
- return getFunction()->getLocalType(iter->second);
- }
- assert(child->type != none);
- return child->type;
- }
-
- // Splitter helper
- struct Splitter {
- Splitter(FlattenControlFlow& parent, Expression* node) : parent(parent), node(node) {}
-
- ~Splitter() {
- finish();
- }
-
- FlattenControlFlow& parent;
- Expression* node;
-
- std::vector<Expression**> children; // TODO: reuse in parent, avoiding mallocing on each node
-
- void note(Expression*& child) {
- // we accept nullptr inputs, for a non-existing child
- if (!child) return;
- children.push_back(&child);
- }
-
- Expression* replacement; // the final replacement for the current node
- bool stop = false; // if a child is unreachable, we can stop
-
- void finish() {
- if (children.empty()) return;
- // first, scan the list
- bool hasControlFlowChild = false;
- bool hasUnreachableChild = false;
- for (auto** childp : children) {
- // it's enough to look at the child, ignoring the contents, as the contents
- // have already been processed before we got here, so they must have been
- // flattened if necessary.
- auto* child = *childp;
- if (ControlFlowChecker::is(child)) {
- hasControlFlowChild = true;
- }
- if (child->type == unreachable) {
- hasUnreachableChild = true;
- }
- }
- if (!hasControlFlowChild) {
- // nothing to do here.
- assert(!hasUnreachableChild); // all of them should be executed
- return;
- }
- // we have at least one child we need to split out, so to preserve the order of operations,
- // split them all out
- Builder* builder = parent.builder.get();
- std::vector<Index> tempIndexes;
- for (auto** childp : children) {
- auto* child = *childp;
- if (isConcreteWasmType(child->type)) {
- tempIndexes.push_back(builder->addVar(parent.getFunction(), child->type));
- } else {
- tempIndexes.push_back(-1);
- }
- }
- // create a new replacement block
- auto* block = builder->makeBlock();
- for (Index i = 0; i < children.size(); i++) {
- auto* child = *children[i];
- auto type = child->type;
- if (isConcreteWasmType(type)) {
- // set the child to a local, and use it later
- block->list.push_back(builder->makeSetLocal(tempIndexes[i], child));
- *children[i] = builder->makeGetLocal(tempIndexes[i], type);
- } else if (type == none) {
- // a nested none can not happen normally, here it occurs after we flattened a nested
- // we can use the local it already assigned to. TODO: don't even allocate one here
- block->list.push_back(child);
- assert(parent.breakExprIndexes.count(child) > 0);
- auto index = parent.breakExprIndexes[child];
- *children[i] = builder->makeGetLocal(
- index,
- parent.getFunction()->getLocalType(index)
- );
- } else if (type == unreachable) {
- block->list.push_back(child);
- break; // no need to push any more
- } else {
- WASM_UNREACHABLE();
- }
- }
- if (!hasUnreachableChild) {
- // we reached the end, so we need to emit the expression itself
- // (which has been modified to replace children usages with get_locals)
- block->list.push_back(node);
- }
- block->finalize();
- // finally, we just created a new block, ending in node. If node is e.g.
- // i32.add, then our block would return a value. so we must convert
- // this new block to return a value through a local
- parent.visitBlock(block);
- // the block is now done
- parent.replaceCurrent(block);
- // if the node was potentially a flowthrough value, then it has an entry
- // in breakExprIndexes, and since we are replacing it with this block,
- // we must note it's index as the same, so it is found by the parent.
- if (parent.breakExprIndexes.find(node) != parent.breakExprIndexes.end()) {
- parent.breakExprIndexes[block] = parent.breakExprIndexes[node];
- }
- }
- };
-
- void visitBlock(Block* curr) {
- if (isConcreteWasmType(curr->type)) {
- curr->list.back() = getFallthroughReplacement(curr->list.back(), getBreakTargetIndex(curr->name, curr->type, curr));
- curr->finalize();
- }
- }
- void visitLoop(Loop* curr) {
- if (isConcreteWasmType(curr->type)) {
- curr->body = getFallthroughReplacement(curr->body, getBreakTargetIndex(Name(), curr->type, curr));
- curr->finalize();
- }
- }
- void visitIf(If* curr) {
- if (isConcreteWasmType(curr->type)) {
- auto targetIndex = getBreakTargetIndex(Name(), curr->type, curr);
- curr->ifTrue = getFallthroughReplacement(curr->ifTrue, targetIndex);
- curr->ifFalse = getFallthroughReplacement(curr->ifFalse, targetIndex);
- curr->finalize();
- }
- Splitter splitter(*this, curr);
- splitter.note(curr->condition);
- }
- void visitBreak(Break* curr) {
- Expression* processed = curr;
- // first of all, get rid of the value if there is one
- if (curr->value) {
- if (curr->value->type != unreachable) {
- auto type = getFallthroughType(curr->value);
- auto index = getBreakTargetIndex(curr->name, type);
- auto* value = getFallthroughReplacement(curr->value, index);
- curr->value = nullptr;
- curr->finalize();
- processed = builder->makeSequence(
- value,
- curr
- );
- replaceCurrent(processed);
- if (curr->condition) {
- // we already called getBreakTargetIndex for the value we send to our
- // break target if we break. as this is a br_if with a value, it also
- // flows out that value, so our parent needs to know how to receive it.
- // we note the already-existing index we prepared before, for that value.
- getBreakTargetIndex(Name(), type, processed, index);
- }
- } else {
- // we have a value, but it has unreachable type. we can just replace
- // ourselves with it, we won't reach a condition (if there is one) or the br
- // itself
- replaceCurrent(curr->value);
- return;
- }
- }
- Splitter splitter(*this, processed);
- splitter.note(curr->condition);
- }
- void visitSwitch(Switch* curr) {
- Expression* processed = curr;
-
- // first of all, get rid of the value if there is one
- if (curr->value) {
- if (curr->value->type != unreachable) {
- auto type = getFallthroughType(curr->value);
- // we must assign the value to *all* the targets
- auto temp = builder->addVar(getFunction(), type);
- auto* value = getFallthroughReplacement(curr->value, temp);
- curr->value = nullptr;
- auto* block = builder->makeBlock();
- block->list.push_back(value);
- std::set<Name> names;
- for (auto target : curr->targets) {
- if (names.insert(target).second) {
- block->list.push_back(
- builder->makeSetLocal(
- getBreakTargetIndex(target, type),
- builder->makeGetLocal(temp, type)
- )
- );
- }
- }
- if (names.insert(curr->default_).second) {
- block->list.push_back(
- builder->makeSetLocal(
- getBreakTargetIndex(curr->default_, type),
- builder->makeGetLocal(temp, type)
- )
- );
- }
- block->list.push_back(curr);
- block->finalize();
- replaceCurrent(block);
- } else {
- // we have a value, but it has unreachable type. we can just replace
- // ourselves with it, we won't reach a condition (if there is one) or the br
- // itself
- replaceCurrent(curr->value);
- return;
- }
- }
- Splitter splitter(*this, processed);
- splitter.note(curr->value);
- splitter.note(curr->condition);
- }
- void visitCall(Call* curr) {
- Splitter splitter(*this, curr);
- for (auto*& operand : curr->operands) {
- splitter.note(operand);
- }
- }
- void visitCallImport(CallImport* curr) {
- Splitter splitter(*this, curr);
- for (auto*& operand : curr->operands) {
- splitter.note(operand);
- }
- }
- void visitCallIndirect(CallIndirect* curr) {
- Splitter splitter(*this, curr);
- for (auto*& operand : curr->operands) {
- splitter.note(operand);
- }
- splitter.note(curr->target);
- }
- void visitSetLocal(SetLocal* curr) {
- Splitter splitter(*this, curr);
- splitter.note(curr->value);
- }
- void visitSetGlobal(SetGlobal* curr) {
- Splitter splitter(*this, curr);
- splitter.note(curr->value);
- }
- void visitLoad(Load* curr) {
- Splitter splitter(*this, curr);
- splitter.note(curr->ptr);
- }
- void visitStore(Store* curr) {
- Splitter splitter(*this, curr);
- splitter.note(curr->ptr);
- splitter.note(curr->value);
- }
- void visitUnary(Unary* curr) {
- Splitter splitter(*this, curr);
- splitter.note(curr->value);
- }
- void visitBinary(Binary* curr) {
- Splitter splitter(*this, curr);
- splitter.note(curr->left);
- splitter.note(curr->right);
- }
- void visitSelect(Select* curr) {
- Splitter splitter(*this, curr);
- splitter.note(curr->ifTrue);
- splitter.note(curr->ifFalse);
- splitter.note(curr->condition);
- }
- void visitDrop(Drop* curr) {
- Splitter splitter(*this, curr);
- splitter.note(curr->value);
- }
- void visitReturn(Return* curr) {
- Splitter splitter(*this, curr);
- splitter.note(curr->value);
- }
- void visitHost(Host* curr) {
- Splitter splitter(*this, curr);
- for (auto*& operand : curr->operands) {
- splitter.note(operand);
- }
- }
-
- void visitFunction(Function* curr) {
- // removing breaks can alter types
- ReFinalize().walkFunctionInModule(curr, getModule());
- }
-};
-
-Pass *createFlattenControlFlowPass() {
- return new FlattenControlFlow();
-}
-
-} // namespace wasm
-
diff --git a/src/passes/ReReloop.cpp b/src/passes/ReReloop.cpp
index 2c212e628..7e202b002 100644
--- a/src/passes/ReReloop.cpp
+++ b/src/passes/ReReloop.cpp
@@ -18,7 +18,7 @@
// Convert the AST to a CFG, and optimize+convert it back to the AST
// using the relooper.
//
-// This pass depends on flatten-control-flow being run before it.
+// This pass depends on flatten being run before it.
//
#include <memory>
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 044a97833..4e105f71d 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -72,7 +72,7 @@ void PassRegistry::registerPasses() {
registerPass("dce", "removes unreachable code", createDeadCodeEliminationPass);
registerPass("duplicate-function-elimination", "removes duplicate functions", createDuplicateFunctionEliminationPass);
registerPass("extract-function", "leaves just one function (useful for debugging)", createExtractFunctionPass);
- registerPass("flatten-control-flow", "flattens out control flow to be only on blocks, not nested as expressions", createFlattenControlFlowPass);
+ registerPass("flatten", "flattens out code, removing nesting", createFlattenPass);
registerPass("inlining", "inlines functions", createInliningPass);
registerPass("inlining-optimizing", "inlines functions and optimizes where we inlined", createInliningOptimizingPass);
registerPass("legalize-js-interface", "legalizes i64 types on the import/export boundary", createLegalizeJSInterfacePass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 58a9e2b27..957ca2d68 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -22,55 +22,55 @@ namespace wasm {
class Pass;
// All passes:
-Pass *createCoalesceLocalsPass();
-Pass *createCoalesceLocalsWithLearningPass();
-Pass *createCodeFoldingPass();
-Pass *createCodePushingPass();
-Pass *createConstHoistingPass();
-Pass *createDeadCodeEliminationPass();
-Pass *createDuplicateFunctionEliminationPass();
-Pass *createExtractFunctionPass();
-Pass *createFlattenControlFlowPass();
-Pass *createFullPrinterPass();
-Pass *createI64ToI32LoweringPass();
-Pass *createInliningPass();
-Pass *createInliningOptimizingPass();
-Pass *createLegalizeJSInterfacePass();
-Pass *createLocalCSEPass();
-Pass *createLogExecutionPass();
-Pass *createInstrumentLocalsPass();
-Pass *createInstrumentMemoryPass();
-Pass *createMemoryPackingPass();
-Pass *createMergeBlocksPass();
-Pass *createMinifiedPrinterPass();
-Pass *createMetricsPass();
-Pass *createNameListPass();
-Pass *createOptimizeInstructionsPass();
-Pass *createPickLoadSignsPass();
-Pass *createPostEmscriptenPass();
-Pass *createPrecomputePass();
-Pass *createPrecomputePropagatePass();
-Pass *createPrinterPass();
-Pass *createPrintCallGraphPass();
-Pass *createRelooperJumpThreadingPass();
-Pass *createRemoveImportsPass();
-Pass *createRemoveMemoryPass();
-Pass *createRemoveUnusedBrsPass();
-Pass *createRemoveUnusedModuleElementsPass();
-Pass *createRemoveUnusedNamesPass();
-Pass *createReorderFunctionsPass();
-Pass *createReorderLocalsPass();
-Pass *createReReloopPass();
-Pass *createSafeHeapPass();
-Pass *createSimplifyLocalsPass();
-Pass *createSimplifyLocalsNoTeePass();
-Pass *createSimplifyLocalsNoStructurePass();
-Pass *createSimplifyLocalsNoTeeNoStructurePass();
-Pass *createSSAifyPass();
-Pass *createTrapModeClamp();
-Pass *createTrapModeJS();
-Pass *createUnteePass();
-Pass *createVacuumPass();
+Pass* createCoalesceLocalsPass();
+Pass* createCoalesceLocalsWithLearningPass();
+Pass* createCodeFoldingPass();
+Pass* createCodePushingPass();
+Pass* createConstHoistingPass();
+Pass* createDeadCodeEliminationPass();
+Pass* createDuplicateFunctionEliminationPass();
+Pass* createExtractFunctionPass();
+Pass* createFlattenPass();
+Pass* createFullPrinterPass();
+Pass* createI64ToI32LoweringPass();
+Pass* createInliningPass();
+Pass* createInliningOptimizingPass();
+Pass* createLegalizeJSInterfacePass();
+Pass* createLocalCSEPass();
+Pass* createLogExecutionPass();
+Pass* createInstrumentLocalsPass();
+Pass* createInstrumentMemoryPass();
+Pass* createMemoryPackingPass();
+Pass* createMergeBlocksPass();
+Pass* createMinifiedPrinterPass();
+Pass* createMetricsPass();
+Pass* createNameListPass();
+Pass* createOptimizeInstructionsPass();
+Pass* createPickLoadSignsPass();
+Pass* createPostEmscriptenPass();
+Pass* createPrecomputePass();
+Pass* createPrecomputePropagatePass();
+Pass* createPrinterPass();
+Pass* createPrintCallGraphPass();
+Pass* createRelooperJumpThreadingPass();
+Pass* createRemoveImportsPass();
+Pass* createRemoveMemoryPass();
+Pass* createRemoveUnusedBrsPass();
+Pass* createRemoveUnusedModuleElementsPass();
+Pass* createRemoveUnusedNamesPass();
+Pass* createReorderFunctionsPass();
+Pass* createReorderLocalsPass();
+Pass* createReReloopPass();
+Pass* createSafeHeapPass();
+Pass* createSimplifyLocalsPass();
+Pass* createSimplifyLocalsNoTeePass();
+Pass* createSimplifyLocalsNoStructurePass();
+Pass* createSimplifyLocalsNoTeeNoStructurePass();
+Pass* createSSAifyPass();
+Pass* createTrapModeClamp();
+Pass* createTrapModeJS();
+Pass* createUnteePass();
+Pass* createVacuumPass();
}
diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h
index 44bd6ed3d..8384c6a6f 100644
--- a/src/wasm-traversal.h
+++ b/src/wasm-traversal.h
@@ -613,6 +613,12 @@ struct ExpressionStackWalker : public PostWalker<SubType, VisitorType> {
}
}
+ Expression* getParent() {
+ if (expressionStack.size() == 1) return nullptr;
+ assert(expressionStack.size() >= 2);
+ return expressionStack[expressionStack.size() - 2];
+ }
+
static void doPreVisit(SubType* self, Expression** currp) {
self->expressionStack.push_back(*currp);
}
diff --git a/src/wasm2asm.h b/src/wasm2asm.h
index 7be5c72f6..5d0058f26 100644
--- a/src/wasm2asm.h
+++ b/src/wasm2asm.h
@@ -386,7 +386,7 @@ Ref Wasm2AsmBuilder::processWasm(Module* wasm) {
PassRunner runner(wasm);
runner.add<AutoDrop>();
runner.add("i64-to-i32-lowering");
- runner.add("flatten-control-flow");
+ runner.add("flatten");
runner.add("vacuum");
runner.setDebug(flags.debug);
runner.run();