summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/MergeBlocks.cpp52
-rw-r--r--test/passes/merge-blocks.txt63
-rw-r--r--test/passes/merge-blocks.wast42
3 files changed, 143 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);
}
diff --git a/test/passes/merge-blocks.txt b/test/passes/merge-blocks.txt
new file mode 100644
index 000000000..152e6fbae
--- /dev/null
+++ b/test/passes/merge-blocks.txt
@@ -0,0 +1,63 @@
+(module
+ (type $0 (func))
+ (type $1 (func (param i32)))
+ (memory $0 0)
+ (func $drop-block (type $0)
+ (block $block
+ (block $x
+ (drop
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ (func $drop-block-br (type $0)
+ (block $block
+ (block $x
+ (block
+ (drop
+ (i32.const 1)
+ )
+ (br $x)
+ )
+ (drop
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ (func $drop-block-br-if (type $0)
+ (block $block
+ (block $x
+ (drop
+ (block i32
+ (drop
+ (i32.const 1)
+ )
+ (br_if $x
+ (i32.const 2)
+ )
+ )
+ )
+ (drop
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ (func $undroppable-block-br-if (type $1) (param $0 i32)
+ (block $block
+ (drop
+ (block $x i32
+ (call $undroppable-block-br-if
+ (br_if $x
+ (i32.const 1)
+ (i32.const 2)
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ )
+ )
+)
diff --git a/test/passes/merge-blocks.wast b/test/passes/merge-blocks.wast
new file mode 100644
index 000000000..161f9d90f
--- /dev/null
+++ b/test/passes/merge-blocks.wast
@@ -0,0 +1,42 @@
+(module
+ (func $drop-block
+ (block
+ (drop
+ (block $x i32
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ (func $drop-block-br
+ (block
+ (drop
+ (block $x i32
+ (br $x (i32.const 1))
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ (func $drop-block-br-if
+ (block
+ (drop
+ (block $x i32
+ (drop (br_if $x (i32.const 1) (i32.const 2)))
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ (func $undroppable-block-br-if (param i32)
+ (block
+ (drop
+ (block $x i32
+ (call $undroppable-block-br-if (br_if $x (i32.const 1) (i32.const 2)))
+ (i32.const 0)
+ )
+ )
+ )
+ )
+)
+