diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/passes/Flatten.cpp | 337 | ||||
-rw-r--r-- | src/passes/FlattenControlFlow.cpp | 476 | ||||
-rw-r--r-- | src/passes/ReReloop.cpp | 2 | ||||
-rw-r--r-- | src/passes/pass.cpp | 2 | ||||
-rw-r--r-- | src/passes/passes.h | 98 | ||||
-rw-r--r-- | src/wasm-traversal.h | 6 | ||||
-rw-r--r-- | src/wasm2asm.h | 2 |
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(); |