diff options
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 56 | ||||
-rw-r--r-- | test/binaryen.js/optimize-levels.js.txt | 4 | ||||
-rw-r--r-- | test/passes/1.txt | 8 | ||||
-rw-r--r-- | test/passes/O.txt | 21 | ||||
-rw-r--r-- | test/passes/O.wast | 21 | ||||
-rw-r--r-- | test/passes/remove-unused-brs.txt | 87 |
6 files changed, 126 insertions, 71 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 7fa94baf0..361e57ad6 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -21,9 +21,10 @@ #include <wasm.h> #include <pass.h> #include <parsing.h> -#include <ir/utils.h> #include <ir/branch-utils.h> +#include <ir/cost.h> #include <ir/effects.h> +#include <ir/utils.h> #include <wasm-builder.h> namespace wasm { @@ -780,27 +781,44 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { 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 - if (!shrink) return; - if (curr->ifFalse && isConcreteType(curr->ifTrue->type) && isConcreteType(curr->ifFalse->type)) { - // if with else, consider turning it into a select if there is no control flow - // TODO: estimate cost - EffectAnalyzer condition(passOptions, curr->condition); - if (!condition.hasSideEffects()) { - EffectAnalyzer ifTrue(passOptions, curr->ifTrue); - if (!ifTrue.hasSideEffects()) { - EffectAnalyzer ifFalse(passOptions, curr->ifFalse); - if (!ifFalse.hasSideEffects()) { - auto* select = getModule()->allocator.alloc<Select>(); - select->condition = curr->condition; - select->ifTrue = curr->ifTrue; - select->ifFalse = curr->ifFalse; - select->finalize(); - replaceCurrent(select); - } + if (auto* select = selectify(curr)) { + replaceCurrent(select); + } + } + + // Convert an if into a select, if possible and beneficial to do so. + Select* selectify(If* iff) { + if (!iff->ifFalse || + !isConcreteType(iff->ifTrue->type) || + !isConcreteType(iff->ifFalse->type)) { + return nullptr; + } + // This is always helpful for code size, but can be a tradeoff with performance + // as we run both code paths. So when shrinking we always try to do this, but + // otherwise must consider more carefully. + if (!passOptions.shrinkLevel) { + // Consider the cost of executing all the code unconditionally + const auto MAX_COST = 7; + auto total = CostAnalyzer(iff->ifTrue).cost + + CostAnalyzer(iff->ifFalse).cost; + if (total >= MAX_COST) return nullptr; + } + // Check if side effects allow this. + EffectAnalyzer condition(passOptions, iff->condition); + if (!condition.hasSideEffects()) { + EffectAnalyzer ifTrue(passOptions, iff->ifTrue); + if (!ifTrue.hasSideEffects()) { + EffectAnalyzer ifFalse(passOptions, iff->ifFalse); + if (!ifFalse.hasSideEffects()) { + return Builder(*getModule()).makeSelect( + iff->condition, + iff->ifTrue, + iff->ifFalse + ); } } } + return nullptr; } void visitSetLocal(SetLocal* curr) { diff --git a/test/binaryen.js/optimize-levels.js.txt b/test/binaryen.js/optimize-levels.js.txt index 8cb0dfea5..5381722c0 100644 --- a/test/binaryen.js/optimize-levels.js.txt +++ b/test/binaryen.js/optimize-levels.js.txt @@ -52,10 +52,10 @@ shrinkLevel=0 (type $i (func (param i32) (result i32))) (export "test" (func $test)) (func $test (; 0 ;) (type $i) (param $0 i32) (result i32) - (if (result i32) - (get_local $0) + (select (get_local $0) (i32.const 0) + (get_local $0) ) ) ) diff --git a/test/passes/1.txt b/test/passes/1.txt index 7b6f4f249..0a7716a23 100644 --- a/test/passes/1.txt +++ b/test/passes/1.txt @@ -19,15 +19,15 @@ (func $ifs (; 4 ;) (type $2) (param $0 i32) (result i32) (if (result i32) (get_local $0) - (if (result i32) - (get_local $0) + (select (i32.const 2) (i32.const 3) - ) - (if (result i32) (get_local $0) + ) + (select (i32.const 4) (i32.const 5) + (get_local $0) ) ) ) diff --git a/test/passes/O.txt b/test/passes/O.txt index 3ba18693f..ca6a7f81f 100644 --- a/test/passes/O.txt +++ b/test/passes/O.txt @@ -1,8 +1,10 @@ (module (type $0 (func (result i32))) (type $1 (func (param i64))) + (type $2 (func (param i32) (result i32))) (export "ret" (func $ret)) (export "waka" (func $if-0-unreachable-to-none)) + (export "many-selects" (func $many-selects)) (func $ret (; 0 ;) (; has Stack IR ;) (type $0) (result i32) (drop (call $ret) @@ -18,4 +20,23 @@ (func $if-0-unreachable-to-none (; 1 ;) (; has Stack IR ;) (type $1) (param $0 i64) (unreachable) ) + (func $many-selects (; 2 ;) (; has Stack IR ;) (type $2) (param $0 i32) (result i32) + (tee_local $0 + (select + (i32.const -1073741824) + (select + (i32.const 1073741823) + (get_local $0) + (i32.gt_s + (get_local $0) + (i32.const 1073741823) + ) + ) + (i32.lt_s + (get_local $0) + (i32.const -1073741824) + ) + ) + ) + ) ) diff --git a/test/passes/O.wast b/test/passes/O.wast index e42541376..e183e02c1 100644 --- a/test/passes/O.wast +++ b/test/passes/O.wast @@ -24,5 +24,26 @@ ) ) ) + (func $many-selects (export "many-selects") (param $0 i32) (result i32) + (if + (i32.lt_s + (get_local $0) + (i32.const -1073741824) + ) + (set_local $0 + (i32.const -1073741824) + ) + (if + (i32.gt_s + (get_local $0) + (i32.const 1073741823) + ) + (set_local $0 + (i32.const 1073741823) + ) + ) + ) + (get_local $0) + ) ) diff --git a/test/passes/remove-unused-brs.txt b/test/passes/remove-unused-brs.txt index 19773b7f6..daa7eca3c 100644 --- a/test/passes/remove-unused-brs.txt +++ b/test/passes/remove-unused-brs.txt @@ -179,8 +179,7 @@ ) ) (func $b14 (; 14 ;) (type $2) (result i32) - (if (result i32) - (i32.const 1) + (select (block $topmost (result i32) (block $block1 (result i32) (i32.const 12) @@ -189,6 +188,7 @@ (block $block3 (result i32) (i32.const 27) ) + (i32.const 1) ) ) (func $b15 (; 15 ;) (type $1) @@ -726,10 +726,10 @@ (i32.const 6) ) ) - (if (result i32) - (i32.const 6) + (select (i32.const 7) (i32.const 8) + (i32.const 6) ) ) ) @@ -1861,10 +1861,10 @@ (i32.const 0) ) (func $if-flow-1 (; 75 ;) (type $2) (result i32) - (if (result i32) - (i32.const 0) + (select (i32.const 1) (i32.const 2) + (i32.const 0) ) ) (func $if-flow-2 (; 76 ;) (type $2) (result i32) @@ -2073,12 +2073,12 @@ ) ) (func $if-block-br-5-value (; 93 ;) (type $2) (result i32) - (if (result i32) - (i32.const 1) + (select (block $label (result i32) (i32.const 2) ) (i32.const 3) + (i32.const 1) ) ) (func $restructure-if-outerType-change (; 94 ;) (type $1) @@ -2249,11 +2249,7 @@ ) ) (drop - (if (result i32) - (i32.eq - (get_local $x) - (i32.const 1) - ) + (select (i32.add (i32.const 2) (i32.const 3) @@ -2262,27 +2258,31 @@ (i32.const 2) (i32.const 3) ) + (i32.eq + (get_local $x) + (i32.const 1) + ) ) ) ) (func $if-one-side (; 103 ;) (type $2) (result i32) (local $x i32) - (if - (i32.const 1) - (set_local $x + (set_local $x + (select (i32.const 2) + (get_local $x) + (i32.const 1) ) ) (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 + (set_local $x + (select + (get_local $x) (i32.const 2) + (i32.const 1) ) ) (get_local $x) @@ -2297,14 +2297,12 @@ (local $z i32) (drop (call $if-one-side-tee-etc - (block (result i32) - (if + (tee_local $x + (select + (i32.const -4) + (get_local $x) (i32.const -3) - (set_local $x - (i32.const -4) - ) ) - (get_local $x) ) ) ) @@ -2313,13 +2311,15 @@ (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 + (set_local $20 + (select + (select (i32.const 4) + (get_local $20) + (i32.const 3) ) + (get_local $20) + (i32.const 2) ) ) ) @@ -2329,12 +2329,11 @@ (local $x i32) (local $y i32) (loop $top - (if - (i32.eqz - (i32.const 1) - ) - (set_local $x + (set_local $x + (select + (get_local $x) (get_local $y) + (i32.const 1) ) ) (br $top) @@ -2372,16 +2371,12 @@ (local $y i32) (loop $top (drop - (block (result i32) - (if - (i32.eqz - (i32.const 1) - ) - (set_local $x - (i32.const 2) - ) + (tee_local $x + (select + (get_local $x) + (i32.const 2) + (i32.const 1) ) - (get_local $x) ) ) (br $top) |