diff options
-rw-r--r-- | src/passes/MergeBlocks.cpp | 76 | ||||
-rw-r--r-- | test/lit/passes/merge-blocks.wast | 27 |
2 files changed, 70 insertions, 33 deletions
diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index 9a0b5d27b..401d2e629 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -197,7 +197,46 @@ static bool hasDeadCode(Block* block) { return false; } -// core block optimizer routine +// Given a dropped block, see if we can simplify it by optimizing the drop into +// the block, removing the return value while doin so. Returns whether we +// succeeded. +static bool optimizeDroppedBlock(Drop* drop, + Block* block, + Module& wasm, + PassOptions& options, + BranchUtils::BranchSeekerCache& branchInfo) { + assert(drop->value == block); + if (block->name.is()) { + // There may be breaks: see if we can remove their values. + Expression* expression = block; + ProblemFinder finder(options); + finder.setModule(&wasm); + finder.origin = block->name; + finder.walk(expression); + if (finder.found()) { + // We found a problem that blocks us. + return false; + } + + // Success. Fix up breaks before continuing to handle the drop itself. + BreakValueDropper fixer(options, branchInfo); + fixer.origin = block->name; + fixer.setModule(&wasm); + fixer.walk(expression); + } + + // Optimize by removing the block. Reuse the drop, if we still need it. + auto* last = block->list.back(); + if (last->type.isConcrete()) { + drop->value = last; + drop->finalize(); + block->list.back() = drop; + } + block->finalize(); + return true; +} + +// Core block optimizer routine. static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions, @@ -218,8 +257,8 @@ static void optimizeBlock(Block* curr, Loop* loop = nullptr; // To to handle a non-block child. if (!childBlock) { - // if we have a child that is (drop (block ..)) then we can move the - // drop into the block, and remove br values. this allows more merging, + // If we have a child that is (drop (block ..)) then we can move the + // drop into the block, and remove br values. This allows more merging. if (auto* drop = list[i]->dynCast<Drop>()) { childBlock = drop->value->dynCast<Block>(); if (childBlock) { @@ -228,36 +267,13 @@ static void optimizeBlock(Block* curr, // dce should have been run anyhow continue; } - if (childBlock->name.is()) { - Expression* expression = childBlock; - // check if it's ok to remove the value from all breaks to us - ProblemFinder finder(passOptions); - finder.setModule(module); - finder.origin = childBlock->name; - finder.walk(expression); - if (finder.found()) { - childBlock = nullptr; - } else { - // fix up breaks - BreakValueDropper fixer(passOptions, branchInfo); - fixer.origin = childBlock->name; - fixer.setModule(module); - fixer.walk(expression); - } - } - if (childBlock) { - // we can do it! - // reuse the drop, if we still need it - auto* last = childBlock->list.back(); - if (last->type.isConcrete()) { - drop->value = last; - drop->finalize(); - childBlock->list.back() = drop; - } - childBlock->finalize(); + if (optimizeDroppedBlock( + drop, childBlock, *module, passOptions, branchInfo)) { child = list[i] = childBlock; more = true; changed = true; + } else { + childBlock = nullptr; } } } else if ((loop = list[i]->dynCast<Loop>())) { diff --git a/test/lit/passes/merge-blocks.wast b/test/lit/passes/merge-blocks.wast index 66b0e8b71..e1ac88dd8 100644 --- a/test/lit/passes/merge-blocks.wast +++ b/test/lit/passes/merge-blocks.wast @@ -15,7 +15,7 @@ (type $array (array (mut i32))) - ;; CHECK: (func $br_on_to_drop (type $5) + ;; CHECK: (func $br_on_to_drop (type $4) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block $label$1 (result i31ref) @@ -43,7 +43,7 @@ ) ) - ;; CHECK: (func $struct.set (type $4) (param $struct (ref null $struct)) + ;; CHECK: (func $struct.set (type $5) (param $struct (ref null $struct)) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1234) @@ -68,7 +68,7 @@ ) ) - ;; CHECK: (func $struct.get (type $4) (param $struct (ref null $struct)) + ;; CHECK: (func $struct.get (type $5) (param $struct (ref null $struct)) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1234) @@ -404,6 +404,27 @@ ) ) + ;; CHECK: (func $toplevel (type $4) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $label (result i32) + ;; CHECK-NEXT: (br $label + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $toplevel + ;; Test we can remove a block value even when the drop is at the toplevel of + ;; a function. This is not done yet TODO + (drop + (block $label (result i32) + (br $label + (i32.const 42) + ) + ) + ) + ) + ;; CHECK: (func $helper (type $7) (param $x i32) (result i32) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) |