summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/asm2wasm.h7
-rw-r--r--src/ast/block-utils.h5
-rw-r--r--src/ast/branch-utils.h75
-rw-r--r--src/ast/effects.h215
-rw-r--r--src/ast_utils.h243
-rw-r--r--src/passes/CodePushing.cpp2
-rw-r--r--src/passes/DeadCodeElimination.cpp4
-rw-r--r--src/passes/LocalCSE.cpp2
-rw-r--r--src/passes/MergeBlocks.cpp3
-rw-r--r--src/passes/OptimizeInstructions.cpp3
-rw-r--r--src/passes/RemoveUnusedBrs.cpp6
-rw-r--r--src/passes/SimplifyLocals.cpp2
-rw-r--r--src/passes/Vacuum.cpp9
-rw-r--r--src/wasm-binary.h1
-rw-r--r--src/wasm/wasm-binary.cpp7
-rw-r--r--src/wasm/wasm-s-parser.cpp4
-rw-r--r--src/wasm/wasm.cpp4
-rw-r--r--test/passes/vacuum.txt14
-rw-r--r--test/passes/vacuum.wast14
19 files changed, 352 insertions, 268 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 4395054f6..b71561d4d 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -32,6 +32,7 @@
#include "pass.h"
#include "parsing.h"
#include "ast_utils.h"
+#include "ast/branch-utils.h"
#include "wasm-builder.h"
#include "wasm-emscripten.h"
#include "wasm-module-building.h"
@@ -2335,9 +2336,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
nameMapper.popLabelName(more);
nameMapper.popLabelName(stop);
// if we never continued, we don't need a loop
- BreakSeeker breakSeeker(more);
- breakSeeker.walk(child);
- if (breakSeeker.found == 0) {
+ BranchUtils::BranchSeeker seeker(more);
+ seeker.walk(child);
+ if (seeker.found == 0) {
auto block = allocator.alloc<Block>();
block->list.push_back(child);
if (isConcreteWasmType(child->type)) {
diff --git a/src/ast/block-utils.h b/src/ast/block-utils.h
index 7b1b9dcff..56c11f240 100644
--- a/src/ast/block-utils.h
+++ b/src/ast/block-utils.h
@@ -19,7 +19,8 @@
#include "literal.h"
#include "wasm.h"
-#include "ast_utils.h"
+#include "ast/branch-utils.h"
+#include "ast/effects.h"
namespace wasm {
@@ -29,7 +30,7 @@ namespace BlockUtils {
template<typename T>
inline Expression* simplifyToContents(Block* block, T* parent, bool allowTypeChange = false) {
auto& list = block->list;
- if (list.size() == 1 && !BreakSeeker::has(list[0], block->name)) {
+ if (list.size() == 1 && !BranchUtils::BranchSeeker::hasNamed(list[0], block->name)) {
// just one element. try to replace the block
auto* singleton = list[0];
auto sideEffects = EffectAnalyzer(parent->getPassOptions(), singleton).hasSideEffects();
diff --git a/src/ast/branch-utils.h b/src/ast/branch-utils.h
index 853b998af..77ec70753 100644
--- a/src/ast/branch-utils.h
+++ b/src/ast/branch-utils.h
@@ -100,6 +100,81 @@ inline std::set<Name> getBranchTargets(Expression* ast) {
return scanner.targets;
}
+// Finds if there are branches targeting a name. Note that since names are
+// unique in our IR, we just need to look for the name, and do not need
+// to analyze scoping.
+// By default we ignore untaken branches. You can set named to
+// note those as well, then any named branch is noted, even if untaken
+struct BranchSeeker : public PostWalker<BranchSeeker> {
+ Name target;
+ bool named = false;
+
+ Index found;
+ WasmType valueType;
+
+ BranchSeeker(Name target) : target(target), found(0) {}
+
+ void noteFound(Expression* value) {
+ found++;
+ if (found == 1) valueType = unreachable;
+ if (!value) valueType = none;
+ else if (value->type != unreachable) valueType = value->type;
+ }
+
+ void visitBreak(Break *curr) {
+ if (!named) {
+ // ignore an unreachable break
+ if (curr->condition && curr->condition->type == unreachable) return;
+ if (curr->value && curr->value->type == unreachable) return;
+ }
+ // check the break
+ if (curr->name == target) noteFound(curr->value);
+ }
+
+ void visitSwitch(Switch *curr) {
+ if (!named) {
+ // ignore an unreachable switch
+ if (curr->condition->type == unreachable) return;
+ if (curr->value && curr->value->type == unreachable) return;
+ }
+ // check the switch
+ for (auto name : curr->targets) {
+ if (name == target) noteFound(curr->value);
+ }
+ if (curr->default_ == target) noteFound(curr->value);
+ }
+
+ static bool has(Expression* tree, Name target) {
+ if (!target.is()) return false;
+ BranchSeeker seeker(target);
+ seeker.walk(tree);
+ return seeker.found > 0;
+ }
+
+ static Index count(Expression* tree, Name target) {
+ if (!target.is()) return 0;
+ BranchSeeker seeker(target);
+ seeker.walk(tree);
+ return seeker.found;
+ }
+
+ static bool hasNamed(Expression* tree, Name target) {
+ if (!target.is()) return false;
+ BranchSeeker seeker(target);
+ seeker.named = true;
+ seeker.walk(tree);
+ return seeker.found > 0;
+ }
+
+ static Index countNamed(Expression* tree, Name target) {
+ if (!target.is()) return 0;
+ BranchSeeker seeker(target);
+ seeker.named = true;
+ seeker.walk(tree);
+ return seeker.found;
+ }
+};
+
} // namespace BranchUtils
} // namespace wasm
diff --git a/src/ast/effects.h b/src/ast/effects.h
new file mode 100644
index 000000000..a7a5d8fb2
--- /dev/null
+++ b/src/ast/effects.h
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+#ifndef wasm_ast_effects_h
+#define wasm_ast_effects_h
+
+namespace wasm {
+
+// Look for side effects, including control flow
+// TODO: optimize
+
+struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
+ EffectAnalyzer(PassOptions& passOptions, Expression *ast = nullptr) {
+ ignoreImplicitTraps = passOptions.ignoreImplicitTraps;
+ debugInfo = passOptions.debugInfo;
+ if (ast) analyze(ast);
+ }
+
+ bool ignoreImplicitTraps;
+ bool debugInfo;
+
+ void analyze(Expression *ast) {
+ breakNames.clear();
+ walk(ast);
+ // if we are left with breaks, they are external
+ if (breakNames.size() > 0) branches = true;
+ }
+
+ bool branches = false; // branches out of this expression
+ bool calls = false;
+ std::set<Index> localsRead;
+ std::set<Index> localsWritten;
+ std::set<Name> globalsRead;
+ std::set<Name> globalsWritten;
+ bool readsMemory = false;
+ bool writesMemory = false;
+ bool implicitTrap = false; // a load or div/rem, which may trap. we ignore trap
+ // differences, so it is ok to reorder these, and we
+ // also allow reordering them with other effects
+ // (so a trap may occur later or earlier, if it is
+ // going to occur anyhow), but we can't remove them,
+ // they count as side effects
+
+ bool accessesLocal() { return localsRead.size() + localsWritten.size() > 0; }
+ bool accessesGlobal() { return globalsRead.size() + globalsWritten.size() > 0; }
+ bool accessesMemory() { return calls || readsMemory || writesMemory; }
+ bool hasSideEffects() { return calls || localsWritten.size() > 0 || writesMemory || branches || globalsWritten.size() > 0 || implicitTrap; }
+ bool hasAnything() { return branches || calls || accessesLocal() || readsMemory || writesMemory || accessesGlobal() || implicitTrap; }
+
+ // checks if these effects would invalidate another set (e.g., if we write, we invalidate someone that reads, they can't be moved past us)
+ bool invalidates(EffectAnalyzer& other) {
+ if (branches || other.branches
+ || ((writesMemory || calls) && other.accessesMemory())
+ || (accessesMemory() && (other.writesMemory || other.calls))) {
+ return true;
+ }
+ for (auto local : localsWritten) {
+ if (other.localsWritten.count(local) || other.localsRead.count(local)) {
+ return true;
+ }
+ }
+ for (auto local : localsRead) {
+ if (other.localsWritten.count(local)) return true;
+ }
+ if ((accessesGlobal() && other.calls) || (other.accessesGlobal() && calls)) {
+ return true;
+ }
+ for (auto global : globalsWritten) {
+ if (other.globalsWritten.count(global) || other.globalsRead.count(global)) {
+ return true;
+ }
+ }
+ for (auto global : globalsRead) {
+ if (other.globalsWritten.count(global)) return true;
+ }
+ // we are ok to reorder implicit traps, but not conditionalize them
+ if ((implicitTrap && other.branches) || (other.implicitTrap && branches)) {
+ return true;
+ }
+ return false;
+ }
+
+ void mergeIn(EffectAnalyzer& other) {
+ branches = branches || other.branches;
+ calls = calls || other.calls;
+ readsMemory = readsMemory || other.readsMemory;
+ writesMemory = writesMemory || other.writesMemory;
+ for (auto i : other.localsRead) localsRead.insert(i);
+ for (auto i : other.localsWritten) localsWritten.insert(i);
+ for (auto i : other.globalsRead) globalsRead.insert(i);
+ for (auto i : other.globalsWritten) globalsWritten.insert(i);
+ }
+
+ // the checks above happen after the node's children were processed, in the order of execution
+ // we must also check for control flow that happens before the children, i.e., loops
+ bool checkPre(Expression* curr) {
+ if (curr->is<Loop>()) {
+ branches = true;
+ return true;
+ }
+ return false;
+ }
+
+ bool checkPost(Expression* curr) {
+ visit(curr);
+ if (curr->is<Loop>()) {
+ branches = true;
+ }
+ return hasAnything();
+ }
+
+ std::set<Name> breakNames;
+
+ void visitBreak(Break *curr) {
+ breakNames.insert(curr->name);
+ }
+ void visitSwitch(Switch *curr) {
+ for (auto name : curr->targets) {
+ breakNames.insert(name);
+ }
+ breakNames.insert(curr->default_);
+ }
+ void visitBlock(Block* curr) {
+ if (curr->name.is()) breakNames.erase(curr->name); // these were internal breaks
+ }
+ void visitLoop(Loop* curr) {
+ if (curr->name.is()) breakNames.erase(curr->name); // these were internal breaks
+ }
+
+ void visitCall(Call *curr) { calls = true; }
+ void visitCallImport(CallImport *curr) {
+ calls = true;
+ if (debugInfo) {
+ // debugInfo call imports must be preserved very strongly, do not
+ // move code around them
+ branches = true; // !
+ }
+ }
+ void visitCallIndirect(CallIndirect *curr) { calls = true; }
+ void visitGetLocal(GetLocal *curr) {
+ localsRead.insert(curr->index);
+ }
+ void visitSetLocal(SetLocal *curr) {
+ localsWritten.insert(curr->index);
+ }
+ void visitGetGlobal(GetGlobal *curr) {
+ globalsRead.insert(curr->name);
+ }
+ void visitSetGlobal(SetGlobal *curr) {
+ globalsWritten.insert(curr->name);
+ }
+ void visitLoad(Load *curr) {
+ readsMemory = true;
+ if (!ignoreImplicitTraps) implicitTrap = true;
+ }
+ void visitStore(Store *curr) {
+ writesMemory = true;
+ if (!ignoreImplicitTraps) implicitTrap = true;
+ }
+ void visitUnary(Unary *curr) {
+ if (!ignoreImplicitTraps) {
+ switch (curr->op) {
+ case TruncSFloat32ToInt32:
+ case TruncSFloat32ToInt64:
+ case TruncUFloat32ToInt32:
+ case TruncUFloat32ToInt64:
+ case TruncSFloat64ToInt32:
+ case TruncSFloat64ToInt64:
+ case TruncUFloat64ToInt32:
+ case TruncUFloat64ToInt64: {
+ implicitTrap = true;
+ }
+ default: {}
+ }
+ }
+ }
+ void visitBinary(Binary *curr) {
+ if (!ignoreImplicitTraps) {
+ switch (curr->op) {
+ case DivSInt32:
+ case DivUInt32:
+ case RemSInt32:
+ case RemUInt32:
+ case DivSInt64:
+ case DivUInt64:
+ case RemSInt64:
+ case RemUInt64: {
+ implicitTrap = true;
+ }
+ default: {}
+ }
+ }
+ }
+ void visitReturn(Return *curr) { branches = true; }
+ void visitHost(Host *curr) { calls = true; }
+ void visitUnreachable(Unreachable *curr) { branches = true; }
+};
+
+} // namespace wasm
+
+#endif // wasm_ast_effects_h
+
diff --git a/src/ast_utils.h b/src/ast_utils.h
index 83818daa8..253da8050 100644
--- a/src/ast_utils.h
+++ b/src/ast_utils.h
@@ -25,247 +25,6 @@
namespace wasm {
-// Finds if there are breaks targeting a name. Note that since names are
-// unique in our IR, we just need to look for the name, and do not need
-// to analyze scoping.
-struct BreakSeeker : public PostWalker<BreakSeeker> {
- Name target;
- Index found;
- WasmType valueType;
-
- BreakSeeker(Name target) : target(target), found(0) {}
-
- void noteFound(Expression* value) {
- found++;
- if (found == 1) valueType = unreachable;
- if (!value) valueType = none;
- else if (value->type != unreachable) valueType = value->type;
- }
-
- void visitBreak(Break *curr) {
- // ignore an unreachable break
- if (curr->condition && curr->condition->type == unreachable) return;
- if (curr->value && curr->value->type == unreachable) return;
- // check the break
- if (curr->name == target) noteFound(curr->value);
- }
-
- void visitSwitch(Switch *curr) {
- // ignore an unreachable switch
- if (curr->condition->type == unreachable) return;
- if (curr->value && curr->value->type == unreachable) return;
- // check the switch
- for (auto name : curr->targets) {
- if (name == target) noteFound(curr->value);
- }
- if (curr->default_ == target) noteFound(curr->value);
- }
-
- static bool has(Expression* tree, Name target) {
- if (!target.is()) return false;
- BreakSeeker breakSeeker(target);
- breakSeeker.walk(tree);
- return breakSeeker.found > 0;
- }
-
- static Index count(Expression* tree, Name target) {
- if (!target.is()) return 0;
- BreakSeeker breakSeeker(target);
- breakSeeker.walk(tree);
- return breakSeeker.found;
- }
-};
-
-// Look for side effects, including control flow
-// TODO: optimize
-
-struct EffectAnalyzer : public PostWalker<EffectAnalyzer> {
- EffectAnalyzer(PassOptions& passOptions, Expression *ast = nullptr) {
- ignoreImplicitTraps = passOptions.ignoreImplicitTraps;
- debugInfo = passOptions.debugInfo;
- if (ast) analyze(ast);
- }
-
- bool ignoreImplicitTraps;
- bool debugInfo;
-
- void analyze(Expression *ast) {
- breakNames.clear();
- walk(ast);
- // if we are left with breaks, they are external
- if (breakNames.size() > 0) branches = true;
- }
-
- bool branches = false; // branches out of this expression
- bool calls = false;
- std::set<Index> localsRead;
- std::set<Index> localsWritten;
- std::set<Name> globalsRead;
- std::set<Name> globalsWritten;
- bool readsMemory = false;
- bool writesMemory = false;
- bool implicitTrap = false; // a load or div/rem, which may trap. we ignore trap
- // differences, so it is ok to reorder these, and we
- // also allow reordering them with other effects
- // (so a trap may occur later or earlier, if it is
- // going to occur anyhow), but we can't remove them,
- // they count as side effects
-
- bool accessesLocal() { return localsRead.size() + localsWritten.size() > 0; }
- bool accessesGlobal() { return globalsRead.size() + globalsWritten.size() > 0; }
- bool accessesMemory() { return calls || readsMemory || writesMemory; }
- bool hasSideEffects() { return calls || localsWritten.size() > 0 || writesMemory || branches || globalsWritten.size() > 0 || implicitTrap; }
- bool hasAnything() { return branches || calls || accessesLocal() || readsMemory || writesMemory || accessesGlobal() || implicitTrap; }
-
- // checks if these effects would invalidate another set (e.g., if we write, we invalidate someone that reads, they can't be moved past us)
- bool invalidates(EffectAnalyzer& other) {
- if (branches || other.branches
- || ((writesMemory || calls) && other.accessesMemory())
- || (accessesMemory() && (other.writesMemory || other.calls))) {
- return true;
- }
- for (auto local : localsWritten) {
- if (other.localsWritten.count(local) || other.localsRead.count(local)) {
- return true;
- }
- }
- for (auto local : localsRead) {
- if (other.localsWritten.count(local)) return true;
- }
- if ((accessesGlobal() && other.calls) || (other.accessesGlobal() && calls)) {
- return true;
- }
- for (auto global : globalsWritten) {
- if (other.globalsWritten.count(global) || other.globalsRead.count(global)) {
- return true;
- }
- }
- for (auto global : globalsRead) {
- if (other.globalsWritten.count(global)) return true;
- }
- // we are ok to reorder implicit traps, but not conditionalize them
- if ((implicitTrap && other.branches) || (other.implicitTrap && branches)) {
- return true;
- }
- return false;
- }
-
- void mergeIn(EffectAnalyzer& other) {
- branches = branches || other.branches;
- calls = calls || other.calls;
- readsMemory = readsMemory || other.readsMemory;
- writesMemory = writesMemory || other.writesMemory;
- for (auto i : other.localsRead) localsRead.insert(i);
- for (auto i : other.localsWritten) localsWritten.insert(i);
- for (auto i : other.globalsRead) globalsRead.insert(i);
- for (auto i : other.globalsWritten) globalsWritten.insert(i);
- }
-
- // the checks above happen after the node's children were processed, in the order of execution
- // we must also check for control flow that happens before the children, i.e., loops
- bool checkPre(Expression* curr) {
- if (curr->is<Loop>()) {
- branches = true;
- return true;
- }
- return false;
- }
-
- bool checkPost(Expression* curr) {
- visit(curr);
- if (curr->is<Loop>()) {
- branches = true;
- }
- return hasAnything();
- }
-
- std::set<Name> breakNames;
-
- void visitBreak(Break *curr) {
- breakNames.insert(curr->name);
- }
- void visitSwitch(Switch *curr) {
- for (auto name : curr->targets) {
- breakNames.insert(name);
- }
- breakNames.insert(curr->default_);
- }
- void visitBlock(Block* curr) {
- if (curr->name.is()) breakNames.erase(curr->name); // these were internal breaks
- }
- void visitLoop(Loop* curr) {
- if (curr->name.is()) breakNames.erase(curr->name); // these were internal breaks
- }
-
- void visitCall(Call *curr) { calls = true; }
- void visitCallImport(CallImport *curr) {
- calls = true;
- if (debugInfo) {
- // debugInfo call imports must be preserved very strongly, do not
- // move code around them
- branches = true; // !
- }
- }
- void visitCallIndirect(CallIndirect *curr) { calls = true; }
- void visitGetLocal(GetLocal *curr) {
- localsRead.insert(curr->index);
- }
- void visitSetLocal(SetLocal *curr) {
- localsWritten.insert(curr->index);
- }
- void visitGetGlobal(GetGlobal *curr) {
- globalsRead.insert(curr->name);
- }
- void visitSetGlobal(SetGlobal *curr) {
- globalsWritten.insert(curr->name);
- }
- void visitLoad(Load *curr) {
- readsMemory = true;
- if (!ignoreImplicitTraps) implicitTrap = true;
- }
- void visitStore(Store *curr) {
- writesMemory = true;
- if (!ignoreImplicitTraps) implicitTrap = true;
- }
- void visitUnary(Unary *curr) {
- if (!ignoreImplicitTraps) {
- switch (curr->op) {
- case TruncSFloat32ToInt32:
- case TruncSFloat32ToInt64:
- case TruncUFloat32ToInt32:
- case TruncUFloat32ToInt64:
- case TruncSFloat64ToInt32:
- case TruncSFloat64ToInt64:
- case TruncUFloat64ToInt32:
- case TruncUFloat64ToInt64: {
- implicitTrap = true;
- }
- default: {}
- }
- }
- }
- void visitBinary(Binary *curr) {
- if (!ignoreImplicitTraps) {
- switch (curr->op) {
- case DivSInt32:
- case DivUInt32:
- case RemSInt32:
- case RemUInt32:
- case DivSInt64:
- case DivUInt64:
- case RemSInt64:
- case RemUInt64: {
- implicitTrap = true;
- }
- default: {}
- }
- }
- }
- void visitReturn(Return *curr) { branches = true; }
- void visitHost(Host *curr) { calls = true; }
- void visitUnreachable(Unreachable *curr) { branches = true; }
-};
-
// Measure the size of an AST
struct Measurer : public PostWalker<Measurer, UnifiedExpressionVisitor<Measurer>> {
@@ -303,7 +62,7 @@ struct ExpressionAnalyzer {
if (auto* br = curr->dynCast<Break>()) {
if (!br->condition) return true;
} else if (auto* block = curr->dynCast<Block>()) {
- if (block->list.size() > 0 && obviouslyDoesNotFlowOut(block->list.back()) && !BreakSeeker::has(block, block->name)) return true;
+ if (block->list.size() > 0 && obviouslyDoesNotFlowOut(block->list.back()) && !BranchUtils::BranchSeeker::has(block, block->name)) return true;
}
return false;
}
diff --git a/src/passes/CodePushing.cpp b/src/passes/CodePushing.cpp
index 2876228ee..e73794f5a 100644
--- a/src/passes/CodePushing.cpp
+++ b/src/passes/CodePushing.cpp
@@ -21,8 +21,8 @@
#include <wasm.h>
#include <pass.h>
-#include <ast_utils.h>
#include <wasm-builder.h>
+#include <ast/effects.h>
namespace wasm {
diff --git a/src/passes/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp
index 209180e23..321bc0f9a 100644
--- a/src/passes/DeadCodeElimination.cpp
+++ b/src/passes/DeadCodeElimination.cpp
@@ -30,9 +30,9 @@
#include <wasm.h>
#include <pass.h>
-#include <ast_utils.h>
#include <wasm-builder.h>
#include <ast/block-utils.h>
+#include <ast/branch-utils.h>
#include <ast/type-updating.h>
namespace wasm {
@@ -184,7 +184,7 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
if (curr->name.is()) {
reachableBreaks.erase(curr->name);
}
- if (isUnreachable(curr->body) && !BreakSeeker::has(curr->body, curr->name)) {
+ if (isUnreachable(curr->body) && !BranchUtils::BranchSeeker::hasNamed(curr->body, curr->name)) {
replaceCurrent(curr->body);
return;
}
diff --git a/src/passes/LocalCSE.cpp b/src/passes/LocalCSE.cpp
index 6d48a1107..a3e0dff76 100644
--- a/src/passes/LocalCSE.cpp
+++ b/src/passes/LocalCSE.cpp
@@ -29,7 +29,7 @@
#include <wasm-builder.h>
#include <wasm-traversal.h>
#include <pass.h>
-#include <ast_utils.h>
+#include <ast/effects.h>
#include <ast/hashed.h>
namespace wasm {
diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp
index 1ec8843d6..8ffbb345d 100644
--- a/src/passes/MergeBlocks.cpp
+++ b/src/passes/MergeBlocks.cpp
@@ -63,8 +63,9 @@
#include <wasm.h>
#include <pass.h>
-#include <ast_utils.h>
#include <wasm-builder.h>
+#include <ast_utils.h>
+#include <ast/effects.h>
namespace wasm {
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 0e7c34564..d1b419a06 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -26,8 +26,9 @@
#include <support/threads.h>
#include <ast_utils.h>
#include <ast/cost.h>
-#include <ast/properties.h>
+#include <ast/effects.h>
#include <ast/manipulation.h>
+#include <ast/properties.h>
namespace wasm {
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp
index 1fa996ae6..572105187 100644
--- a/src/passes/RemoveUnusedBrs.cpp
+++ b/src/passes/RemoveUnusedBrs.cpp
@@ -21,6 +21,8 @@
#include <wasm.h>
#include <pass.h>
#include <ast_utils.h>
+#include <ast/branch-utils.h>
+#include <ast/effects.h>
#include <wasm-builder.h>
namespace wasm {
@@ -257,7 +259,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
// so only do it if it looks useful, which it definitely is if
// (a) $somewhere is straight out (so the br out vanishes), and
// (b) this br_if is the only branch to that block (so the block will vanish)
- if (brIf->name == block->name && BreakSeeker::count(block, block->name) == 1) {
+ if (brIf->name == block->name && BranchUtils::BranchSeeker::count(block, block->name) == 1) {
// note that we could drop the last element here, it is a br we know for sure is removable,
// but telling stealSlice to steal all to the end is more efficient, it can just truncate.
list[i] = builder.makeIf(brIf->condition, builder.makeBreak(brIf->name), builder.stealSlice(block, i + 1, list.size()));
@@ -447,7 +449,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
auto* br = list[0]->dynCast<Break>();
if (br && br->condition && br->name == curr->name) {
assert(!br->value); // can't, it would be dropped or last in the block
- if (BreakSeeker::count(curr, curr->name) == 1) {
+ if (BranchUtils::BranchSeeker::count(curr, curr->name) == 1) {
// no other breaks to that name, so we can do this
Builder builder(*getModule());
replaceCurrent(builder.makeIf(
diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp
index 4c896dbba..2d4a02337 100644
--- a/src/passes/SimplifyLocals.cpp
+++ b/src/passes/SimplifyLocals.cpp
@@ -45,8 +45,8 @@
#include <wasm-builder.h>
#include <wasm-traversal.h>
#include <pass.h>
-#include <ast_utils.h>
#include <ast/count.h>
+#include <ast/effects.h>
#include <ast/manipulation.h>
namespace wasm {
diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp
index 64c7f276a..4adfc1289 100644
--- a/src/passes/Vacuum.cpp
+++ b/src/passes/Vacuum.cpp
@@ -20,9 +20,9 @@
#include <wasm.h>
#include <pass.h>
-#include <ast_utils.h>
#include <wasm-builder.h>
#include <ast/block-utils.h>
+#include <ast/effects.h>
#include <ast/type-updating.h>
namespace wasm {
@@ -272,10 +272,11 @@ struct Vacuum : public WalkerPass<PostWalker<Vacuum>> {
// we may be able to remove this, if there are no brs
bool canPop = true;
if (block->name.is()) {
- BreakSeeker breakSeeker(block->name);
+ BranchUtils::BranchSeeker seeker(block->name);
+ seeker.named = true;
Expression* temp = block;
- breakSeeker.walk(temp);
- if (breakSeeker.found && breakSeeker.valueType != none) {
+ seeker.walk(temp);
+ if (seeker.found && seeker.valueType != none) {
canPop = false;
}
}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 6b984642c..ca3d69989 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -30,7 +30,6 @@
#include "asmjs/shared-constants.h"
#include "asm_v_wasm.h"
#include "wasm-builder.h"
-#include "ast_utils.h"
#include "parsing.h"
#include "wasm-validator.h"
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index efbd82e8b..2df7f2e26 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#include "wasm-binary.h"
-
#include <fstream>
+
#include "support/bits.h"
+#include "wasm-binary.h"
+#include "ast/branch-utils.h"
namespace wasm {
@@ -544,7 +545,7 @@ void WasmBinaryWriter::recurse(Expression*& curr) {
}
static bool brokenTo(Block* block) {
- return block->name.is() && BreakSeeker::has(block, block->name);
+ return block->name.is() && BranchUtils::BranchSeeker::has(block, block->name);
}
void WasmBinaryWriter::visitBlock(Block *curr) {
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 2fede28fc..e991f71de 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -22,7 +22,7 @@
#include "asm_v_wasm.h"
#include "asmjs/shared-constants.h"
-#include "ast_utils.h"
+#include "ast/branch-utils.h"
#include "shared-constants.h"
#include "wasm-binary.h"
#include "wasm-builder.h"
@@ -1262,7 +1262,7 @@ Expression* SExpressionWasmBuilder::makeIf(Element& s) {
ret->finalize(type);
nameMapper.popLabelName(label);
// create a break target if we must
- if (BreakSeeker::has(ret, label)) {
+ if (BranchUtils::BranchSeeker::hasNamed(ret, label)) {
auto* block = allocator.alloc<Block>();
block->name = label;
block->list.push_back(ret);
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index d331ba3a2..ec53ab25a 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -16,7 +16,7 @@
#include "wasm.h"
#include "wasm-traversal.h"
-#include "ast_utils.h"
+#include "ast/branch-utils.h"
namespace wasm {
@@ -175,7 +175,7 @@ static void handleUnreachable(Block* block) {
for (auto* child : block->list) {
if (child->type == unreachable) {
// there is an unreachable child, so we are unreachable, unless we have a break
- BreakSeeker seeker(block->name);
+ BranchUtils::BranchSeeker seeker(block->name);
Expression* expr = block;
seeker.walk(expr);
if (!seeker.found) {
diff --git a/test/passes/vacuum.txt b/test/passes/vacuum.txt
index ec6e3ad44..30474280e 100644
--- a/test/passes/vacuum.txt
+++ b/test/passes/vacuum.txt
@@ -5,6 +5,7 @@
(type $3 (func (result i32)))
(type $4 (func (param i32 f64 i32 i32)))
(type $FUNCSIG$i (func (result i32)))
+ (type $6 (func (result f64)))
(import "env" "int" (func $int (result i32)))
(global $Int i32 (i32.const 0))
(memory $0 256 256)
@@ -221,4 +222,17 @@
)
)
)
+ (func $leave-block-even-if-br-not-taken (type $6) (result f64)
+ (block $label$0
+ (f64.store align=1
+ (i32.const 879179022)
+ (br_if $label$0
+ (loop $label$9
+ (br $label$9)
+ )
+ (i32.const 677803374)
+ )
+ )
+ )
+ )
)
diff --git a/test/passes/vacuum.wast b/test/passes/vacuum.wast
index 20bbfe625..594554846 100644
--- a/test/passes/vacuum.wast
+++ b/test/passes/vacuum.wast
@@ -520,4 +520,18 @@
)
)
)
+ (func $leave-block-even-if-br-not-taken (result f64)
+ (block $label$0 (result f64)
+ (f64.store align=1
+ (i32.const 879179022)
+ (br_if $label$0
+ (loop $label$9
+ (br $label$9)
+ )
+ (i32.const 677803374)
+ )
+ )
+ (f64.const 2097914503796645752267195e31)
+ )
+ )
)