diff options
-rw-r--r-- | src/passes/CoalesceLocals.cpp | 33 | ||||
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 159 | ||||
-rw-r--r-- | test/passes/coalesce-locals.txt | 45 | ||||
-rw-r--r-- | test/passes/remove-unused-brs.txt | 156 | ||||
-rw-r--r-- | test/passes/remove-unused-brs.wast | 153 |
5 files changed, 460 insertions, 86 deletions
diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp index 63912db19..1bcc74f79 100644 --- a/src/passes/CoalesceLocals.cpp +++ b/src/passes/CoalesceLocals.cpp @@ -315,25 +315,6 @@ void CoalesceLocals::pickIndices(std::vector<Index>& indices) { } } -// Remove a copy from a set of an if, where one if arm is a get of the same set -static void removeIfCopy(Expression** origin, SetLocal* set, If* iff, Expression*& copy, Expression*& other, Module* module) { - // replace the origin with the if, and sink the set into the other non-copying arm - bool tee = set->isTee(); - *origin = iff; - set->value = other; - set->finalize(); - other = set; - // if this is not a tee, then we can get rid of the copy in that arm - if (!tee) { - // we don't need the copy at all - copy = nullptr; - if (!iff->ifTrue) { - Builder(*module).flip(iff); - } - iff->finalize(); - } -} - void CoalesceLocals::applyIndices(std::vector<Index>& indices, Expression* root) { assert(indices.size() == numLocals); for (auto& curr : basicBlocks) { @@ -362,20 +343,6 @@ void CoalesceLocals::applyIndices(std::vector<Index>& indices, Expression* root) } continue; } - if (auto* iff = set->value->dynCast<If>()) { - if (auto* get = iff->ifTrue->dynCast<GetLocal>()) { - if (get->index == set->index) { - removeIfCopy(action.origin, set, iff, iff->ifTrue, iff->ifFalse, getModule()); - continue; - } - } - if (auto* get = iff->ifFalse->dynCast<GetLocal>()) { - if (get->index == set->index) { - removeIfCopy(action.origin, set, iff, iff->ifFalse, iff->ifTrue, getModule()); - continue; - } - } - } } } } diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 62ecb3ed3..7fa94baf0 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -804,53 +804,150 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } void visitSetLocal(SetLocal* curr) { - // Undo an if return value into a local, if one arm is a br, as we - // prefer a br_if: - // (set_local $x - // (if (result i32) - // (..condition..) - // (br $somewhere) - // (..result) - // ) - // ) - // => - // (br_if $somewhere - // (..condition..) - // ) - // (set_local $x - // (..result) - // ) - // TODO: handle a condition in the br? need to watch for side effects - auto* iff = curr->value->dynCast<If>(); - if (!iff) return; - if (!isConcreteType(iff->type) || !isConcreteType(iff->condition->type)) return; + // Sets of an if can be optimized in various ways that remove part of + // the if branching, or all of it. + // The optimizations we can do here can recurse and call each + // other, so pass around a pointer to the output. + optimizeSetIf(getCurrentPointer()); + } + + void optimizeSetIf(Expression** currp) { + if (optimizeSetIfWithBrArm(currp)) return; + if (optimizeSetIfWithCopyArm(currp)) return; + } + + // If one arm is a br, we prefer a br_if and the set later: + // (set_local $x + // (if (result i32) + // (..condition..) + // (br $somewhere) + // (..result) + // ) + // ) + // => + // (br_if $somewhere + // (..condition..) + // ) + // (set_local $x + // (..result) + // ) + // TODO: handle a condition in the br? need to watch for side effects + bool optimizeSetIfWithBrArm(Expression** currp) { + auto* set = (*currp)->cast<SetLocal>(); + auto* iff = set->value->dynCast<If>(); + if (!iff || + !isConcreteType(iff->type) || + !isConcreteType(iff->condition->type)) { + return false; + } auto tryToOptimize = [&](Expression* one, Expression* two, bool flipCondition) { if (one->type == unreachable && two->type != unreachable) { if (auto* br = one->dynCast<Break>()) { if (ExpressionAnalyzer::isSimple(br)) { // Wonderful, do it! Builder builder(*getModule()); - br->condition = iff->condition; if (flipCondition) { - br->condition = builder.makeUnary(EqZInt32, br->condition); + builder.flip(iff); } + br->condition = iff->condition; br->finalize(); - curr->value = two; - replaceCurrent( - builder.makeSequence( - br, - curr - ) - ); + set->value = two; + auto* block = builder.makeSequence(br, set); + *currp = block; + // Recurse on the set, which now has a new value. + optimizeSetIf(&block->list[1]); return true; } } } return false; }; - if (!tryToOptimize(iff->ifTrue, iff->ifFalse, false)) { - tryToOptimize(iff->ifFalse, iff->ifTrue, true); + return tryToOptimize(iff->ifTrue, iff->ifFalse, false) || + tryToOptimize(iff->ifFalse, iff->ifTrue, true); + } + + // If one arm is a get of the same outer set, it is a copy which + // we can remove. If this is not a tee, then we remove the get + // as well as the if-else opcode in the binary format, which is + // great: + // (set_local $x + // (if (result i32) + // (..condition..) + // (..result) + // (get_local $x) + // ) + // ) + // => + // (if + // (..condition..) + // (set_local $x + // (..result) + // ) + // ) + // If this is a tee, then we can do the same operation but + // inside a block, and keep the get: + // (tee_local $x + // (if (result i32) + // (..condition..) + // (..result) + // (get_local $x) + // ) + // ) + // => + // (block (result i32) + // (if + // (..condition..) + // (set_local $x + // (..result) + // ) + // ) + // (get_local $x) + // ) + // We save the if-else opcode, and add the block's opcodes. + // This may be detrimental, however, often the block can be + // merged or eliminated given the outside scope, and we + // removed one of the if branches. + bool optimizeSetIfWithCopyArm(Expression** currp) { + auto* set = (*currp)->cast<SetLocal>(); + auto* iff = set->value->dynCast<If>(); + if (!iff || + !isConcreteType(iff->type) || + !isConcreteType(iff->condition->type)) { + return false; + } + Builder builder(*getModule()); + GetLocal* get = iff->ifTrue->dynCast<GetLocal>(); + if (get && get->index == set->index) { + builder.flip(iff); + } else { + get = iff->ifFalse->dynCast<GetLocal>(); + if (get && get->index != set->index) { + get = nullptr; + } + } + if (!get) return false; + // We can do it! + bool tee = set->isTee(); + assert(set->index == get->index); + assert(iff->ifFalse == get); + set->value = iff->ifTrue; + set->finalize(); + iff->ifTrue = set; + iff->ifFalse = nullptr; + iff->finalize(); + Expression* replacement = iff; + if (tee) { + set->setTee(false); + // We need a block too. + replacement = builder.makeSequence( + iff, + get // reuse the get + ); } + *currp = replacement; + // Recurse on the set, which now has a new value. + optimizeSetIf(&iff->ifTrue); + return true; } // (br_if)+ => br_table diff --git a/test/passes/coalesce-locals.txt b/test/passes/coalesce-locals.txt index 4f856d0dc..a46c645a5 100644 --- a/test/passes/coalesce-locals.txt +++ b/test/passes/coalesce-locals.txt @@ -1010,11 +1010,10 @@ (local $0 i32) (local $1 i32) (loop $top - (if - (i32.eqz + (set_local $0 + (if (result i32) (i32.const 1) - ) - (set_local $0 + (get_local $0) (get_local $1) ) ) @@ -1031,10 +1030,11 @@ (local $0 i32) (local $1 i32) (loop $top - (if - (i32.const 1) - (set_local $1 + (set_local $1 + (if (result i32) + (i32.const 1) (get_local $0) + (get_local $1) ) ) (drop @@ -1050,10 +1050,11 @@ (local $0 i32) (local $1 i32) (loop $top - (if - (i32.const 1) - (tee_local $0 + (set_local $0 + (if (result i32) + (i32.const 1) (unreachable) + (get_local $0) ) ) (drop @@ -1090,10 +1091,10 @@ (local $1 i32) (loop $top (drop - (if (result i32) - (i32.const 1) - (get_local $0) - (tee_local $0 + (tee_local $0 + (if (result i32) + (i32.const 1) + (get_local $0) (i32.const 2) ) ) @@ -1127,10 +1128,10 @@ ) (func $tee_if_with_unreachable_else (; 50 ;) (type $8) (param $0 f64) (param $1 i32) (result i64) (call $tee_if_with_unreachable_else - (if (result f64) - (get_local $1) - (get_local $0) - (tee_local $0 + (tee_local $0 + (if (result f64) + (get_local $1) + (get_local $0) (unreachable) ) ) @@ -1142,12 +1143,12 @@ ) (func $tee_if_with_unreachable_true (; 51 ;) (type $8) (param $0 f64) (param $1 i32) (result i64) (call $tee_if_with_unreachable_else - (if (result f64) - (get_local $1) - (tee_local $0 + (tee_local $0 + (if (result f64) + (get_local $1) (unreachable) + (get_local $0) ) - (get_local $0) ) (f64.lt (f64.const -128) diff --git a/test/passes/remove-unused-brs.txt b/test/passes/remove-unused-brs.txt index 774110e23..19773b7f6 100644 --- a/test/passes/remove-unused-brs.txt +++ b/test/passes/remove-unused-brs.txt @@ -2231,4 +2231,160 @@ ) ) ) + (func $selectify (; 102 ;) (type $0) (param $x i32) + (drop + (if (result i32) + (i32.eq + (get_local $x) + (i32.const 1) + ) + (i32.mul + (i32.const 2) + (i32.const 3) + ) + (i32.mul + (i32.const 2) + (i32.const 3) + ) + ) + ) + (drop + (if (result i32) + (i32.eq + (get_local $x) + (i32.const 1) + ) + (i32.add + (i32.const 2) + (i32.const 3) + ) + (i32.add + (i32.const 2) + (i32.const 3) + ) + ) + ) + ) + (func $if-one-side (; 103 ;) (type $2) (result i32) + (local $x i32) + (if + (i32.const 1) + (set_local $x + (i32.const 2) + ) + ) + (get_local $x) + ) + (func $if-one-side-b (; 104 ;) (type $2) (result i32) + (local $x i32) + (if + (i32.eqz + (i32.const 1) + ) + (set_local $x + (i32.const 2) + ) + ) + (get_local $x) + ) + (func $if-one-side-tee-etc (; 105 ;) (type $10) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $x i32) + (local $y i32) + (local $z i32) + (drop + (call $if-one-side-tee-etc + (block (result i32) + (if + (i32.const -3) + (set_local $x + (i32.const -4) + ) + ) + (get_local $x) + ) + ) + ) + (i32.const 0) + ) + (func $ifs-copies-recursive (; 106 ;) (type $10) (param $20 i32) (result i32) + (if + (i32.const 1) + (if + (i32.const 2) + (if + (i32.const 3) + (set_local $20 + (i32.const 4) + ) + ) + ) + ) + (get_local $20) + ) + (func $if-copy1 (; 107 ;) (type $1) + (local $x i32) + (local $y i32) + (loop $top + (if + (i32.eqz + (i32.const 1) + ) + (set_local $x + (get_local $y) + ) + ) + (br $top) + ) + ) + (func $if-copy3 (; 108 ;) (type $1) + (local $x i32) + (local $y i32) + (loop $top + (if + (i32.const 1) + (tee_local $x + (unreachable) + ) + ) + (br $top) + ) + ) + (func $if-copy4 (; 109 ;) (type $1) + (local $x i32) + (local $y i32) + (loop $top + (set_local $x + (if (result i32) + (i32.const 1) + (unreachable) + (get_local $y) + ) + ) + (br $top) + ) + ) + (func $if-copy-tee (; 110 ;) (type $1) + (local $x i32) + (local $y i32) + (loop $top + (drop + (block (result i32) + (if + (i32.eqz + (i32.const 1) + ) + (set_local $x + (i32.const 2) + ) + ) + (get_local $x) + ) + ) + (br $top) + ) + ) ) diff --git a/test/passes/remove-unused-brs.wast b/test/passes/remove-unused-brs.wast index d1afba4a5..fe195317d 100644 --- a/test/passes/remove-unused-brs.wast +++ b/test/passes/remove-unused-brs.wast @@ -1837,5 +1837,158 @@ ) ) ) + (func $selectify (param $x i32) + (drop + (if (result i32) + (i32.eq + (get_local $x) + (i32.const 1) + ) + (i32.mul + (i32.const 2) + (i32.const 3) + ) + (i32.mul + (i32.const 2) + (i32.const 3) + ) + ) + ) + (drop + (if (result i32) + (i32.eq + (get_local $x) + (i32.const 1) + ) + (i32.add + (i32.const 2) + (i32.const 3) + ) + (i32.add + (i32.const 2) + (i32.const 3) + ) + ) + ) + ) + (func $if-one-side (result i32) + (local $x i32) + (set_local $x + (if (result i32) + (i32.const 1) + (i32.const 2) + (get_local $x) + ) + ) + (get_local $x) + ) + (func $if-one-side-b (result i32) + (local $x i32) + (set_local $x + (if (result i32) + (i32.const 1) + (get_local $x) + (i32.const 2) + ) + ) + (get_local $x) + ) + (func $if-one-side-tee-etc (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $x i32) + (local $y i32) + (local $z i32) + (drop + (call $if-one-side-tee-etc + (tee_local $x + (if (result i32) + (i32.const -3) + (i32.const -4) + (get_local $x) + ) + ) + ) + ) + (i32.const 0) + ) + (func $ifs-copies-recursive (param $20 i32) (result i32) + (set_local $20 + (if (result i32) + (i32.const 1) + (if (result i32) + (i32.const 2) + (if (result i32) + (i32.const 3) + (i32.const 4) + (get_local $20) + ) + (get_local $20) + ) + (get_local $20) + ) + ) + (get_local $20) + ) + (func $if-copy1 + (local $x i32) + (local $y i32) + (loop $top + (set_local $x + (if (result i32) + (i32.const 1) + (get_local $x) + (get_local $y) + ) + ) + (br $top) + ) + ) + (func $if-copy3 + (local $x i32) + (local $y i32) + (loop $top + (set_local $x + (if (result i32) + (i32.const 1) + (unreachable) + (get_local $x) + ) + ) + (br $top) + ) + ) + (func $if-copy4 + (local $x i32) + (local $y i32) + (loop $top + (set_local $x + (if (result i32) + (i32.const 1) + (unreachable) + (get_local $y) + ) + ) + (br $top) + ) + ) + (func $if-copy-tee + (local $x i32) + (local $y i32) + (loop $top + (drop + (tee_local $x + (if (result i32) + (i32.const 1) + (get_local $x) + (i32.const 2) + ) + ) + ) + (br $top) + ) + ) ) |