summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/RemoveUnusedBrs.cpp56
-rw-r--r--test/binaryen.js/optimize-levels.js.txt4
-rw-r--r--test/passes/1.txt8
-rw-r--r--test/passes/O.txt21
-rw-r--r--test/passes/O.wast21
-rw-r--r--test/passes/remove-unused-brs.txt87
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)