diff options
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 70 | ||||
-rw-r--r-- | test/passes/remove-unused-brs_shrink-level=1.txt | 78 | ||||
-rw-r--r-- | test/passes/remove-unused-brs_shrink-level=1.wast | 39 |
3 files changed, 163 insertions, 24 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 63f5d6585..69a7c4ff1 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -393,6 +393,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs, Visitor<R // perform some final optimizations struct FinalOptimizer : public PostWalker<FinalOptimizer, Visitor<FinalOptimizer>> { + bool selectify; + void visitBlock(Block* curr) { // if a block has an if br else br, we can un-conditionalize the latter, allowing // the if to become a br_if. @@ -422,35 +424,55 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs, Visitor<R continue; } } - // Restructuring of ifs: if we have - // (block $x - // (br_if $x (cond)) - // .., no other references to $x - // ) - // then we can turn that into (if (!cond) ..). - // Code size wise, we turn the block into an if (no change), and - // lose the br_if (-2). .. turns into the body of the if in the binary - // format. We need to flip the condition, which at worst adds 1. - if (curr->name.is() && list.size() >= 2) { - 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) { - // no other breaks to that name, so we can do this - Builder builder(*getModule()); - replaceCurrent(builder.makeIf( - builder.makeUnary(EqZInt32, br->condition), - curr - )); - curr->name = Name(); - ExpressionManipulator::nop(br); + if (list.size() >= 2) { + if (selectify) { + // Join adjacent br_ifs to the same target, making one br_if with + // a "selectified" condition that executes both. + for (Index i = 0; i < list.size() - 1; i++) { + auto* br1 = list[i]->dynCast<Break>(); + if (!br1 || !br1->condition) continue; + auto* br2 = list[i + 1]->dynCast<Break>(); + if (!br2 || !br2->condition) continue; + if (br1->name == br2->name) { + assert(!br1->value && !br2->value); + if (!EffectAnalyzer(br2->condition).hasSideEffects()) { + // it's ok to execute them both, do it + Builder builder(*getModule()); + br1->condition = builder.makeBinary(OrInt32, br1->condition, br2->condition); + ExpressionManipulator::nop(br2); + } + } + } + } + // Restructuring of ifs: if we have + // (block $x + // (br_if $x (cond)) + // .., no other references to $x + // ) + // then we can turn that into (if (!cond) ..). + // Code size wise, we turn the block into an if (no change), and + // lose the br_if (-2). .. turns into the body of the if in the binary + // format. We need to flip the condition, which at worst adds 1. + if (curr->name.is()) { + 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) { + // no other breaks to that name, so we can do this + Builder builder(*getModule()); + replaceCurrent(builder.makeIf( + builder.makeUnary(EqZInt32, br->condition), + curr + )); + curr->name = Name(); + ExpressionManipulator::nop(br); + return; + } } } } } - bool selectify; - void visitIf(If* curr) { // we may have simplified ifs enough to turn them into selects // this is helpful for code size, but can be a tradeoff with performance as we run both code paths diff --git a/test/passes/remove-unused-brs_shrink-level=1.txt b/test/passes/remove-unused-brs_shrink-level=1.txt index 0356cc1da..68015f73f 100644 --- a/test/passes/remove-unused-brs_shrink-level=1.txt +++ b/test/passes/remove-unused-brs_shrink-level=1.txt @@ -16,4 +16,82 @@ ) ) ) + (func $join-br_ifs (type $1) + (block $out + (br_if $out + (i32.or + (i32.const 1) + (i32.const 2) + ) + ) + (nop) + (br_if $out + (i32.const 3) + ) + ) + (block $out2 + (block $out3 + (br_if $out2 + (i32.const 1) + ) + (br_if $out3 + (i32.const 2) + ) + (br_if $out2 + (i32.const 3) + ) + ) + (unreachable) + ) + (block $out4 + (block $out5 + (br_if $out4 + (i32.const 1) + ) + (br_if $out5 + (i32.or + (i32.const 2) + (i32.const 3) + ) + ) + (nop) + ) + (unreachable) + ) + (block $out6 + (block $out7 + (br_if $out6 + (i32.or + (i32.const 1) + (i32.const 2) + ) + ) + (nop) + (br_if $out7 + (i32.const 3) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (i32.or + (call $b14) + (i32.const 0) + ) + ) + (block + (nop) + (nop) + ) + ) + (block $out80 + (br_if $out80 + (i32.const 1) + ) + (br_if $out80 + (call $b14) + ) + ) + ) ) diff --git a/test/passes/remove-unused-brs_shrink-level=1.wast b/test/passes/remove-unused-brs_shrink-level=1.wast index 9698bd899..5a2234de6 100644 --- a/test/passes/remove-unused-brs_shrink-level=1.wast +++ b/test/passes/remove-unused-brs_shrink-level=1.wast @@ -16,5 +16,44 @@ ) ) ) + (func $join-br_ifs + (block $out + (br_if $out (i32.const 1)) + (br_if $out (i32.const 2)) + (br_if $out (i32.const 3)) + ) + (block $out2 + (block $out3 + (br_if $out2 (i32.const 1)) + (br_if $out3 (i32.const 2)) + (br_if $out2 (i32.const 3)) + ) + (unreachable) + ) + (block $out4 + (block $out5 + (br_if $out4 (i32.const 1)) + (br_if $out5 (i32.const 2)) + (br_if $out5 (i32.const 3)) + ) + (unreachable) + ) + (block $out6 + (block $out7 + (br_if $out6 (i32.const 1)) + (br_if $out6 (i32.const 2)) + (br_if $out7 (i32.const 3)) + ) + (unreachable) + ) + (block $out8 + (br_if $out8 (call $b14)) ;; side effect + (br_if $out8 (i32.const 0)) + ) + (block $out8 + (br_if $out8 (i32.const 1)) + (br_if $out8 (call $b14)) ;; side effect + ) + ) ) |