diff options
author | Alon Zakai <azakai@google.com> | 2022-09-01 10:34:19 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-01 10:34:19 -0700 |
commit | f058bb53b3d8977f800894d305b7f537b9aff3d5 (patch) | |
tree | 2da33a1d13c9d8c0b63c8fdbfb2f00993d0740e4 /src/passes/OptimizeInstructions.cpp | |
parent | 7c5f6c2aca80d70cf05fc83739f87ff23c75a1e3 (diff) | |
download | binaryen-f058bb53b3d8977f800894d305b7f537b9aff3d5.tar.gz binaryen-f058bb53b3d8977f800894d305b7f537b9aff3d5.tar.bz2 binaryen-f058bb53b3d8977f800894d305b7f537b9aff3d5.zip |
OptimizeInstructions: Select => and/or in more cases (#4154)
x ? 0 : y ==> z & y where z = !x
x ? y : 1 ==> z | y where z = !x
Only do this when we have z = !x, that is, we can invert x without adding
an actual eqz (which would add work).
To do this, canonicalize selects to prefer to flip the arms, when
possible, if it would move a constant to a location that the existing
optimizations already turn into an and/or. That is,
x >= 5 ? 0 : y != 42
would be canonicalized into
x < 5 ? y != 42 : 0
and existing opts turn that into
(x < 5) & (y != 42)
The canonicalization does not always help this optimization, as we need
the values to be boolean to do this, but canonicalizing is still nice to get
more regular code which might compress slightly better.
Diffstat (limited to 'src/passes/OptimizeInstructions.cpp')
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 42d92751f..047ed8551 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2418,6 +2418,26 @@ private: return curr->type == Type::i64 ? builder.makeUnary(ExtendUInt32, c) : c; } } + // Flip the arms if doing so might help later optimizations here. + if (auto* binary = curr->condition->dynCast<Binary>()) { + auto inv = invertBinaryOp(binary->op); + if (inv != InvalidBinary) { + // For invertible binary operations, we prefer to have non-zero values + // in the ifTrue, and zero values in the ifFalse, due to the + // optimization right after us. Even if this does not help there, it is + // a nice canonicalization. (To ensure convergence - that we don't keep + // doing work each time we get here - do nothing if both are zero, or + // if both are nonzero.) + Const* c; + if ((matches(curr->ifTrue, ival(0)) && + !matches(curr->ifFalse, ival(0))) || + (!matches(curr->ifTrue, ival()) && + matches(curr->ifFalse, ival(&c)) && !c->value.isZero())) { + binary->op = inv; + std::swap(curr->ifTrue, curr->ifFalse); + } + } + } if (curr->type == Type::i32 && Bits::getMaxBits(curr->condition, this) <= 1 && Bits::getMaxBits(curr->ifTrue, this) <= 1 && @@ -4168,8 +4188,9 @@ private: } } + // Invert (negate) the opcode, so that it has the exact negative meaning as it + // had before. BinaryOp invertBinaryOp(BinaryOp op) { - // use de-morgan's laws switch (op) { case EqInt32: return NeInt32; @@ -4228,6 +4249,9 @@ private: } } + // Change the opcode so it is correct after reversing the operands. That is, + // we had X OP Y and we need OP' so that this is equivalent to that: + // Y OP' X BinaryOp reverseRelationalOp(BinaryOp op) { switch (op) { case EqInt32: |