diff options
-rw-r--r-- | src/passes/MergeBlocks.cpp | 52 | ||||
-rw-r--r-- | test/passes/merge-blocks.txt | 63 | ||||
-rw-r--r-- | test/passes/merge-blocks.wast | 42 |
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) + ) + ) + ) + ) +) + |