summaryrefslogtreecommitdiff
path: root/src/passes/MergeBlocks.cpp
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2016-10-29 10:45:38 -0700
committerAlon Zakai <alonzakai@gmail.com>2016-10-29 11:01:53 -0700
commitc7cb56099d6a3b8b1dfb540cc64f6572c1101ed4 (patch)
tree9842517a87add9d70fc6289330420274452d3e22 /src/passes/MergeBlocks.cpp
parent1cdee58fed2479997c1e386236879be87036d60c (diff)
downloadbinaryen-c7cb56099d6a3b8b1dfb540cc64f6572c1101ed4.tar.gz
binaryen-c7cb56099d6a3b8b1dfb540cc64f6572c1101ed4.tar.bz2
binaryen-c7cb56099d6a3b8b1dfb540cc64f6572c1101ed4.zip
handle the case of a br_if whose value is used in merge-blocks
Diffstat (limited to 'src/passes/MergeBlocks.cpp')
-rw-r--r--src/passes/MergeBlocks.cpp52
1 files changed, 38 insertions, 14 deletions
diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp
index bde9397a8..f7fac9594 100644
--- a/src/passes/MergeBlocks.cpp
+++ b/src/passes/MergeBlocks.cpp
@@ -68,29 +68,53 @@
namespace wasm {
-struct SwitchFinder : public ControlFlowWalker<SwitchFinder, Visitor<SwitchFinder>> {
- Expression* origin;
- bool found = false;
+// Looks for reasons we can't remove the values from breaks to an origin
+// For example, if there is a switch targeting us, we can't do it - we can't remove the value from other targets
+struct ProblemFinder : public ControlFlowWalker<ProblemFinder, Visitor<ProblemFinder>> {
+ Name origin;
+ bool foundSwitch = false;
+ // count br_ifs, and dropped br_ifs. if they don't match, then a br_if flow value is used, and we can't drop it
+ Index brIfs = 0;
+ Index droppedBrIfs = 0;
+
+ void visitBreak(Break* curr) {
+ if (curr->name == origin && curr->condition) {
+ brIfs++;
+ }
+ }
+
+ void visitDrop(Drop* curr) {
+ if (auto* br = curr->value->dynCast<Break>()) {
+ if (br->name == origin && br->condition) {
+ droppedBrIfs++;
+ }
+ }
+ }
void visitSwitch(Switch* curr) {
- if (findBreakTarget(curr->default_) == origin) {
- found = true;
+ if (curr->default_ == origin) {
+ foundSwitch = true;
return;
}
for (auto& target : curr->targets) {
- if (findBreakTarget(target) == origin) {
- found = true;
+ if (target == origin) {
+ foundSwitch = true;
return;
}
}
}
+
+ bool found() {
+ assert(brIfs >= droppedBrIfs);
+ return foundSwitch || brIfs > droppedBrIfs;
+ }
};
struct BreakValueDropper : public ControlFlowWalker<BreakValueDropper, Visitor<BreakValueDropper>> {
- Expression* origin;
+ Name origin;
void visitBreak(Break* curr) {
- if (curr->value && findBreakTarget(curr->name) == origin) {
+ if (curr->value && curr->name == origin) {
Builder builder(*getModule());
replaceCurrent(builder.makeSequence(builder.makeDrop(curr->value), curr));
curr->value = nullptr;
@@ -118,16 +142,16 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks, Visitor<MergeBloc
if (child) {
if (child->name.is()) {
Expression* expression = child;
- // if there is a switch targeting us, we can't do it - we can't remove the value from other targets too
- SwitchFinder finder;
- finder.origin = child;
+ // check if it's ok to remove the value from all breaks to us
+ ProblemFinder finder;
+ finder.origin = child->name;
finder.walk(expression);
- if (finder.found) {
+ if (finder.found()) {
child = nullptr;
} else {
// fix up breaks
BreakValueDropper fixer;
- fixer.origin = child;
+ fixer.origin = child->name;
fixer.setModule(getModule());
fixer.walk(expression);
}