diff options
author | Alon Zakai <azakai@google.com> | 2021-04-20 09:03:23 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-20 09:03:23 -0700 |
commit | e651186bbdbd36e775236c23f24f0baef1699101 (patch) | |
tree | 13d7bcee1a22e853fa8c1785336a36a4efb2aaee /src | |
parent | 1333d9a31ecacb39643e132400d9409aa3f989be (diff) | |
download | binaryen-e651186bbdbd36e775236c23f24f0baef1699101.tar.gz binaryen-e651186bbdbd36e775236c23f24f0baef1699101.tar.bz2 binaryen-e651186bbdbd36e775236c23f24f0baef1699101.zip |
Implement missing if restructuring (#3819)
The existing restructuring code could turn a block+br_if into an if in
simple cases, but it had some TODOs that I noticed were helpful on
GC benchmarks.
One limitation was that we need to reorder the condition and the value,
(block
(br_if
(value)
(condition)
)
(...)
)
=>
(if
(condition)
(value)
(...)
)
The old code checked for side effects in the condition. But it is ok for it
to have side effects if they can be reordered with the value (for example,
if the value is a constant then it definitely does not care about side effects
in the condition).
The other missing TODO is to use a select when we can't use an if:
(block
(drop
(br_if
(value)
(condition)
)
)
(...)
)
=>
(select
(value)
(...)
(condition)
)
In this case we do not reorder the condition and the value, but we do
reorder the condition with the rest of the block.
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 67 |
1 files changed, 55 insertions, 12 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index cacd24402..88a87ba1f 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -915,7 +915,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // Restructuring of ifs: if we have // (block $x - // (br_if $x (cond)) + // (drop (br_if $x (cond))) // .., no other references to $x // ) // then we can turn that into (if (!cond) ..). @@ -925,7 +925,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // If the block has a return value, we can do something similar, removing // the drop from the br_if and putting the if on the outside, // (block $x - // (br_if $x (value) (cond)) + // (drop (br_if $x (value) (cond))) // .., no other references to $x // ..final element.. // ) @@ -956,30 +956,73 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // not have a value, depending on if it was dropped or not. If the // type is unreachable that means it is not actually reached, which we // can ignore. + Builder builder(*getModule()); if (br && br->condition && br->name == curr->name && br->type != Type::unreachable) { if (BranchUtils::BranchSeeker::count(curr, curr->name) == 1) { // no other breaks to that name, so we can do this if (!drop) { assert(!br->value); - Builder builder(*getModule()); replaceCurrent(builder.makeIf( builder.makeUnary(EqZInt32, br->condition), curr)); ExpressionManipulator::nop(br); curr->finalize(curr->type); } else { - // If the items we move around have side effects, we can't do - // this. - // TODO: we could use a select, in some cases..? + // To use an if, the value must have no side effects, as in the + // if it may not execute. FeatureSet features = getModule()->features; if (!EffectAnalyzer(passOptions, features, br->value) - .hasSideEffects() && - !EffectAnalyzer(passOptions, features, br->condition) .hasSideEffects()) { - ExpressionManipulator::nop(list[0]); - Builder builder(*getModule()); - replaceCurrent( - builder.makeIf(br->condition, br->value, curr)); + // We also need to reorder the condition and the value. + if (EffectAnalyzer::canReorder( + passOptions, features, br->condition, br->value)) { + ExpressionManipulator::nop(list[0]); + replaceCurrent( + builder.makeIf(br->condition, br->value, curr)); + } + } else { + // The value has side effects, so it must always execute. We + // may still be able to optimize this, however, by using a + // select: + // (block $x + // (drop (br_if $x (value) (cond))) + // ..., no other references to $x + // ..final element.. + // ) + // => + // (select + // (value) + // (block $x + // ..., no other references to $x + // ..final element.. + // ) + // (cond) + // ) + // To do this we must be able to reorder the condition with + // the rest of the block (but not the value), and we must be + // able to make the rest of the block always execute, so it + // must not have side effects. + // TODO: we can do this when there *are* other refs to $x, + // with a larger refactoring here. + + // Test for the conditions with a temporary nop instead of the + // br_if. + Expression* old = list[0]; + Nop nop; + // After this assignment, curr is what is left in the block + // after ignoring the br_if. + list[0] = &nop; + auto canReorder = EffectAnalyzer::canReorder( + passOptions, features, br->condition, curr); + auto hasSideEffects = + EffectAnalyzer(passOptions, features, curr) + .hasSideEffects(); + list[0] = old; + if (canReorder && !hasSideEffects) { + ExpressionManipulator::nop(list[0]); + replaceCurrent( + builder.makeSelect(br->condition, br->value, curr)); + } } } } |