summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/CoalesceLocals.cpp33
-rw-r--r--src/passes/RemoveUnusedBrs.cpp159
-rw-r--r--test/passes/coalesce-locals.txt45
-rw-r--r--test/passes/remove-unused-brs.txt156
-rw-r--r--test/passes/remove-unused-brs.wast153
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)
+ )
+ )
)